Почему нельзя инициализировать веса нейронной сети нулями?

«Почему нельзя инициализировать веса нейронной сети нулями?» — вопрос из категории Нейронные сети и Deep Learning, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Инициализация весов нулями (или одинаковыми константами) приводит к проблеме симметрии, которая блокирует обучение сети.

Механизм проблемы:

  1. Прямой проход: Если все веса в слое равны нулю, каждый нейрон получит одинаковый взвешенный вход (ноль). После применения функции активации (например, ReLU) все нейроны выдадут одинаковый выход (например, ноль для ReLU).
  2. Обратное распространение (Backpropagation): Поскольку выходы нейронов одинаковы, градиенты ошибки по весам, пришедшие к ним, также будут идентичными.
  3. Обновление весов: Все веса в слое обновятся на одну и ту же величину. После первой же итерации они перестанут быть нулями, но останутся равными друг другу.

В результате все нейроны в слое будут вычислять одну и ту же функцию, что эквивалентно наличию в слое всего одного нейрона. Сеть теряет способность изучать разнообразные признаки, и ее емкость резко падает.

Код, иллюстрирующий проблему:

import numpy as np

# ПЛОХО: Инициализация нулями
def zero_init(size):
    return np.zeros(size)

# ХОРОШО: Случайная инициализация (например, He для ReLU)
def he_init(size):
    # size = (n_neurons_current, n_neurons_previous)
    fan_in = size[1]
    return np.random.randn(*size) * np.sqrt(2.0 / fan_in)

# Сравнение
layer_size = (5, 10)  # 5 нейронов, 10 входов
weights_zero = zero_init(layer_size)
weights_he = he_init(layer_size)

print("Zero init (первые два нейрона идентичны):n", weights_zero[0], "n", weights_zero[1])
print("nHe init (нейроны различны):n", weights_he[0][:3], "...n", weights_he[1][:3], "...")

Решение: Использовать случайную инициализацию, которая нарушает симметрию:

  • Xavier/Glorot: W = np.random.randn(n_in, n_out) * np.sqrt(1 / n_in) — хорошо для сигмоидных/гиперболических активаций.
  • He: W = np.random.randn(n_in, n_out) * np.sqrt(2 / n_in) — стандарт для слоев с ReLU и его вариациями.