PyTorchで重みを初期化する方法は?


回答:


150

単層

単一レイヤーの重みを初期化するには、から関数を使用します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)

6
reset_parameters多くのモジュールのソースコードにメソッドを見つけました。重みの初期化のメソッドをオーバーライドする必要がありますか?
ヤンボー

1
何らかの平均と標準の正規分布を使用したい場合はどうすればよいですか?
チャーリーパーカー

12
指定しない場合のデフォルトの初期化は何ですか?
xjcl 2019年

少なくとも線形レイヤーのデフォルトの初期化は彼女です:pytorch.org/docs/stable/nn.html#linear-layers
arash javan

40

同じニューラルネットワーク(NN)アーキテクチャを使用して、重みの初期化の異なるモードを比較します。

すべて0または1

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)
  • 2エポック後:

一定に重みを初期化したトレーニング損失のプロット

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クラスコード以外)を示します。モデル定義の外で重みを定義するために、次のことができます。

  1. 、ネットワーク層の種類によって重みを割り当てる機能を定義し、次いで
  2. 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)
  • 2エポック後:

ここに画像の説明を入力してください

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)と、一般的なルールを使用して重みが初期化された重みを比較します。

  • 2エポック後:

重みの均一な初期化と初期化の一般的なルールのパフォーマンスを示すプロット

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つは正規分布を使用して初期化されます。

  • 2エポック後:

正規分布と正規分布を使用した重み初期化のパフォーマンス

Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329  -- Uniform Rule [-y, y)
0.443  -- Normal Distribution

7
最適化するタスクは何ですか?そして、どのようにすべてゼロのソリューションがゼロの損失を与えることができますか?
dedObed

19

レイヤーを初期化するには、通常は何もする必要はありません。

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チェックしてください

注意:適切な初期化を行うと、トレーニング速度が速くなります。問題が特別な初期化に値するなら、あとでそれを行うことができます。


ただし、デフォルトの初期化では常に最良の結果が得られるとは限りません。私は最近PytorchにVGG16アーキテクチャを実装し、CIFAR-10データセットでトレーニングしましxavier_uniformた。デフォルトの初期化を使用するのではなく、重みの初期化に切り替えることで(バイアスを0に初期化)、検証精度は30 RMSpropのエポックが82%から86%に増加しました。また、Pytorchの組み込みVGG16モデル(事前トレーニングされていない)を使用すると86%の検証精度を得たので、正しく実装したと思います。(私は0.00001の学習率を使用しました。)
littleO

これは、VGG16でBatch Normsを使用していないためです。適切な初期化が重要であり、一部のアーキテクチャでは注意を払うことは事実です。たとえば、(nn.conv2d()、ReLU()シーケンス)を使用する場合は、コンバージョンレイヤーを解決するために設計されたKaiming He初期化を初期化します。PyTorchは、conv2d後のアクティベーション関数を予測できません。これは、eignevaluesを評価する場合に意味がありますが、通常、Batch Normsを使用する場合、多くのことを行う必要はありません。それらは出力を正規化します。SotaBenchの競争に勝つことを計画する場合それは重要です。
prosti

7
    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)

5

遅くなってすみません、私の回答がお役に立てば幸いです。

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

ここでテンソルを初期化する他の方法をチェックできます


2

柔軟性を高めたい場合は、ウェイトを手動で設定することもできます

あなたがすべてのものを入力したとしましょう:

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になります。


1

パラメータを反復する

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))
}


0

今のところ評判が足りないので、コメントを追加できません

196月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の誰もがディープラーニングトレーニングを促進するその力を目撃しています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.