# Производные с вычислительным графом

Зачем это нужно: в [«Вычислительном графе»](/notes/computational-graph/) мы разбили выражение $J(a,b,c) = 3(a+bc)$ на узлы и посчитали его вперёд, прямым ходом. Но реальный вопрос при обучении нейросети другой: не «чему равно $J$», а «как $J$ изменится, если чуть подвинуть вес». Эта статья проходит тот же граф в обратную сторону и получает ответ — механику, на которой держится backprop любой нейросети. В [«Бинарной классификации»](/notes/binary-classification/) и [«Логистической регрессии»](/notes/logistic-regression/) уже появлялись $dz = a - y$ и родственные формулы — здесь показано, откуда берётся сила инструмента, частным случаем которого они являются. Все числа перепроверены в Python.

## Оглавление

- [Напоминание: прямой ход по графу](#напоминание)
- [Обратный ход и локальные наклоны](#обратный-ход)
- [Цепное правило](#цепное-правило)
- [Численная проверка](#численная-проверка)
- [Связь с логистической регрессией](#связь)
- [Что дальше](#что-дальше)

## Напоминание: прямой ход по графу {#напоминание}

Коротко: граф для $J(a,b,c) = 3(a+bc)$ раскладывается на три узла — $u = b \cdot c$, $v = a + u$, $J = 3v$. При $a=4$, $b=3$, $c=2$ прямой ход даёт $u=6$, $v=10$, $J=30$ (полный разбор — в [«Вычислительном графе»](/notes/computational-graph/)). Теперь вопрос: как узнать, насколько $J$ изменится, если чуть подвинуть $a$, $b$ или $c$?

## Обратный ход и локальные наклоны {#обратный-ход}

Метод — пройти граф **в обратную сторону**, справа налево, и на каждом узле посчитать локальный наклон, а потом перемножить их по цепочке. Это и называют **обратным ходом** (backward pass), и это тот же механизм, что стоит за backprop в нейросети.

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

Локальные наклоны берутся прямо из формул узлов — каждый по отдельности простой (наклон умножения на константу — сама константа, наклон сложения — единица, наклон умножения двух переменных по одной из них — вторая переменная; подробности этих трёх правил — в [«Производных»](/notes/derivatives-intuition/) и [«Больше примеров»](/notes/derivatives-more-examples/)):

- $J = 3v$, значит $dJ/dv = 3$.
- $v = a + u$, значит $dv/da = 1$ и $dv/du = 1$.
- $u = b \cdot c$, значит $du/db = c = 2$ и $du/dc = b = 3$.

## Цепное правило {#цепное-правило}

Дальше — **цепное правило**: если $a$ влияет на $J$ через промежуточную $v$, то полный наклон — произведение наклонов вдоль пути. Записывают это как $dJ/da = (dJ/dv) \cdot (dv/da)$.

Представь игру в испорченный телефон, только вместо слов передаётся коэффициент усиления громкости: первый шепчет второму «громче в 3 раза», второй передаёт третьему «громче в 1 раз» (то есть без изменений). Прежде чем читать дальше — угадай: во сколько раз итоговый шёпот громче самого первого? Если сложить коэффициенты, получится $3+1=4$; но угадать нужно было умножение: $3 \cdot 1 = 3$. Цепное правило работает именно так — коэффициенты вдоль пути от переменной до результата перемножаются, а не складываются.

$$\frac{dJ}{da} = \frac{dJ}{dv} \cdot \frac{dv}{da} = 3 \cdot 1 = 3$$

Путь от $b$ до $J$ длиннее — идёт через два узла, $u$ и $v$, — поэтому перемножаются три наклона подряд:

$$\frac{dJ}{db} = \frac{dJ}{dv} \cdot \frac{dv}{du} \cdot \frac{du}{db} = 3 \cdot 1 \cdot 2 = 6$$

$$\frac{dJ}{dc} = \frac{dJ}{dv} \cdot \frac{dv}{du} \cdot \frac{du}{dc} = 3 \cdot 1 \cdot 3 = 9$$

**Мини-квиз:** а если бы первый множитель в $J = 3(a+bc)$ был не 3, а 5 (то есть $dJ/dv = 5$), чему стал бы равен $dJ/db$? <details><summary>Ответ</summary>По цепному правилу $dJ/db = (dJ/dv) \cdot (dv/du) \cdot (du/db) = 5 \cdot 1 \cdot c = 5 \cdot 1 \cdot 2 = 10$.</details>

## Численная проверка {#численная-проверка}

Проверим все три численно — тем же приёмом, что и для отдельных функций: подвинуть вход на $\varepsilon = 10^{-6}$ и посмотреть, на сколько сдвинулся $J$: $dJ/da \approx 3.0000000$, $dJ/db \approx 5.9999999$, $dJ/dc \approx 8.9999999$. Совпадает с ручным счётом по графу.

Обрати внимание на практическую деталь из области программирования: код backprop называет каждую такую производную «*производная итогового выхода по этой переменной*» коротким именем `dvar` — не «частная производная $J$ по $a$», а просто `da`, `db`, `dc`. Именно так были названы `dw`, `db`, `dz` в предыдущих статьях ([«Бинарной классификации»](/notes/binary-classification/) и [«Логистической регрессии»](/notes/logistic-regression/)): не потому, что там особая математика, а потому что это тот же самый обратный ход по графу, применённый к предсказанию логистической регрессии.

## Связь с логистической регрессией {#связь}

И здесь смыкается круг с предыдущими статьями ([«Бинарной классификацией»](/notes/binary-classification/) и [«Логистической регрессией»](/notes/logistic-regression/)): граф предсказания — цепочка $z \to a \to L$ ($a = \sigma(z)$, дальше кросс-энтропия — штраф за ошибку предсказания, подробно разобранный в [«Функции стоимости»](/notes/cost-function/#кросс-энтропия)). Обратный ход по этому графу — умножение производной ошибки по $a$ на производную сигмоиды (σ) по $z$ — сворачивается в изящное $dz = a - y$ (полный вывод — там, повторять не будем). Механика идентична этому игрушечному примеру: те же узлы, стрелки и цепное правило, просто с другими функциями внутри узлов. Вычислительный граф — это не отдельная тема, а язык, на котором формулируется backprop любой нейросети, от одного нейрона до десятков слоёв.

Если можешь пересказать: обратный ход считает локальный наклон на каждом узле и перемножает их по цепочке от выхода к нужной переменной; $dz = a - y$ из [«Логистической регрессии»](/notes/logistic-regression/) — частный случай этого же обратного хода, — значит, тема усвоена.

## Что дальше {#что-дальше}

Мы прошли путь от «что такое наклон» до полного численного обратного хода по игрушечному графу $J = 3(a+bc)$: прямой ход считает значение, обратный — производные, цепное правило связывает узлы. Логистическая регрессия из прошлых статей ([«Бинарной классификации»](/notes/binary-classification/) и [«Логистической регрессии»](/notes/logistic-regression/)) — тот же механизм, просто с конкретными узлами $z = w^Tx+b$, $a=\sigma(z)$, $L(a,y)$.

Но пока весь разбор шёл про **один** обучающий пример. На практике обучают сразу на $m$ примерах, и функция стоимости $J$ — это среднее по ним всем. Как аккуратно усреднить градиенты по $m$ примерам, почему это работает именно как среднее, и что выглядит как самый наивный, но абсолютно рабочий способ это запрограммировать — тема [следующего разбора](/notes/gradient-descent-on-m-examples/).
