Зачем нужен backward
От вопроса «как уменьшать loss» к вопросу «как получить градиент скрытого веса»
Главная идея
Вес первого слоя влияет на loss через цепочку промежуточных операций. Нужно проследить это влияние назад.
В предыдущей главе мы научились уменьшать функцию потерь. Если для параметра модели известен его градиент, можно сделать шаг градиентного спуска:
\[ w_{\text{new}}=w_{\text{old}}-\eta\,\frac{\partial L}{\partial w}. \]
\(\eta\) — скорость обучения (learning rate). Она задаёт размер шага: насколько сильно мы изменяем вес за один шаг обучения.
\(\frac{\partial L}{\partial w}\) — производная функции потерь \(L\) по весу \(w\). Она показывает, как изменится loss при небольшом изменении именно этого веса.
Знак производной показывает направление. Если \(\frac{\partial L}{\partial w}>0\), то увеличение веса увеличивает loss, поэтому вес нужно уменьшать. Если \(\frac{\partial L}{\partial w}<0\), то для уменьшения loss вес, наоборот, нужно увеличить.
Например, если
\[ \frac{\partial L}{\partial w}=-4, \]
то небольшое увеличение \(w\) уменьшает функцию потерь. Поэтому в формуле
\[ w_{\text{new}}=w_{\text{old}}-\eta(-4) \]
вес увеличится.
Для простой линейной модели производную ещё можно получить непосредственно. Например, если
\[ \hat y=wx, \qquad L=(\hat y-y)^2, \]
то можно подставить прогноз в функцию потерь:
\[ L=(wx-y)^2, \]
и получить производную по весу непосредственно.
Но в многослойной сети вес первого слоя влияет на ошибку через несколько промежуточных операций.
Вернёмся к прогнозу времени доставки. Теперь используем сеть:
признаки → скрытый слой → ReLU → выходной слой → прогноз → loss
Изменение веса первого слоя сначала меняет значение скрытого нейрона. Затем меняется результат после ReLU, потом значение следующего слоя, прогноз и только после этого функция потерь.
Главный вопрос главы: как получить \(\frac{\partial L}{\partial w}\) для веса, если между этим весом и функцией потерь находится целая цепочка операций?
Маленькая модель той же проблемы
Начнём не с нейронной сети, а с трёх простых операций:
\[ x=2,\qquad a=3x,\qquad b=a+4,\qquad L=b^2. \]
Сначала просто посчитаем значения слева направо:
\[ a=3\cdot2=6, \] \[ b=6+4=10, \] \[ L=10^2=100. \]
Прямой проход, или forward pass, — вычисление значений от входа сети к её результату.
На прямом проходе мы узнали, что получилось: \(L=100\). Но само число 100 ещё не говорит, какая из предыдущих величин и насколько повлияла на этот результат.
Именно это мы начнём выяснять, двигаясь по вычислениям в обратную сторону.