深層学習前編2
勾配消失問題
- 勾配消失問題とは誤差逆伝播法が下位層に進んでいくにつれて、勾配がどんどん緩やかになっていく問題。つまり、下位層のパラメータを更新してもほとんど値が変わらず最適値に収束しなくなってしまう
- 特にシグモイド関数を使っている場合に起きやすい。その理由はシグモイド関数の微分は最大値が0.25であるためパラメータの更新値が必然的に小さくなってしまうからである
勾配消失問題の解決法1 活性化関数の選択
- ReLU関数を使用する(ReLU関数については活性化関数の項で説明済み)
勾配消失問題の解決法2 初期値の設定
- ReLU関数、シグモイド関数、双曲線正接関数を使う場合にXavierという手法をつかって重みの初期値を設定すると勾配消失問題の解決につながる
- Xavierとは標準正規分布でランダムに生成した値を前の層のノード数で除算し、各パラメータの初期値とする方法
network['W1'] = wieght_init * np.random.randn(input_layer_size, hidden_layer_1_size)
network['W2'] = wieght_init * np.random.randn(hidden_layer_1_size, hidden_layer_2_size)
network['W3'] = wieght_init * np.random.randn(hidden_layer_2_size, output_layer_size)
- またReLU関数にはHeという設定方法もある
- Heとは標準正規分布でランダムに生成した値を前の層のノード数で除算し、√2を掛けたものを各パラメータの初期値とする方法
network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / np.sqrt(input_layer_size) * np.sqrt(2)
network['W2'] = np.random.randn(hidden_layer_1_size, hidden_layer_2_size) / np.sqrt(hidden_layer_1_size) * np.sqrt(2)
network['W3'] = np.random.randn(hidden_layer_2_size, output_layer_size) / np.sqrt(hidden_layer_2_size) * np.sqrt(2)
- 重みの初期値を0に設定してしまうとすべてのデータが同じ値で次の層に渡されるのでパラメータの更新がされなくなってしまう
勾配消失問題の解決法3 バッチ正規化
- ミニバッチ単位で入力値のデータの偏りを抑制する手法。活性化関数に値を渡す前後に、バッチ正規化の処理を行う層を加える。
- バッチ正規化を行うことで、計算の高速化や勾配消失問題の解消などの効果がある
実装演習
考察
- 予想通り初期値ランダムでシグモイド関数を使った場合は勾配消失を起こし学習が進まなかった
- 初期値ランダムでReLU関数を使うと、時間はかかるが学習が進み最終的な精度も高くなることが確認できた
- 初期値をXavierで決めシグモイド関数を使うと、勾配消失は起こらなかったがReLU関数ほど良い結果は得られなかった
- 初期値をHeで決めReLU関数を使うと、初期値をランダムで比べた時に比べ学習が早く進み、同程度に良い結果が得られた
- 初期値をHeで決めシグモイド関数を使うと、初期値をXavierで決めたときと同じような結果になった
- 初期値をXavierで決めReLU関数を使うと初期値をHeで決めた時と同じような結果を得られた
- 全体的にReLE関数を使ったほうが良い結果を得ることができた。学習データの性質によってはシグモイド関数のほうが良い結果を出すことも考えられる。
学習率最適化法
- 勾配降下法を行うとき、学習率の大小により学習の結果が左右される。
- 最適な学習率を求める方法としてモメンタム、AdaGrad、RMSPrp、Adamなどがある
モメンタム
- モメンタムとは重みの修正量に、前回の重みの修正量を加算する方法
- μは前回の重みの加算量を決めるハイパーパラメータで通常0.5~0.9の範囲から選ばれる
- モメンタムの利点は局所的最適解ではなく、大域的最適解を得ることができる点や勾配が急な点から、なだらかな点に行くまでの時間が早いなどがあげられる。
- pythonで記述すると以下の通りになる
v[key] = momentum * v[key] - learning_rate * grad[key]
network.params[key] += v[key]
AdaGrad
- AdaGradとは誤差をパラメータで微分した値の二乗和を使って学習率の値を調整する方法
- θは分子が0にならないようにするための微小な値
- モメンタムと違い、勾配の緩やかな斜面に対して最適値に近づけることができる
- 学習率が徐々に小さくなる(増加することはない)ので鞍点問題を引き起こすことがある
- 鞍点問題とは多次元データを扱っているとき、ある次元から見た場合は極小値だが別の次元から見た場合は極大値になる点で学習が止まってしまう現象である
- pythonで記述すると以下の通りになる
if i == 0:
h[key] = np.full_like(network.params[key], 1e-4)
else:
h[key] += np.square(grad[key])
network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key]))
RMSProp
- RMSPropとはAdaGradを改良したもので、勾配の二乗和ではなく勾配の指数移動平均を使って学習率を更新する方法である
- 利点として、局所的最適値にならず大域的最適値となる点やハイパーパラメータの調整が必要になる場合が少ない点があげられる
- pythonで記述すると以下の通りになる
if i == 0:
h[key] = np.zeros_like(network.params[key])
h[key] *= decay_rate
h[key] += (1 - decay_rate) * np.square(grad[key])
network.params[key] -= learning_rate * grad[key] /(np.sqrt(h[key]) + 1e-7)
Adam
- Adamとは勾配の和と勾配の二乗和を使って学習率を更新する方法
- pythonで記述すると以下の通りになる
learning_rate_t = learning_rate * np.sqrt(1.0 - beta2 ** (i + 1)) / (1.0 - beta1 ** (i + 1))
for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
if i == 0:
m[key] = np.zeros_like(network.params[key])
v[key] = np.zeros_like(network.params[key])
m[key] += (1 - beta1) * (grad[key] - m[key])
v[key] += (1 - beta2) * (grad[key] ** 2 - v[key])
network.params[key] -= learning_rate_t * m[key] /(np.sqrt(v[key]) + 1e-7)
実装演習
考察
- 初期値をランダムに決定し、ReLU関数を使った場合学習がうまくいかなかった
- 同じ条件でモメンタムを使用すると、学習は遅いが最終的に良い結果を得られた
- ここから慣性の値を変更してみたが学習はうまくいかなかった。今回の条件に関して言えば、慣性の値は0.9が最適のようだ
- そのため慣性の値を変えずに学習率を増やしてみると、早い段階でよい結果を得ることができた
- 今回の条件ではAdaGrad、RMSProp、Adamで学習した場合、パラメータの調整をせずに良い結果を得ることができた
- 通常の確率的勾配降下法をシグモイド関数で行った結果、勾配消失を起こし学習が進まなかった
- 学習率と初期値の分散の値を調整してみたところ、分散1、学習率0.5のとき比較的良い結果を得ることができた。しかしXavierや学習率最適化法を使ったほうが、手早く良い結果を得ることができると考える
- 最適化法を使わずにバッチ正規化だけ実行した場合、学習は少し進んでいるようだが良い結果を得ることができなかった。どれか1つの最適化手法を使うのではなく、複数の手法を組み合わせて使うことが望ましいと考える