3.3 重み共有

Ivoryライブラリでは重みを共有したときの勾配の加算は自動で行われます。

import numpy as np
from ivory.core.trainer import sequential

N, L, M = 3, 2, 3
net = [("input", L), ("affine", L), ("affine", L, "softmax_cross_entropy")]
trainer = sequential(net)
model = trainer.model
m1, m2, s = model.layers
w = np.random.randn(L, L)
m1.W.variable.data = w
m2.W.variable.data = w.copy()

[1] 2019-06-19 22:10:46 (193ms) python3 (193ms)

データを作成します。

x = np.random.randn(N, L)
t = np.random.randint(0, L, N)
model.set_data(x, t)
model.forward()
model.backward()
print(m1.W.g)
print(model.numerical_gradient(m1.W.variable))

print(m2.W.g)
print(model.numerical_gradient(m2.W.variable))

[2] 2019-06-19 22:10:46 (15.1ms) python3 (208ms)

[[-0.40498775  1.0927607 ]
 [ 0.28648926 -0.77302142]]
[[-0.40498734  1.0927596 ]
 [ 0.2864887  -0.77301992]]
[[-0.08368836  0.08368836]
 [ 1.0943208  -1.0943208 ]]
[[-0.08368839  0.08368839]
 [ 1.09431886 -1.09431886]]

勾配を加算してみます。

print(m1.W.g + m2.W.g)

[3] 2019-06-19 22:10:46 (5.00ms) python3 (213ms)

[[-0.48867611  1.17644907]
 [ 1.38081006 -1.86734222]]

重みを共有し、トレーナーをビルドします。

m2.W.share_variable(m1.W)
trainer.build()
for v in trainer.optimizer.variables:
    print(v)

[4] 2019-06-19 22:10:46 (7.99ms) python3 (221ms)

<Variable(['Affine.1.W', 'Affine.2.W'], (2, 2)) at 0x210cf452390>
<Variable(['Affine.1.b'], (2,)) at 0x210cf4523c8>
<Variable(['Affine.2.b'], (2,)) at 0x210cf452438>

重みが共有されていることが分かります。順伝搬と逆伝搬を行います。

model.forward()
model.backward()
print(m1.W.g)
print(m2.W.g)
print(model.numerical_gradient(m1.W.variable))
print(model.numerical_gradient(m2.W.variable))

[5] 2019-06-19 22:10:46 (13.0ms) python3 (234ms)

[[-0.48867611  1.17644907]
 [ 1.38081006 -1.86734222]]
[[-0.48867611  1.17644907]
 [ 1.38081006 -1.86734222]]
[[-0.48867573  1.17644799]
 [ 1.38080756 -1.86733878]]
[[-0.48867573  1.17644799]
 [ 1.38080756 -1.86733878]]

加算した勾配に等しいことが分かります。

次に、転置した重みを共有することを考えます。

net = [("input", L), ("affine", M), ("affine", L, "softmax_cross_entropy")]
trainer = sequential(net)
model = trainer.model
m1, m2, s = model.layers
w = np.random.randn(L, M)
m1.W.variable.data = w
m2.W.variable.data = w.T.copy()

[6] 2019-06-19 22:10:46 (8.03ms) python3 (242ms)

データを入力します。

model.set_data(x, t)
model.forward()
model.backward()
print(m1.W.g)
print(model.numerical_gradient(m1.W.variable))

print(m2.W.g)
print(model.numerical_gradient(m2.W.variable))

[7] 2019-06-19 22:10:46 (14.0ms) python3 (256ms)

[[-0.00575904  1.01910273 -0.23117434]
 [ 0.0054599  -0.9661672   0.21916639]]
[[-0.00575904  1.01910201 -0.23117417]
 [ 0.00545988 -0.96616376  0.21916561]]
[[-8.19761747e-04  8.19761747e-04]
 [ 9.63815137e-01 -9.63815137e-01]
 [-2.25673019e-01  2.25673019e-01]]
[[-8.19503663e-04  8.19503663e-04]
 [ 9.63811574e-01 -9.63811574e-01]
 [-2.25672573e-01  2.25672573e-01]]

勾配を加算してみます。

print(m1.W.g + m2.W.g.T)

[8] 2019-06-19 22:10:46 (8.02ms) python3 (264ms)

[[-0.0065788   1.98291786 -0.45684736]
 [ 0.00627966 -1.92998234  0.44483941]]

重みを共有し、トレーナーをビルドします。このとき、transposeキーワードにTrueを指定します。

m2.W.share_variable(m1.W, transpose=True)
trainer.build()
for v in trainer.optimizer.variables:
    print(v)

[9] 2019-06-19 22:10:46 (7.00ms) python3 (271ms)

<Variable(['Affine.3.W', 'Affine.4.W'], (2, 3)) at 0x210ce6053c8>
<Variable(['Affine.3.b'], (3,)) at 0x210ce605898>
<Variable(['Affine.4.b'], (2,)) at 0x210ce6058d0>

重みが共有されていることが分かります。順伝搬と逆伝搬を行います。

model.forward()
model.backward()
print(m1.W.g)
print(m2.W.g)
print(model.numerical_gradient(m1.W.variable))
print(model.numerical_gradient(m2.W.variable))

[10] 2019-06-19 22:10:46 (14.0ms) python3 (285ms)

[[-0.0065788   1.98291786 -0.45684736]
 [ 0.00627966 -1.92998234  0.44483941]]
[[-0.0065788   1.98291786 -0.45684736]
 [ 0.00627966 -1.92998234  0.44483941]]
[[-0.00657854  1.98291358 -0.45684675]
 [ 0.00627938 -1.92997533  0.44483818]]
[[-0.00657854  1.98291358 -0.45684675]
 [ 0.00627938 -1.92997533  0.44483818]]

加算した勾配に等しいことが分かります。