バナキュラードロップアウト
記事ディレクトリ
ドロップアウトとは何ですか
ドロップアウトは、指定された確率ppでトレーニングされるニューラル ネットワークの正則化手法です。p (一般的な値はp = 0.5 p=0.5p=0.5 ) ユニットを (接続とともに) 破棄します。テスト時にはすべての単位が存在しますが、重みはpppスケーリング (つまり、pw pw.pw)。
この考えは、ニューラル ネットワークが特定の接続に過度に依存する共適応を防ぐことです。これは過剰学習の症状である可能性があるためです。直観的には、ドロップアウトは、隠れたニューラル ネットワークのアンサンブルを作成するものと考えることができます。
この定義によれば、PyTorch は「確率ppnn.Dropout
のベルヌーイ分布からのサンプルを使用します」p は、入力テンソルのいくつかの要素をランダムにゼロにします。各チャネルは、通話が転送されるたびに独立してゼロに設定されます。」
ドロップアウトは、与えられた確率ppと考えることができます。p は、入力テンソルの一部の要素をゼロにランダム化します。これが発生すると、出力の一部が失われます。これを考慮して、出力も1 1 − p \frac{1}{1-p}1 − p1。
スケーリングにより、入力平均と出力平均がほぼ等しくなります。
ズームを理解する
多くの人は、ドロップアウト層が入力をスケーリングする方法と理由について混乱しているかもしれません。詳細な説明は次のとおりです。
PyTorch の公式ドキュメントには次のように記載されています。
また、トレーニング中の出力は1 1 − p \frac{1}{1-p}です。1 − p1スケーリング。これは、評価時にモジュールが恒等関数を計算するだけであることを意味します。
では、これはどのように行われるのでしょうか? どうしてそれをするの?Pytorch のコードを見てみましょう。
ドロップ率を作成するp = 0.4 p=0.4p=0.4のドロップアウト層m
:
import torch
import numpy as np
p = 0.4
m = torch.nn.Dropout(p)
PyTorch のドキュメントでは次のように説明されています。
トレーニング中に、ベルヌーイ分布からのサンプルが確率ppで使用されます。p は、入力テンソルのいくつかの要素をランダムにゼロにします。ゼロ化された要素は、転送呼び出しごとにランダム化されます。
ドロップアウト層にランダム入力を入れて約40%を確認( p = 0.4 p=0.4p=0.4 ) 要素が 0 になりました。
nbig = 5000000
inp = torch.rand(nbig, 10)
outp = m(inp)
print(f'输入中0元素的比例为: {
(outp==0).numpy().mean():.5f}, p={
p}')
上記のコードを実行した後の出力:
$ 输入中0元素的比例为: 0.40007, p=0.4
スケーリングの部分に移りましょう。
小さなランダム入力を作成し、それをドロップアウト層に置きます。入力と出力を比較します。
np.random.seed(42)
inp = torch.rand(5, 4)
inp
上記のコードは 5 行 4 列のランダム テンソルを作成し、出力は次のようになります。
$ tensor([[0.6485, 0.3114, 0.1626, 0.1022],
[0.7352, 0.4634, 0.8206, 0.4228],
[0.0322, 0.9399, 0.9163, 0.4169],
[0.2574, 0.0467, 0.2213, 0.6171],
[0.4146, 0.2288, 0.0388, 0.7752]])
以下の 2 つのテンソルの非ゼロ要素を比較すると、トレーニング中の出力は1 1 − p \frac{1}{1-p}であることがわかります。1 − p1ズーム時間:
outp = m(inp)
inp/(1-p)
$ tensor([[1.0808, 0.5191, 0.2710, 0.1703],
[1.2254, 0.7723, 1.3676, 0.7046],
[0.0537, 1.5665, 1.5272, 0.6948],
[0.4290, 0.0778, 0.3689, 1.0284],
[0.6909, 0.3813, 0.0646, 1.2920]])
出力output
$ tensor([[1.0808, 0.5191, 0.2710, 0.0000],
[0.0000, 0.7723, 0.0000, 0.0000],
[0.0000, 1.5665, 1.5272, 0.6948],
[0.4290, 0.0778, 0.3689, 1.0284],
[0.6909, 0.0000, 0.0646, 0.0000]])
この観察結果をコードで主張できます。
idx_nonzero = outp!=0
assert np.allclose(outp[idx_nonzero].numpy(), (inp/(1-p))[idx_nonzero].numpy())
では、なぜそれをするのでしょうか?
基本的に、ドロップアウト層は恒等関数となり、評価/テスト/推論中に入力を変更しません。Dropout はトレーニング中にのみアクティブになり、スケーリングを行わない推論中はアクティブではないため、要素がランダムにドロップされなくなる (0 に設定される) ため、予想される出力は推論中に大きくなります。ただし、ドロップアウト層を通過するかどうかに関係なく、期待される出力が同じであることが必要です。したがって、トレーニング中にドロップアウト層の出力を1 1 − p \frac{1}{1−p}で増幅します。1 − p1補正するスケール係数。pppが大きいほど、ドロップアウトがより積極的であることを意味します。つまり、より多くの補正、つまりスケーリング係数1 1 − p \frac{1}{1−p}1 − p1より大きい。
以下のコードは、スケール係数によって出力が入力と同じスケールにどのように復元されるかを示しています。
inp = torch.rand(nbig, 10)
outp = m(inp)
print(f'dropout 层的平均输出 ({
outp.mean():.4f}) 接近平均输入 ({
inp.mean():.4f})')
$ dropout 层的平均输出 (0.5000) 接近平均输入 (0.5000)
例えば
100 個のテンソルを使用した例を使用して、ドロップアウトとそのスケーリングが入力にどのような影響を与えるかを示します。
import torch
import torch.nn as nn
# 生成 100 个 1
x = torch.ones(100)
$ tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
ドロップ率p = 0.1 の場合 p = 0.1p=0.1、約 10 個の値を 0 に設定する必要があります。スケーリング係数は
1 1 − 0.1 = 1 0.9 = 1.1 ˙ \frac{1}{1-0.1} = \frac{1}{0.9} = 1.\dot{1} です。1−0.11=0.91=1.1˙
# 输入 Dropout 层
output = nn.Dropout(p=0.1)(x)
$ tensor([1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 0.0000, 0.0000, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111, 1.1111, 0.0000, 0.0000,
1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 1.1111,
1.1111, 1.1111, 1.1111, 1.1111, 1.1111, 0.0000, 1.1111, 1.1111, 1.1111,
1.1111])
結果は予想どおりで、10 個の値が完全にゼロになり、入力と出力の平均が同じになるように、または可能な限り近い値になるように結果がスケーリングされました。
print(x.mean(), output.mean())
$ tensor(1.) tensor(1.0000)
この例では、入力と出力の平均は 1.0 です。