ほのぼのしたエンジニアのブログ

触った技術についてまとめていくブログです。

Chainerに入門してみる:Links

どうもShZgZです。

shzgz.hatenablog.com
↑前回に引き続きChainerの公式ガイドを進めていきます。

↓Chainer公式ガイド
Links — Chainer 4.5.0 documentation

"補足"ってなってるところは付け足しているところで、
それ以外は大体原文の和訳になってます。

Links

NN(ニューラルネット)をコーディングするためには、
パラメータを持った関数を結合し、そのパラメータを
最適化しなければならない。
このために(最適化の目標となるような)パラメータを持つ
Linkクラスのオブジェクトを用いる。

リンクの中で最も基本的なものはパラメータによって
いくつかの引数の値を置き換える正則関数のような
振る舞いをするリンクです。今後、よりハイレベルなリンクを導入
するが、ここではリンクはパラメータを持つシンプルな関数とみなす。

もっとも頻繁に使うリンクはLinearリンクです。(Linearリンクは
Fully-connected Layerもしくはアフィン変換としても知られている)
Linearリンクは数学的な関数f(x) = Wx + bを表しており、
行列W、ベクトルbはパラメータである。このリンクは
純粋にx, W, bを引数にとるlinear()に対応している。
3次元から2次元への線形リンクは以下のように定義される。

f = L.Linear(3,2)

~~~!Note
ほとんどの関数やリンクは、入力列の第1次元がバッチの次元と
見なされるようなミニバッチの入力のみを受けとる。
上の線形リンクの場合は入力は(N, 3)という形にならなければならない。
(Nはミニバッチのサイズ)
~~~

リンクのパラメータは属性として保存され、それぞれの
パラメータはVariableのインスタンスとなる。線形リンクの
場合はパラメータW,bが保存される。デフォルトでは
行列Wはランダムに、ベクトルbは0で初期化される。
これらのパラメータを初期化する上では、この方法は
よく使われるものです。

f.W.data # array([[ 1.0184761 ,  0.23103087,  0.5650746 ],
         #        [ 1.2937803 ,  1.0782351 , -0.56423163]], dtype=float32)
f.b.data # array([0., 0.], dtype=float32)

線形リンクのインスタンスの挙動は普通の関数と似ている

x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
y = f(x)
y.data # array([[3.1757617, 1.7575557],
       #        [8.619507 , 7.1809077]], dtype=float32)

~~~!Note
入力空間の次元を計算することは能率の悪いことがある。
線形リンクといくつかの(逆)畳み込み((de)convolution)は
そのインスタンスの入力次元を省略し、最初のミニバッチの
入力から推論することができる。

例えば、以下のリンクは出力層の次元が2の線形リンクとなる。

f = L.Linear(2)

もし、(2,M)の形のミニバッチを渡せば、入力次元はMとして
推論される。これはl.Wが2×W行列となるということ。
最初のミニバッチにおいてゆっくりとパラメータが初期化される。
そのため、データがリンクに与えられない限りlはW属性を持たない。
~~~

パラメータの勾配はbackward()メソッドによって計算される。
勾配がメソッドによって上書きではなく、積み重ねられることに
注意すると、まずは計算を更新するために勾配をクリアしなければならない。
それはcleargrads()メソッドで行う。

f.cleargrads()

~~~!Note
cleargrads()は効率化のためzerograds()に代わるため
v1.15で導入された。zerograds()は後方互換性のために残っている。
~~~

y.grad = np.ones((2, 2), dtype=np.float32)
y.backward()
f.W.grad # array([[5., 7., 9.],
         #        [5., 7., 9.]], dtype=float32)
f.b.grad # array([2., 2.], dtype=float32)