From 2be753f3e26431ed7acc216e6db7e3510779ba8e Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 9 Jan 2018 13:20:01 -0500 Subject: [PATCH 1/5] resnet.py: rename `input` to `input_data` `input` shadows a built-in name, which can cause bugs, confusion, and unhappy IDEs. --- resnet.py | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/resnet.py b/resnet.py index 8c3f9ed..bf2630a 100644 --- a/resnet.py +++ b/resnet.py @@ -19,10 +19,10 @@ from keras import backend as K -def _bn_relu(input): +def _bn_relu(input_data): """Helper to build a BN -> relu block """ - norm = BatchNormalization(axis=CHANNEL_AXIS)(input) + norm = BatchNormalization(axis=CHANNEL_AXIS)(input_data) return Activation("relu")(norm) @@ -36,11 +36,11 @@ def _conv_bn_relu(**conv_params): padding = conv_params.setdefault("padding", "same") kernel_regularizer = conv_params.setdefault("kernel_regularizer", l2(1.e-4)) - def f(input): + def f(input_data): conv = Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, kernel_initializer=kernel_initializer, - kernel_regularizer=kernel_regularizer)(input) + kernel_regularizer=kernel_regularizer)(input_data) return _bn_relu(conv) return f @@ -57,8 +57,8 @@ def _bn_relu_conv(**conv_params): padding = conv_params.setdefault("padding", "same") kernel_regularizer = conv_params.setdefault("kernel_regularizer", l2(1.e-4)) - def f(input): - activation = _bn_relu(input) + def f(input_data): + activation = _bn_relu(input_data) return Conv2D(filters=filters, kernel_size=kernel_size, strides=strides, padding=padding, kernel_initializer=kernel_initializer, @@ -67,19 +67,19 @@ def f(input): return f -def _shortcut(input, residual): +def _shortcut(input_data, residual): """Adds a shortcut between input and residual block and merges them with "sum" """ # Expand channels of shortcut to match residual. # Stride appropriately to match residual (width, height) # Should be int if network architecture is correctly configured. - input_shape = K.int_shape(input) + input_shape = K.int_shape(input_data) residual_shape = K.int_shape(residual) stride_width = int(round(input_shape[ROW_AXIS] / residual_shape[ROW_AXIS])) stride_height = int(round(input_shape[COL_AXIS] / residual_shape[COL_AXIS])) equal_channels = input_shape[CHANNEL_AXIS] == residual_shape[CHANNEL_AXIS] - shortcut = input + shortcut = input_data # 1 X 1 conv if shape is different. Else identity. if stride_width > 1 or stride_height > 1 or not equal_channels: shortcut = Conv2D(filters=residual_shape[CHANNEL_AXIS], @@ -87,7 +87,7 @@ def _shortcut(input, residual): strides=(stride_width, stride_height), padding="valid", kernel_initializer="he_normal", - kernel_regularizer=l2(0.0001))(input) + kernel_regularizer=l2(0.0001))(input_data) return add([shortcut, residual]) @@ -95,14 +95,14 @@ def _shortcut(input, residual): def _residual_block(block_function, filters, repetitions, is_first_layer=False): """Builds a residual block with repeating bottleneck blocks. """ - def f(input): + def f(input_data): for i in range(repetitions): init_strides = (1, 1) if i == 0 and not is_first_layer: init_strides = (2, 2) - input = block_function(filters=filters, init_strides=init_strides, - is_first_block_of_first_layer=(is_first_layer and i == 0))(input) - return input + input_data = block_function(filters=filters, init_strides=init_strides, + is_first_block_of_first_layer=(is_first_layer and i == 0))(input_data) + return input_data return f @@ -111,7 +111,7 @@ def basic_block(filters, init_strides=(1, 1), is_first_block_of_first_layer=Fals """Basic 3 X 3 convolution blocks for use on resnets with layers <= 34. Follows improved proposed scheme in http://arxiv.org/pdf/1603.05027v2.pdf """ - def f(input): + def f(input_data): if is_first_block_of_first_layer: # don't repeat bn->relu since we just did bn->relu->maxpool @@ -119,13 +119,13 @@ def f(input): strides=init_strides, padding="same", kernel_initializer="he_normal", - kernel_regularizer=l2(1e-4))(input) + kernel_regularizer=l2(1e-4))(input_data) else: conv1 = _bn_relu_conv(filters=filters, kernel_size=(3, 3), - strides=init_strides)(input) + strides=init_strides)(input_data) residual = _bn_relu_conv(filters=filters, kernel_size=(3, 3))(conv1) - return _shortcut(input, residual) + return _shortcut(input_data, residual) return f @@ -137,7 +137,7 @@ def bottleneck(filters, init_strides=(1, 1), is_first_block_of_first_layer=False Returns: A final conv layer of filters * 4 """ - def f(input): + def f(input_data): if is_first_block_of_first_layer: # don't repeat bn->relu since we just did bn->relu->maxpool @@ -145,14 +145,14 @@ def f(input): strides=init_strides, padding="same", kernel_initializer="he_normal", - kernel_regularizer=l2(1e-4))(input) + kernel_regularizer=l2(1e-4))(input_data) else: conv_1_1 = _bn_relu_conv(filters=filters, kernel_size=(1, 1), - strides=init_strides)(input) + strides=init_strides)(input_data) conv_3_3 = _bn_relu_conv(filters=filters, kernel_size=(3, 3))(conv_1_1) residual = _bn_relu_conv(filters=filters * 4, kernel_size=(1, 1))(conv_3_3) - return _shortcut(input, residual) + return _shortcut(input_data, residual) return f @@ -207,8 +207,8 @@ def build(input_shape, num_outputs, block_fn, repetitions): # Load function from str if needed. block_fn = _get_block(block_fn) - input = Input(shape=input_shape) - conv1 = _conv_bn_relu(filters=64, kernel_size=(7, 7), strides=(2, 2))(input) + input_data = Input(shape=input_shape) + conv1 = _conv_bn_relu(filters=64, kernel_size=(7, 7), strides=(2, 2))(input_data) pool1 = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding="same")(conv1) block = pool1 @@ -228,7 +228,7 @@ def build(input_shape, num_outputs, block_fn, repetitions): dense = Dense(units=num_outputs, kernel_initializer="he_normal", activation="softmax")(flatten1) - model = Model(inputs=input, outputs=dense) + model = Model(inputs=input_data, outputs=dense) return model @staticmethod From f637474f7909e9c37eb8883513a36c31ca2eb76f Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 9 Jan 2018 13:21:33 -0500 Subject: [PATCH 2/5] setup.py: Add setup script This allows resnet.py to be used as a library in a much more accessible fashion. --- setup.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 setup.py diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..1f15c9b --- /dev/null +++ b/setup.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +import os + +from setuptools import setup + +with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md')) as f: + long_description = f.read() + +setup( + name='keras-resnet', + version='0.0.1', + description='Residual networks implementation using Keras-1.0 functional API', + long_description=long_description, + url='https://github.com/raghakot/keras-resnet', + author='raghakot', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + + # Specify the Python versions you support here. In particular, ensure + # that you indicate whether you support Python 2, Python 3 or both. + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + ], + keywords='keras resnet residual-networks deep-learning cnn', + py_modules=['resnet'], + install_requires=['keras>=1.0', 'six'], + extras_require={ + 'test': ['pytest'], + }, +) From 045b6374501a378c6c0784e4e1f00b1d03be6da3 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 9 Jan 2018 13:31:48 -0500 Subject: [PATCH 3/5] .gitignore: Ignore *egg-info/ (packaging directory) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 5ef699f..9d26e32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# packaging-related +*.egg-info/ + # test-related .coverage .cache From 416da3e992511c87886c0072246ffb58d3958675 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 9 Jan 2018 13:40:43 -0500 Subject: [PATCH 4/5] README.md: Installation instructions --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5290829..b534635 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,28 @@ There are two key aspects to note here 2. At the end of the first skip connection of a block, there is a disconnect in num_filters, width and height at the merge layer. This is addressed in [`_shortcut`](https://github.com/raghakot/keras-resnet/blob/master/resnet.py#L41) by using `conv 1X1` with an appropriate stride. For remaining cases, input is directly merged with residual block as identity. -### ResNetBuilder factory +### Usage + +Install your backend of choice ([theano](http://deeplearning.net/software/theano/install.html)/ +[tensorflow](https://www.tensorflow.org/install/)), and then + +```bash +pip install git+git://github.com/raghakot/keras-resnet.git +``` + +From python: + +```python +from resnet import ResnetBuilder + +INPUT_SHAPE = (32, 32) +OUTPUT_CLASSES = 10 + +model = ResnetBuilder.build_resnet_18(INPUT_SHAPE, OUTPUT_CLASSES) + +# train and use model as appropriate +``` + - Use ResNetBuilder [build](https://github.com/raghakot/keras-resnet/blob/master/resnet.py#L135-L153) methods to build standard ResNet architectures with your own input shape. It will auto calculate paddings and final pooling layer filters for you. - Use the generic [build](https://github.com/raghakot/keras-resnet/blob/master/resnet.py#L99) method to setup your own architecture. From e0bd8a86da378bac40d11739cde55ec55f8dddb9 Mon Sep 17 00:00:00 2001 From: Chris Barnes Date: Tue, 9 Jan 2018 13:54:41 -0500 Subject: [PATCH 5/5] .travis.yml: Manually install mkl The py2 build using the theano backend breaks unless mkl is installed manually and an environment variable set. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a647190..9d95b87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ matrix: include: - python: 2.7 env: KERAS_BACKEND=theano + env: MKL_THREADING_LAYER=GNU - python: 2.7 env: KERAS_BACKEND=tensorflow - python: 3.4 @@ -31,6 +32,8 @@ install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib pandas pytest h5py - source activate test-environment + # Prevent py2 theano build getting upset + - conda install -q -y mkl-service - pip install git+git://github.com/Theano/Theano.git - pip install keras @@ -52,4 +55,4 @@ script: - echo -e "Running tests with the following config:\n$(cat ~/.keras/keras.json)" PYTHONPATH=../$PWD:$PYTHONPATH py.test tests/; after_success: - - coveralls \ No newline at end of file + - coveralls