ゼロから作るDeep Learning 3 ステップ33~ステップ36 まとめ
hirohirohirohiros.hatenablog.com
ステップ35
高階微分の計算グラフ
tanh関数を作成し,微分を何回も行うことで複雑な計算グラフを作る事が出来ます.本書でも,8階微分の計算グラフを掲載し,ひときわ目立つページとなっています.
自分のPCでもより大きな階数の計算グラフを作ろうと思いましたがかなり時間がかかります.6階のグラフを実行するだけで20秒ほどかかります.そのため,自分のheapqで実装したコードが高速化に役立っているかも?と思い,本書のソート版と自分のヒープ版の速度比較をしようかと思いましたが,それより先に確認すべき事があります.
このようなコードを用意しました.
start = time.time() x = Variable(np.array(1.0)) y = F.tanh(x) x.name = "x" y.name = "y" y.backward(create_graph=True) iters = 5 for i in range(iters): gx = x.grad x.cleargrad() gx.backward(create_graph=True) gx = x.grad gx.name = "gx"+str(iters+1) print(f"finish make graph, time:{time.time()-start}") plot_dot_graph(gx, verbose=False, to_file="tanh.png") print(f"finish plot graph, time:{time.time()-start}")
timeに関連する所以外は全て本書のコードと同じです.plot_dot_graph関数の前と後でかかった時間を見ることで,計算グラフを作るところと,それをプロットするところでどれだけ時間がかかっているか分かります.
実行してみると,
finish make graph, time:0.007999658584594727 finish plot graph, time:21.891921520233154
となりました.なんと計算グラフを作る工程でかかる時間は0.01秒もありません.グラフをプロットし,画像を生成するところで21秒もかかっています.これではソートとヒープの早さの違いもほとんどなさそうです.
グラフのプロット工程の高速化は難しそうなのでそのまま実行します.
8階微分の計算グラフを表示させようとしましたが問題が起きました.
finish make graph, time:0.05601334571838379 finish plot graph, time:381.08068919181824
と6分以上グラフのプロットに時間を掛けたのに画像が作られていませんでした.もしかしたらオブジェクト数が多すぎて画像を作れなかったのかもしれません.
更に,9階微分のクラフを作ろうとしたら
finish make graph, time:0.5899984836578369 finish plot graph, time:6.70032811164856
となって,明らかにグラフをプロットする前に処理が終了してしまっています.処理が重たすぎて自動的に終了させるようにgraphvizがなっているのかもしれません.
グラフの計算だけなら0.5秒で出来ているので,通常の操作なら問題無いのでとりあえずよしとします.
ニュートン法の問題点
ニュートン法は2回微分することで最適化しますが,多次元配列の時は2回微分に当たるヘッセ行列を使います.ニュートン法の問題点はパラメータの数が多すぎるとヘッセ行列の逆行列の計算に時間がかかることです.
メモリスペースがn^2オーダーで必要になり,n^3オーダーの計算量が求められます.ニューラルネットワークはパラメータの数が10^6個を超えることが普通にあります.そして,10^6個のパラメータのヘッセ行列は10^6*10^6サイズのヘッセ行列が必要になり,10^12もの計算になってしまいます.10^24を扱えるメモリサイズを用意するのは現実的ではなさそうです.
このような問題を回避するために,ヘッセ行列の逆行列を近似して求める手法が準ニュートン法としてあります.しかし,現状ディープラーニングの分野では,最適化はSGDやAdamなど,勾配だけを使った最適化が主流で,L-BFGSなどの準ニュートン法が使われることは多くないそうです.理由は本書に載っておらず,分からなかったので今後調べてみたいと思います.