/
bilinear.py
110 lines (91 loc) · 4.55 KB
/
bilinear.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import numpy
from chainer.backends import cuda
from chainer.functions.connection import bilinear
from chainer import initializers
from chainer import link
from chainer import variable
class Bilinear(link.Link):
"""Bilinear layer that performs tensor multiplication.
Bilinear is a primitive link that wraps the
:func:`~chainer.functions.bilinear` functions. It holds parameters ``W``,
``V1``, ``V2``, and ``b`` corresponding to the arguments of
:func:`~chainer.functions.bilinear`.
Args:
left_size (int): Dimension of input vector :math:`e^1` (:math:`J`)
right_size (int): Dimension of input vector :math:`e^2` (:math:`K`)
out_size (int): Dimension of output vector :math:`y` (:math:`L`)
nobias (bool): If ``True``, parameters ``V1``, ``V2``, and ``b`` are
omitted.
initialW (:ref:`initializer <initializer>`): Initializer to
initialize the weight. When it is :class:`numpy.ndarray`,
its ``ndim`` should be 3.
initial_bias (tuple of :ref:`initializer <initializer>`):
Initial values of :math:`V^1`, :math:`V^2` and
:math:`b`. The length of this argument must be 3.
Each element of this tuple must have the shapes of
``(left_size, out_size)``, ``(right_size, out_size)``, and
``(out_size,)``, respectively if they are :class:`numpy.ndarray`.
If ``None``, :math:`V^1` and :math:`V^2` are initialized
by the default initializer and :math:`b` is set to :math:`0`.
.. seealso:: See :func:`chainer.functions.bilinear` for details.
Attributes:
W (~chainer.Variable): Bilinear weight parameter.
V1 (~chainer.Variable): Linear weight parameter for the first argument.
V2 (~chainer.Variable): Linear weight parameter for the second
argument.
b (~chainer.Variable): Bias parameter.
"""
def __init__(self, left_size, right_size, out_size, nobias=False,
initialW=None, initial_bias=None):
super(Bilinear, self).__init__()
self.in_sizes = (left_size, right_size)
self.nobias = nobias
# TODO(Kenta OONO): I do not know appropriate way of
# initializing weights in tensor network.
# This initialization is a modification of
# that of Linear function.
with self.init_scope():
shape = (left_size, right_size, out_size)
if isinstance(initialW, (numpy.ndarray, cuda.ndarray)):
assert initialW.shape == shape
self.W = variable.Parameter(
initializers._get_initializer(initialW), shape)
if not self.nobias:
V1_shape = (left_size, out_size)
V2_shape = (right_size, out_size)
b_shape = (out_size,)
if isinstance(initial_bias, tuple):
initialV1, initialV2, initialb = initial_bias
if isinstance(initialV1, (numpy.ndarray, cuda.ndarray)):
assert initialV1.shape == V1_shape
if isinstance(initialV2, (numpy.ndarray, cuda.ndarray)):
assert initialV2.shape == V2_shape
if isinstance(initialb, (numpy.ndarray, cuda.ndarray)):
assert initialb.shape == b_shape
initialV1 = initializers._get_initializer(initialV1)
initialV2 = initializers._get_initializer(initialV2)
initialb = initializers._get_initializer(initialb)
elif initial_bias is None:
initialV1 = initializers._get_initializer(None)
initialV2 = initializers._get_initializer(None)
initialb = 0
else:
raise ValueError('initial_bias must be tuple or None')
self.V1 = variable.Parameter(initialV1, V1_shape)
self.V2 = variable.Parameter(initialV2, V2_shape)
self.b = variable.Parameter(initialb, b_shape)
def __call__(self, e1, e2):
"""Applies the bilinear function to inputs and the internal parameters.
Args:
e1 (~chainer.Variable): Left input.
e2 (~chainer.Variable): Right input.
Returns:
~chainer.Variable: Output variable.
"""
if self.nobias:
return bilinear.bilinear(e1, e2, self.W)
else:
return bilinear.bilinear(e1, e2, self.W, self.V1, self.V2, self.b)
def zero_grads(self):
# Left for backward compatibility
self.zerograds()