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

blocks.monitoring.take_last()   A

Complexity

Conditions 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 1
dl 0
loc 4
rs 10
1
"""Evaluate Theano variables on auxiliary data and during training."""
2
import logging
3
from abc import ABCMeta, abstractmethod
4
5
from six import add_metaclass
6
from theano import tensor
7
from theano.ifelse import ifelse
8
9
from blocks.utils import shared_like
10
11
logger = logging.getLogger(__name__)
12
13
14
@add_metaclass(ABCMeta)
15
class AggregationScheme(object):
16
    """How to incrementally evaluate a Theano variable over minibatches.
17
18
    An AggregationScheme allocates :class:`Aggregator` that can
19
    incrementally compute the value of a Theano variable on a full dataset
20
    by aggregating partial results computed on multiple batches.
21
22
    The AggregationScheme should be attached via the tag
23
    ``aggregation_scheme`` to a Theano variable which computes the desired
24
    value on a single batch.
25
26
    Parameters
27
    ----------
28
    variable: :class:`~tensor.TensorVariable`
29
        The variable that holds the desired value on a single batch.
30
31
    """
32
    @abstractmethod
33
    def get_aggregator(self):
34
        """Return a new Aggregator for this variable."""
35
        pass
36
37
38
class Aggregator(object):
39
    """An Aggregator incrementally evaluates a Theano variable on a dataset.
40
41
    .. warning::
42
        The Aggregators should never be created directly. Instead use the
43
        :meth:`AggregationScheme.get_aggregator` method.
44
45
    Example usages are:
46
47
    * compute the mean of some value over examples, sequence lengths etc.
48
    * track a parameter of a model
49
    * monitor a penalty
50
51
    The Aggregator maintains a set of Theano sharer values called
52
    accumulators and specifies how they should be initialized, and
53
    updated with incremental calculations. Finally, it
54
    provides a Theano variable that reads the accumulators
55
    and computes the final value.
56
57
    Parameters
58
    ----------
59
    aggregation_scheme : :class:`AggregationScheme`
60
        The aggregation scheme that constructed this Aggregator
61
    initialization_updates : list of Theano updates
62
        Updates that specify how to initialize shared variables of
63
        this Aggregator. *Can only refer to shared variables and
64
        constants.*
65
    accumulation_updates : list of Theano updates
66
        Updates that specify how a new batch of data gets processed
67
        by this Aggregator. *Can refer to model inputs.*
68
    readout_variable : :class:`~tensor.TensorVariable`
69
        Theano variable that holds the final value based on accumulated
70
        partial results. *readout_variable must only consist of shared
71
        variables and constants.*
72
73
    Attributes
74
    ----------
75
    All constructor parameters are accessible as attributes.
76
77
    """
78
    def __init__(self, aggregation_scheme, initialization_updates=None,
79
                 accumulation_updates=None, readout_variable=None):
80
        self.aggregation_scheme = aggregation_scheme
81
        self.readout_variable = readout_variable
82
83
        if initialization_updates is None:
84
            initialization_updates = []
85
        if accumulation_updates is None:
86
            accumulation_updates = []
87
        self.initialization_updates = initialization_updates
88
        self.accumulation_updates = accumulation_updates
89
90
91
class Mean(AggregationScheme):
92
    """Aggregation scheme which computes the mean.
93
94
    Parameters
95
    ----------
96
    numerator : :class:`~tensor.TensorVariable`
97
        Theano variable for the numerator e.g. the likelihood
98
    denominator : :class:`~tensor.TensorVariable`
99
        Theano variable for the denominator e.g. the batch size
100
101
    """
102
    def __init__(self, numerator, denominator):
103
        self.numerator = numerator
104
        self.denominator = denominator
105
106
    def get_aggregator(self):
107
        initialized = shared_like(0.)
108
        numerator_acc = shared_like(self.numerator)
109
        denominator_acc = shared_like(self.denominator)
110
111
        # Dummy default expression to use as the previously-accumulated
112
        # value, that has the same shape as the new result
