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

Серым — прямой проход: значения текут слева направо. Красным — обратный: начав с , производная течёт справа налево, на каждом узле умножаясь на локальную. В итоге , , .
Каждый узел знает лишь свою операцию, свои входы и производную, пришедшую с выхода. Ему не нужно видеть всю сеть — только своих соседей. Из этих локальных правил и собирается глобальный градиент.
Ветвление складывает вклады
Одна тонкость ломает больше всего ручных выводов. Если число участвует в нескольких путях к потере, его производная — сумма по всем путям:
Ученик, выводящий градиент на бумаге, часто пишет , потеряв второй путь. Верный ответ : производная приходит и через ветку (даёт ), и через прямую ветку (даёт ), а в узле они складываются. Система автодифференцирования не забывает ни одного пути — она копит вклады со знаком «плюс равно».
Матрица делает то же самое
Слой сети — это не отдельные узлы, а линейное преобразование и нелинейность . Обратный проход работает и здесь, только локальные производные становятся матричными. Если с выхода пришла чувствительность , то
Формы служат проверкой без всякой теории. Производная по обязана иметь ту же форму, что : если размера , то и такой же. Внешнее произведение как раз рождает матрицу . Перепутанное транспонирование сразу даёт неверную форму.
Почему обратный режим дёшев
Теперь главное — откуда экономия. У сети миллионы параметров, но потеря одна, скалярная. Можно было бы гнать производную вперёд, отдельно для каждого параметра, — это прямой режим, и он стоил бы миллион проходов. Обратный режим делает наоборот: пускает одну выходную чувствительность назад и раздаёт производные всем предкам за работу порядка одного-двух прямых проходов. Один forward, один backward — и готов весь градиент.
Когда выходов много, а входов мало, выгоднее прямой режим. Но обучение сети — всегда много параметров и один скалярный критерий, поэтому обратный режим тут единственно разумен. Это и есть цепное правило в самой экономной раскладке, а не особое свойство нейросетей.
Именно поэтому обратное распространение встроено в каждую современную
библиотеку глубокого обучения. Записывая формулу сети, вы, сами того не
замечая, строите граф вычислений; вызов вроде loss.backward() запускает
по нему обратный проход и раскладывает градиенты по всем параметрам. Эту
машинерию называют автоматическим дифференцированием, и она работает для
любой программы из дифференцируемых операций, а не только для нейросетей.
Понимая механику графа, вы перестаёте видеть в ней чёрный ящик: за
магическим backward стоит ровно то цепное правило, что мы разобрали на
трёх узлах.
Проверка на реальных данных
Backprop легко запрограммировать неверно — забыть путь, перепутать транспонирование. Поэтому его всегда проверяют численно. Возьмём маленькую сеть на данных об ирисах: четыре признака цветка, скрытый слой с сигмоидой, три класса на выходе, перекрёстная энтропия как потеря. Для одного веса сравним производную от backprop с центральной разностью:

Каждая точка — один вес сети на реальных данных. Все проверенных весов легли точно на диагональ: производная от backprop и численная совпали с наибольшим расхождением порядка . Это и есть доказательство, что цепное правило разложено по графу без ошибок.
Совпадение до одиннадцатого знака — не совпадение, а подтверждение корректности. Проверку ведут в двойной точности, вдали от излома ReLU и на нескольких случайных весах разных слоёв: одного мало.
Когда градиент затухает или взрывается
У обратного прохода есть коварная особенность. Проходя назад через слои, градиент умножается на локальную производную каждого. У сигмоиды эта производная не больше (мы считали это в уроке об активациях). На цепочке из слоёв набегает множитель : к десятому слою он уже , к двадцатому — . Градиент у первых слоёв почти исчезает, и они не обучаются.

Каждый слой домножает градиент на свой множитель. Меньше единицы (сигмоида, ) — и градиент затухает до нуля на глубине. Больше единицы — и он взрывается до бесконечности. Ровно единица — редкий счастливый случай сохранения.
Обратная беда — взрыв. Если множители в среднем больше единицы, их
произведение растёт лавиной, градиент вылетает в бесконечность, и
обучение срывается в NaN. Грубое, но действенное лекарство — обрезка
градиента: если его норма превысила порог, вектор укорачивают, сохраняя
направление. Этот приём мы уже видели в лаборатории спуска,
где он не давал одиночному шагу разнести траекторию.
Отсюда растут почти все приёмы глубокого обучения: ReLU с производной вместо затухающей сигмоиды, нормировки, удерживающие множители около единицы, остаточные связи, пропускающие градиент в обход слоёв. Все они борются за одно — чтобы произведение множителей вдоль цепочки не убегало от единицы ни вниз, ни вверх. Без этой борьбы глубокие сети попросту не обучались бы, и почти двадцать лет — между первыми работами о backprop и революцией глубокого обучения — ушли как раз на то, чтобы приручить множители слоёв.
Русская линия: кто открыл обратное распространение
Обратное распространение переоткрывали много раз. Обратный режим дифференцирования описал финский студент Сеппо Линнайнмаа ещё в году. Всемирную славу методу принесла статья Румельхарта, Хинтона и Уильямса года. А в том же году, независимо и по другую сторону железного занавеса, красноярские учёные Сергей Барцев и Владимир Охонин вывели тот же метод обучения многослойных сетей обратным распространением ошибки.
Советская школа нейросетей была сильна: Александр Галушкин строил их общую теорию ещё с -х, а работы по адаптивным системам и распознаванию образов шли параллельно западным. Разделённые языком и закрытыми границами, коллективы годами не знали о результатах друг друга и приходили к одному методу порознь. История backprop — напоминание, что глубокая идея редко рождается в одной голове; когда почва созрела, важные результаты всходят сразу в нескольких местах. Для нас же существенно, что за громким словом «обучение сети» стоит проверяемая, воспроизводимая механика — та самая, что мы разобрали и сверили с численной производной.
Память против пересчёта
За экономию времени backprop платит памятью. Чтобы пройти назад, нужны значения, сохранённые на прямом проходе: входы слоёв, маски ReLU, нормировки. На глубокой сети они занимают больше места, чем сами параметры.
Отсюда приём контрольных точек: хранить лишь каждый третий-четвёртый слой, а промежуточные пересчитывать заново на обратном ходе. Это прямой обмен времени на память, спасающий обучение очень глубоких сетей.
Лаборатория обратного прохода
Порядок опытов. Перед вами граф вычислений. Двигайте входы , , и следите: вверху каждого узла загорается значение прямого прохода, внизу — производная потери по этому узлу, принесённая обратным проходом. Проверьте на себе цепное правило: на табло всегда равно . Затем переключитесь на ветвление и убедитесь, что в узле , куда сходятся два пути, производные складываются в , а не в . Это тот самый забытый путь, на котором спотыкается ручной вывод.
Сборка: цепное правило, разложенное по графу
Обратное распространение — не магия, а бухгалтерия цепного правила. Один прямой проход считает и запоминает значения; один обратный, начав с , гонит производную назад, на каждом узле умножая её на локальную и складывая вклады сходящихся путей. За два прохода добывается градиент по всем миллионам весов сразу — оттого метод и сделал обучение глубоких сетей возможным. Мы проверили его численно на реальной сети: backprop и конечная разность совпали до одиннадцатого знака. Увидели и его слабость: на глубине множители слоёв уводят градиент в нуль или в бесконечность, и вся инженерия глубокого обучения борется с этим. Теперь у нас есть всё: сеть считает прямой проход, потеря измеряет ошибку, backprop даёт градиент, а стохастический спуск делает шаг. Дальше — как из этих кирпичей собираются работающие архитектуры зрения и языка.