顔のキーをボリュームニューラルネットワークで検出するチュートリアル(3)学習率,学習運動量,dropout


本文はUsing convolutional neural nets to detect facial keypoints tutorialから翻訳した.
顔のキーをボリュームニューラルネットワークで検出するチュートリアル(一)環境配置と浅層ネットワーク訓練用ボリュームニューラルネットワークで顔のキーを検出するチュートリアル(二)ボリュームニューラルネットワーク訓練とデータ拡張ボリュームニューラルネットワークで顔のキーを検出するチュートリアル(三)学習率、学習運動量、dropoutはボリュームニューラルネットワークで顔のキーを検出するチュートリアル(4)前訓練(pre-train)を通じて特定ネットワークを訓練する
前回のモデルの嫌なところは訓練だけで1時間もかかったことで、結果を待つのは気持ちのいいことではありません.この部分では、2つのテクニックを組み合わせて、ネットワークトレーニングをより速くすることについて議論します.
直感的な解決策は,訓練開始時に高い学習率をとり,反復回数の増加に伴ってこの値を減少させることである.これは理にかなっている.最初は全体の最も良い点から非常に遠いので、私たちは最も良い点の方向に向かって大股で前進したいと思っています.しかし、最も良い点が近いほど、私たちは一歩越えないように慎重に前進します.例を挙げると、あなたは汽車で家に帰りますが、家に入るときは必ず入って、汽車を中に入れてはいけません.
深さ学習における初期化と運動量の重要性についてはIlya Sutskeverらの談話と論文のタイトルである.そこで,深さ学習を促進するためのもう一つの有用な技法を学習した:すなわち,訓練中に最適化法の運動量パラメータを増加させる.
我々の以前のモデルでは,学習率と学習ポテンシャルを静的0.01と0.9に初期化した.この2つのパラメータを変更して,学習率を反復回数とともに線形に減少させ,同時に学習運動量を増大させた.
NeuralNetでは、トレーニング中にon_を通過することができます.epoch_finished関数を使用してパラメータを更新します.そこで私たちはon_に関数を伝えましたepoch_finishedは、この関数が反復するたびに呼び出されるようにします.しかし,学習率と学習ポテンシャルの2つのパラメータを変更する前に,この2つのパラメータをTheano shared variablesに変更しなければならない.幸いなことに、これはとても簡単です.
import theano

def float32(k):
    return np.cast['float32'](k)

net4 = NeuralNet(
    # ...
    update_learning_rate=theano.shared(float32(0.03)),
    update_momentum=theano.shared(float32(0.9)),
    # ...
    )

コールバック関数またはコールバックリストを渡すには、NeuralNetのインスタンスであるnnという2つのパラメータが必要です.train_それとnnhistoryは同じ値です.
ハードコーディング値の破壊関数を使用しないで、パラメータ化可能なクラスを使用して、コールバック関数としてcall関数を定義します.このクラスをAdjustVariableと呼び、実現はかなり簡単です.
class AdjustVariable(object):
    def __init__(self, name, start=0.03, stop=0.001):
        self.name = name
        self.start, self.stop = start, stop
        self.ls = None

    def __call__(self, nn, train_history):
        if self.ls is None:
            self.ls = np.linspace(self.start, self.stop, nn.max_epochs)

        epoch = train_history[-1]['epoch']
        new_value = float32(self.ls[epoch - 1])
        getattr(nn, self.name).set_value(new_value)

これらの変化を一緒にして、トレーニングネットワークの準備を始めましょう.
net4 = NeuralNet(
    # ...
    update_learning_rate=theano.shared(float32(0.03)),
    update_momentum=theano.shared(float32(0.9)),
    # ...
    regression=True,
    # batch_iterator_train=FlipBatchIterator(batch_size=128),
    on_epoch_finished=[
        AdjustVariable('update_learning_rate', start=0.03, stop=0.0001),
        AdjustVariable('update_momentum', start=0.9, stop=0.999),
        ],
    max_epochs=3000,
    verbose=1,
    )

X, y = load2d()
net4.fit(X, y)

with open('net4.pickle', 'wb') as f:
    pickle.dump(net4, f, -1)

