ModulesΒΆ

Modules build the foundation of DLTK and are mostly used to implement layers of neural networks. Modules are implemented as objects which handle variable sharing. To do this we followed the style of Sonnet and used tf.make_template. To implement a new module you simply need to inherit from the dltk.core.modules.base.AbstractModule class and overwrite the __init__ and build methods.

A simple example is the dltk.core.modules.linear.Linear layer that implements a simple matrix multiplication. The __init__ method stores all necessary parameters for the module like the number of output units and whether to add a bias or not. It then calls the super class‘ __init__ function to build the template:

class Linear(AbstractModule):
    """Linear layer module

    This module builds a linear layer

    """
    def __init__(self, out_units, use_bias=True, name='linear'):
        """Constructs linear layer

        Parameters
        ----------
        out_units : int
            number of output units
        use_bias : bool, optional
            flag to toggle the addition of a bias
        name : string
            name of the module
        """
        self.out_units = out_units
        self.in_units = None
        self.use_bias = use_bias

        super(Linear, self).__init__(name=name)

The build function then handles the actual implementation of the module. All variables have to be created with tf.get_variable. Variable sharing and scoping is automatically handled by tf.make_template which is called in the dltk.core.modules.base.AbstractModule class.

    def _build(self, inp):
       """Applies the linear layer operation to an input tensor

       Parameters
       ----------
       inp : tf.Tensor
           input tensor


       Returns
       -------
       tf.Tensor
           transformed tensor

       """
       assert(len(inp.get_shape().as_list())  == 2, 'Layer needs 2D input.')

       self.in_shape = tuple(inp.get_shape().as_list())
       if self.in_units is None:
           self.in_units = self.in_shape[-1]

       assert(self.in_units == self.in_shape[-1], 'Layer was initialised for a different number of input units.')

       w_shape = (self.in_units, self.out_units)

       self._w = tf.get_variable("w", shape=w_shape, initializer=tf.uniform_unit_scaling_initializer(),
                                 collections=self.WEIGHT_COLLECTIONS)
       self.variables.append(self._w)

       if self.use_bias:
           self._b = tf.get_variable("b", shape=(self.out_units,), initializer=tf.constant_initializer(),
                                     collections=self.BIAS_COLLECTIONS)
           self.variables.append(self._b)
           outp = tf.nn.xw_plus_b(inp, self._w, self._b, 'linear')
       else:
           outp = tf.matmul(inp, self._w, 'linear')

       return outp

The output of this function is passed through wrappers and returned when an instance of this module is called:

linear = Linear(100) outp = linear(x) #this is the same outp as returned in `build`