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

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

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

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

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

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

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

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

Цепное правило

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

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

dJda=dJdv⋅dvda=3⋅1=3

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

dJdb=dJdv⋅dvdu⋅dudb=3⋅1⋅2=6

dJdc=dJdv⋅dvdu⋅dudc=3⋅1⋅3=9

Мини-квиз: а если бы первый множитель в J=3(a+bc) был не 3, а 5 (то есть dJ/dv=5), чему стал бы равен dJ/db?

ОтветПо цепному правилу dJ/db=(dJ/dv)⋅(dv/du)⋅(du/db)=5⋅1⋅c=5⋅1⋅2=10.

Численная проверка

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

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

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

И здесь смыкается круг с предыдущими статьями («Бинарной классификацией» и «Логистической регрессией»): граф предсказания — цепочка z→a→L (a=σ(z), дальше кросс-энтропия — штраф за ошибку предсказания, подробно разобранный в «Функции стоимости»). Обратный ход по этому графу — умножение производной ошибки по a на производную сигмоиды (σ) по z — сворачивается в изящное dz=a−y (полный вывод — там, повторять не будем). Механика идентична этому игрушечному примеру: те же узлы, стрелки и цепное правило, просто с другими функциями внутри узлов. Вычислительный граф — это не отдельная тема, а язык, на котором формулируется backprop любой нейросети, от одного нейрона до десятков слоёв.

Если можешь пересказать: обратный ход считает локальный наклон на каждом узле и перемножает их по цепочке от выхода к нужной переменной; dz=a−y из «Логистической регрессии» — частный случай этого же обратного хода, — значит, тема усвоена.

Что дальше

Мы прошли путь от «что такое наклон» до полного численного обратного хода по игрушечному графу J=3(a+bc): прямой ход считает значение, обратный — производные, цепное правило связывает узлы. Логистическая регрессия из прошлых статей («Бинарной классификации» и «Логистической регрессии») — тот же механизм, просто с конкретными узлами z=wTx+b, a=σ(z), L(a,y).

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