Как инициализировать весы в PyTorch?

Как инициализировать весы и предубеждения (например, с инициализацией He или Xavier) в сети в PyTorch?

Ответ 1

Одиночный слой

Для инициализации весов одного слоя используйте функцию from torch.nn.init. Например:

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

Кроме того, вы можете изменять параметры, записывая в conv1.weight.data (который является torch.Tensor. torch.Tensor). Пример:

conv1.weight.data.fill_(0.01)

То же самое относится к предубеждениям:

conv1.bias.data.fill_(0.01)

nn.Sequential или custom nn.Module

Передайте функцию инициализации torch.nn.Module.apply. Он будет инициализировать весы во всем nn.Module рекурсивно.

apply (fn): Применяет fn рекурсивно к каждому подмодулю (как возвращено .children()), а также self. Типичное использование включает инициализацию параметров модели (см. Также 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)

Ответ 2

Мы сравниваем различные режимы инициализации веса, используя одну и ту же архитектуру нейронной сети (NN).

Все нули или единицы

Если вы следуете принципу бритвы Оккама, вы можете подумать, что установка всех весов на 0 или 1 будет лучшим решением. Это не вариант.

При одинаковом весе все нейроны на каждом слое дают одинаковый выход. Это затрудняет решение, какие веса отрегулировать.

    # initialize two NN 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. Определите функцию, которая назначает веса по типу сетевого уровня, затем
  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)

ниже мы показываем производительность двух NN, один из которых инициализирован с использованием равномерного распределения, а другой - с использованием нормального распределения.

  • После 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

Ответ 3

Извините за опоздание, я надеюсь, что мой ответ поможет.

Для инициализации весов с 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

Вы можете проверить другие методы для инициализации тензоров здесь

Ответ 4

    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

Для инициализации слоев вам обычно ничего не нужно делать.

PyTorch сделает это за вас. Если подумать, это имеет большой смысл. Зачем нам инициализировать слои, если PyTorch может делать это в соответствии с последними тенденциями.

Например, проверьте линейный слой.

В методе __init__ он вызовет функцию Kamming He.

    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 проверьте здесь.

Примечание: выигрыш от правильной инициализации - более высокая скорость обучения. Если ваша проблема заслуживает специальной инициализации, вы можете сделать это после слов.

Ответ 6

Не в первую очередь, но не в последнюю очередь, наилучшей практикой для сверток является инициализация Kaimimg He (представленная в статье " Углубление в выпрямители: превосходящая производительность на уровне человека при классификации ImageNet)" из нормального распределения, которое he_normal в Tensorflow и kaiming_normal в PyTorch, в то время как униформа для уклонов,

Ответ 7

Если вы видите предупреждение об устаревании (@Fábio Perez)...

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)

Ответ 8

Перебирать параметры

Если вы не можете использовать 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))
}