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

Transposed Convolution Layers from base class #850

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

frapa
Copy link

@frapa frapa commented Jun 17, 2017

I made a bit of mess with the rebase (first time trying it...). I tried to merge the changes meaningfully. I'm uploading it because today I have no more time, then on Monday I'll see...

@frapa frapa mentioned this pull request Jun 17, 2017
@frapa
Copy link
Author

frapa commented Jun 17, 2017

I really do not understand the error message I get from Travis... some import problem. The funny thing is on my pc py.test runs fine.

Copy link
Member

@f0k f0k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, looks good in principle! And sorry for the extra work of rebasing. Apart from the comments I have, the tests will need to be refactored as well to test all three dimensionalities. Check how it was done for the forward convolutional layers, and let me know if you need help. Cheers!

Deconv2DLayer
Deconv3DLayer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good catch, we missed to add it there in #841.

I think I'd prefer the order:

TransposedConv1DLayer
Deconv1DLayer
TransposedConv2DLayer
Deconv2DLayer
TransposedConv3DLayer
Deconv3DLayer

So the alias always comes directly after a documented layer.

docs/conf.py Outdated
@@ -354,6 +354,9 @@ def setup(app):
if not hasattr(theano.tensor.nnet, 'conv3d'):
theano.tensor.nnet.conv3d = Mock()
reload(lasagne.layers.conv)
if not hasattr(theano.tensor.nnet.abstract_conv, 'AbstractConv_gradInputs'):
theano.tensor.nnet.abstract_conv.AbstractConv1d_gradInputs = Mock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you meant AbstractConv_gradInputs, without 1d.


.. autoclass:: Deconv2DLayer

.. autoclass:: Deconv3DLayer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, thank you for catching this, but I'd prefer to have each alias come directly after the actual class.

"Deconv2DLayer",
"Deconv3DLayer",
"DilatedConv2DLayer",
"TransposedConv3DLayer",
"Deconv3DLayer",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 3Ds are duplicated now. I'm fine with placing them before the dilated layers, but again I'd prefer the aliases directly after the respective classes.

conved = op(self.W, input, output_size)
return conved


class TransposedConv1DLayer(BaseTransposedConvLayer): # pragma: no cover
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Detail: We usually (hopefully always) have two spaces before line comments.

the input (by adding ``stride - 1`` zeros between adjacent input elements),
padding it with ``filter_size - 1 - crop`` zeros, and cross-correlating it
with the filters. See [1]_ for more background.
See `BaseTransposedConvLayer` for more.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha, I guess we will have to copy this note to all subclasses, as we cannot refer to the base class.


Examples
--------
To transpose an existing convolution, with tied filter weights:

