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

Потеря в конце длинной цепочки

Стохастический спуск требовал градиент i\nabla\ell_i — производную потери по каждому весу. Для одного нейрона мы считали её руками. Но в сети из многих слоёв потеря отделена от первых весов десятком преобразований: вход умножается на матрицу, проходит нелинейность, снова умножается, и так слой за слоем. Как найти производную потери по весу, спрятанному в самом начале?

Наивный ответ — конечная разность: чуть-чуть подтолкнуть вес и посмотреть, как изменилась потеря. Но весов миллионы, а каждый толчок требует целого прямого прохода по сети. Миллион весов — миллион проходов на один шаг обучения. Это безнадёжно дорого. Обратное распространение добывает все производные разом, за один проход назад, и в этом вся его сила.

Стоит вдуматься в этот выигрыш. Наивный способ масштабируется как произведение: число весов, умноженное на стоимость прохода. Обратное распространение масштабируется как сумма: один прямой проход плюс один обратный, независимо от числа весов. Для сети с миллиардом параметров разница — между «невозможно» и «доля секунды». Именно эта асимметрия, а не какая-то особая математика, сделала глубокое обучение практичным.

Цепное правило на трёх узлах

Начнём с крошечного примера, чтобы увидеть механику целиком. Пусть

u=wx,v=u+b,L=v2.u=wx,\qquad v=u+b,\qquad L=v^2.

Прямой проход слева направо считает и запоминает значения. Для x=3x=3, w=2w=2, b=1b=-1 выходит u=6u=6, v=5v=5, L=25L=25. Теперь обратный проход справа налево. Он начинается с очевидного L/L=1\partial L/\partial L=1 и на каждом узле умножает пришедшую производную на локальную:

Lv=2v=10,Lu=Lv1=10,Lw=Lux=30.\frac{\partial L}{\partial v}=2v=10,\quad \frac{\partial L}{\partial u}=\frac{\partial L}{\partial v}\cdot1=10,\quad \frac{\partial L}{\partial w}=\frac{\partial L}{\partial u}\cdot x=30.
Рисунок шире экрана — проведите по немуОткрыть целиком ↗
Граф вычислений: входы x, w, b слева, узел u равно w на x, узел v равно u плюс b, узел L равно v в квадрате справа; серым подписаны значения прямого прохода, красным — производные потери по каждому узлу, красная стрелка показывает движение градиента справа налево
Рис. 25.1. Прямой проход считает значения, обратный — производные

Серым — прямой проход: значения текут слева направо. Красным — обратный: начав с L/L=1\partial L/\partial L=1, производная течёт справа налево, на каждом узле умножаясь на локальную. В итоге L/w=2vx=30\partial L/\partial w=2vx=30, L/b=2v=10\partial L/\partial b=2v=10, L/x=2vw=20\partial L/\partial x=2vw=20.

Каждый узел знает лишь свою операцию, свои входы и производную, пришедшую с выхода. Ему не нужно видеть всю сеть — только своих соседей. Из этих локальных правил и собирается глобальный градиент.

Ветвление складывает вклады

Одна тонкость ломает больше всего ручных выводов. Если число участвует в нескольких путях к потере, его производная — сумма по всем путям:

Lx=Laax+Lbbx.\frac{\partial L}{\partial x} =\frac{\partial L}{\partial a}\frac{\partial a}{\partial x} +\frac{\partial L}{\partial b}\frac{\partial b}{\partial x}.

Ученик, выводящий градиент L=x2+xL=x^2+x на бумаге, часто пишет 2x2x, потеряв второй путь. Верный ответ 2x+12x+1: производная приходит и через ветку x2x^2 (даёт 2x2x), и через прямую ветку (даёт 11), а в узле xx они складываются. Система автодифференцирования не забывает ни одного пути — она копит вклады со знаком «плюс равно».

Матрица делает то же самое

Слой сети — это не отдельные узлы, а линейное преобразование z=Wx+bz=Wx+b и нелинейность h=ϕ(z)h=\phi(z). Обратный проход работает и здесь, только локальные производные становятся матричными. Если с выхода пришла чувствительность hˉ=L/h\bar h=\partial L/\partial h, то

