From f996579d4c4321e2af81c848b8aa127530eaec1d Mon Sep 17 00:00:00 2001 From: Michael Oliver Date: Wed, 22 Feb 2017 13:06:52 -0800 Subject: [PATCH] initial commit --- keras_contrib/layers/convolutional.py | 222 ++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/keras_contrib/layers/convolutional.py b/keras_contrib/layers/convolutional.py index faf1d03..2bf4659 100644 --- a/keras_contrib/layers/convolutional.py +++ b/keras_contrib/layers/convolutional.py @@ -13,6 +13,7 @@ from keras.layers.convolutional import Convolution3D from keras.utils.generic_utils import get_custom_objects from keras.utils.np_utils import conv_output_length from keras.utils.np_utils import conv_input_length +import numpy as np class Deconvolution3D(Convolution3D): @@ -227,3 +228,224 @@ class Deconvolution3D(Convolution3D): Deconv3D = Deconvolution3D get_custom_objects().update({"Deconvolution3D": Deconvolution3D}) get_custom_objects().update({"Deconv3D": Deconv3D}) + + +class CosineConvolution2D(Layer): + """Convolution operator for filtering windows of two-dimensional inputs. + + When using this layer as the first layer in a model, + provide the keyword argument `input_shape` + (tuple of integers, does not include the sample axis), + e.g. `input_shape=(3, 128, 128)` for 128x128 RGB pictures. + + # Examples + + ```python + # apply a 3x3 convolution with 64 output filters on a 256x256 image: + model = Sequential() + model.add(Convolution2D(64, 3, 3, + border_mode='same', + input_shape=(3, 256, 256))) + # now model.output_shape == (None, 64, 256, 256) + + # add a 3x3 convolution on top, with 32 output filters: + model.add(Convolution2D(32, 3, 3, border_mode='same')) + # now model.output_shape == (None, 32, 256, 256) + ``` + + # Arguments + nb_filter: Number of convolution filters to use. + nb_row: Number of rows in the convolution kernel. + nb_col: Number of columns in the convolution kernel. + init: name of initialization function for the weights of the layer + (see [initializations](../initializations.md)), or alternatively, + Theano function to use for weights initialization. + This parameter is only relevant if you don't pass + a `weights` argument. + activation: name of activation function to use + (see [activations](../activations.md)), + or alternatively, elementwise Theano function. + If you don't specify anything, no activation is applied + (ie. "linear" activation: a(x) = x). + weights: list of numpy arrays to set as initial weights. + border_mode: 'valid', 'same' or 'full' + ('full' requires the Theano backend). + subsample: tuple of length 2. Factor by which to subsample output. + Also called strides elsewhere. + W_regularizer: instance of [WeightRegularizer](../regularizers.md) + (eg. L1 or L2 regularization), applied to the main weights matrix. + b_regularizer: instance of [WeightRegularizer](../regularizers.md), + applied to the bias. + activity_regularizer: instance of [ActivityRegularizer](../regularizers.md), + applied to the network output. + W_constraint: instance of the [constraints](../constraints.md) module + (eg. maxnorm, nonneg), applied to the main weights matrix. + b_constraint: instance of the [constraints](../constraints.md) module, + applied to the bias. + dim_ordering: 'th' or 'tf'. In 'th' mode, the channels dimension + (the depth) is at index 1, in 'tf' mode is it at index 3. + It defaults to the `image_dim_ordering` value found in your + Keras config file at `~/.keras/keras.json`. + If you never set it, then it will be "tf". + bias: whether to include a bias + (i.e. make the layer affine rather than linear). + + # Input shape + 4D tensor with shape: + `(samples, channels, rows, cols)` if dim_ordering='th' + or 4D tensor with shape: + `(samples, rows, cols, channels)` if dim_ordering='tf'. + + # Output shape + 4D tensor with shape: + `(samples, nb_filter, new_rows, new_cols)` if dim_ordering='th' + or 4D tensor with shape: + `(samples, new_rows, new_cols, nb_filter)` if dim_ordering='tf'. + `rows` and `cols` values might have changed due to padding. + """ + + def __init__(self, nb_filter, nb_row, nb_col, + init='glorot_uniform', activation=None, weights=None, + border_mode='valid', subsample=(1, 1), dim_ordering='default', + W_regularizer=None, b_regularizer=None, + activity_regularizer=None, + W_constraint=None, b_constraint=None, + bias=True, **kwargs): + if dim_ordering == 'default': + dim_ordering = K.image_dim_ordering() + if border_mode not in {'valid', 'same', 'full'}: + raise ValueError('Invalid border mode for Convolution2D:', border_mode) + self.nb_filter = nb_filter + self.nb_row = nb_row + self.nb_col = nb_col + self.init = initializations.get(init) + self.activation = activations.get(activation) + self.border_mode = border_mode + self.subsample = tuple(subsample) + if dim_ordering not in {'tf', 'th'}: + raise ValueError('dim_ordering must be in {tf, th}.') + self.dim_ordering = dim_ordering + + self.W_regularizer = regularizers.get(W_regularizer) + self.b_regularizer = regularizers.get(b_regularizer) + self.activity_regularizer = regularizers.get(activity_regularizer) + + self.W_constraint = constraints.get(W_constraint) + self.b_constraint = constraints.get(b_constraint) + + self.bias = bias + self.input_spec = [InputSpec(ndim=4)] + self.initial_weights = weights + super(CosineConvolution2D, self).__init__(**kwargs) + + def build(self, input_shape): + if self.dim_ordering == 'th': + stack_size = input_shape[1] + self.W_shape = (self.nb_filter, stack_size, self.nb_row, self.nb_col) + self.W_norm_shape = (1, stack_size, self.nb_row, self.nb_col) + elif self.dim_ordering == 'tf': + stack_size = input_shape[3] + self.W_shape = (self.nb_row, self.nb_col, stack_size, self.nb_filter) + self.W_norm_shape = (self.nb_row, self.nb_col, stack_size, 1) + else: + raise ValueError('Invalid dim_ordering:', self.dim_ordering) + self.W = self.add_weight(self.W_shape, + initializer=functools.partial(self.init, + dim_ordering=self.dim_ordering), + name='{}_W'.format(self.name), + regularizer=self.W_regularizer, + constraint=self.W_constraint) + + self.W_norm = K.variable(np.ones(self.W_norm_shape), name='{}_W_norm'.format(self.name)) + + if self.bias: + self.b = self.add_weight((self.nb_filter,), + initializer='zero', + name='{}_b'.format(self.name), + regularizer=self.b_regularizer, + constraint=self.b_constraint) + else: + self.b = None + + if self.initial_weights is not None: + self.set_weights(self.initial_weights) + del self.initial_weights + self.built = True + + def get_output_shape_for(self, input_shape): + if self.dim_ordering == 'th': + rows = input_shape[2] + cols = input_shape[3] + elif self.dim_ordering == 'tf': + rows = input_shape[1] + cols = input_shape[2] + else: + raise ValueError('Invalid dim_ordering:', self.dim_ordering) + + rows = conv_output_length(rows, self.nb_row, + self.border_mode, self.subsample[0]) + cols = conv_output_length(cols, self.nb_col, + self.border_mode, self.subsample[1]) + + if self.dim_ordering == 'th': + return (input_shape[0], self.nb_filter, rows, cols) + elif self.dim_ordering == 'tf': + return (input_shape[0], rows, cols, self.nb_filter) + + def call(self, x, mask=None): + if self.dim_ordering == 'th': + W_sum_axes = [1, 2, 3] + b = K.reshape(self.b, (self.nb_filter, 1, 1, 1)) + elif self.dim_ordering == 'tf': + W_sum_axes = [0, 1, 2] + b = K.reshape(self.b, (1, 1, 1, self.nb_filter)) + + Wnorm = K.sqrt(K.sum(K.square(self.W), axis=W_sum_axes, keepdims=True) + K.square(b) + K.epsilon()) + W = self.W / Wnorm + + output = K.conv2d(x, W, strides=self.subsample, + border_mode=self.border_mode, + dim_ordering=self.dim_ordering, + filter_shape=self.W_shape) + + xnorm = K.sqrt(K.conv2d(x**2, self.W_norm, strides=self.subsample, + border_mode=self.border_mode, + dim_ordering=self.dim_ordering, + filter_shape=self.W_shape) + K.epsilon()) + + if K.backend() == 'theano': + xnorm = K.pattern_broadcast(xnorm, [False, True, False, False]) + + output /= xnorm + + if self.bias: + if self.dim_ordering == 'th': + output += K.reshape(self.b, (1, self.nb_filter, 1, 1)) + elif self.dim_ordering == 'tf': + output += K.reshape(self.b, (1, 1, 1, self.nb_filter)) + else: + raise ValueError('Invalid dim_ordering:', self.dim_ordering) + output = self.activation(output) + return output + + def get_config(self): + config = {'nb_filter': self.nb_filter, + 'nb_row': self.nb_row, + 'nb_col': self.nb_col, + 'init': self.init.__name__, + 'activation': self.activation.__name__, + 'border_mode': self.border_mode, + 'subsample': self.subsample, + 'dim_ordering': self.dim_ordering, + 'W_regularizer': self.W_regularizer.get_config() if self.W_regularizer else None, + 'b_regularizer': self.b_regularizer.get_config() if self.b_regularizer else None, + 'activity_regularizer': self.activity_regularizer.get_config() if self.activity_regularizer else None, + 'W_constraint': self.W_constraint.get_config() if self.W_constraint else None, + 'b_constraint': self.b_constraint.get_config() if self.b_constraint else None, + 'bias': self.bias} + base_config = super(CosineConvolution2D, self).get_config() + return dict(list(base_config.items()) + list(config.items())) + +CosineConv2D = CosineConvolution2D +get_custom_objects().update({"CosineConvolution2D": CosineConvolution2D}) +get_custom_objects().update({"CosineConv2D": CosineConv2D}) \ No newline at end of file