#はじめに
ニューラルネットワークにおける重みパラメータ初期値の与え方について、基礎的な内容をまとめました。重みパラメータとは、ニューロンを組み合わせる際に用いる係数のことです。下記図では層の間を繋ぐ際に入力層と掛け合わせる値になります。
###参考文献
今回は、どちらもオライリーから出版されている下記二冊を参考に記事を作成しました。
- ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- scikit-learnとTensorFlowによる実践機械学習
概要は下記です。
- 重みパラメータの設定
- 勾配消失の課題解決(Xavierの初期値について)
- MNISTデータを用いて比較する
##重みパラメータの設定
多層パーセプトロンにおいて、重みパラメータを更新する手法はモデルの学習精度・速度を決めるため重要です。重みパラメータの更新は損失関数の勾配が必要になります。この勾配は、多層パーセプトロンにおいては逆誤差伝播法によって求められます。
この逆誤差伝播法において、重みパラメータはどのように設定されるべきか考えます。サンプルプログラムとして、5つの層があり、それぞれの層が100個のニューロンを持つ多層パーセプトロンを考えます。
入力データは1000個のデータをガウス分布でランダムに生成し、それらをこの多層パーセプトロンへ流します。活性化関数はシグモイド関数を利用します。
input_data = np.random.randn(1000, 100) # 1000個のデータ
node_num = 100 # 各隠れ層のノード(ニューロン)の数
hidden_layer_size = 5 # 隠れ層が5層
activations = {} # ここにアクティベーションの結果を格納する
x = input_data
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
w = np.random.normal(0,0,(node_num, node_num))#この値を変えて重みパラメータを決めます
a = np.dot(x, w)
z = sigmoid(a)
activations[i] = z
###重みパラメータ値が大きい場合
まず、重みパラメータの初期値が極端に値が大きい場合を考えます。その時の重みパラメータと各層のアクティベーションの結果(=活性化関数後の値)を見てみます。
重みパラメータの標準偏差を10とした場合です。
各層のアクティベーション値は0と1に偏った分布となることが分かります。活性化関数の出力値が0か1に偏ることは、重みパラメータの更新においてこの出力値を用いる際に勾配の値が小さくなることを意味します。すると、重みパラメータが更新されないことに繋がります。これは勾配消失と呼ばれる問題になります。
###重みパラメータが0の場合
続いて、重みパラメータの標準偏差を0.0とした場合です。
アクティベーションの値は0.5に集中する結果となりました。**これはアクティベーションに偏りがある状態です。**先ほどの勾配消失の問題は起きていません。**しかし、偏りがあることはモデルの表現力に問題があることを意味します。
というのも、複数のニューロンから同じ値を出力するとすると、複数存在している意味合いが無くなるからです。つまり層の数を少なくしても同様の結果となるということです。
###深層学習の発展経緯との関係
先ほどみられた勾配消失現象は、経験的に観測されていた課題です。この課題は多層パーセプトロン特有であり、研究があまり進められていない(≒人気ではなかった)一因でした。
多層パーセプトロン(≒ニューラルネットワーク)発想自体は1980年代から提唱されていたものの、莫大な計算コストや先ほどの勾配消失、局所最適解等といった課題があるため、あまり発展しませんでした。
2010年代に入って、GPUなどの高性能なハードウェアの開発や各種課題を解決するアルゴリズムが開発されたため、今日の流行に繋がっているようです。
##勾配消失の課題解決(Xavierの初期値について)
この勾配消失に関する課題については、2006年にトロント大学のHinton氏らが提案した事前学習と呼ばれる初期化手法が提案されています。
Reducing the Dimensionality of Data with Neural Networks
http://www.cs.toronto.edu/~hinton/science.pdf
本論文では多層パーセプトロンの重みパラメータを、**一旦オートエンコーダとして学習させて適切な値させる方法を提案しています。**また、一方でこのオートエンコーダ自体の重みパラメータをどのように与えるか、という問題もあります。
一方で、この勾配消失に関する挙動についての進展が2010年にありました。Xavier氏らの研究グループによって下記論文が発表されました。
Understanding the difficulty of training deep feedforward neural networks
http://proceedings.mlr.press/v9/glorot10a.html
当時最も使われていたシグモイド関数と重み初期化テクニックに対する疑問点を挙げました。この時、平均が0で標準偏差が1の正規分布を使ったランダムな初期化を行っていました。この場合、各層の出力の分散は、入力の分散より大きくなってしまいます。これで順方向にネットワークが進むと、分散が大きくなるためシグモイド関数の出力が0か1になってしまいます。
そこで、重みパラメータを各層で初期化する操作を行うことを著者らは提案しています。$n_{in}$は入力層のニューロン数、$n_{out}$は出力層のニューロン数としたとき、以下で示す分布を与えてることとしました。
$$
\theta \sim \mathcal{U}\left(-\sqrt{\frac{6}{n_{\textrm{in}}+n_{\textrm{out}}}}, \sqrt{\frac{6}{n_{\textrm{in}}+n_{\textrm{out}}}}\right) \quad \textrm{or} \quad \mathcal{N}\left(0, \sqrt{\frac{2}{n_{\textrm{in}}+n_{\textrm{out}}}}\right)
$$
この初期化方法を、Xavierの初期値またはGlorotの初期値と呼びます。
###実装により確認する
試しにXavierの初期値を用いて実行してみます。
この結果ではこれまでの結果よりも広がりのある分布となっていることが分かります。適度な広がりがあるため、シグモイド関数の表現力も制限されることもなく効率的に学習が行えそうです。
##Heの初期値について
同様に、He氏らの研究グループでも下記式による勾配消失が解消される方法を提案しています。先ほどのXavierの初期値は、活性化関数がシグモイド関数やTanh関数などの左右対称な場合に適している初期値です。
一方、ReLU関数を用いる場合に適している手法がHeの初期値になります。
各層の入力次元を$n_{\textrm{in}}$として, 次のように初期化します.
$$
\theta \sim \mathcal{U}\left(-\sqrt{\frac{6}{n_{\textrm{in}}}}, \sqrt{\frac{6}{n_{\textrm{in}}}}\right) \quad \textrm{or} \quad \mathcal{N}\left(0, \sqrt{\frac{2}{n_{\textrm{in}}}}\right)
$$
なお、$\mathcal{U}$は一様分布、$\mathcal{N}$は正規分布を表します。
##MNISTデータを用いて比較する
さて、MNISTデータを5層の多層パーセプトロン(各層100個のニューロン)で学習させます。この時、初期値の重みパラメータを以下のように与えて計算します。
- 標準偏差0.01
- Xavierの初期値(ガウス分布)、活性化関数はシグモイド関数
- Heの初期値(ガウス分布)、活性化関数はReLU関数
最適化手法は全てSGDを用いています。
Heの初期値、Xavierの初期値、標準偏差0.01の順で損失関数が下がりくくなっていることが分かりました。
#おわりに
今回、ニューラルネットワークにおけるパラメータ初期化の考え方をまとめました。ニューラルネットワークの学習においては、重みの初期値はとても重要なポイントであることを確認できました。重みの初期値によって、ニューラルネットワークの学習の成否が分かれることが多くあります。
自身で作成するモデルにおいてこれら初期値及び活性化関数が適切かどうかは注意しておく必要があるかもしれません。
プログラム全文はこちらです。
https://github.com/Fumio-eisan/Weight_20200502
Xavier,He等の重みパラメータ更新の比較はweight_init_activation_histogram.py
MNISTデータによる比較はweight_init_compare.py
になります。