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.