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クラスコード以外)を示します。モデル定義の外側で重みを定義するには、次のことができます。
- ネットワーク層のタイプごとに重みを割り当てる関数を定義するthen
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)
以下では、uniform-distributionを使用して初期化された2つのNNと、normal-distribution
Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329 -- Uniform Rule [-y, y)
0.443 -- Normal Distribution
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
あなたはテンソルを初期化する他の方法をチェックすることができます here
非推奨の警告(@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)
PyTorchがあなたのためにそれを行います。考えてみれば、これには多くの意味があります。 PyTorchが最新のトレンドに従ってそれを行うことができるのに、なぜレイヤーを初期化する必要があるのですか。
たとえば、 Linear layer を確認してください。
__init__
メソッドでは、 Kamming He 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
の場合は、 here を確認してください。
注:適切な初期化の利点は、トレーニング速度が速いことです。問題が特別な初期化に値する場合は、あとで行うことができます。
モデルが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))
}