読者です 読者をやめる 読者になる 読者になる

Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Python DeepLearningに再挑戦 20 学習に関するテクニック ReLUの場合の重みの初期値 Batch Norm

概要

Python DeepLearningに再挑戦 20 学習に関するテクニック ReLUの場合の重みの初期値

参考書籍

ReLUの場合の重みの初期値

  • Xavier の初期値は、活性化関数が線形であることが前提。
  • ReLUの場合は、「Heの初期値」がオススメ。
  • Heの初期値は、前層のノード数がn個の場合、√2/n を標準偏差とするガウス分布を用いる。
  • ReLUの場合は、負の領域が0になるので、直感的に倍の係数が必要になると予想する。
  • ReLUの場合は、Heの初期値。sigmoid, tanhなどのS字カーブの時は、Xavierの初期値を使うのが、現時点でのベストプラクティス。

MNISTデータセットによる重み初期値の比較

  • std=0.01, Xavierの初期値、Heの初期値 で実験するとわかりやすい。

Batch Normalization

  • 各層で適度な広がりを持つように強制的にアクティベーションの分布を調整する。

  • 2015年に出てきた手法だけど、コンペなどで結果を出している手法の1つ。

Batch Normの利点: * 学習を早く進行させられる(学習係数を大きくすることができる) * 初期値にそれほど依存しない(初期値に神経質にならなくてもいい) * 過学習を抑制する(Dropoutなどの必要性を減らす)

Batch Normのレイヤを差し込む

f:id:riikku:20161225215512p:plain

  • Batch Normは、学習を行う際のミニバッチを単位としてミニバッチごとに正規化を行う。具体的には、データの分布が平均が0で分散が1になるように正規化を行う。

  • 数式で表すと以下の通り。

μB <- 1/m mΣ(i=1) xi
σ**2*B <- 1/m mΣ(i=1) (xi-μB)**2
^xi <- xi -μB/ √σ**2*B + ε

あー、自己流で書くとわけわからんな(^ ^;)

f:id:riikku:20161226072052p:plain

これが、BatchNormの順伝播のアルゴリズム。 逆伝搬は以下のサイトを参照。

Understanding the backward pass through Batch Normalization Layer

Batch Normalizationの評価

batch_norm_test.py を実行してみると、確かに、めっちゃ効率いい!w

Batch Normを使用すると、重みの初期値にロバスト になるらしい。(ロバストは初期値に依存しない。)

正則化 過学習

過学習が起こる原因

  • パラメータが大量にあり、表現力の高いモデルであること
  • 訓練データが少ないこと

あえて、訓練データを少なくしてあるoverfit_weight_ decay.pyを実行すると、確かに訓練データと、テストデータでかなり認識精度に隔たりが出てしまう。

Weight decay

  • Weight dcay(荷重減衰):過学習抑制のための手法の1つ。学習の過程で、大きな重みを持つことに対してペナルティを課すことでか学習を抑制する。

  • ニューラルネットワークの学習は、損失関数の値を小さくすることが目的。重みの2乗ノルム(L2ノルム)を損失関数に加算する(重みに応じて、損失関数に値を加算するという意味?)。そのことにより、重みが大きくなることを抑えられる。

  • 重みをWとするとL2ノルム(2乗ノルム)のWeight decayは、1/2λW2 になり、この1/2 λW2を損失関数に加算する。

  • λは正則化の強さをコントロールするハイパーパラメータ。λを大きくとればとるほど、重みに対するペナルティは大きくなる。

  • 1/2 は、1/2λW**2のWに対しての微分を、λWにするための調整用の数字らしい。

  • Weight decayは、すべての損失関数に1/2λW**2を追加する。そのため、誤差逆伝播法で勾配を求める場合、正則化項の微分λWを加算する。

  • L2ノルムは、各要素の2乗和に対応している。W = (w1, w2,....wn)という重みがあるとすると、√w1**2 + w2**2 + .... wn**2 で計算できる。(L1ノルムというのもあって、それは、絶対値 |w1| + |w2| + .... + |Wn| というように計算する。L2, L1ともに目的は同じで、絶対値の和を求めるということなのか!)

  • overfit_weight_decay.pyを実行してみる。。。確かに隔たりが小さくなってる!

  • あと訓練データの認識精度が100%に行っていない点からも、過学習が抑えられていることがわかる。

