Chainer:初期勾配を与えたら配列でも微分できた


2017年 01月 25日

前回、変数を配列にしたらエラーがでてしまった。

それで、やはりマニュアル、イントロなどを読まねばと非常にまともなことを考えて読み始めた。

Introduction to Chainerの中のForward/Backward Computationに、配列を与えた場合の微分の例が見つかった。

こういうときは、初期勾配(全要素が1.0)を用意してからbackward()をやらないといけないらしい。
それで、このように変更してみた。
>>> y = F.sin(x)
>>> y.grad = np.ones(21,dtype=np.float16)     # 追加, 勾配の初期化
>>> y.backward()
>>> x.grad
array([ -1.00000000e+00,  -9.51171875e-01,  -8.09082031e-01,
-5.87890625e-01,  -3.08837891e-01,   4.83751297e-04,
3.08837891e-01,   5.87890625e-01,   8.09082031e-01,
9.51171875e-01,   1.00000000e+00,   9.51171875e-01,
8.09082031e-01,   5.87890625e-01,   3.08837891e-01,
4.83751297e-04,  -3.08837891e-01,  -5.87890625e-01,
-8.09082031e-01,  -9.51171875e-01,  -1.00000000e+00], dtype=float16)
これで、配列xの各値に対するsin(x)の微分係数が求まっているはず。
もちろん、sin(x)の微分はcos(x)なので確認しよう。

>>> dy = F.cos(x)
>>> dy.data
array([ -1.00000000e+00,  -9.51171875e-01,  -8.09082031e-01,
-5.87890625e-01,  -3.08837891e-01,   4.83751297e-04,
3.08837891e-01,   5.87890625e-01,   8.09082031e-01,
9.51171875e-01,   1.00000000e+00,   9.51171875e-01,
8.09082031e-01,   5.87890625e-01,   3.08837891e-01,
4.83751297e-04,  -3.08837891e-01,  -5.87890625e-01,
-8.09082031e-01,  -9.51171875e-01,  -1.00000000e+00], dtype=float16)
どうやら一致しているようだが、目視確認なんて面倒くさい。

ということで、こうやって確認した。
>>> x.grad-dy.data
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,
0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.], dtype=float16)
Lazyはプログラマの最大の美徳であることを忘れてはいけない。
Lazyな確認方法により信頼性が一気に向上した。

これで、どうやらsin(x)の微分はできているみたいだが、こんな簡単な微分ができたから、Chainerの微分がちゃんと動作していると考えるのは甘いだろう。
次からは、もっと複雑な関数を微分してみよう。