Инициализация весов

Урок объясняет, почему стартовые веса нельзя ставить как попало и как их правильно масштабировать.

Инициализация весов — это выбор стартовых значений перед обучением; от неё зависит, заработает обучение бодро или захлебнётся с первых шагов.

Почему не нули и не одинаковые числа

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

Почему важен масштаб

Случайность — необходимое, но не достаточное условие. Если веса слишком крупные, взвешенная сумма получается большой по модулю, sigmoid/tanh насыщаются, градиент затухает. Если слишком мелкие — сигнал угасает по слоям. Нужен «средний» масштаб, и он зависит от числа входов нейрона: чем входов больше, тем меньше должен быть каждый вес.

Xavier-инициализация

Популярный рецепт (Xavier/Glorot): масштабировать веса множителем 1/sqrt(n_in), где n_in — число входов. Для ReLU есть похожий вариант He с множителем sqrt(2/n_in). Сравним наивную инициализацию с Xavier на нейроне со ста входами.

import random, math
random.seed(42)

def variance(xs):
    m = sum(xs) / len(xs)
    return sum((x - m) ** 2 for x in xs) / len(xs)

n_in = 100
naive  = [random.uniform(-1, 1) for _ in range(n_in)]          # без масштаба
scale  = 1.0 / math.sqrt(n_in)
xavier = [random.uniform(-scale, scale) for _ in range(n_in)]  # Xavier

x = [1.0] * n_in   # все входы = 1, смотрим на взвешенную сумму
s_naive  = sum(w * xi for w, xi in zip(naive,  x))
s_xavier = sum(w * xi for w, xi in zip(xavier, x))
print("Взвешенная сумма (наивная):", round(s_naive, 3))
print("Взвешенная сумма (Xavier): ", round(s_xavier, 3))
print("Дисперсия весов наивная:", round(variance(naive), 4))
print("Дисперсия весов Xavier: ", round(variance(xavier), 6))

Вывод:

Взвешенная сумма (наивная): -4.086
Взвешенная сумма (Xavier):  -0.222
Дисперсия весов наивная: 0.3446
Дисперсия весов Xavier:  0.003403

У наивной инициализации сумма уехала к -4 — на таком входе sigmoid почти насытится и градиент будет крошечным. У Xavier сумма около нуля — в «рабочей зоне» активации, где обучение идёт хорошо. Один множитель 1/sqrt(n_in) удержал сигнал в нужном масштабе.

Памятка

АктивацияРекомендуемая инициализация
sigmoid, tanhXavier / Glorot
ReLU и вариантыHe

Почему это особенно важно в глубоких сетях

В одном слое неудачный масштаб весов терпим, но в глубокой сети ошибка накапливается: если каждый слой чуть-чуть усиливает сигнал, к десятому слою он взорвётся, а если чуть-чуть ослабляет — угаснет до нуля. Грамотная инициализация подбирает масштаб так, чтобы дисперсия сигнала сохранялась от слоя к слою — ни роста, ни затухания. Именно поэтому формула зависит от числа входов n_in (а вариант для backward — ещё и от числа выходов): цель — удержать сигнал в рабочей зоне на всей глубине сети как при прямом, так и при обратном проходе.

Смещения, в отличие от весов, обычно инициализируют нулями — проблемы симметрии у них нет, ведь веса уже случайны и разводят нейроны. Исключение делают изредка: например, для ReLU иногда ставят маленькое положительное смещение, чтобы нейроны на старте не оказались в «мёртвой» зоне с нулевым выходом.

Итог

  • Веса инициализируют случайно, чтобы избежать симметрии нейронов.
  • Масштаб весов зависит от числа входов: множитель 1/sqrt(n_in) (Xavier) удерживает сигнал.
  • Для ReLU используют He-инициализацию.
Проверьте себя
1. Почему нельзя инициализировать все веса слоя одинаково?
AЭто медленно
BSigmoid выдаст ошибку
CНейроны останутся идентичными и не научатся разному (симметрия)
DГрадиент станет нулевым навсегда
2. На что множат веса в Xavier-инициализации?
AНа число эпох
BНа learning rate
CНа 0.5
DНа 1/sqrt(n_in), где n_in — число входов нейрона
3. Чем грозят слишком крупные стартовые веса при sigmoid?
AНасыщением активации и затуханием градиента
BНичем
CСлишком быстрым обучением
DВзрывом числа слоёв