Dropout

  • 複雑なモデルになってくると、Weight decayだけでは対応できなくなってくる。そこでよく利用されるのが、Dropout。

  • Dropoutは、ニューロンをランダムに消去しながら学習する方法。

  • 最終的に、訓練時に消去した割合も乗算してして出力する。

  • ランダム性を高めて、汎用性を高くするための手法。

  • pythonでの実装の例は以下の通り。

class Dropout:
    def __init__(self, dropout_ratio =0.5):# Dropoutclassインスタンス変数の初期化
        self.dropout_ratio = dropout_ratio
        self.mask = None # xと同じ形状の配列をランダムに作成して格納して、dropout_ratioより大きいものだけをTrueとして残す。
        
    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio #ここで、消すニューロンなどを判断する。
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)
        
    def backward(self, dout):
        return dout * self.mask
  • ch06/overfit_dropout.pyを実行してみる、、、確かに、訓練データと、テストデータの隔たりが小さくなった!

ハイパーパラメータの検証

  • ハイパーパラメータの例:weight decay, Learning Rate, バッチサイズ、各層のニューロンの数などの手動で設定が必要な箇所。

検証データ

  • テストデータを使って、ハイパーパラメータの性能を評価してはいけない。それは結局そのテストデータに合わせて学習を設計することに他ならないため、訓練データでパラメータを調整->検証データで性能を比較->その結果、テストデータでもいいスコアが出るようになるという過程が大事。

  • 検証データvalidation data:ハイパーパラメータの調整用のデータのこと。この検証データを使って、ハイパーパラメータの良さを評価する。

  • データセットによっては、訓練データ、検証データ、テストデータの3つに分離されているものがある。

  • 2つだけの場合は、訓練データの中から、20%程度を検証データとして先に分離しておく方法がある。以下はコード例。

(x_train, t_train), (x_test, t_test ) = load_mnist()

#訓練データをシャッフル
x_train, t_train = shuffle_dataset(x_train, t_train)

# 検証データの分割
validation_rate = 0.2 #検証データの割合
validation_num = int(x_train.shape[0] * validation_rate) #検証データの具体的な数

x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]

などなど

ハイパーパラメータの最適化

  • 重要なポイントは、ハイパーパラメータの良い値が存在する範囲を徐々に絞り込んでいくという点。

  • 適当な範囲からハイパーパラメータのサンプルをいくつか抽出して、そのスコアを比較して、良い値が存在するハイパーパラメータの範囲を抽出していく。

  • ざっくりと、10の階乗のスケールで範囲を指定する。(対数スケールlog scaleで範囲を指定する)

  • ステップ0: ハイパーパラメータの範囲を指定

  • ステップ1:設定されたハイパーパラメータの範囲から、ランダムにサンプリングする。

  • ステップ2:ステップ1でサンプリングされたハイパーパラメータの値をしてしてしてして学習を行い、検証データで認識精度を評価する。(エポックは小さく設定)

  • ステップ3:ステップ1〜2を繰り返し、その認識精度の結果からハイパーパラメータの範囲を狭める。

  • ステップ4:ステップ3までである程度絞り込んだハイパーパラメータの範囲から値を一つ選ぶ。

  • より高度なハイパーパラメータの最適化の手法の1つはベイズ最適化。

ハイパーパラメータの最適化の実装

  • learning rateと、weight decayの2つを探索する実装を考えてみる。
weight_decay = 10 ** np.random.uniform(-8, -4) #これは10**-8, 10**-4の範囲という意味
lr = 10 ** np.random.uniform(-6,-2) #これは、10**-6, 10**-2の範囲
  • これで実装して、ch06/hyperparameter_optimization.pyを実行すると以下のようになるようだ。
Best-1(val acc:0.85) | lr:0.009261642471835013, weight decay:7.229607006918746e-06
Best-2(val acc:0.72) | lr:0.006112245214274025, weight decay:3.604145438971605e-06
Best-3(val acc:0.69) | lr:0.00581098917918343, weight decay:1.8044914932049401e-07
Best-4(val acc:0.69) | lr:0.0063406949576014, weight decay:3.879837584173024e-07
Best-5(val acc:0.69) | lr:0.007019636336626195, weight decay:8.695187030487348e-05
  • グラフを見ると、best5ぐらいまではうまく学習が進んでいるので、学習係数が0.001~0.01、weight decayの係数が、10-8, 10-6の範囲ぽい。 こういう感じで繰り返していくようだ。

これで6章読破!!