Completed
Pull Request — master (#941)
by David
01:45
created

blocks.bricks.RNGMixin.rng()   A

Complexity

Conditions 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 7
rs 9.4285
1
"""Bricks that are interfaces and/or mixins."""
2
import numpy
3
from six import add_metaclass
4
from theano.sandbox.rng_mrg import MRG_RandomStreams
5
6
from ..config import config
7
from .base import _Brick, Brick, lazy
8
9
10
class ActivationDocumentation(_Brick):
11
    """Dynamically adds documentation to activations.
12
13
    Notes
14
    -----
15
    See http://bugs.python.org/issue12773.
16
17
    """
18
    def __new__(cls, name, bases, classdict):
0 ignored issues
show
Coding Style Best Practice introduced by
The first argument of the __new__ class method of metaclasses should be named mcs by convention.
Loading history...
19
        classdict['__doc__'] = \
20
            """Elementwise application of {0} function.""".format(name.lower())
21
        if 'apply' in classdict:
22
            classdict['apply'].__doc__ = \
23
                """Apply the {0} function element-wise.
24
25
                Parameters
26
                ----------
27
                input_ : :class:`~tensor.TensorVariable`
28
                    Theano variable to apply {0} to, element-wise.
29
30
                Returns
31
                -------
32
                output : :class:`~tensor.TensorVariable`
33
                    The input with the activation function applied.
34
35
                """.format(name.lower())
36
        return super(ActivationDocumentation, cls).__new__(cls, name, bases,
37
                                                           classdict)
38
39
40
@add_metaclass(ActivationDocumentation)
41
class Activation(Brick):
42
    """A base class for simple, element-wise activation functions.
43
44
    This base class ensures that activation functions are automatically
45
    documented using the :class:`ActivationDocumentation` metaclass.
46
47
    """
48
    pass
49
50
51
class Feedforward(Brick):
52
    """Declares an interface for bricks with one input and one output.
53
54
    Many bricks have just one input and just one output (activations,
55
    :class:`Linear`, :class:`MLP`). To make such bricks interchangable
56
    in most contexts they should share an interface for configuring
57
    their input and output dimensions. This brick declares such an
58
    interface.
59
60
    Attributes
61
    ----------
62
    input_dim : int
63
        The input dimension of the brick.
64
    output_dim : int
65
        The output dimension of the brick.
66
67
    """
68
    def __getattr__(self, name):
69
        message = ("'{}' object does not have an attribute '{}'"
70
                   .format(self.__class__.__name__, name))
71
        if name in ('input_dim', 'output_dim'):
72
            message += (" (which is a part of 'Feedforward' interface it"
73
                        " claims to support)")
74
        raise AttributeError(message)
75
76
77
class RNGMixin(object):
78
    """Mixin for initialization random number generators."""
79
80
    seed_rng = numpy.random.RandomState(config.default_seed)
81
82
    @property
83
    def seed(self):
84
        if getattr(self, '_seed', None) is not None:
85
            return self._seed
86
        else:
87
            self._seed = self.seed_rng.randint(
88
                numpy.iinfo(numpy.int32).max)
89
            return self._seed
90
91
    @seed.setter
92
    def seed(self, value):
93
        if hasattr(self, '_seed'):
94
            raise AttributeError("seed already set")
95
        self._seed = value
96
97
    @property
98
    def rng(self):
99
        if getattr(self, '_rng', None) is not None:
100
            return self._rng
101
        else:
102
            self._rng = numpy.random.RandomState(self.seed)
103
            return self._rng
104
105
    @rng.setter
106
    def rng(self, rng):
107
        self._rng = rng
108
109
110
class Initializable(RNGMixin, Brick):
111
    """Base class for bricks which push parameter initialization.
112
113
    Many bricks will initialize children which perform a linear
114
    transformation, often with biases. This brick allows the weights
115
    and biases initialization to be configured in the parent brick and
116
    pushed down the hierarchy.
117
118
    Parameters
119
    ----------
120
    weights_init : object
121
        A `NdarrayInitialization` instance which will be used by to
122
        initialize the weight matrix. Required by
123
        :meth:`~.Brick.initialize`.
124
    biases_init : :obj:`object`, optional
125
        A `NdarrayInitialization` instance that will be used to initialize
126
        the biases. Required by :meth:`~.Brick.initialize` when `use_bias`
127
        is `True`. Only supported by bricks for which :attr:`has_biases` is
128
        ``True``.
129
    use_bias : :obj:`bool`, optional
130
        Whether to use a bias. Defaults to `True`. Required by
131
        :meth:`~.Brick.initialize`. Only supported by bricks for which
132
        :attr:`has_biases` is ``True``.
133
    rng : :class:`numpy.random.RandomState`
134
135
    Attributes
136
    ----------
137
    has_biases : bool
138
        ``False`` if the brick does not support biases, and only has
139
        :attr:`weights_init`.  For an example of this, see
140
        :class:`.Bidirectional`. If this is ``False``, the brick does not
141
        support the arguments ``biases_init`` or ``use_bias``.
142
143
    """
144
    has_biases = True
145
146
    @lazy()
147
    def __init__(self, weights_init=None, biases_init=None, use_bias=True,
148
                 seed=None, **kwargs):
149
        super(Initializable, self).__init__(**kwargs)
150
        self.weights_init = weights_init
151
        if self.has_biases:
152
            self.biases_init = biases_init
153
        elif biases_init is not None or not use_bias:
154
            raise ValueError("This brick does not support biases config")
155
        self.use_bias = use_bias
156
        self.seed = seed
157
158
    def _push_initialization_config(self):
159
        for child in self.children:
160
            if isinstance(child, Initializable):
161
                child.rng = self.rng
162
                if self.weights_init:
163
                    child.weights_init = self.weights_init
164
        if hasattr(self, 'biases_init') and self.biases_init:
165
            for child in self.children:
166
                if (isinstance(child, Initializable) and
167
                        hasattr(child, 'biases_init')):
168
                    child.biases_init = self.biases_init
169
170
171
class Random(Brick):
172
    """A mixin class for Bricks which need Theano RNGs.
173
174
    Parameters
175
    ----------
176
    theano_seed : int or list, optional
177
        Seed to use for a
178
        :class:`~theano.sandbox.rng_mrg.MRG_RandomStreams` object.
179
180
    """
181
    seed_rng = numpy.random.RandomState(config.default_seed)
182
183
    def __init__(self, theano_seed=None, **kwargs):
184
        super(Random, self).__init__(**kwargs)
185
        self.theano_seed = theano_seed
186
187
    @property
188
    def theano_seed(self):
189
        if getattr(self, '_theano_seed', None) is not None:
190
            return self._theano_seed
191
        else:
192
            self._theano_seed = self.seed_rng.randint(
193
                numpy.iinfo(numpy.int32).max)
194
            return self._theano_seed
195
196
    @theano_seed.setter
197
    def theano_seed(self, value):
198
        if hasattr(self, '_theano_seed'):
199
            raise AttributeError("seed already set")
200
        self._theano_seed = value
201
202
    @property
203
    def theano_rng(self):
204
        """Returns Brick's Theano RNG, or a default one.
205
206
        The default seed can be set through ``blocks.config``.
207
208
        """
209
        if not hasattr(self, '_theano_rng'):
210
            self._theano_rng = MRG_RandomStreams(self.theano_seed)
211
        return self._theano_rng
212
213
    @theano_rng.setter
214
    def theano_rng(self, theano_rng):
215
        self._theano_rng = theano_rng
216