web-dev-qa-db-ja.com

PyTorchでウェイトを初期化する方法は?

PyTorchのネットワークで重みとバイアスを初期化する方法(たとえば、HeまたはXavierの初期化で)

50
Fábio Perez

単層

単一のレイヤーの重みを初期化するには、 torch.nn.init の関数を使用します。例えば:

conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)

または、conv1.weight.datatorch.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)
78
Fábio Perez

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

すべてゼロまたは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エポック後:

plot of training loss with weight initialization to constant

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. ネットワーク層のタイプごとに重みを割り当てる関数を定義するthen
  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エポック後:

enter image description here

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

plot showing performance of uniform initialization of weight versus general rule of initialization

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

  • 2エポック後:

performance of weight initialization using uniform-distribution versus the 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
12
ashunigion
    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)
4
Duane

遅れてすみません、私の答えが役に立てば幸いです。

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

3
Luca Di Liello

非推奨の警告(@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)
0
Joseph Konan

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

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 を確認してください。

注:適切な初期化の利点は、トレーニング速度が速いことです。問題が特別な初期化に値する場合は、あとで行うことができます。

0
prosti

パラメータを反復する

モデルが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
ted