|
1
|
|
|
from collections import OrderedDict |
|
2
|
|
|
from ..roles import add_role, AUXILIARY |
|
3
|
|
|
|
|
4
|
|
|
|
|
5
|
|
|
def add_annotation(var, annotation): |
|
6
|
|
|
annotations = getattr(var.tag, 'annotations', []) |
|
7
|
|
|
if any(old_annotation.__class__ == annotation.__class__ |
|
8
|
|
|
for old_annotation in annotations): |
|
9
|
|
|
raise ValueError |
|
10
|
|
|
else: |
|
11
|
|
|
var.tag.annotations = annotations + [annotation] |
|
12
|
|
|
|
|
13
|
|
|
|
|
14
|
|
|
class Annotation(object): |
|
15
|
|
|
"""Annotations on Theano variables in a graph. |
|
16
|
|
|
|
|
17
|
|
|
In Blocks annotations are automatically attached to variables created |
|
18
|
|
|
using bricks. One form of annotation is that many variables are |
|
19
|
|
|
assigned a role (see :class:`.VariableRole`). A second form of |
|
20
|
|
|
annotation comes in the form of attaching a :class:`Annotation` |
|
21
|
|
|
instance to the variable's ``tag`` attribute, with auxiliary variables |
|
22
|
|
|
and/or updates. |
|
23
|
|
|
|
|
24
|
|
|
For example, we might be interested in the mean activation of certain |
|
25
|
|
|
application of a :class:`.Linear` brick. The variable representing the |
|
26
|
|
|
mean activation is attached as an auxiliary variable to the annotations |
|
27
|
|
|
of the input and output variables of this brick. Using the |
|
28
|
|
|
:class:`ComputationGraph` class (the |
|
29
|
|
|
:attr:`~ComputationGraph.variables`, |
|
30
|
|
|
:attr:`~ComputationGraph.auxiliary_variables`, etc. attributes in |
|
31
|
|
|
particular) we can retrieve these Theano variables to pass on to the |
|
32
|
|
|
monitor, use as a regularizer, etc. |
|
33
|
|
|
|
|
34
|
|
|
In most cases, annotations are added on a brick level (e.g. each brick |
|
35
|
|
|
will assign the weight norm of its weights as an auxiliary value) or on |
|
36
|
|
|
an application level (e.g. each time a brick is applied, its mean |
|
37
|
|
|
activation will become an auxiliary variable). However, you can also |
|
38
|
|
|
add annotations manually, by setting the ``annotation`` value of a |
|
39
|
|
|
variable's ``tag`` field. |
|
40
|
|
|
|
|
41
|
|
|
Examples |
|
42
|
|
|
-------- |
|
43
|
|
|
>>> from theano import tensor |
|
44
|
|
|
>>> x = tensor.vector() |
|
45
|
|
|
>>> annotation = Annotation() |
|
46
|
|
|
>>> annotation.add_auxiliary_variable(x + 1, name='x_plus_1') |
|
47
|
|
|
>>> add_annotation(x, annotation) |
|
48
|
|
|
>>> y = x ** 2 |
|
49
|
|
|
>>> from blocks.graph import ComputationGraph |
|
50
|
|
|
>>> cg = ComputationGraph([y]) |
|
51
|
|
|
>>> cg.auxiliary_variables |
|
52
|
|
|
[x_plus_1] |
|
53
|
|
|
|
|
54
|
|
|
""" |
|
55
|
|
|
def __init__(self): |
|
56
|
|
|
self.auxiliary_variables = [] |
|
57
|
|
|
self.updates = OrderedDict() |
|
58
|
|
|
|
|
59
|
|
|
def add_auxiliary_variable(self, variable, roles=None, name=None): |
|
60
|
|
|
"""Attach an auxiliary variable to the graph. |
|
61
|
|
|
|
|
62
|
|
|
Auxiliary variables are Theano variables that are not part of a |
|
63
|
|
|
brick's output, but can be useful nonetheless e.g. as a regularizer |
|
64
|
|
|
or to monitor during training progress. |
|
65
|
|
|
|
|
66
|
|
|
Parameters |
|
67
|
|
|
---------- |
|
68
|
|
|
variable : :class:`~tensor.TensorVariable` |
|
69
|
|
|
The variable you want to add. |
|
70
|
|
|
roles : list of :class:`.VariableRole` instances, optional |
|
71
|
|
|
The roles of this variable. The :const:`.AUXILIARY` |
|
72
|
|
|
role will automatically be added. Other options are |
|
73
|
|
|
:const:`.COST`, :const:`.WEIGHT`, etc. |
|
74
|
|
|
name : str, optional |
|
75
|
|
|
Name to give to the variable. If the variable already has a |
|
76
|
|
|
name it will be overwritten. |
|
77
|
|
|
|
|
78
|
|
|
Examples |
|
79
|
|
|
-------- |
|
80
|
|
|
>>> from blocks.bricks.base import application, Brick |
|
81
|
|
|
>>> from blocks.roles import COST |
|
82
|
|
|
>>> from blocks.utils import shared_floatx_nans |
|
83
|
|
|
>>> class Foo(Brick): |
|
84
|
|
|
... def _allocate(self): |
|
85
|
|
|
... W = shared_floatx_nans((10, 10)) |
|
86
|
|
|
... self.add_auxiliary_variable(W.mean(), name='mean_W') |
|
87
|
|
|
... @application |
|
88
|
|
|
... def apply(self, x, application_call): |
|
89
|
|
|
... application_call.add_auxiliary_variable( |
|
90
|
|
|
... x - 1, name='x_minus_1') |
|
91
|
|
|
... application_call.add_auxiliary_variable( |
|
92
|
|
|
... x.mean(), roles=[COST], name='mean_x') |
|
93
|
|
|
... return x + 1 |
|
94
|
|
|
>>> from theano import tensor |
|
95
|
|
|
>>> x = tensor.vector() |
|
96
|
|
|
>>> y = Foo().apply(x) |
|
97
|
|
|
>>> from blocks.graph import ComputationGraph |
|
98
|
|
|
>>> cg = ComputationGraph([y]) |
|
99
|
|
|
>>> from blocks.filter import VariableFilter |
|
100
|
|
|
>>> var_filter = VariableFilter(roles=[AUXILIARY]) |
|
101
|
|
|
>>> var_filter(cg.variables) # doctest: +SKIP |
|
102
|
|
|
{x_minus_1, mean_W, mean_x} |
|
103
|
|
|
>>> var_filter = VariableFilter(roles=[COST]) |
|
104
|
|
|
>>> var_filter(cg.variables) # doctest: +SKIP |
|
105
|
|
|
{mean_x} |
|
106
|
|
|
|
|
107
|
|
|
""" |
|
108
|
|
|
add_annotation(variable, self) |
|
109
|
|
|
if name is not None: |
|
110
|
|
|
variable.name = name |
|
111
|
|
|
variable.tag.name = name |
|
112
|
|
|
add_role(variable, AUXILIARY) |
|
113
|
|
|
if roles is not None: |
|
114
|
|
|
for role in roles: |
|
115
|
|
|
add_role(variable, role) |
|
116
|
|
|
self.auxiliary_variables.append(variable) |
|
117
|
|
|
|