113
        numerator_zeros = tensor.as_tensor(self.numerator).zeros_like()
114
        denominator_zeros = tensor.as_tensor(self.denominator).zeros_like()
115
116
        conditional_update_num = self.numerator + ifelse(initialized,
117
                                                         numerator_acc,
118
                                                         numerator_zeros)
119
        conditional_update_den = self.denominator + ifelse(initialized,
120
                                                           denominator_acc,
121
                                                           denominator_zeros)
122
123
        initialization_updates = [(numerator_acc,
124
                                   tensor.zeros_like(numerator_acc)),
125
                                  (denominator_acc,
126
                                   tensor.zeros_like(denominator_acc)),
127
                                  (initialized, 0.)]
128
        accumulation_updates = [(numerator_acc,
129
                                 conditional_update_num),
130
                                (denominator_acc,
131
                                 conditional_update_den),
132
                                (initialized, 1.)]
133
        aggregator = Aggregator(aggregation_scheme=self,
134
                                initialization_updates=initialization_updates,
135
                                accumulation_updates=accumulation_updates,
136
                                readout_variable=(numerator_acc /
137
                                                  denominator_acc))
138
        return aggregator
139
140
141
def mean(numerator, denominator=1.):
142
    """Mean of quantity (numerator) over a number (denominator) values."""
143
    variable = numerator / denominator
144
    variable.tag.aggregation_scheme = Mean(numerator, denominator)
145
    variable.name = numerator.name
146
    return variable
147
148
149
class _DataIndependent(AggregationScheme):
150
    """Dummy aggregation scheme for values that don't depend on data."""
151
    def __init__(self, variable):
152
        self.variable = variable
153
154
    def get_aggregator(self):
155
        return Aggregator(aggregation_scheme=self,
156
                          initialization_updates=[],
157
                          accumulation_updates=[],
158
                          readout_variable=self.variable)
159
160
161
class TakeLast(AggregationScheme):
162
    """Aggregation scheme which remembers only the last value."""
163
    def __init__(self, variable):
164
        self.variable = variable
165
166
    def get_aggregator(self):
167
        self.storage = shared_like(self.variable)
168
        return Aggregator(aggregation_scheme=self,
169
                          initialization_updates=[
170
                              (self.storage, tensor.zeros_like(self.storage))],
171
                          accumulation_updates=[(self.storage, self.variable)],
172
                          readout_variable=self.storage)
173
174
175
def take_last(variable):
176
    variable = variable.copy(variable.name)
177
    variable.tag.aggregation_scheme = TakeLast(variable)
178
    return variable
179
180
181
@add_metaclass(ABCMeta)
0 ignored issues
show
Unused Code introduced by
This abstract class does not seem to be used anywhere.
Loading history...
182
class MonitoredQuantity(object):
183
    """The base class for monitored-quantities.
184
185
    To monitor a non-Theano quantity in Blocks you have to implement this
186
    interface for it. The initialize method initializes accumulators and
187
    the parameters needed to compute this quantity, accumulate method
188
    accumulates results for every batch, and finally readout is called
189
    to get the accumulated results.
190
191
    Attributes
192
    ----------
193
    requires : list
194
        List of Theano variables needed to calculate this quantity.
195
    name : str
196
        The name of monitored quantity which appears in the log.
197
198
    See Also
199
    --------
200
    :class:`~blocks.monitoring.evaluators.DatasetEvaluator`
201
    :class:`~blocks.extensions.DataStreamMonitoring`
202
203
    """
204
    def __init__(self, requires=None, name=None):
205
        if requires is None:
206
            requires = []
207
        self.requires = requires
208
        self.name = name
209
210
    @abstractmethod
211
    def initialize(self):
212
        """Initialize accumulators for this monitored quantity."""
213
        pass
214
215
    @abstractmethod
216
    def accumulate(self):
217
        """Accumulate results for every batch."""
218
        pass
219
220
    @abstractmethod
221
    def readout(self):
222
        """Readout the accumulated results to capture the final result."""
223
        pass
224