5 Предыстория backprop
Контекст
Чтобы обучать сеть градиентным спуском, нужна производная ошибки по каждому весу. Наивный подсчёт (по одному весу) для сети с миллионами параметров безнадёжно дорог. Решение пришло не из ИИ, а из численного анализа.
Идея и механизм
Любое вычисление — это граф: узлы-операции, рёбра-значения. Производную даёт цепное правило; вопрос — в каком порядке его применять. Адъоинт (чувствительность выхода к узлу) накапливается из «детей» узла:
Линнайнмаа описал это в 1970-м как способ отслеживать ошибки округления — без всякой связи с нейросетями (диплом на финском). Вербос (1974, Гарвард) первым предложил применить приём к обучению сетей.
мат. анализ Forward-mode vs reverse-mode: почему для ML выигрывает обратный
Пусть сеть — композиция f = fL ∘ … ∘ f1. Градиент по цепному правилу — это произведение якобианов JL · … · J1. Стоимость зависит от порядка умножения матриц:
- Forward-mode умножает со стороны входа (производит Якобиан-вектор произведения). Цена ∝ числу входов n.
- Reverse-mode умножает со стороны выхода (вектор-Якобиан произведения). Цена ∝ числу выходов m.
В машинном обучении выход — один скаляр потерь (m = 1), а параметров миллионы (n огромно). Значит:
Отсюда и эффективность backprop: один обратный проход даёт градиент по всем весам ценой порядка одного прямого. Плата — нужно хранить промежуточные значения (память ∝ размеру графа).
Python Обратный проход вручную на маленьком выражении
# f(a,b) = a*b + a; найдём ∂f/∂a и ∂f/∂b обратным проходом
a, b = 3.0, 4.0
u = a * b # прямой проход
f = u + a
df = 1.0 # адъоинты от выхода к входам:
du = df * 1.0 # f = u + a → ∂f/∂u = 1
da = df * 1.0 # → вклад в ∂f/∂a = 1
da += du * b # u = a*b → ∂u/∂a = b
db = du * a # → ∂u/∂b = a
print(da, db) # → 5.0, 3.0 (∂f/∂a = b+1, ∂f/∂b = a)
Почему это важно
Reverse-mode AD — вычислительный движок всего глубокого обучения. Когда PyTorch вызывает loss.backward(), он исполняет ровно этот алгоритм. А ещё это хрестоматийный пример многократного независимого открытия: алгоритм появился за 12–16 лет до знаменитой статьи 1986-го (#7), которая лишь сделала его известным.
Связи
Та же идея обратного режима, но громко и с демонстрацией: статья 1986-го показывает, что им реально обучаются многослойные сети и что скрытые слои учат признаки. Изобретение — здесь; слава — там.
Обучение одного слоя простое. Как только захотели скрытые слои (чтобы обойти XOR), понадобился эффективный способ считать градиент сквозь них — его и даёт обратный режим.
Backprop даёт градиент; оптимизатор решает, какой шаг по нему сделать. Разделение «как посчитать производную» (AD) и «как по ней двигаться» (SGD/Adam) — два независимых слоя, на которых стоит всё обучение.
Вопросы пытливого ума
Если reverse-mode так эффективен, зачем вообще нужен forward-mode?
Он выигрывает в обратной ситуации: мало входов, много выходов (тогда цена ∝ числу входов мала). Forward-mode даёт Якобиан-вектор произведения, удобен для анализа чувствительности и не требует хранить весь граф — память постоянна. В ML же выход один (скаляр потерь), а входов-параметров тьма, поэтому почти всегда правит reverse.
Reverse-mode требует хранить активации всего графа — это не дорого по памяти?
Дорого, и это реальная проблема обучения больших моделей: память ∝ размеру графа (всем промежуточным активациям). Лечат gradient checkpointing — часть активаций не хранят, а пересчитывают на обратном проходе, меняя память на дополнительные вычисления. Классический trade-off compute↔memory.
Почему тогда честь достаётся 1986-му, а не Линнайнмаа?
Потому что наука награждает не только за открытие, но и за «доведение до сообщества». Диплом Линнайнмаа на финском про ошибки округления никто не связал с нейросетями; статья 1986-го в Nature показала пользу и попала в нужный момент. Юрген Шмидхубер много лет настаивает, что приоритет за Линнайнмаа — и формально он прав.
Что читать в оригинале
Первоисточники нишевые и труднодоступные. Достаточно понять идею обратного режима и историю многократного открытия — это лучшее, что даёт работа для канона.