/
linear.py
108 lines (87 loc) · 3.37 KB
/
linear.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
from chainer import function_node
import chainer.functions
from chainer.utils import type_check
class LinearFunction(function_node.FunctionNode):
def check_type_forward(self, in_types):
n_in = in_types.size()
type_check.expect(2 <= n_in, n_in <= 3)
x_type, w_type = in_types[:2]
type_check.expect(
x_type.dtype.kind == 'f',
w_type.dtype.kind == 'f',
x_type.ndim == 2,
w_type.ndim == 2,
x_type.shape[1] == w_type.shape[1],
)
if type_check.eval(n_in) == 3:
b_type = in_types[2]
type_check.expect(
b_type.dtype == x_type.dtype,
b_type.ndim == 1,
b_type.shape[0] == w_type.shape[0],
)
def forward(self, inputs):
x = inputs[0]
W = inputs[1]
if not type_check.same_types(*inputs):
raise ValueError('numpy and cupy must not be used together\n'
'type(W): {0}, type(x): {1}'
.format(type(W), type(x)))
y = x.dot(W.T).astype(x.dtype, copy=False)
if len(inputs) == 3:
b = inputs[2]
y += b
self.retain_inputs((0, 1)) # b is not retained
return y,
def backward(self, indexes, grad_outputs):
x, W = self.get_retained_inputs()
gy, = grad_outputs
ret = []
if 0 in indexes:
gx = linear(gy, W.T)
ret.append(chainer.functions.cast(gx, x.dtype))
if 1 in indexes:
gW = linear(gy.T, x.T)
ret.append(chainer.functions.cast(gW, W.dtype))
if 2 in indexes:
gb = chainer.functions.sum(gy, axis=0)
ret.append(gb)
return ret
def linear(x, W, b=None):
"""Linear function, or affine transformation.
It accepts two or three arguments: an input minibatch ``x``, a weight
matrix ``W``, and optionally a bias vector ``b``. It computes
.. math:: Y = xW^\\top + b.
Args:
x (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
:class:`cupy.ndarray`): Input variable, which is a :math:`(s_B, s_1, \
s_2, ..., s_n)`-shaped float array. Its first dimension
:math:`(s_B)` is assumed to be the *minibatch dimension*. The
other dimensions are treated as concatenated one dimension whose
size must be :math:`(s_1 * ... * s_n = N)`.
W (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
:class:`cupy.ndarray`): Weight variable of shape :math:`(M, N)`,
where :math:`(N = s_1 * ... * s_n)`.
b (:class:`~chainer.Variable` or :class:`numpy.ndarray` or \
:class:`cupy.ndarray`): Bias variable (optional) of shape
:math:`(M,)`.
Returns:
~chainer.Variable: Output variable. A float array with shape
of :math:`(s_B, M)`.
.. seealso:: :class:`~chainer.links.Linear`
.. admonition:: Example
>>> x = np.random.uniform(0, 1, (3, 4)).astype('f')
>>> W = np.random.uniform(0, 1, (5, 4)).astype('f')
>>> b = np.random.uniform(0, 1, (5,)).astype('f')
>>> y = F.linear(x, W, b)
>>> y.shape
(3, 5)
"""
if x.ndim > 2:
x = x.reshape(len(x), -1)
if b is None:
args = x, W
else:
args = x, W, b
y, = LinearFunction().apply(args)
return y