zˉ=hˉϕ(z),Wˉ=zˉx,bˉ=zˉ,xˉ=Wzˉ.\bar z=\bar h\odot\phi'(z),\quad \bar W=\bar z\,x^\top,\quad \bar b=\bar z,\quad \bar x=W^\top\bar z.

Формы служат проверкой без всякой теории. Производная по WW обязана иметь ту же форму, что WW: если WW размера m×dm\times d, то и Wˉ\bar W такой же. Внешнее произведение zˉx\bar z\,x^\top как раз рождает матрицу m×dm\times d. Перепутанное транспонирование сразу даёт неверную форму.

Почему обратный режим дёшев

Теперь главное — откуда экономия. У сети миллионы параметров, но потеря одна, скалярная. Можно было бы гнать производную вперёд, отдельно для каждого параметра, — это прямой режим, и он стоил бы миллион проходов. Обратный режим делает наоборот: пускает одну выходную чувствительность назад и раздаёт производные всем предкам за работу порядка одного-двух прямых проходов. Один forward, один backward — и готов весь градиент.

Когда выходов много, а входов мало, выгоднее прямой режим. Но обучение сети — всегда много параметров и один скалярный критерий, поэтому обратный режим тут единственно разумен. Это и есть цепное правило в самой экономной раскладке, а не особое свойство нейросетей.

Именно поэтому обратное распространение встроено в каждую современную библиотеку глубокого обучения. Записывая формулу сети, вы, сами того не замечая, строите граф вычислений; вызов вроде loss.backward() запускает по нему обратный проход и раскладывает градиенты по всем параметрам. Эту машинерию называют автоматическим дифференцированием, и она работает для любой программы из дифференцируемых операций, а не только для нейросетей. Понимая механику графа, вы перестаёте видеть в ней чёрный ящик: за магическим backward стоит ровно то цепное правило, что мы разобрали на трёх узлах.

Проверка на реальных данных

Backprop легко запрограммировать неверно — забыть путь, перепутать транспонирование. Поэтому его всегда проверяют численно. Возьмём маленькую сеть на данных об ирисах: четыре признака цветка, скрытый слой с сигмоидой, три класса на выходе, перекрёстная энтропия как потеря. Для одного веса сравним производную от backprop с центральной разностью:

gnum=L(w+h)L(wh)2h.g_{\text{num}}=\frac{L(w+h)-L(w-h)}{2h}.
Рисунок шире экрана — проведите по немуОткрыть целиком ↗
Диаграмма рассеяния: по горизонтали численная производная, по вертикали производная от backprop для 140 весов сети на ирисах; все точки лежат точно на диагонали, наибольшее расхождение порядка десять в минус одиннадцатой
Рис. 25.2. Backprop против конечной разности

Каждая точка — один вес сети на реальных данных. Все 140140 проверенных весов легли точно на диагональ: производная от backprop и численная совпали с наибольшим расхождением порядка 101110^{-11}. Это и есть доказательство, что цепное правило разложено по графу без ошибок.

Совпадение до одиннадцатого знака — не совпадение, а подтверждение корректности. Проверку ведут в двойной точности, вдали от излома ReLU и на нескольких случайных весах разных слоёв: одного мало.

Когда градиент затухает или взрывается

У обратного прохода есть коварная особенность. Проходя назад через слои, градиент умножается на локальную производную каждого. У сигмоиды эта производная не больше 14\tfrac14 (мы считали это в уроке об активациях). На цепочке из LL слоёв набегает множитель (14)L(\tfrac14)^L: к десятому слою он уже 10610^{-6}, к двадцатому — 101210^{-12}. Градиент у первых слоёв почти исчезает, и они не обучаются.

Рисунок шире экрана — проведите по немуОткрыть целиком ↗
График в логарифмическом масштабе: множитель градиента против глубины сети; синяя кривая для сигмоиды с множителем одна четвёртая падает до десять в минус двенадцатой к двадцатому слою, зелёная при множителе один держится на месте, красная при множителе один и двадцать пять сотых растёт до десятков
Рис. 25.3. Почему у глубокой сети градиент затухает или взрывается

Каждый слой домножает градиент на свой множитель. Меньше единицы (сигмоида, 14\le\tfrac14) — и градиент затухает до нуля на глубине. Больше единицы — и он взрывается до бесконечности. Ровно единица — редкий счастливый случай сохранения.