>>> from lasagne.layers import Conv3DLayer, TransposedConv3DLayer
>>> conv = Conv3DLayer((None, 1, 32, 32, 32), 16, 3, stride=2, pad=2)
>>> conv = Conv3DLayer((None, 1, 32, 64, 64), 16, 3, stride=2, pad=2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the previous one was okay.

.. [1] Vincent Dumoulin, Francesco Visin (2016):
A guide to convolution arithmetic for deep learning. arXiv.
http://arxiv.org/abs/1603.07285,
https://github.com/vdumoulin/conv_arithmetic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I think we'll have to replicate this reference in all three subclasses as well.

def __init__(self, incoming, num_filters, filter_size,
stride=(1, 1, 1), crop=0, untie_biases=False,
def __init__(self, incoming, num_filters, filter_size, stride=1,
crop=0, untie_biases=False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer (1, 1, 1) just for consistency with the forward convolutional layers.

# Hide TransposedConv2DLayer for old Theano versions
del TransposedConv2DLayer, Deconv2DLayer
__all__.remove('TransposedConv2DLayer')
__all__.remove('Deconv2DLayer')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 2D version is always available in the Theano versions we support. I'd prefer to only add the check for 1D, directly after the 1D definitions.

@f0k
Copy link
Member

f0k commented Jun 21, 2017

I really do not understand the error message I get from Travis... some import problem.

The exact error is AttributeError: 'module' object has no attribute 'TransposedConv3DLayer', thrown on from .conv import *. A * import will import all names defined in __all__. I can't spot the mistake either; it should be removed from __all__ just like before your PR.

The funny thing is on my pc py.test runs fine.

Even with Theano 0.8?

@frapa
Copy link
Author

frapa commented Jul 4, 2017

No, maybe theano 0.8 is the problem then. I tried with the bleeding-edge version.

I fixed all the things you mentioned (thanks a lot for the in-depth review). I'm sorry for the many mistakes but it's my first time rebasing and I made a mess...

@frapa
Copy link
Author

frapa commented Jul 4, 2017

Ok, I tried adding test by copying and modifying the tests for the other cases. I am not at all sure how this whole unit-test thing works (I understand that it should check the output shape, but the code is a bit weird and right now I do not have time to delve into it.)

Copy link
Member

@f0k f0k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates, and sorry for the thesis-based delay. Looks much better, there are still a few inconsistencies in the docstrings from copying things around, and the tests need to skip the TransposedConv1DLayer if it's not available. Otherwise the tests look good. It might be possible to refactor them as well to minimize code duplication, but we can leave that for another time (it's actually good to first check that the old tests still run through with the new code).

@pytest.mark.parametrize(
"input, kernel, output, kwargs", list(transp_conv1d_test_sets()))
def test_defaults(self, DummyInputLayer, input, kernel, output, kwargs):
from lasagne.layers import TransposedConv1DLayer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to wrap this in a try..except:

try:
    from lasagne.layers import TransposedConv1DLayer
except ImportError:
    pytest.skip("TransposedConv1DLayer not available")

Same for the second test below. This ensures it will run through on Travis / on Theano 0.8.x.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

The dimensionality of the transposed convolution (i.e., the number of
spatial dimensions of each feature map and each convolutional filter).
If ``None``, will be inferred from the input shape.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The untie_biases docstring still needs to be adapted (https://github.com/Lasagne/Lasagne/pull/850/files#r123304501). Unfortunately I cannot comment on the correct line for that because it's not part of the diff.

W=lasagne.init.GlorotUniform(), b=lasagne.init.Constant(0.),
nonlinearity=lasagne.nonlinearities.rectify, flip_filters=False, **kwargs)
nonlinearity=lasagne.nonlinearities.rectify, flip_filters=False,
n=None, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output_size is mysteriously missing here (not your fault, but would be good to fix while we're at it, should come before n)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was also missing from the three subclasses. Fixed.

incoming : a :class:`Layer` instance or a tuple
The layer feeding into this layer, or the expected input shape. The
output of this layer should be a 4D tensor, with shape
``(batch_size, num_input_channels, input_rows, input_columns)``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be 3D, without input_columns

An integer or 1-element tuple specifying the size of the filters.

stride : int
An integeror 1-element tuple specifying the stride of the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

integeror -> integer or

W : Theano shared variable, expression, numpy array or callable
Initial value, expression or initializer for the weights.
These should be a 4D tensor with shape
``(num_input_channels, num_filters, filter_rows, filter_columns)``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct now. I wonder whether my previous comment (https://github.com/Lasagne/Lasagne/pull/850/files#r123304988) was originally attached to the 1D version and github messed up, or whether I accidentally thought it was about the 1D version.

``None``, the layer will have no biases. Otherwise, biases should be
a 1D array with shape ``(num_filters,)`` if `untied_biases` is set to
``False``. If it is set to ``True``, its shape should be
``(num_filters, output_rows, output_columns)`` instead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

``(batch_size, num_input_channels, input_depth, input_rows,
input_columns)``.
output of this layer should be a 4D tensor, with shape
``(batch_size, num_input_channels, input_rows, input_columns)``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -1006,7 +1325,8 @@ class TransposedConv3DLayer(BaseConvLayer): # pragma: no cover
W : Theano shared variable, expression, numpy array or callable
Initial value, expression or initializer for the weights.
These should be a 5D tensor with shape
``(num_input_channels, num_filters, filter_rows, filter_columns)``.
``(num_input_channels, num_filters, input_depth, input_rows,
- input_columns)``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you left an initial - in there.

def __init__(self, incoming, num_filters, filter_size,
stride=(1, 1, 1), crop=0, untie_biases=False,
def __init__(self, incoming, num_filters, filter_size, stride=(1, 1, 1),
crop=0, untie_biases=False,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary change, better to avoid, but not a big deal.

@f0k f0k mentioned this pull request Feb 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants