Completed
Pull Request — master (#1007)
by Dmitry
01:33
created

blocks.bricks.MLP.__init__()   C

Complexity

Conditions 7

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 7
dl 0
loc 23
rs 5.5
1
"""Bricks that compose together other bricks in linear sequences."""
0 ignored issues
show
Bug introduced by
There seems to be a cyclic import (blocks.bricks -> blocks.bricks.bn -> blocks.graph -> blocks.graph.bn).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (blocks.bricks.base -> blocks.graph -> blocks.graph.bn -> blocks.filter).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (blocks.bricks -> blocks.bricks.bn -> blocks.bricks.interfaces -> blocks.bricks.base -> blocks.graph -> blocks.graph.bn).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (blocks.bricks -> blocks.bricks.bn -> blocks.bricks.sequences -> blocks.bricks.simple -> blocks.bricks.interfaces -> blocks.bricks.base -> blocks.graph -> blocks.graph.bn).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (blocks.bricks -> blocks.bricks.bn -> blocks.bricks.sequences -> blocks.bricks.simple -> blocks.bricks.wrappers -> blocks.bricks.base -> blocks.graph -> blocks.graph.bn).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
Bug introduced by
There seems to be a cyclic import (blocks.bricks -> blocks.bricks.bn -> blocks.bricks.sequences -> blocks.bricks.interfaces -> blocks.bricks.base -> blocks.graph -> blocks.graph.bn).

Cyclic imports may cause partly loaded modules to be returned. This might lead to unexpected runtime behavior which is hard to debug.

Loading history...
2
import copy
3
from toolz import interleave
4
from picklable_itertools.extras import equizip
5
6
from ..utils import pack
7
from .base import Brick, application, lazy
8
from .interfaces import Feedforward, Initializable
9
from .simple import Linear
10
11
12
class Sequence(Brick):
13
    """A sequence of bricks.
14
15
    This brick applies a sequence of bricks, assuming that their in- and
16
    outputs are compatible.
17
18
    Parameters
19
    ----------
20
    application_methods : list
21
        List of :class:`.BoundApplication` to apply
22
23
    """
24
    def __init__(self, application_methods, **kwargs):
25
        super(Sequence, self).__init__(**kwargs)
26
        self.application_methods = application_methods
27
28
        seen = set()
29
        self.children = [app.brick for app in application_methods
30
                         if not (app.brick in seen or seen.add(app.brick))]
31
32
    @application
33
    def apply(self, *args):
34
        child_input = args
35
        for application_method in self.application_methods:
36
            output = application_method(*pack(child_input))
37
            child_input = output
38
        return output
39
40
    @apply.property('inputs')
41
    def apply_inputs(self):
42
        return self.application_methods[0].inputs
43
44
    @apply.property('outputs')
45
    def apply_outputs(self):
46
        return self.application_methods[-1].outputs
47
48
49
class FeedforwardSequence(Sequence, Feedforward):
50
    """A sequence where the first and last bricks are feedforward.
51
52
    Parameters
53
    ----------
54
    application_methods : list
55
        List of :class:`.BoundApplication` to apply. The first and last
56
        application method should belong to a :class:`Feedforward` brick.
57
58
    """
59
    @property
60
    def input_dim(self):
61
        return self.children[0].input_dim
62
63
    @input_dim.setter
64
    def input_dim(self, value):
65
        self.children[0].input_dim = value
66
67
    @property
68
    def output_dim(self):
69
        return self.children[-1].output_dim
70
71
    @output_dim.setter
72
    def output_dim(self, value):
73
        self.children[-1].output_dim = value
74
75
76
class MLP(Sequence, Initializable, Feedforward):
77
    """A simple multi-layer perceptron.
78
79
    Parameters
80
    ----------
81
    activations : list of :class:`.Brick`, :class:`.BoundApplication`,
82
                  or ``None``
83
        A list of activations to apply after each linear transformation.
84
        Give ``None`` to not apply any activation. It is assumed that the
85
        application method to use is ``apply``. Required for
86
        :meth:`__init__`.
87
    dims : list of ints
88
        A list of input dimensions, as well as the output dimension of the
89
        last layer. Required for :meth:`~.Brick.allocate`.
90
    prototype : :class:`.Brick`, optional
91
        The transformation prototype. A copy will be created for every
92
        activation. If not provided, an instance of :class:`~simple.Linear`
93
        will be used.
94
95
    Notes
96
    -----
97
    See :class:`Initializable` for initialization parameters.
98
99
    Note that the ``weights_init``, ``biases_init`` and ``use_bias``
100
    configurations will overwrite those of the layers each time the
101
    :class:`MLP` is re-initialized. For more fine-grained control, push the
102
    configuration to the child layers manually before initialization.
103
104
    >>> from blocks.bricks import Tanh
105
    >>> from blocks.initialization import IsotropicGaussian, Constant
106
    >>> mlp = MLP(activations=[Tanh(), None], dims=[30, 20, 10],
107
    ...           weights_init=IsotropicGaussian(),
108
    ...           biases_init=Constant(1))
109
    >>> mlp.push_initialization_config()  # Configure children
110
    >>> mlp.children[0].weights_init = IsotropicGaussian(0.1)
111
    >>> mlp.initialize()
112
113
    """
114
    @lazy(allocation=['dims'])
115
    def __init__(self, activations, dims, prototype=None, **kwargs):
116
        self.activations = activations
117
        self.prototype = Linear() if prototype is None else prototype
118
        self.linear_transformations = []
119
        for i in range(len(activations)):
120
            linear = copy.deepcopy(self.prototype)
121
            name = self.prototype.__class__.__name__.lower()
122
            linear.name = '{}_{}'.format(name, i)
123
            self.linear_transformations.append(linear)
124
        # Interleave the transformations and activations
125
        application_methods = []
126
        for entity in interleave([self.linear_transformations, activations]):
127
            if entity is None:
128
                continue
129
            if isinstance(entity, Brick):
130
                application_methods.append(entity.apply)
131
            else:
132
                application_methods.append(entity)
133
        if not dims:
134
            dims = [None] * (len(activations) + 1)
135
        self.dims = dims
136
        super(MLP, self).__init__(application_methods, **kwargs)
137
138
    @property
139
    def input_dim(self):
140
        return self.dims[0]
141
142
    @input_dim.setter
143
    def input_dim(self, value):
144
        self.dims[0] = value
145
146
    @property
147
    def output_dim(self):
148
        return self.dims[-1]
149
150
    @output_dim.setter
151
    def output_dim(self, value):
152
        self.dims[-1] = value
153
154
    def _push_allocation_config(self):
155
        if not len(self.dims) - 1 == len(self.linear_transformations):
156
            raise ValueError
157
        for input_dim, output_dim, layer in \
158
                equizip(self.dims[:-1], self.dims[1:],
159
                        self.linear_transformations):
160
            layer.input_dim = input_dim
161
            layer.output_dim = output_dim
162
            layer.use_bias = self.use_bias
163