Обратная беда — взрыв. Если множители в среднем больше единицы, их произведение растёт лавиной, градиент вылетает в бесконечность, и обучение срывается в NaN. Грубое, но действенное лекарство — обрезка градиента: если его норма превысила порог, вектор укорачивают, сохраняя направление. Этот приём мы уже видели в лаборатории спуска, где он не давал одиночному шагу разнести траекторию.

Отсюда растут почти все приёмы глубокого обучения: ReLU с производной 11 вместо затухающей сигмоиды, нормировки, удерживающие множители около единицы, остаточные связи, пропускающие градиент в обход слоёв. Все они борются за одно — чтобы произведение множителей вдоль цепочки не убегало от единицы ни вниз, ни вверх. Без этой борьбы глубокие сети попросту не обучались бы, и почти двадцать лет — между первыми работами о backprop и революцией глубокого обучения — ушли как раз на то, чтобы приручить множители слоёв.

Русская линия: кто открыл обратное распространение

Обратное распространение переоткрывали много раз. Обратный режим дифференцирования описал финский студент Сеппо Линнайнмаа ещё в 19701970 году. Всемирную славу методу принесла статья Румельхарта, Хинтона и Уильямса 19861986 года. А в том же 19861986 году, независимо и по другую сторону железного занавеса, красноярские учёные Сергей Барцев и Владимир Охонин вывели тот же метод обучения многослойных сетей обратным распространением ошибки.

Советская школа нейросетей была сильна: Александр Галушкин строил их общую теорию ещё с 19701970-х, а работы по адаптивным системам и распознаванию образов шли параллельно западным. Разделённые языком и закрытыми границами, коллективы годами не знали о результатах друг друга и приходили к одному методу порознь. История backprop — напоминание, что глубокая идея редко рождается в одной голове; когда почва созрела, важные результаты всходят сразу в нескольких местах. Для нас же существенно, что за громким словом «обучение сети» стоит проверяемая, воспроизводимая механика — та самая, что мы разобрали и сверили с численной производной.

Память против пересчёта

За экономию времени backprop платит памятью. Чтобы пройти назад, нужны значения, сохранённые на прямом проходе: входы слоёв, маски ReLU, нормировки. На глубокой сети они занимают больше места, чем сами параметры.

Отсюда приём контрольных точек: хранить лишь каждый третий-четвёртый слой, а промежуточные пересчитывать заново на обратном ходе. Это прямой обмен времени на память, спасающий обучение очень глубоких сетей.

Лаборатория обратного прохода

Прямые значения и обратные градиенты

Загружается живая иллюстрация…

Порядок опытов. Перед вами граф вычислений. Двигайте входы xx, ww, bb и следите: вверху каждого узла загорается значение прямого прохода, внизу — производная потери по этому узлу, принесённая обратным проходом. Проверьте на себе цепное правило: L/w\partial L/\partial w на табло всегда равно 2vx2vx. Затем переключитесь на ветвление L=x2+xL=x^2+x и убедитесь, что в узле xx, куда сходятся два пути, производные складываются в 2x+12x+1, а не в 2x2x. Это тот самый забытый путь, на котором спотыкается ручной вывод.

Сборка: цепное правило, разложенное по графу

Обратное распространение — не магия, а бухгалтерия цепного правила. Один прямой проход считает и запоминает значения; один обратный, начав с L/L=1\partial L/\partial L=1, гонит производную назад, на каждом узле умножая её на локальную и складывая вклады сходящихся путей. За два прохода добывается градиент по всем миллионам весов сразу — оттого метод и сделал обучение глубоких сетей возможным. Мы проверили его численно на реальной сети: backprop и конечная разность совпали до одиннадцатого знака. Увидели и его слабость: на глубине множители слоёв уводят градиент в нуль или в бесконечность, и вся инженерия глубокого обучения борется с этим. Теперь у нас есть всё: сеть считает прямой проход, потеря измеряет ошибку, backprop даёт градиент, а стохастический спуск делает шаг. Дальше — как из этих кирпичей собираются работающие архитектуры зрения и языка.

Задачи