updated on 2020-05-02
データは多くの場合、訓練、開発、テストに分割される。
開発セットの目的: 様々なアリゴリズムを比較し、どれが一番よく機能するか調べるため。
訓練/開発/テストセットの分割では以下のパターンがすごく一般的
数年前までは、データ数が100~10000ぐらいだったので理にかなっていたが、現代はビッグデータ時代になり、比率が変わってきた。
総データが100万以上の場合の適用例
(よくない例)
訓練セット: webページから取得して利用
開発セット、テストセット: アプリを使用しているユーザーから取得して利用
高バイアスの時
(ニューラルネットワークのアーキテクチャーの検索)
高分散の時
(ニューラルネットワークのアーキテクチャーの検索)
過学習が疑われるとき、つまり高分散の時にまず試すべきは、正則化
高分散に対処する別に方法は、訓練データをより集めることだが、いつでも多くのデータを集めれるわけではないし、多くのデータを集めるにはお金がかかる
正則化することで、過学習を防ぎ、誤差を減らせる。
L2(Ridge)正則化
損失関数にパラメータの2乗ノルムを加える。
L1(Lasso)正則化
スパース性(回帰係数の多くを0と推定する性質)がある
過学習の抑制においてはL2正則化の方が効果があり、ほとんどL2正則化が使われる。
wのみに正則化を施し、bに正則化をしないのは、実際には入れてもいいのだが、wは非常に高次元のベクトルで高分散の問題が起きやすい。
bはwに比べて低次元で、大量のパラメータの1つに過ぎないのでわざわざ入れる必要はない。一般的には省略。
次に、ニューラルネットワークのL2正則化
L2正則化が荷重減衰と呼ばれる理由は、通常の勾配降下で重みを更新する際、誤差逆伝播から得られた元の勾配のα倍を引くようなものだから。
過学習が起きる主な原因は、以下の3つ
(1)重みの値が大きいこと(あまり捉えなくて良い、細かな特徴量まで捉えてしまう) (2)ニューロンの数が多いこと(=重みパラメータの数が多いこと) (3)訓練データが少ないこと
正則化は(1)に対する解決策となる
ちなみに、(2)は各層のニューロンの数を手動で調節すること。
(3)は訓練データを増やすことで解決できる。
λは正則化の効果の強さをコントロールするための値。
大きければ大きいほど、損失関数の値が大きくなってしまうため、その分重みは小さくなろうとすることで整合性を保とうとする。
正則化は重みを0に近づけることで、過学習を防いでいる。
ドロップアウトはランダムに任意のニューロン(次元)を何割か無視してしまう技術である。入力データを増やせずとも、次元を減らすことで解の有意性を上げることができる。ドロップアウトして得た学習結果は、テスト時には同時に使用し、結果は平均して用いる。これはRandom forestと同様、検出率の低い識別器でも並列化することで信頼度を上げることができるためである。
ドロップアウトを適用せず(ランダムにユニットを排除しない)、トレーニングセット時のように(1/keep-prob)で割らない。
ドロップアウト率を重みの値に掛けてやる
ドロップアウト率0.5で学習データを訓練させた場合、テスト時には、ノードをドロップアウトさせることはせず、全てのノードを用いてデータを学習するが、その代わり各ノードの重みに0.5を掛けて値を小さくする。
ドロップアウトを使うと1つ1つのノードの重みが大きくなる。この状態でテスト時に全ノードを使って学習してしまうと、重みの値が大きすぎて正しい数値が計算できないので、重み全体をドロップアウト率で掛けてその値を小さくしている。
Hiltonの論文によれば、この方法を使えばドロップアウトしたのとほぼ同じ値が算出可能のようである。
ドロップアウトはニューラルネットワークの学習時に、一定割合のノードを不活性化させながら学習を行うことで過学習を防ぎ(緩和し)、精度をあげるために手法。
学習時に特定のノードを不活性化させて、学習を進めていくことで、過学習を抑えながらパラメーターの更新を行える。
データを増やす
過学習を抑えるためには、データを増やすことが有効
データを入手するにはお金がかかるが、以下の方法で無料でデータを増やすことができる
早期打ち切り (early stopping)
学習の反復において、訓練データと評価データの両方の評価値を監視し、評価データでの評価値が悪化し始める所で学習を早期に打ち切る方法
正規化が必要な理由
正規化をしていないとw, bから見たコスト関数は細長い関数になり, 最適解にたどり着くまでに, 学習率をとても小さな値に設定して、多くのステップを踏まなければならない。
正規化をしていると、よりシンメトリーな関数になり、学習が進みやすい。
シグモイド関数の微分は次の式
\( \sigma ' (x) = \sigma (x)(1- \sigma (x)) \)
シグモイド関数は0~1の出力をおこなう関数なので、逆伝播法で下流に流す勾配は上記の式より、1未満になる
逆伝播法で下流に流す勾配はだんだんと小さくなっていき消失してしまう。
ハイパボリックタンジェント関数の微分は次の式
$$ { y=tanh(x)= \frac{ e ^{ x } - e ^{ -x } }{ e ^{ x } + e ^{ -x } } \\ y'=1- y ^{ 2 } } $$
tanh関数を微分した値も1より小さい値となるため、逆伝播法で下流に流す勾配はだんだんと小さくなっていき消失してしまう。
勾配爆発と勾配消失を理解するために、L層のニューラルネットワークで考える。
簡単にするために、バイアスは0, 0層からL-1層までの重みは同じものとする。
重みが単位行列より大きい時、出力が指数関数的に大きくなり、単位行列より小さい時、出力が指数関数的に小さくなる。
同じく、誤差逆伝播法で勾配を下流に流すときも、Zの勾配にW.Tをかけて前の層に流していくため、指数関数的に大きくまたは小さくなる。
■行列の乗法の性質
行列の積が定義できるとき,一般に
1 (積に関する)結合法則が成立します。
(AB)C=A(BC)
[p×q型][q×r型][r×s型]→[p×s型]となります。(しりとりのルールです。)
2 (和の上への積の)分配法則が成立します。
(A+B)C=AC+BC
C(A+B)=CA+CB
3[重要] (積に関する)交換法則は成立しません
すなわち,AB=BAは必ずしも成立しません
一般に重みの初期値のバラツキを大きくすぎても、小さすぎても学習速度は遅くなる。このことから適切な初期値を設定しないといけないことが分かります。
また初期値は「勾配消失」、「表現力の制限」といった問題を引き起こすことがある。
\( この初期値はノード数nに対して平均0、標準偏差\sqrt{ \frac{ 2 }{ n } }である正規分布から重みを設定 \)
\( w ^{ [l] } = np.random.randn(n^{ [l] }, n^{ [l-1] }) \ast \sqrt{ \frac{ 2 }{ n^{ [l-1]} } } \)
Heの初期値はReLU関数を活性化関数とするときに用いるとよいでしょう。ReLU関数を活性化関数としたモデルにXavierの初期値を用いるとアクティベーション分布に偏りが生じやすくなることが知られています。
\( この初期値はノード数nに対して平均0、標準偏差\sqrt{ \frac{ 1 }{ n } }である正規分布から重みを設定 \)
\( w ^{ [l] } = np.random.randn(n^{ [l] }, n^{ [l-1] }) \ast \sqrt{ \frac{ 1 }{ n^{ [l-1]} } } \)
\( w ^{ [l] } = np.random.randn(n^{ [l] }, n^{ [l-1] }) \ast \sqrt{ \frac{ 2 }{ n^{ [l-1]}+n^{ [l]} } } \)
Xaivierの初期値は様々なニューラルネットで用いられている。sigmoid関数やtanh関数を活性化関数として用いる時、このXavierの初期値を用いるとよいでしょう。
ニューラルネットワークの活性化関数がReLU関数の時はHeの初期値を用いて、それ以外の時はXivierの初期値を用いるとよいでしょう。
微分の計算は\( f'(x)= \textstyle \lim_{h \to 0} \dfrac{ f(x+h)-f(x) }{ h } \)
しかし、リミットを使って計算をコンピューターで再現するときには近似式を用いる。
hを十分に小さい値( 10 ^{ -4 } ぐらい)に設定し、中心差分を使う。
前進差分、後退差分、中心差分があるが、中心差分の方が近似になる。
以下の方法で確認ができる。
\( 10 ^{ -7 } \) ぐらいになっていると正しく計算できている。
ニューラルネットワークの訓練には多くのデータを扱い、多くのイテレーションを行うため、学習に時間がかかる。
しかし、データを1つ1つ処理するのではなく、複数のデータをまとめて計算することで並列処理により、時間を節約できる。
5,000,000個のデータがある時、ベクトルとして1000個をまとめて計算する。
まとめて計算する数が多ければ多いほど、計算が早くなる一方で、コスト関数の最適化にノイズが生じる。
訓練データの総数がm個
バッチ数がmの場合、バッチ勾配降下法 (Batch Gradient Descent)
バッチ数が1の場合、確率的勾配降下法 (Stochastic Gradient Descent)
バッチ数が1からmの間の場合、ミニバッチ勾配降下法 (Mini Batch Gradient Descent)
バッチ勾配降下法
確率的勾配降下法
バッチ勾配降下法
ミニバッチの選択
小さなテストセット: バッチ勾配降下(m<=2000)
典型的なミニバッチのサイズ: \( 2 ^{ 6 }(64), 2 ^{ 7 }(128), 2 ^{ 8 }(256), 2 ^{ 9 }(512) \)
確率的勾配降下法だと、コスト関数の最適化において、上下に発振しながら最適値を探すため、学習に多くのステップの経過を必要とする。
確率的勾配降下法(SGD)のこの弱点を補った手法の1つがモーメンタム(Momentum)
SGDより滑らかな経路でパラメータを探索する
Root Mean Squared Propの略。モーメンタムのように、勾配降下を早める手法の1つ。
$$ { S _{ dw } = \beta S _{ dw } + (1-\beta)d w ^{ 2 } \\ S _{ db } = \beta S _{ db } + (1-\beta)d b ^{ 2 } \\ w:=w-\alpha \dfrac{ dw }{ \sqrt{ S _{ dw } } }, b:=b-\alpha \dfrac{ db }{ \sqrt{ S _{ db } } } \\ ただし、指数の2は行列のアダマール積(要素積)を表す } $$
MomentumとRMSPropの両方を利用した手法
$$ { V _{ dw }=0, S _{ dw }=0, V _{ db }=0, S _{ db }=0 \\ On\ iteration\ t: \\ \ \ Compute\ dw,\ db\ using\ current\ mini batch \\ \ \ V _{ dw } = \beta _{ 1 } V _{ dw } + (1-\beta _{ 1 })d w, V _{ db } = \beta _{ 1 } V _{ db } + (1-\beta _{ 1 })d b \\ \ \ S _{ dw } = \beta _{ 2 } S _{ dw } + (1-\beta _{ 2 })d w ^{ 2 }, S _{ db } = \beta _{ 2 } S _{ db } + (1-\beta _{ 2 })d b ^{ 2 } \\ \ \ V_{dw}^{current} = V _{ dw } / (1-\beta _{ 1 }^{t}), V_{db}^{corrected} = V _{ db } / (1-\beta _{ 1 }^{t}) \\ \ \ S_{dw}^{corrected} = S _{ dw } / (1-\beta _{ 2 }^{t}), S_{db}^{corrected} = S _{ db } / (1-\beta _{ 2 }^{t}) \\ \ \ w:=w-\alpha \frac{ V_{dw}^{corrected} }{ \sqrt{ S_{dw}^{corrected} }+\varepsilon }, b:=b-\alpha \frac{ V_{db}^{corrected} }{ \sqrt{ S_{db}^{corrected} }+\varepsilon } } $$
ハイパーパラメータの選択
$$ { ->\alpha: 調整が必要 \\->\beta_{1}: 0.9 -> (dw) \\->\beta_{2}: 0.999 -> (dw^{2}) \\-> \varepsilon: 10 ^{ -8 } } $$
学習を早めるのを助ける方法の1つとして、繰り返し徐々に学習率を下げていく方法がある。(いわゆる学習率の減衰)
その他の学習率減衰(learning rate decay)
$$formula\left\{\begin{array}{ll}\alpha= \frac{ 1 }{ 1+decay\_rate \ast epoch\_num } \alpha _{ 0 } & (先に挙げた手法) \\\alpha=0.95^{epoch\_num} \alpha _{ 0 } & (exponentially \ decay) \\ \frac{ k }{ \sqrt{ epoch\_num } } \alpha _{ 0 } \ または \frac{ k }{ \sqrt{mini\_batch\_num } } \alpha _{ 0 } \\その他,decrete\ staircase\ decay, manual\ decay & などがある \\\end{array}\right.$$
上の左側の画像は、人々が局所最適について心配するときに心に留めていた写真。
この写真には局所的最適解が多く存在する。
しかし、実際は異なる。右が実際のコスト関数の描画であり、ゼロ勾配のほとんどの点は、このような点のような局所最適では無い。代わりに、コスト関数の勾配がゼロのほとんどの点は鞍点(あんてん)である。
まとめ
適度に大きなニューラルネットワークをトレーニングし、多くのパラメーターを保存し、コスト関数Jが比較的高次元の空間で定義されている限り、実際には悪いローカルオプティマで立ち往生することはほとんどない。
局所最適はあまり問題では無い。本当の問題は、高原をランダムに動きながら降っていくため、パラメータを見つけるまでに多くのステップを必要とすること。これを解決するために、RMSProp, Adamがある。
$$ { β= 10 ^{ a }...10 ^{ b }でランダムサンプリング \\ \log_{ 10 }β=a...b \\ r=[a,b]=np.random.uniform(low=a, high=b) \ (np.random.uniformはlow以上high未満の少数を返す) \\ β=10 ^{ r }\\ } $$
$$ { β= 10 ^{ a }...10 ^{ b } \\ β=0.9...0.99の範囲でβをランダムサンプリング \\ 1-β=10 ^{ -2 }...10 ^{ -1 } \\ \log_{ 10 } (1-β)=-2...-1 \\ r=[-2,-1]=-np.random.rand()-1\ (np.random.rand()は0以上1未満の少数を出力する関数)\\ 1-β=10 ^{ r } \\ β=1-10 ^{ r } =1-10^{ -np.random.rand()-1 } } $$
大きく分けて、3 つあるようだ。
昔からある手法で、格子状の空間で最適なパラメータを探索する方法です。 格子の範囲を総当りするため、膨大な計算時間がかかるという課題がある。
無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがある。
無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがある。 ディープラーニングを含む機械学習の手法で、比較的良いハイパーパラメータを探索できることが知られている。
また、bengio 先生のおすすめレシピというのがある。
ハイパーパラメータはグリッドサーチするのではなく,ランダムサンプリングしたほうが性能が出る場合が多い
$$ { l層の入力として、m個のデータからなるミニバッチ \\ Z= \left \{ { z ^{[l](1)}, z ^{[l](2)}, ..., z ^{[l](m)} } \right \} \\ (以降[l]は省略) \\ μ: ミニバッチの入力の平均 \\ σ^{2}: ミニバッチの入力の分散 (ここで、γとβはモデルの中で学習できるパラメータ) \\ μ= \dfrac{ 1 }{ m } \sum_{i=1}^m z ^{ (i) } \\ σ^{ 2 } = \dfrac{ 1 }{ m } \sum_{i=1}^m (z ^{ (i) } - μ)^{ 2 } \\ z_{ norm } ^{ (i) } = \frac{ z^{ (i) }-μ }{ \sqrt{ σ^{2} + ε} } \\ \tilde{ z }^{(i)} = γ \ z_{ norm } ^{ (i) }+β \ (Batch \ Normalizationの出力) } $$
訓練時はミニバッチごとに正規化し、一方、テスト時は、全訓練データから学習した情報で正規化したいため、 分散は訓練データの平均、不偏分散を使う。
しかし、バッチノルムは一度に1つのミニバッチでデータを処理するが、テスト時はデータが多くなくて、例を1つずつ処理する必要がある(m=1)場合があります。
そのような場合、一般的な実装では、ミニバッチ全体の平均である指数加重平均を使用してこれを推定する。
$${ テスト時に、訓練時と異なるバッチサイズを使用するとき\\ 訓練データのμ ^{ [l] } は使用せずに、訓練中に見られたミニバッチ全体の指数加重平均 μ ^{ \{1\}[l] }, μ ^{ \{2\}[l] },...を使ってμを推定する(分散も同様) \\ μ ^{ [l] }=0 \\ for\ i\ in\ range(1, t): \\ \ \ μ ^{ [l] }=βμ ^{ [l] }+(1-β)μ ^{ \{i\}[l] } \\ \ \ {σ^2} ^{ [l] }=β{σ^2} ^{ [l] }+(1-β){σ^2} ^{ \{i\}[l] } }$$
$$ { z_{ norm } ^{ (i) } = \frac{ z^{ (i) }-μ ^{ [l] } }{ \sqrt{ σ^{2[l]} + ε} } \\ \tilde{ z }^{(i)} = γ \ z_{ norm } ^{ (i) }+β \ (Batch \ Normalizationの出力) } $$
ニューラルネットワークでは、活性化関数を適用する前に、バッチ正規化を行う。
内部共変量シフトの補足:
内部共変量シフトとは、入力時のxの分布が、学習途中で大きく変わってくることをさす。
このシフトに合わせることにweightたちは躍起になるので、層自体の学習はそのあとでしか進まない。
そのため、学習が遅くなる。
バッチ正規化は、このシフトを正規化してくれるもの。
同時に白色化(正規化+無相関化)おこなう。
出力層は隠れ層とは区別して設計する。活性化関数と誤差関数をセットで設計する必要がある。そして、問題の種類ごとにそれらは異なる。絶対的なルールはないが、下記の方法が一般的な方法である。ここであげた手法は誤差関数の偏微分が全て出力と目標値の差になっていて扱いやすく設定されている。
変数の使い方は以下の通り。
$${ {\displaystyle N}:訓練データの個数 \\ {\displaystyle d_{n}}:n番目の訓練データの目標値(正解値) \\ {\displaystyle y_{n}}:n番目の訓練データの出力 }$$
活性化関数は恒等関数、誤算関数は二乗和誤差を用いる。
$${ 活性化関数:{\displaystyle \varphi (u)=u}\varphi(u) = u \\ 誤差関数:{\displaystyle E(w)={\frac {1}{2}}\sum _{n=1}^{N}\|y_{n}-d_{n}\|^{2}}E(w) = \frac{1}{2} \sum_{n=1}^N \| y_n - d_n \|^2 \\ 誤差関数の偏微分:{\displaystyle {\frac {\partial E_{n}(w)}{\partial u}}=y_{n}-d_{n}}\frac{ \partial E_n(w) }{ \partial u } = y_n - d_n } $$
活性化関数はシグモイド関数、誤算関数は二乗和誤差を用いる。
出力は1つで確率と解釈する。d_n は 0 または 1 もしくは確率。誤差関数は最尤推定で導出できる。
$${ 活性化関数:{\displaystyle \varphi (u)=\varsigma _{1}(u)={\frac {1}{1+e^{-u}}}}\varphi(u) = \varsigma_1 (u) = \frac{1}{1+e^{-u}} \\ 誤差関数:{\displaystyle E(w)=-\sum _{n=1}^{N}(d_{n}\log y_{n}+(1-d_{n})\log(1-y_{n}))}E(w) = - \sum_{n=1}^N (d_n \log y_n + (1 - d_n) \log (1 - y_n)) \\ 誤差関数の偏微分:{\displaystyle {\frac {\partial E_{n}(w)}{\partial u}}=y_{n}-d_{n}}\frac{ \partial E_n(w) }{ \partial u } = y_n - d_n \\ }$$
活性化関数はソフトマックス関数、誤算関数は交差エントロピー誤差を用いる。
$$ { K 個のクラスに分類する。\\ 出力は K 個で、総和は 1 であり、そのクラスに所属する確率と解釈する。\\ {\displaystyle \sum _{k}d_{n,k}=1} \\ {\displaystyle d_{n,k}} は 0 または 1 もしくは確率で、n 番目の訓練データがクラス k に所属する時 1。\\ ソフトマックス関数では、指数の割り算になっている。\\ e ^{ 100 } は0 が40 個以上も並ぶ大きな値になりe ^{ 1000 }の結果は無限大を表す\infが返って来るため、数値が不安定な結果になってしまう。\\ コンピュータで表現できる数値の範囲を超えることをオーバーフローという。\\ {\displaystyle u_{k}}は、全て同じ数を引いた場合は結果が同じになる事を利用して、{\displaystyle \max _{k}u_{k}}を全てから減算して活性化関数を計算すると良い。 } $$
$${ {\displaystyle ソフトマックス関数: \varphi (u_{k})=\frac{ exp(u_{k}) }{ {\sum_{i=1}^{K}exp(u_{i})} }=\frac{ Cexp(u_{k}) }{ {C \sum_{i=1}^{K}exp(u_{i})} } }\\ {\displaystyle =\frac{ exp(u_{k}+logC) }{ {\sum_{i=1}^{K}exp(u_{i}+logC)} } }\\ {\displaystyle =\frac{ exp(u_{k}+C') }{ {\sum_{i=1}^{K}exp(u_{i}+C')} } }\\ {\displaystyle C'は、\max _{k}u_{k} が一般的に使われる} }$$
$${ {\displaystyle 活性化関数: \varphi (u_{k})=\frac{ exp(u_{k}) }{ {\sum_{i=1}^{K}exp(u_{i})} }=\frac{ exp(u_{k}-\max _{k}u_{k}) }{ {\sum_{i=1}^{K}exp(u_{i}-\max _{k}u_{k})} } }\\ 誤差関数:{\displaystyle E(w)=-\sum _{n=1}^{N}\sum _{k=1}^{K}d_{n,k}\log y_{n,k}} \\ 誤差関数の偏微分:{\displaystyle {\frac {\partial E_{n}(w)}{\partial u_{k}}}=y_{n,k}-d_{n,k}} } $$
フレームワークを選ぶ観点
注意を払うべきことの1つは、フレームワークが単一の企業の管理下に置かれるのではなく、長期にわたってオープンソースのままであることにどれだけ信頼できるかどうか。
ソフトウェアが現在オープンソースでリリースされている場合でも、何らかの理由で将来的には終了することを選択する可能性がある。
今回受講したのはこちらのコースです。
https://www.coursera.org/learn/deep-neural-network
実装する以前に、ニューラルネットワークの知識が不十分である人向けの内容であると感じました。
きちんとニューラルネットワークに関して未学習や過学習、その他勾配降下や誤差関数をどのようなものか理解していない人は、コリラのコースでしっかり学習と内容の定着ができるでのおすすめです。