Инициализация весов
Урок объясняет, почему стартовые веса нельзя ставить как попало и как их правильно масштабировать.
Инициализация весов — это выбор стартовых значений перед обучением; от неё зависит, заработает обучение бодро или захлебнётся с первых шагов.
Почему не нули и не одинаковые числа
Если задать всем весам слоя одно и то же значение, все нейроны слоя будут считать одно и то же и получать одинаковые градиенты — они навсегда останутся идентичными (это называют проблемой симметрии). Поэтому веса инициализируют случайно, чтобы нейроны с самого начала были разными и учились разному.
Почему важен масштаб
Случайность — необходимое, но не достаточное условие. Если веса слишком крупные, взвешенная сумма получается большой по модулю, 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, tanh | Xavier / Glorot |
| ReLU и варианты | He |
Почему это особенно важно в глубоких сетях
В одном слое неудачный масштаб весов терпим, но в глубокой сети ошибка накапливается: если каждый слой чуть-чуть усиливает сигнал, к десятому слою он взорвётся, а если чуть-чуть ослабляет — угаснет до нуля. Грамотная инициализация подбирает масштаб так, чтобы дисперсия сигнала сохранялась от слоя к слою — ни роста, ни затухания. Именно поэтому формула зависит от числа входов n_in (а вариант для backward — ещё и от числа выходов): цель — удержать сигнал в рабочей зоне на всей глубине сети как при прямом, так и при обратном проходе.
Смещения, в отличие от весов, обычно инициализируют нулями — проблемы симметрии у них нет, ведь веса уже случайны и разводят нейроны. Исключение делают изредка: например, для ReLU иногда ставят маленькое положительное смещение, чтобы нейроны на старте не оказались в «мёртвой» зоне с нулевым выходом.
Итог
- Веса инициализируют случайно, чтобы избежать симметрии нейронов.
- Масштаб весов зависит от числа входов: множитель
1/sqrt(n_in)(Xavier) удерживает сигнал. - Для ReLU используют He-инициализацию.