|
Keraflow
Deep Learning for Python.
|
In the following, we describe the basic and advanced issues about writing customized layer.
To make a customized layer works:
__init__ should accept **kwargs and call super(NewLayerClass, self).__init__(**kwargs) to initialize common arguments of Layer.__init__ to make it serializable, else you will need to implement this function if you want to correctly serialize the layer.The following example implements a layer that take an 1D tensor, discard the first half units, and fully connects the second half of the units to the output:
Sometimes you might want to use operations implemented by existing layers. Layer.embed is a syntax sugar for this purpose. It embeds the target layer such that the its trainable parameters (along with regularizers and constraints on the parameters) are treated as the host layer's parameters and are updated during the training process. You could use it in a customized layer's output() function like:
In fact, SequentialLayer is implemented using this functions:
We do not need to implement init_param function since SequentialLayer itself has no parameters.
You could also check the implementation of the output function of Convolution1D, Convolution2D and TimeDistributed for example on how to use embed.
The core of tensors linkage is Layer.__call__. It accepts a Kensor (or as list of Kensors) and return a single Kensor. Each kensor embeds a tensor (a theano/tensorflow variable) and the major function of __call__ is to define the relation between the input tensor(s) and the output tensor.
Another function of __call__ is to maintain a global list of trainable parameters, regularizers, constraints and parameter updates, which will be latter used in Model.compile to determine the total loss and what to update during the training process. The task is done by adding current layer's parameters' info into the list carried by the input kensor and then assign the updated list to the output kensor. The model then fetch the list from the output kensor(s).
One final task of __call__ is to ensure the model could be reconstructed, this is done by keeping a global list of path in the form [path1, path2...], where path1, path2... are in the form of [input kensor's name, layer's name, output kensor's name]. When reconstructing the model, we could then find kensors and layers by name and decide to feed which kensor to which layer. The path is again passed from kensor to kensor and the model collects the final path from the output kensors.
Keraflow serialize an object (model, layer, regularizer...) by memorizing the object's class, and the value of the arguments listed in its __init__. For un-serialization, we simply call __init__ of the memorized class with the memorized argument values.
Note that the serialization process is a recursive process, i.e. serialize(obj) calls pack_init_params(obj), which in term calls serialize on all __init__ arguments of obj.
Due to such implementation, the constraints for an object to be serializable are:
__init__ should be in the object's __dict__ when calling serialize(obj)__init__ should be stored as is passed to the layer's __init__, i.e. you should not do the following layer_config), output_dim is equal to 3 and additional_dim equals to 2 . This will make reconstructed have output_dim equal to 5, which makes reconstructed and layer behave differently.If you really need to modify the value of the arguments, you should implement the pack_init_param as the following:
Since serialize check if an object implements pack_init_param before the default behavior of saving the arguments as is, the pack_init_param of MyLayer get called and reset output_dim to 1 (as passed during initialization).
Note that when implementing pack_init_param for a new layer, we should first call Layer.pack_init_param (line 8), which packs not only arguments listed in MyLayer.__init__ but also Layer.__init__ (including regularizers, constraints). Otherwise, you should then handle those things your self. For details, please check the code.