私たちは2つのネットワークを訓練します:net 4は私たちのFlipBatchIteratorを使用しないで、net 5は採用しました.それ以外は同じです.
これはnet 4の勉強です.
Epoch  | Train loss | Valid loss | Train / Val
--------|--------------|--------------|----------------
    50  | 0.004216 | 0.003996 | 1.055011
   100  | 0.003533 | 0.003382 | 1.044791
   250  | 0.001557 | 0.001781 | 0.874249
   500  | 0.000915 | 0.001433 | 0.638702
   750  | 0.000653 | 0.001355 | 0.481806
  1000  | 0.000496 | 0.001387 | 0.357917

クール、訓練はもっと速く起きた!学習速度と学習運動量を調整する前に,500世代と1000世代の訓練誤差はnet 2の半分であった.今回、汎化の程度は750前後の時期以降に改善が止まったようだ.何の意味もない訓練がもっと長いように見えます.
Net 5はデータ拡張を使ったらどうですか?
poch  | Train loss | Valid loss | Train / Val
--------|--------------|--------------|----------------
    50  | 0.004317 | 0.004081 | 1.057609
   100  | 0.003756 | 0.003535 | 1.062619
   250  | 0.001765 | 0.001845 | 0.956560
   500  | 0.001135 | 0.001437 | 0.790225
   750  | 0.000878 | 0.001313 | 0.668903
  1000  | 0.000705 | 0.001260 | 0.559591
  1500  | 0.000492 | 0.001199 | 0.410526
  2000  | 0.000373 | 0.001184 | 0.315353

再び、net 3よりも速い訓練があり、より良い結果が得られました.1000回反復した後、結果はnet 3より3000回反復したほうが効果的である.さらに,データ拡張訓練を用いたモデルは,現在,データ拡張のないモデルより約10%優れている.
廃棄テクニック(Dropout)
2012年,特徴検出器の共適応を防止することによってニューラルネットワークを改善する論文にdropoutを導入し,流行の正規化技術であり,非常によく動作した.私はそれがなぜこんなに良いのか深く理解していません.他の場所で読むことができます.
他の正規化技術のように、もし私たちが過度にフィッティングされたネットワークを持っていたら、dropoutは意味があります.これは前節で私たちが訓練したnet 5ネットワークで明らかにそうです.重要なのは、あなたのネットワークをよく訓練して、まずフィットして、それから正規化することを覚えておくことです.
Lasagneでdropoutを使用するには、既存のレイヤー間にDropoutLayerレイヤーを追加し、各レイヤーに終了確率を指定します.ここでは、新しいネットワークの完全な定義です.これらの行の最後に#を追加しました!、net 5との違いを区別するために使用されます.
net6 = NeuralNet(
    layers=[
        ('input', layers.InputLayer),
        ('conv1', layers.Conv2DLayer),
        ('pool1', layers.MaxPool2DLayer),
        ('dropout1', layers.DropoutLayer),  # !
        ('conv2', layers.Conv2DLayer),
        ('pool2', layers.MaxPool2DLayer),
        ('dropout2', layers.DropoutLayer),  # !
        ('conv3', layers.Conv2DLayer),
        ('pool3', layers.MaxPool2DLayer),
        ('dropout3', layers.DropoutLayer),  # !
        ('hidden4', layers.DenseLayer),
        ('dropout4', layers.DropoutLayer),  # !
        ('hidden5', layers.DenseLayer),
        ('output', layers.DenseLayer),
        ],
    input_shape=(None, 1, 96, 96),
    conv1_num_filters=32, conv1_filter_size=(3, 3), pool1_pool_size=(2, 2),
    dropout1_p=0.1,  # !
    conv2_num_filters=64, conv2_filter_size=(2, 2), pool2_pool_size=(2, 2),
    dropout2_p=0.2,  # !
    conv3_num_filters=128, conv3_filter_size=(2, 2), pool3_pool_size=(2, 2),
    dropout3_p=0.3,  # !
    hidden4_num_units=500,
    dropout4_p=0.5,  # !
    hidden5_num_units=500,
    output_num_units=30, output_nonlinearity=None,

    update_learning_rate=theano.shared(float32(0.03)),
    update_momentum=theano.shared(float32(0.9)),

    regression=True,
    batch_iterator_train=FlipBatchIterator(batch_size=128),
    on_epoch_finished=[
        AdjustVariable('update_learning_rate', start=0.03, stop=0.0001),
        AdjustVariable('update_momentum', start=0.9, stop=0.999),
        ],
    max_epochs=3000,
    verbose=1,
    )

私たちのネットワークは現在、Pythonに「最大再帰制限を超える」エラーを報告できるほど大きくなっているので、それを避けるためにpythonの再帰制限を増やしたほうがいいです.
import sys
sys.setrecursionlimit(10000)

