Як ініціалізувати ваги та упередження (наприклад, за допомогою ініціалізації He або Xavier) у мережі в PyTorch?
Як ініціалізувати ваги та упередження (наприклад, за допомогою ініціалізації He або Xavier) у мережі в PyTorch?
Відповіді:
Щоб ініціалізувати ваги одного шару, використовуйте функцію від torch.nn.init
. Наприклад:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
Крім того, ви можете змінити параметри, написавши на conv1.weight.data
(що є a 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)
Якщо ви будете слідувати принципу бритви Оккама , ви можете подумати, що найкращим рішенням буде встановлення всіх ваг на 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) для ініціалізації ваг мережі. Щоб визначити ваги поза визначенням моделі, ми можемо:
- Визначте функцію, яка призначає ваги за типом мережевого рівня, Тоді
- Застосуйте ці ваги до ініціалізованої моделі за допомогою
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)
нижче ми показуємо ефективність двох NN, один ініціалізований за допомогою рівномірного розподілу, а інший - за допомогою нормального розподілу
Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329 -- Uniform Rule [-y, y)
0.443 -- Normal Distribution
PyTorch зробить це за вас. Якщо задуматися, це має багато сенсу. Навіщо нам ініціалізувати шари, коли PyTorch може це робити, слідуючи останнім тенденціям.
Перевірте, наприклад, Лінійний шар .
У __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
, наприклад , перевірити тут .
Зверніть увагу: Виграш правильної ініціалізації полягає у вищій швидкості тренування. Якщо ваша проблема заслуговує на особливу ініціалізацію, ви можете зробити це післямови.
xavier_uniform
ініціалізацію для ваг (з упередженнями, ініціалізованими до 0), а не використовуючи ініціалізацію за замовчуванням, моя точність перевірки після 30 епохи RMSprop зросли з 82% до 86%. Я також отримав 86% точності перевірки при використанні вбудованої моделі VGG16 від Pytorch (не попередньо навченої), тому, думаю, я її правильно застосував. (Я використовував рівень навчання 0,00001.)
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
Ви можете перевірити інші методи ініціалізації тензорів тут
Якщо вам потрібна додаткова гнучкість, ви також можете встановити ваги вручну .
Скажімо, ви ввели всі:
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 для кожного.
Якщо ви не можете використовувати, 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))
}
Якщо ви бачите попередження про зневажання (@ 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)
Тому що у мене поки що недостатньо репутації, я не можу додати коментар під
відповідь Написав PrOSTi в 26 '19 червня о 13:16 .
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 He , Deving Deep into Rectifiers: Supering Performance на людському рівні щодо класифікації ImageNet , не є доречними, хоча, схоже, свідомо розроблений метод ініціалізації робить хіт на практиці .
Наприклад, у підрозділі Справа про зворотне поширення про зворотне вони припускають, що $ w_l $ і $ \ delta y_l $ не залежать один від одного. Але, як ми всі знаємо, візьмемо оціночну карту $ \ delta y ^ L_i $ як примірник, часто це $ y_i-softmax (y ^ L_i) = y_i-softmax (w ^ L_ix ^ L_i) $, якщо ми використовуємо типовий ціль функції перехресної ентропії.
Тому я думаю, що справжня основна причина того, чому Він ініціалізує добре, залишається розгадати. Тому що кожен пересвідчився в його силі щодо підвищення рівня глибокого навчання.
reset_parameters
метод у вихідному коді багатьох модулів. Чи слід замінювати метод ініціалізації ваги?