chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

【DLfS】バックプロパゲーションの実装

「ゼロから作るDeep Learning」(DLfS本)を読んで、算術的微分で実装して一定の学習効果を確認したので、次はバックプロパゲーションの理解にとりかかった。
バックプロパゲーションを理解するのに、DLfS本では計算グラフを取り上げ、計算を演算子のノードとノード間の結合で表現した計算グラフを作り、出力側から逆向きにノードの偏微分で掛け合わせてノードを入力まで辿ることで、式全体の微分が出来上がると書かれていた。大よそ、これは微分の連鎖律なのだというのと、掛け算、足し算のノードの実装(逆伝搬)までは理解できた。が、、P147、Affine/Softmaxレイヤの微分(行列の微分と言えばいいのか)については、DLfS本では次数を合わせるという程度の説明に留まっており、なんで転置行列を掛けたのが偏微分なのか分からず(P148,(5.13))。やはり行列の微分を基礎からやらないとキッチリ理解できないのだろうと思った。
計算グラフによる微分を理解しようと、自動微分の本も買ったけど、サクサク読むには難しくて、自動微分の根本原理を押さえるというか数学的にきっちり積み上げるのは一旦放置して、、DLfS本で示された、バックワードの演算式を盲目的に使って一旦バックプロパゲーションを組むことにした。
いきなり大規模なネットワークを組んでしまうとデバッグも難しいのでミニマムなネットワークで、従来の算術的微分と、バックプロパゲーションによる微分を比較した。使ったネットワークは左の図の通り。全結合(Affine)1層とSoftMax、CrossEntropyErrorの計算によるシンプルな構造。従来の数値的微分バックプロパゲーションによる微分を比べてみた(Weightの傾きを計算)。

結果、数値的微分(重みの傾き)と

[[-0.40279987313873722, 0.40279987313873722], 
 [-0.050349984141995208, 0.050349984141995208], 
 [-0.050349984141995208, 0.050349984141995208]]

バックプロパゲーションによる微分(重みの傾き)がほぼ一致したので、バックプロパゲーションの実装は正しく行えたと判断

[[-0.40479977  0.40479977]
 [-0.05059997  0.05059997]
 [-0.05059997  0.05059997]]

今後の取り組みは、俺NNをバッチ対応にして、過去に実装したのと同じネットワークを構築してバックプロパゲーションで学習させて、正しく学習できるか、どの程度早くなったのかを確認する予定。

■補足
DLfS本で不明な点
(1)Biasの計算方法
P150 5.6.2 バッチ版Affineレイヤの章で、順伝播でのバイアス加算は、、逆伝搬では集約される必要があるという説明ですが、(+)の演算は偏微分してもそのまま流すだったのでは??
doutは2次元の行列なので、Biasの形状である1次元に集約されるべきという観点では、sumで情報を集約するのだろうという気はするけど、数学的に理解できず。。
ー>この疑問に対しては、各バッチで得られたバイアスの平均を計算しているのだと理解。すなわち、平均とは「バイアス合計/バッチ総数」で算出されるのだけど、「/バッチ総数」の演算はSoftMaxWithLossで済んでおり、平均を出すためのバイアス合計を伝播した誤差に対して行っているのだろうと。。

(2)バックプロパゲーション(バッチ版)による複数サンプルの傾き平均値
バッチ版は複数のサンプルにより複数の傾きが出るので、数値的微分での傾きの算出はバッチサイズで合計値を割って補正していました(NLfS本 P94で示される式より)。
一方、バックプロパゲーションでは、Wの微分は行列計算したら各サンプルが加算された結果が出る。でもその行列は要素数で割らない(P152 Affineのbackwardメソッド)。割らないと平均にならないのだけど、、それでいいのか???
ー>この疑問については、、P156でSoftMaxWithLossのメソッド、backward内でバッチサイズでdoutをあらかじめ割り算されていました。ですので、、解決です。