Skip to content

Instantly share code, notes, and snippets.

@craffel
Created November 19, 2015 00:22
Show Gist options
  • Save craffel/15efe84504fd466357c2 to your computer and use it in GitHub Desktop.
Save craffel/15efe84504fd466357c2 to your computer and use it in GitHub Desktop.
Faster 'same' mode convolutions in Lasagne, for even filter sizes too!
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using gpu device 1: GeForce GTX 780 Ti (CNMeM is disabled)\n"
]
}
],
"source": [
"import lasagne\n",
"import theano\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1 loops, best of 3: 198 ms per loop\n",
"10 loops, best of 3: 133 ms per loop\n",
"10 loops, best of 3: 82.3 ms per loop\n",
"True\n",
"True\n"
]
}
],
"source": [
"filter_size = (5, 12)\n",
"input_shape = (100, 3, 30, 40)\n",
"n_filters = 16\n",
"dummy_input = np.random.standard_normal(input_shape).astype(theano.config.floatX)\n",
"\n",
"# To produce the same results for each network\n",
"lasagne.random.get_rng().seed(1234)\n",
"l_in = lasagne.layers.InputLayer(input_shape)\n",
"# Use 'full' convolution (relying on Theano to do the padding)\n",
"l_conv = lasagne.layers.Conv2DLayer(l_in, n_filters, filter_size, pad='full')\n",
"# Crop out the 'same' portion of the convolution using SliceLayers\n",
"shift_x = (filter_size[0] - 1) // 2\n",
"shift_y = (filter_size[1] - 1) // 2\n",
"l_conv = lasagne.layers.SliceLayer(l_conv, slice(shift_x, input_shape[2] + shift_x), 2)\n",
"l_conv = lasagne.layers.SliceLayer(l_conv, slice(shift_y, input_shape[3] + shift_y), 3)\n",
"output_a = lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input})\n",
"%timeit lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input})\n",
"\n",
"lasagne.random.get_rng().seed(1234)\n",
"l_in = lasagne.layers.InputLayer(input_shape)\n",
"# This mode will no longer work for even filter sizes\n",
"l_conv = lasagne.layers.Conv2DLayer(l_in, n_filters, filter_size, pad='same')\n",
"output_b = lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input})\n",
"%timeit lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input})\n",
"\n",
"lasagne.random.get_rng().seed(1234)\n",
"l_in = lasagne.layers.InputLayer(input_shape)\n",
"# Not specifying a pad argument corresponds to pad=0, or 'valid' mode\n",
"l_conv = lasagne.layers.Conv2DLayer(l_in, n_filters, filter_size)\n",
"# Pre-pad the input with zeros, which will be cropped by the convolution operation\n",
"shift_x = int(np.ceil((filter_size[0] - 1) / 2.))\n",
"shift_y = int(np.ceil((filter_size[1] - 1) / 2.))\n",
"dummy_input_padded = np.zeros((input_shape[0], input_shape[1],\n",
" input_shape[2] + filter_size[0] - 1,\n",
" input_shape[3] + filter_size[1] - 1),\n",
" dtype=theano.config.floatX)\n",
"dummy_input_padded[:, :, shift_x:input_shape[2] + shift_x,\n",
" shift_y:input_shape[3] + shift_y] = dummy_input\n",
"output_c = lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input_padded})\n",
"%timeit lasagne.layers.get_output(l_conv).eval({l_in.input_var: dummy_input_padded})\n",
"\n",
"print np.allclose(output_a, output_b)\n",
"print np.allclose(output_b, output_c)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment