$ cat toc.txt

В прошлой статье мы построили интуицию backpropagation: волна «желаний» бежит от выхода ко входу, и каждый вес подкручивается в меру своей активности. В финале я пообещал показать, что эта волна — не просто красивая метафора, а в точности градиент функции потерь.

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

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

1. Минимальная сеть: один нейрон на слой

Возьмём сеть до безобразия маленькую: три слоя, в каждом — по одному нейрону. Вход $a^{(0)}$, скрытый нейрон $a^{(1)}$, выход $a^{(2)}$. Верхний индекс в скобках — номер слоя (не степень!).

Обозначения (они стандартные, встретите в любой статье):

$$ z^{(L)} = w^{(L)} a^{(L-1)} + b^{(L)}, \qquad a^{(L)} = \sigma\left(z^{(L)}\right) $$

Читается так: взвешенная сумма $z$ на слое $L$ — это вес, умноженный на активацию предыдущего слоя, плюс смещение. Активация — сигмоида от этой суммы. Функция потерь для одного обучающего примера — квадрат ошибки на выходе:

$$ C_0 = \left(a^{(2)} - y\right)^2 $$

где $y$ — желаемое значение выхода. Цель: посчитать производную $\partial C_0 / \partial w^{(2)}$ — чувствительность потерь к весу последнего слоя. Это и есть «виновность» конкретного веса из прошлой статьи, теперь в строгой форме.

2. Цепное правило: три звена цепи

Вес $w^{(2)}$ не влияет на потери напрямую. Он влияет на сумму $z^{(2)}$; сумма — на активацию $a^{(2)}$; активация — на потери. Цепочка зависимостей:

$$ w^{(2)} ;\to; z^{(2)} ;\to; a^{(2)} ;\to; C_0 $$

Цепное правило говорит: производная по началу цепи — произведение производных по каждому звену:

$$ \frac{\partial C_0}{\partial w^{(2)}} = \frac{\partial z^{(2)}}{\partial w^{(2)}} \cdot \frac{\partial a^{(2)}}{\partial z^{(2)}} \cdot \frac{\partial C_0}{\partial a^{(2)}} $$

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

3. Три производные и что каждая значит

Звено 1: $\partial z^{(2)} / \partial w^{(2)} = a^{(1)}$. Сумма $z^{(2)} = w^{(2)} a^{(1)} + b^{(2)}$, производная по $w^{(2)}$ — это просто $a^{(1)}$: активация предыдущего слоя. Узнаваете? Это правило «кто активен, тот и учится» из прошлой статьи, теперь в виде формулы: если предыдущий нейрон молчит ($a^{(1)} \approx 0$), производная близка к нулю — вес не виноват, учить его незачем.

Звено 2: $\partial a^{(2)} / \partial z^{(2)} = \sigma’(z^{(2)})$. Как быстро сигмоида меняется в точке $z^{(2)}$. Сигмоида пологая на краях и крутая в середине: нейрон, чья сумма лежит в «насыщении» (около 0 или 1), и обучать его почти бесполезно — $\sigma’$ близка к нулю. Запомним этот факт, он ещё выстрелит.

Звено 3: $\partial C_0 / \partial a^{(2)} = 2(a^{(2)} - y)$. Производная квадрата ошибки: удвоенная невязка. Это и есть «сила желания»: чем дальше выход от цели, тем сильнее давление на все зависимости этого выхода. (Двойка — артефакт квадрата; поэтому в ML часто берут $\frac{1}{2}(a-y)^2$, чтобы она сократилась.)

Перемножаем:

$$ \frac{\partial C_0}{\partial w^{(2)}} = a^{(1)} \cdot \sigma’\left(z^{(2)}\right) \cdot 2\left(a^{(2)} - y\right) $$

Три множителя — три вопроса: «насколько активен вход?», «насколько нейрон вообще способен измениться?», «насколько сильно мы хотим изменения?». Вся интуиция прошлой статьи уместилась в одну строчку.

4. Смещение: та же цепь, но проще

Для смещения $b^{(2)}$ цепь та же, только первое звено другое: $\partial z^{(2)} / \partial b^{(2)} = 1$ (смещение входит в сумму с коэффициентом единица). Получаем:

$$ \frac{\partial C_0}{\partial b^{(2)}} = \sigma’\left(z^{(2)}\right) \cdot 2\left(a^{(2)} - y\right) $$

Та же логика без множителя-активности: смещение — безадресный рычаг, оно не привязано ни к какому входу.

5. Волна вглубь: производная по весу первого слоя

Самое интересное — вес скрытого слоя $w^{(1)}$, который находится двумя шагами от потерь. Его цепь длиннее: $w^{(1)}$ меняет $z^{(1)}$, которое меняет $a^{(1)}$, которое меняет $z^{(2)}$, затем $a^{(2)}$, затем $C_0$. Пять звеньев вместо трёх:

$$ \frac{\partial C_0}{\partial w^{(1)}} = \underbrace{a^{(0)}}{\partial z^{(1)}/\partial w^{(1)}} \cdot \underbrace{\sigma’\left(z^{(1)}\right)}{\partial a^{(1)}/\partial z^{(1)}} \cdot \underbrace{w^{(2)}}{\partial z^{(2)}/\partial a^{(1)}} \cdot \underbrace{\sigma’\left(z^{(2)}\right)}{\partial a^{(2)}/\partial z^{(2)}} \cdot \underbrace{2\left(a^{(2)}-y\right)}_{\partial C_0/\partial a^{(2)}} $$

Присмотритесь: правые три звена — это почти производная последнего слоя из раздела 3, а левые два — рекурсивное продолжение вглубь. Хвост волны (желание измениться, $2(a^{(2)}-y)$) один и тот же; при переходе через каждый слой он умножается на вес связи и наклон сигмоиды. Именно поэтому процедуру называют распространением назад: производные дальних слоёв получаются из производных ближних умножением, а не пересчитываются с нуля. Один проход назад — и все 13 002 (или 70 миллиардов) производных готовы.

6. Много нейронов в слое: сумма по путям

В настоящей сети в слое 16 нейронов, и каждый влияет на каждый следующий. Звучит пугающе? Нет: принцип не меняется, появляется только суммирование. Когда активация $a_k^{(1)}$ влияет на потери через несколько путей сразу (через все нейроны следующего слоя), производная по ней — сумма цепных правил по всем путям:

$$ \frac{\partial C_0}{\partial a_k^{(1)}} = \sum_{j} \frac{\partial C_0}{\partial a_j^{(2)}} \cdot \frac{\partial a_j^{(2)}}{\partial a_k^{(1)}} $$

Это то же суммирование желаний выходных нейронов, которое мы видели в прошлой статье. Индексов становится много, поэтому на практике всё пакуют в векторно-матричную запись ($\nabla_a C$, транспонированные матрицы весов и т.д.) — и именно эту упаковку делает за вас любой автоград.

7. Среднее по выборке и последний штрих

Мы выводили производные для одного примера $C_0$. Полная функция потерь — среднее по всей выборке:

$$ C = \frac{1}{n} \sum_{k} C_k $$

Производная суммы — сумма производных, так что полный градиент — просто усреднение «волн» по всем примерам (а на практике — по мини-батчу, как мы разбирали в прошлой статье). Заметьте: ни одно звено цепочки не потребовало ничего, кроме школьной производной квадрата, производной линейной функции и знания, что такое сигмоида.

8. Зачем это инженеру: затухающие градиенты и ResNet

Раз каждое звено-умножение — это $\sigma’$ или вес, взгляните на формулу раздела 5 ещё раз. Наклон сигмоиды нигде не превышает 0.25, а в насыщении он близок к нулю. Значит, у глубокой сети произведение многих таких множителей стремительно улетает в ноль: слои у входа получают крошечные градиенты и перестают учиться. Это знаменитая проблема затухающих градиентов — прямой вывод из нашей формулы, а не абстрактное заклинание.

Теперь понятно, зачем в ResNet и трансформерах остаточные связи (residual connections): они добавляют в цепь звено с производной, равной единице, — волне есть по чему пройти назад без затухания. Замена сигмоиды на ReLU — та же история: у ReLU производная равна 1 на всей положительной полуоси. Целая эпоха архитектур глубокого обучения читается как набор заплаток к формуле из раздела 5.

Autograd. Всё, что мы вывели руками, фреймворки делают автоматически: PyTorch строит граф вычислений, а .backward() запускает произведение звеньев в обратном порядке. Когда вы видите в документации «computational graph» — это цепь из раздела 2; «gradient checkpointing» в конфигах обучения LLM — трюк, чтобы хранить меньше промежуточных звеньев, пересчитывая их при необходимости.

Заключение

Мы прошли серию до конца: от «нейрон — это число» до точной формулы, по которой вычисляется вина каждого веса. Цепное правило разложило интуицию backpropagation на множители, у каждого из которых оказался физический смысл: активность входа, способность нейрона меняться, сила желания. И даже классические проблемы глубокого обучения — затухающие градиенты, ReLU, остаточные связи — читаются как следствия этой же строчки формулы.

Куда дальше? Материал серии — фундамент, на котором стоят все современные модели. Следующая ступень — архитектура Transformer и самовнимание: как та же самая механика обучения применяется к сети, которая читает текст. Об этом — в отдельной серии блога.

← все посты [поделиться] [rss]