Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setup.py, resolve name shadowing #52

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# packaging-related
*.egg-info/

# test-related
.coverage
.cache
Expand Down
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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
- coveralls
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
50 changes: 25 additions & 25 deletions resnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand All @@ -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
Expand All @@ -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,
Expand All @@ -67,42 +67,42 @@ 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],
kernel_size=(1, 1),
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])


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

Expand All @@ -111,21 +111,21 @@ 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
conv1 = Conv2D(filters=filters, kernel_size=(3, 3),
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

Expand All @@ -137,22 +137,22 @@ 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
conv_1_1 = Conv2D(filters=filters, kernel_size=(1, 1),
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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
36 changes: 36 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -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'],
},
)