PyTorchのネットワークで重みとバイアスを初期化する方法(たとえば、HeまたはXavierの初期化を使用)
回答:
単一レイヤーの重みを初期化するには、から関数を使用しますtorch.nn.init
。例えば:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
また、あなたはに書き込むことによってパラメータを変更することができますconv1.weight.data
(ですtorch.Tensor
)。例:
conv1.weight.data.fill_(0.01)
同じことがバイアスにも当てはまります。
conv1.bias.data.fill_(0.01)
nn.Sequential
またはカスタム nn.Module
初期化関数をに渡しますtorch.nn.Module.apply
。全体の重みをnn.Module
再帰的に初期化します。
apply(fn):(
fn
によって返される.children()
)すべてのサブモジュールと自己に再帰的に適用します。通常の使用には、モデルのパラメーターの初期化が含まれます(torch-nn-initも参照)。
例:
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
Occamのかみそりの原則に従う場合、すべての重みを0または1に設定することが最善の解決策になると考えるかもしれません。これはそうではありません。
すべての重みが同じ場合、各層のすべてのニューロンは同じ出力を生成します。これにより、調整する重みを決定するのが難しくなります。
# initialize two NN's with 0 and 1 constant weights
model_0 = Net(constant_weight=0)
model_1 = Net(constant_weight=1)
Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304 -- All Zeros
1552.281 -- All Ones
均一な分布は、数値の集合から任意の数を選ぶの等しい確率を有します。
さんがどれだけ均一な重量の初期化、ニューラルネットワークの列車を見てみましょうlow=0.0
としますhigh=1.0
。
以下に、ネットワークの重みを初期化する別の方法(Netクラスコード以外)を示します。モデル定義の外で重みを定義するために、次のことができます。
- 、ネットワーク層の種類によって重みを割り当てる機能を定義し、次いで
model.apply(fn)
各モデルレイヤーに関数を適用するを使用して、初期化されたモデルにそれらの重みを適用します。
# takes in a module and applies the specified weight initialization
def weights_init_uniform(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find('Linear') != -1:
# apply a uniform distribution to the weights and a bias=0
m.weight.data.uniform_(0.0, 1.0)
m.bias.data.fill_(0)
model_uniform = Net()
model_uniform.apply(weights_init_uniform)
Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208 -- Uniform Weights
ニューラルネットワークで重みを設定するための一般的なルールは、重みを小さくしすぎずにゼロに近づけることです。
[-y、y]の範囲で重みを開始することをお勧めします
y=1/sqrt(n)
(ここで、nは特定のニューロンへの入力数です)。
# takes in a module and applies the specified weight initialization
def weights_init_uniform_rule(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find('Linear') != -1:
# get the number of the inputs
n = m.in_features
y = 1.0/np.sqrt(n)
m.weight.data.uniform_(-y, y)
m.bias.data.fill_(0)
# create a new model with these weights
model_rule = Net()
model_rule.apply(weights_init_uniform_rule)
以下では、NNのパフォーマンス、均一分布で初期化された重み[-0.5,0.5)と、一般的なルールを使用して重みが初期化された重みを比較します。
Validation Accuracy
75.817% -- Centered Weights [-0.5, 0.5)
85.208% -- General Rule [-y, y)
Training Loss
0.705 -- Centered Weights [-0.5, 0.5)
0.469 -- General Rule [-y, y)
正規分布は、平均が0で標準偏差が
y=1/sqrt(n)
である必要があります。ここで、nはNNへの入力の数です。
## takes in a module and applies the specified weight initialization
def weights_init_normal(m):
'''Takes in a module and initializes all linear layers with weight
values taken from a normal distribution.'''
classname = m.__class__.__name__
# for every Linear layer in a model
if classname.find('Linear') != -1:
y = m.in_features
# m.weight.data shoud be taken from a normal distribution
m.weight.data.normal_(0.0,1/np.sqrt(y))
# m.bias.data should be 0
m.bias.data.fill_(0)
以下では、2つのNNのパフォーマンスを示します。1つは均一分布を使用して初期化され、もう1つは正規分布を使用して初期化されます。
Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329 -- Uniform Rule [-y, y)
0.443 -- Normal Distribution
PyTorchが代わりにやってくれます。考えてみれば、これは理にかなっています。PyTorchが最新のトレンドに従ってそれを行うことができるのに、なぜレイヤーを初期化する必要があるのですか?
たとえば、線形レイヤーを確認します。
では__init__
メソッドは呼び出しますKaiming彼のinit機能を。
def reset_parameters(self):
init.kaiming_uniform_(self.weight, a=math.sqrt(3))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
他のレイヤータイプについても同様です。例えばここをconv2d
チェックしてください。
注意:適切な初期化を行うと、トレーニング速度が速くなります。問題が特別な初期化に値するなら、あとでそれを行うことができます。
xavier_uniform
た。デフォルトの初期化を使用するのではなく、重みの初期化に切り替えることで(バイアスを0に初期化)、検証精度は30 RMSpropのエポックが82%から86%に増加しました。また、Pytorchの組み込みVGG16モデル(事前トレーニングされていない)を使用すると86%の検証精度を得たので、正しく実装したと思います。(私は0.00001の学習率を使用しました。)
import torch.nn as nn
# a simple network
rand_net = nn.Sequential(nn.Linear(in_features, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, 1),
nn.ReLU())
# initialization function, first checks the module type,
# then applies the desired changes to the weights
def init_normal(m):
if type(m) == nn.Linear:
nn.init.uniform_(m.weight)
# use the modules apply function to recursively apply the initialization
rand_net.apply(init_normal)
遅くなってすみません、私の回答がお役に立てば幸いです。
normal distribution
用途に応じて重みを初期化するには:
torch.nn.init.normal_(tensor, mean=0, std=1)
または、constant distribution
書き込みを使用するには:
torch.nn.init.constant_(tensor, value)
またはを使用するにはuniform distribution
:
torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound
柔軟性を高めたい場合は、ウェイトを手動で設定することもできます。
あなたがすべてのものを入力したとしましょう:
import torch
import torch.nn as nn
input = torch.ones((8, 8))
print(input)
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.]])
そして、バイアスのない密な層を作成したいと思います(視覚化できるように):
d = nn.Linear(8, 8, bias=False)
すべての重みを0.5(またはその他)に設定します。
d.weight.data = torch.full((8, 8), 0.5)
print(d.weight.data)
重み:
Out[14]:
tensor([[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000]])
すべてのウェイトが0.5になりました。データを渡す:
d(input)
Out[13]:
tensor([[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.]], grad_fn=<MmBackward>)
各ニューロンは8つの入力を受け取り、そのすべてに重み0.5と値1(バイアスなし)があるため、合計はそれぞれ4になります。
apply
たとえば、モデルがSequential
直接実装されていない場合に使用できません。
# see UNet at https://github.com/milesial/Pytorch-UNet/tree/master/unet
def init_all(model, init_func, *params, **kwargs):
for p in model.parameters():
init_func(p, *params, **kwargs)
model = UNet(3, 10)
init_all(model, torch.nn.init.normal_, mean=0., std=1)
# or
init_all(model, torch.nn.init.constant_, 1.)
def init_all(model, init_funcs):
for p in model.parameters():
init_func = init_funcs.get(len(p.shape), init_funcs["default"])
init_func(p)
model = UNet(3, 10)
init_funcs = {
1: lambda x: torch.nn.init.normal_(x, mean=0., std=1.), # can be bias
2: lambda x: torch.nn.init.xavier_normal_(x, gain=1.), # can be weight
3: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv1D filter
4: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv2D filter
"default": lambda x: torch.nn.init.constant(x, 1.), # everything else
}
init_all(model, init_funcs)
あなたはtorch.nn.init.constant_(x, len(x.shape))
それらが適切に初期化されていることを確認するために試すことができます:
init_funcs = {
"default": lambda x: torch.nn.init.constant_(x, len(x.shape))
}
廃止の警告が表示された場合(@FábioPerez)...
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform_(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
今のところ評判が足りないので、コメントを追加できません
19年6月26日13:16にprostiによって投稿された回答。
def reset_parameters(self):
init.kaiming_uniform_(self.weight, a=math.sqrt(3))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
しかし、私は実際に私たちが紙でいくつかの仮定を知っていることを指摘したいKaiming彼、整流器掘り下げるディープ:ImageNet分類上の人間レベルのパフォーマンスを上回ります、それは意図的に設計された初期化メソッドのように見えるものの、実際にヒットを行い、適切ではありません、 。
例:後方伝播ケースのサブセクション内、彼らは$ w_l $と$ \ delta y_l $が互いに独立していると仮定します。しかし、すべての人が知っているように、スコアマップ$ \ delta y ^ L_i $をインスタンスとして使用します。通常、クロスエントロピー損失関数の目的。
ですから、彼の初期化がうまく機能する根本的な理由はまだ解明されていないと思います。Cuzの誰もがディープラーニングトレーニングを促進するその力を目撃しています。
reset_parameters
多くのモジュールのソースコードにメソッドを見つけました。重みの初期化のメソッドをオーバーライドする必要がありますか?