-
Notifications
You must be signed in to change notification settings - Fork 951
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
New Layer base class #678
Comments
Thank you for advancing this issue; I'm especially looking forward to multiple output layers. If I correctly understood the issue with backwards compatibility of A temporary work-around might be something like import warnings
class LayerBackwardCompatMeta(type):
def __instancecheck__(cls, instance):
if type(instance).__name__ == 'BaseLayer':
warnings.warn('isinstance(instance, Layer) is deprecated, use isinstance(instance, BaseLayer) instead')
return type(instance).__name__ in ['BaseLayer', 'Layer']
#def __subclasscheck__(cls, subclass):
# ...
class BaseLayer(object):
pass
class Layer(BaseLayer):
__metaclass__ = LayerBackwardCompatMeta
print('isinstance(Layer(), Layer): {}'.format(isinstance(Layer(), Layer))) # True
print('isinstance(Layer(), BaseLayer): {}'.format(isinstance(Layer(), BaseLayer))) # True
print('isinstance(BaseLayer(), Layer): {}'.format(isinstance(BaseLayer(), Layer))) # True, but warn
print('isinstance(BaseLayer(), BaseLayer): {}'.format(isinstance(BaseLayer(), BaseLayer))) # True There may be quite some cases where the decision whether a merge layer should accept only lists or only dicts is not so clear. For those cases would the variant of your code sketch require deriving from both |
True, the
No, it would require deriving from
It's just that existing |
I've only glanced over the proposal so far, but it looks good to me. It complicates the code quite a bit, unfortunately, but I think the use cases for this are becoming too important and we need to adapt. I'm not a huge fan of the names Also, how common is |
If we only allow plain inputs, lists of inputs and dicts of inputs (no nesting), it's fairly easy. Otherwise it involves some recursive function in the base class constructor and in
In a perfect world,
Maybe that's really something we shouldn't care too much about. |
If we decide not to worry about it, that would mean we are free to rename |
If we decide not to worry about |
Right, makes sense. Bummer :) |
It also occurs frequently for anyone who is trying to develop libraries on top of lasagne, when trying to assert anything or figuring out whether user sent a single layer or several ones. p.s. since we're talking about layer redesign, is there any way to include a mechanic that quickly creates a duplicate of a layer with same shared params. This would be useful every time one has to use same embedding or convolution for different network inputs. p.p.s. |
That sounds like something that should be doable, although it could get complicated because the |
Some more thoughts. There are more options that wouldn't require adding a new base class, but using
An advantage of placing all checking code in the base class is that it can easily be shared. Note that proper checking needs more than just verifying whether we have a if isinstance(self.input_shapes, tuple) and ('plain' in self.accepts):
# verify that it's a valid shape (only nonnegative int or None)
elif isinstance(self.input_shapes, list) and ('list' in self.accepts):
# verify that all items are valid shapes
elif isinstance(self.input_shapes, dict) and ('dict' in self.accepts):
# verify that all keys are strings, and all values are valid shapes
else:
# raise an error Some layers may want to accept both lists and dicts, or all options. The advantage of keeping Was there any reason for not only extending |
So just to through out something I did over the weekend, which the main goal was restructuring the Lasagne Recurrent implementation. Nevertheless, the root of all evils of making a single "Step" Layer was the fact that some layer has single input/output some multiple. So what I did was remove the
I'm pretty sure you probably don't want so radical change, but hey I really enjoyed it, and now things are way more simple to manipulate around. And the whole recurrent module with 2 more layers implemented is 500 LOC. |
Thanks for sharing!
Exactly -- any changes that break existing
That's also the goal of #629, still blocked by this very issue (to allow multiple outputs), which in turn is blocked by milestone 0.2, for which I'd appreciate additional contributors! |
So it does not make any breaks in the user code unless the user has own classes which extended Nevertheless, I totally understand you guys why you don't want such a brake. Potentially, I could help with the RNN module, but I have to implement a few more things in our fork, like variational stuff, before I have time. |
From your description, you renamed |
This is a proposal to resolve the long-standing discussions on multiple-output layers (#337) and merge layers accepting dictionaries (#537), both required for recurrent containers (#629). It was originally formulated in #537 (comment); I'll give some more detail here and hope to reignite the discussion.
Main idea: Create a new
BaseLayer
class, withLayer
,InputLayer
andMergeLayer
subclassing it.The
BaseLayer
would be something quite universal: itsincomings
could be a single layer, or a list of layers, or a dictionary of layers, or a list of dictionaries of layers, or anything along those lines. Similarly, its output shape could be a single tuple, or a list of tuples, or a dictionary of tuples, or a list of dictionaries of tuples, or anything along those lines. Its output would match its output shape definition (i.e., it would produce a tensor, or a list of tensors, or a dictionary of tensors, you get it).The
Layer
subclass would check that, after construction, its input shape is a single shape tuple.The
InputLayer
subclass would further check that its input layer isNone
. It can subclassLayer
.The
MergeLayer
subclass would instead check that its input shape is a list of tuples. We can have another subclass,DictMergeLayer
, that checks that its input shape is a dictionary of tuples.lasagne.layers.get_output_for()
would only need to know how to handle aBaseLayer
and anInputLayer
.This change would require
lasagne.layers.get_output_for()
to become quite a bit more generic, as it needs to deal with nested dictionaries and lists. However, it would avoid furtherisinstance
checks. The only special case would be forInputLayer
, in case an input is passed toget_output_for()
and we need to find the layer it should be linked to (and we don't want to link it to any free-floating layer we can find). While nested dictionaries and lists seem unnecessarily complex, they would seamlessly allowMergeLayer
s of multiple-output layers. But we don't necessarily have to allow them.The gist of this is that any
BaseLayer
will just have to rely on itsinput_shapes
property to see what to expect in itsget_output_for()
method. It doesn't have to care whether it is linked to a single layer producing multiple outputs, or multiple layers producing single outputs, or anything more complex.Sketch of the code:
Note that this will not change anything for existing
Layer
andMergeLayer
subclasses -- they can still access their incomings viaself.input_shape
orself.input_shapes
and know they're of the expected form (a single shape or a list of shapes). New code can continue to use either of these base classes, or start withBaseLayer
if they want to support wild combinations of multiple inputs.If you're still with me, what do you think of this proposal?
Something more technical that's just relevant to the concrete implementation: For full backward compatibility, we would need to have
MergeLayer
subclassLayer
-- user code may haveisinstance(..., Layer)
in place and expect to catch everything. This is annoying, as we can't do the "is it a single input tensor" check in theLayer
constructor then, but will require a separateself.check_inputs
method called from the constructor that subclasses can override. If we're that far, we can also turn things around, ditch theBaseLayer
and haveLayer
implement all its functionality, with a newUniversalLayer
that subclassesLayer
just to override thecheck_inputs
method with something more liberal (return True
). Either this, or we tell users to replaceisinstance(..., Layer)
withisinstance(..., BaseLayer)
as needed.As another variation,
MergeLayer
could accept either a list or a dict, andDictMergeLayer
/ListMergeLayer
would restrict this further.The text was updated successfully, but these errors were encountered: