Writing XNMT classes¶
In order to write new components that can be created both from YAML config files as well as programmatically, support sharing of DyNet parameters, etc., one must adhere to the Serializable interface including a few simple conventions:
XNMT will perform automatic checks and raise an informative error in case these conventions are violated, so there is no need to worry about these too much.
Marking classes as serializable¶
Classes are marked as serializable by specifying
xnmt.persistence.Serializable as super class. They must
specify a unique yaml_tag class attribute, set to
!ClassName with ClassName replaced by the class name. It follows
that class names must be unique, even across different XNMT modules.
(Note: Serializable should be explicitly specified even if another super class already does the same)
Specifying init arguments¶
The arguments accepted in the YAML config file correspond directly to the arguments of the class’s
__init__ is required to be decorated with
Note that sub-objects are initialized before being passed to
__init__, and in the order in which they are
Using DyNet parameters¶
If the component uses DyNet parameters, the calls to
dynet_model.add_parameters() etc. must take place in
__init__ (or a helper called from within
__init__). It is not possible to allocate parameters after
__init__ has returned.
The component will get assigned its own unique DyNet parameter collection, which can be requested using
xnmt.param_collection.ParamManager.my_params(self). Subcollections should never be passed to sub-objects
Serializable. Behind the scenes, components will get assigned a unique subcollection id which ensures
that they can be loaded later along with their pretrained weights, and even combined with components trained from
a different config file.
Using Serializable subcomponents¶
If a class uses helper objects that are also
Serializable, this must occur in a certain way:
Serializableobject must be accepted as argument in
It can be set to
Noneby default, in which case it must be constructed manually within
__init__. This should take place using the
Serializable.add_serializable_component()helper, e.g. with the following idiom:@serializable_init def __init__(self, ..., vocab_projector=None, ...): ... self.vocab_projector = \ self.add_serializable_component(\ "vocab_projector", vocab_projector, lambda: xnmt.linear.Linear(input_dim=mlp_hidden_dim, output_dim=vocab_size, param_init=param_init_output, bias_init=bias_init_output)) ...