X, y = load2d()
net6.fit(X, y)

import cPickle as pickle
with open('net6.pickle', 'wb') as f:
    pickle.dump(net6, f, -1)

私たちの今の訓練を見てみると、訓練速度がまた遅くなったことに気づき、dropoutを追加したと思っていました.これは予想外の効果です.しかし、ネットワーク全体の表現は実際にnet 5を上回っています.
Epoch  | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
    50  | 0.004619 | 0.005198 | 0.888566
   100  | 0.004369 | 0.004182 | 1.044874
   250  | 0.003821 | 0.003577 | 1.068229
   500  | 0.002598 | 0.002236 | 1.161854
  1000  | 0.001902 | 0.001607 | 1.183391
  1500  | 0.001660 | 0.001383 | 1.200238
  2000  | 0.001496 | 0.001262 | 1.185684
  2500  | 0.001383 | 0.001181 | 1.171006
  3000  | 0.001306 | 0.001121 | 1.164100

フィットしすぎてもそんなに悪くないようです.これらの数字に注意しなければなりませんが、トレーニングエラーと検証エラーの比率は、トレーニングエラーの評価と漏れ、検証エラーの評価に漏れがないため、少し異なる意味を持っています.訓練ミスのもっと価値のある値は
from sklearn.metrics import mean_squared_error
print mean_squared_error(net6.predict(X), y)
# prints something like 0.0010073791

我々の以前のdropoutのないモデルでは,訓練上の誤差は0.000373であった.だから私たちのdropoutネットワークの表現は少し良いだけでなく、そのオーバーフィットも私たちの以前のモデルよりずっと少ないです.これは、ネットワークをより大きく(より表現力がある)すると、より良いパフォーマンスが期待できることを意味するため、良いニュースです.これが次のステップです.最後の2つの非表示レイヤの単位数を500から1000に増やします.これらの行を変更する必要があります.
net7 = NeuralNet(
    # ...
    hidden4_num_units=1000,  # !
    dropout4_p=0.5,
    hidden5_num_units=1000,  # !
    # ...
    )

dropoutのないネットワークに比べて、改善効果はより顕著です.
 Epoch  | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
    50  | 0.004756 | 0.007043 | 0.675330
   100  | 0.004440 | 0.005321 | 0.834432
   250  | 0.003974 | 0.003928 | 1.011598
   500  | 0.002574 | 0.002347 | 1.096366
  1000  | 0.001861 | 0.001613 | 1.153796
  1500  | 0.001558 | 0.001372 | 1.135849
  2000  | 0.001409 | 0.001230 | 1.144821
  2500  | 0.001295 | 0.001146 | 1.130188
  3000  | 0.001195 | 0.001087 | 1.099271

少しフィットしすぎますが、効果は本当にいいです.トレーニング回数を増やし続けると、モデル効果がもっと良くなるような気がします.試してみる:
net12 = NeuralNet(
    # ...
    max_epochs=10000,
    # ...
    )
Epoch  | Train loss | Valid loss | Train / Val
--------|--------------|--------------|---------------
    50  | 0.004756 | 0.007027 | 0.676810
   100  | 0.004439 | 0.005321 | 0.834323
   500  | 0.002576 | 0.002346 | 1.097795
  1000  | 0.001863 | 0.001614 | 1.154038
  2000  | 0.001406 | 0.001233 | 1.140188
  3000  | 0.001184 | 0.001074 | 1.102168
  4000  | 0.001068 | 0.000983 | 1.086193
  5000  | 0.000981 | 0.000920 | 1.066288
  6000  | 0.000904 | 0.000884 | 1.021837
  7000  | 0.000851 | 0.000849 | 1.002314
  8000  | 0.000810 | 0.000821 | 0.985769
  9000  | 0.000769 | 0.000803 | 0.957842
 10000  | 0.000760 | 0.000787 | 0.966583

今あなたはdropout魔力の証人です.:-)
これまでにトレーニングしたネットワークを比較してみましょう.
Name
Description
Epochs
Train loss
Valid loss
net1
single hidden
400
0.002244
0.003255
net2
convolutions
1000
0.001079
0.001566
net3
augmentation
3000
0.000678
0.001288
net4
mom + lr adj
1000
0.000496
0.001387
net5
net4 + augment
2000
0.000373
0.001184
net6
net5 + dropout
3000
0.001306
0.001121
net7
net6 + epochs
10000
0.000760
0.000787