Skip to content
This repository has been archived by the owner on Jul 10, 2021. It is now read-only.

Commit

Permalink
Calculating the output space of the convolution kernel to initialise …
Browse files Browse the repository at this point in the history
…the unit_counts as correctly as possible.

Closes #30.
  • Loading branch information
alexjc committed May 4, 2015
1 parent 47fddcc commit b15407f
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 21 deletions.
44 changes: 23 additions & 21 deletions sknn/mlp.py
Expand Up @@ -180,7 +180,7 @@ def __init__(
channels=None,
pieces=None,
kernel_shape=None,
pool_shape=None,
pool_shape=(1,1),
pool_type=None,
dropout=None):

Expand Down Expand Up @@ -422,14 +422,14 @@ def _create_convolution_layer(self, name, layer, irange):
self._check_layer(layer, ['channels', 'kernel_shape'],
['pool_shape', 'pool_type'])

if layer.type == "Rectifier":
if layer.type == 'Rectifier':
nl = mlp.RectifierConvNonlinearity(0.0)
elif layer.type == "Sigmoid":
elif layer.type == 'Sigmoid':
nl = mlp.SigmoidConvNonlinearity()
elif layer.type == "Tanh":
elif layer.type == 'Tanh':
nl = mlp.TanhConvNonlinearity()
else:
assert layer.type == "Linear",\
assert layer.type == 'Linear',\
"Convolution layer type `%s` is not supported." % layer.type
nl = mlp.IdentityConvNonlinearity()

Expand All @@ -438,7 +438,7 @@ def _create_convolution_layer(self, name, layer, irange):
nonlinearity=nl,
output_channels=layer.channels,
kernel_shape=layer.kernel_shape,
pool_shape=layer.pool_shape or (1,1),
pool_shape=layer.pool_shape,
pool_type=layer.pool_type or 'max',
pool_stride=(1,1),
irange=irange)
Expand All @@ -447,43 +447,43 @@ def _create_layer(self, name, layer, irange):
if isinstance(layer, Convolution):
return self._create_convolution_layer(name, layer, irange)

if layer.type == "Rectifier":
if layer.type == 'Rectifier':
self._check_layer(layer, ['units'])
return mlp.RectifiedLinear(
layer_name=name,
dim=layer.units,
irange=irange)

if layer.type == "Sigmoid":
if layer.type == 'Sigmoid':
self._check_layer(layer, ['units'])
return mlp.Sigmoid(
layer_name=name,
dim=layer.units,
irange=irange)

if layer.type == "Tanh":
if layer.type == 'Tanh':
self._check_layer(layer, ['units'])
return mlp.Tanh(
layer_name=name,
dim=layer.units,
irange=irange)

if layer.type == "Maxout":
if layer.type == 'Maxout':
self._check_layer(layer, ['units', 'pieces'])
return maxout.Maxout(
layer_name=name,
num_units=layer.units,
num_pieces=layer.pieces,
irange=irange)

if layer.type == "Linear":
if layer.type == 'Linear':
self._check_layer(layer, ['units'])
return mlp.Linear(
layer_name=layer.name,
dim=layer.units,
irange=irange)

if layer.type == "Gaussian":
if layer.type == 'Gaussian':
self._check_layer(layer, ['units'])
return mlp.LinearGaussian(
layer_name=layer.name,
Expand All @@ -494,7 +494,7 @@ def _create_layer(self, name, layer, irange):
dim=layer.units,
irange=irange)

if layer.type == "Softmax":
if layer.type == 'Softmax':
self._check_layer(layer, ['units'])
return mlp.Softmax(
layer_name=layer.name,
Expand All @@ -509,12 +509,12 @@ def _create_mlp(self):
fan_out = self.unit_counts[i + 1]

lim = numpy.sqrt(6) / numpy.sqrt(fan_in + fan_out)
if layer.type == "Tanh":
if layer.type == 'Tanh':
lim *= 1.1 * lim
elif layer.type in ("Rectifier", "Maxout", "Convolution"):
elif layer.type in ('Rectifier', 'Maxout'):
# He, Rang, Zhen and Sun, converted to uniform.
lim *= numpy.sqrt(2)
elif layer.type == "Sigmoid":
elif layer.type == 'Sigmoid':
lim *= 4

mlp_layer = self._create_layer(layer.name, layer, irange=lim)
Expand Down Expand Up @@ -557,13 +557,15 @@ def _create_specs(self, X, y=None):
"Initializing neural network with %i layers, %i inputs and %i outputs.",
len(self.layers), X.shape[1], self.layers[-1].units)

self.unit_counts = [X.shape[1]]
self.unit_counts = [numpy.product(X.shape[1:]) if self.is_convolution else X.shape[1]]
resolution = X.shape[1:3] if self.is_convolution else None
for layer in self.layers:
if layer.units is not None:
self.unit_counts.append(layer.units)
if isinstance(layer, Convolution):
resolution = ((resolution[0] - layer.kernel_shape[0] + 1) / layer.pool_shape[0],
(resolution[1] - layer.kernel_shape[1] + 1) / layer.pool_shape[1])
self.unit_counts.append(numpy.prod(resolution) * layer.channels)
else:
# TODO: Compute correct number of outputs for convolution.
self.unit_counts.append(layer.channels)
self.unit_counts.append(layer.units)

log.debug(" - Type: {}{: <10}{} Units: {}{: <4}{}".format(
ansi.BOLD, layer.type, ansi.ENDC, ansi.BOLD, layer.units or "N/A", ansi.ENDC))
Expand Down
40 changes: 40 additions & 0 deletions sknn/tests/test_conv.py
Expand Up @@ -84,6 +84,46 @@ def test_PoolingMeanType(self):
n_iter=1))


class TestConvolutionSpecs(unittest.TestCase):

def test_SmallSquareKernel(self):
nn = MLPR(layers=[
C("Rectifier", channels=4, kernel_shape=(3,3)),
L("Linear", units=5)])

a_in = numpy.zeros((8,32,32,1))
nn._create_specs(a_in)
assert_equal(nn.unit_counts, [1024, 30 * 30 * 4, 5])

def test_HorizontalKernel(self):
nn = MLPR(layers=[
C("Rectifier", channels=7, kernel_shape=(16,1)),
L("Linear", units=5)])

a_in = numpy.zeros((8,16,16,1))
nn._create_specs(a_in)
assert_equal(nn.unit_counts, [256, 16 * 7, 5])

def test_VerticalKernel(self):
nn = MLPR(layers=[
C("Rectifier", channels=4, kernel_shape=(1,16)),
L("Linear", units=7)])

a_in = numpy.zeros((8,16,16,1))
nn._create_specs(a_in)
assert_equal(nn.unit_counts, [256, 16 * 4, 7])

def test_SquareKernelPool(self):
# TODO: After creation the outputs don't seem to correspond; pooling enabled?
nn = MLPR(layers=[
C("Rectifier", channels=4, kernel_shape=(3,3), pool_shape=(2,2)),
L("Linear", units=5)])

a_in = numpy.zeros((8,32,32,1))
nn._create_specs(a_in)
assert_equal(nn.unit_counts, [1024, 15 * 15 * 4, 5])


class TestActivationTypes(unittest.TestCase):

def _run(self, activation):
Expand Down

0 comments on commit b15407f

Please sign in to comment.