Как инициализировать весы и предубеждения (например, с инициализацией He или Xavier) в сети в PyTorch?
Как инициализировать весы в 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 эпох:
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)
- После 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)
ниже мы показываем производительность двух NN, один из которых инициализирован с использованием равномерного распределения, а другой - с использованием нормального распределения.
- После 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
Ответ 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))
}