6.3. Инициализация параметров

Теперь, когда мы знаем, как получить доступ к параметрам, рассмотрим, как их правильно инициализировать. Необходимость правильной инициализации мы обсуждали в Section 5.4. Фреймворк глубокого обучения предоставляет слоям случайную инициализацию по умолчанию. Однако зачастую нам требуется инициализировать веса в соответствии с различными другими схемами. Фреймворк поддерживает наиболее часто используемые схемы, а также позволяет создавать пользовательские инициализаторы.

import torch
from torch import nn

По умолчанию PyTorch инициализирует матрицы весов и смещений, равномерно выбирая значения из диапазона, вычисляемого на основе размерности входа и выхода. Модуль nn.init PyTorch предоставляет множество предустановленных методов инициализации.

net = nn.Sequential(nn.LazyLinear(8), nn.ReLU(), nn.LazyLinear(1))
X = torch.rand(size=(2, 4))
net(X).shape

6.3.1. Встроенная инициализация

Начнем с использования встроенных инициализаторов. Приведённый ниже код инициализирует все параметры весов как гауссовы случайные величины со стандартным отклонением 0.01, а параметры смещения обнуляются.

def init_normal(module):
    if type(module) == nn.Linear:
        nn.init.normal_(module.weight, mean=0, std=0.01)
        nn.init.zeros_(module.bias)

net.apply(init_normal)
net[0].weight.data[0], net[0].bias.data[0]

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

def init_constant(module):
    if type(module) == nn.Linear:
        nn.init.constant_(module.weight, 1)
        nn.init.zeros_(module.bias)

net.apply(init_constant)
net[0].weight.data[0], net[0].bias.data[0]

Мы также можем применять различные инициализаторы для отдельных блоков. Например, ниже мы инициализируем первый слой с помощью инициализатора Глорота, а второй слой — константным значением 42.

def init_xavier(module):
    if type(module) == nn.Linear:
        nn.init.xavier_uniform_(module.weight)

def init_42(module):
    if type(module) == nn.Linear:
        nn.init.constant_(module.weight, 42)

net[0].apply(init_xavier)
net[2].apply(init_42)
print(net[0].weight.data[0])
print(net[2].weight.data)

6.3.1.1. Пользовательская инициализация

Иногда необходимые методы инициализации не предоставляются фреймворком глубокого обучения. В примере ниже мы определяем инициализатор для любого параметра веса \(w\), используя следующее странное распределение:

(6.3.1)\[\begin{split}\begin{aligned} w \sim \begin{cases} U(5, 10) & \textrm{ with probability } \frac{1}{4} \\ 0 & \textrm{ with probability } \frac{1}{2} \\ U(-10, -5) & \textrm{ with probability } \frac{1}{4} \end{cases} \end{aligned}\end{split}\]

Снова мы реализуем функцию my_init, которую будем применять к net.

def my_init(module):
    if type(module) == nn.Linear:
        print("Init", *[(name, param.shape)
                        for name, param in module.named_parameters()][0])
        nn.init.uniform_(module.weight, -10, 10)
        module.weight.data *= module.weight.data.abs() >= 5

net.apply(my_init)
net[0].weight[:2]

Обратите внимание, что мы всегда можем задавать параметры напрямую.

net[0].weight.data[:] += 1
net[0].weight.data[0, 0] = 42
net[0].weight.data[0]

6.3.2. Итоги

Параметры можно инициализировать с помощью встроенных и пользовательских инициализаторов.

6.3.3. Упражнения

Посмотрите онлайн-документацию, чтобы узнать о других встроенных инициализаторах.

Обсуждения