Dropoutの学習時と推論時の動きをわかりやすく解説【初級 深層学習講座】
ドロップアウト(Dropout)は、過学習を防ぐために利用される層です。
この記事ではDropoutの動きを実際にPytrochのDropoutを動かしながら確認していきます
ドロップアウト(Dropout)とは
ドロップアウト(Dropout)は、ニューラルネットの正則化手法の1つです。
主に、過学習を防ぐために、全結合層(fully connected layers, FC)や、畳み込み層(convolutional layers, CNN)などのネットワーク層の接続の間に挿入されます。
具体的なドロップアウトは、入力の一部の要素を確率的にゼロにする操作です。
入力の一部がゼロになるということは、入力の一部が隠された状態で学習することになります。また、隠される場所も毎回ランダムに変化します。
したがって、全てのデータを利用できる場合に比べて難易度が高い問題を解く形になります。
ドロップアウトされた入力で問題に正解するためには、より汎化された知識を学習する必要があります。
簡単に言えば、ドロップアウトは、ネットワークが特定のパターン(入力データ)に特化した学習をしないようにするための工夫といえます。
ある特定の入力要素が強く結果に依存している場合、ドロップアウトがなければそこだけみて推論するようになるかもしれません。
ドロップアウトで、そこが隠されれば他の要素を使って推論しようと試むでしょう
つまり、入力のいろいろな特徴を使って推論しようとする(=汎化しようとする)はずです。
PytorchのDropoutの動き
Dropoutについて簡単に説明しましたが、理解できましたでしょうか。
ここからは、PytorchのDropoutの動きを見ていきます。
Pytorchで、Dropoutを利用する場合は以下のように記述します。
Pytorchのインポート
まず、Pytorchをインポートします
import torch
from torch import nn
Dropoutのオブジェクトを生成する
Dropoutを利用するために、ドロップアウトのオブジェクトを生成します。
d = nn.Dropout(p = 0.5)
引数p
は、ゼロにする確率です。0.5を指定すると、各要素は1/2の確率でゼロになります。
では、実際の動きを見ていきます。
Dropoutの動きを確認
1.0を100個並べたデータを用意して、実際にドロップアウトの出力を確認してみます。
p = 0.4
d = nn.Dropout(p = p)
x = torch.tensor([1.0]*10)
print(x)
y = d(x)
print(y)
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
tensor([1.6667, 0.0000, 0.0000, 0.0000, 1.6667, 1.6667, 0.0000, 1.6667, 1.6667, 0.0000])
結果を見ると、いくつかの値が0.0000になっているのが確認できます。
ただ、1も1.6667になっています。これはどういうことでしょうか。
Pytorchのマニュアルを見ると、出力は$\frac{1}{1-p}$でスケーリングされるということです。
$\frac{1}{1-0.4} \sim 1.6667$なので、1.0がこれでスケーリングされて$1.0 \times 1.6667 = 1.6667$と変化したためです。
これは、入力の総和がドロップアウト前と同じになるようにする操作です。
数学的には、期待値を変化させないようにするという説明になりますが、ドロップアウト前と後で総和を一致させるためと考えてOKです
スケーリングされることは知らない人も多そうですね
確率で指定した数になっているか確認してみる
引数p
で指定した確率と、実際にゼロに変化した個数を見てみます。
以下のコードは、1.0が100個の入力データに対して、p=0.5
としてドロップアウトを繰り返して、何個がゼロになったかを確認するものです。
for _ in range(10) :
p = 0.5
d = nn.Dropout(p = p)
x = torch.tensor([1.0]*100)
y = d(x)
print(sum(y==0))
tensor(48)
tensor(50)
tensor(46)
tensor(51)
tensor(57)
tensor(50)
tensor(53)
tensor(37)
tensor(51)
tensor(41)
結果を見ると、50個が変化しているのではなく、結構ばらつきがあることがわかります。
確率0.5でランダムにゼロにしているので、ばらつきによってゼロが多かったり、少なかったりするためです。
具体的には、ベルヌーイ分布からのサンプルに従って要素の確率をゼロにします
訓練時と推論時の動き
ドロップアウトは、訓練時(トレーニング時)だけ、動作するものです。
これを確認してみます。
p = 0.5
d = nn.Dropout(p = p)
x = torch.tensor([1.0]*10)
# 訓練モード
d.train()
y = d(x)
print(y)
# 推論モード
d.eval()
y = d(x)
print(y)
tensor([0., 0., 2., 2., 0., 2., 2., 0., 0., 0.])
tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
.train()
と.eval()
を呼び出すことで、訓練モードと推論モードの切り替えができます。
結果を見ると、推論モードではドロップアウトが行われていないことが確認できます。
これを見ると、ドロップアウト層が存在するモデルを間違って訓練モードで実行すると性能が落ちることがわかります。
推論で使うときは、eval()
を忘れないよう
BatchNormalizationも同じように訓練と推論で動きが違うので注意
ドロップアウトさせる確率p
はどれくらいに設定するのがよいか?
モデルによって異なりますが、一般的には0.2〜0.5の範囲を設定することが多いようです
あくまで一般的な指針なので、最適というわけではありません。
まずは、この範囲の値を試してみれば良いと思います。
実際には、試行錯誤が必要です。実際に学習を行う場合は、いくつかの値を試してみましょう。
まとめ
以上、Pytorchのドロップアウトの動きについて説明しました。
ドロップアウトとバッチ正規化はディープラーニングの分野の重要な発明と言われています。この2つにより、訓練の安定性・性能が向上しました。
今回は、この1つのドロップアウトについて説明しましたが、興味がある方はバッチ正規化の記事も読んでみてください。