前回、層を重ねると抽象度の異なる特徴を段階的に抽出できるという話を書きました。ただ、2010年代初頭まで、ネットワークを深くすると学習がうまくいかないという問題がありました。理論的には深いほうがいいのに、実際に深くすると性能が出ない。
今回はその原因である勾配消失問題と、解決策として普及したReLU、そして重みの初期化について書きます。
シグモイドの微分が小さすぎる
深層ネットワークの学習では、出力側で計算した誤差を連鎖律で入力側に伝播させます。ここでシグモイド関数を活性化関数に使っていると問題が起きます。
シグモイド関数の微分値は最大でも0.25です。層を1つ遡るたびに勾配にこの0.25以下の値が掛かるので、10層も遡れば勾配はほぼ0になります。入力に近い層の重みがほとんど更新されなくなる。
これが勾配消失問題(Vanishing Gradient Problem)です。層を深くするメリットが、活性化関数の微分の小ささで打ち消されていました。逆に、初期値が大きすぎると勾配が層を遡るごとに膨らんで発散する「勾配爆発」も起きます。どちらにしても学習が進まない。
ReLUが問題を解いた
ReLU(Rectified Linear Unit)は見た目も中身も単純な関数です。
\[ f(z) = \max(0, z) \]入力が正ならそのまま通し、負なら0にする。微分は正の領域で常に1です。
シグモイドだと層ごとに0.25をかけていたのが、ReLUなら1をかけるだけなので、何層遡っても勾配が小さくならない。これで勾配消失が解消されます。
シグモイドのような滑らかな曲線のほうが脳に近いと長く考えられていたので、こんな折れ線グラフみたいな関数がうまくいくのは当時意外だったらしく、2012年のAlexNetが画像認識コンペで圧勝したあたりから急速に広まりました。自分も最初に知ったとき、こんなに単純でいいのかと拍子抜けした覚えがあります。
重みの初期化も大事
活性化関数を変えても、重みの初期値が悪いと学習は進みません。
全ての重みを0で始めると、すべてのニューロンが同じ出力を返して対称性が崩れず、学習が止まります。大きすぎると勾配爆発、小さすぎると勾配消失。
現在は層のノード数に合わせて初期値の分散を調整する方法が標準です。シグモイド系にはXavier初期化、ReLU系にはHe初期化が使われています。PyTorchではデフォルトで設定されているので自分で触らないことも多いですが、学習がうまくいかないときに初期化を見直すのは基本的なデバッグ手順の一つです。
まとめ
深いネットワークが学習できなかったのは、シグモイドの微分が小さすぎて勾配が入力側に届かなかったからです。ReLUと適切な初期化でこの問題が解消され、深層学習が実用になりました。
ただ、層を深くしてパラメータを増やせば過学習のリスクも上がりますし、学習の安定性にも別の工夫が要ります。次回はバッチ正規化とドロップアウトについて書きます。