Наклон по формуле
Теперь выведем формулу производной напрямую. Возьмём один заказ и посмотрим, что с ним происходит, когда вес меняется с w на w + h.
Обозначим промах одной буквой e:
e(w) = прогноз − факт
= w · км + остальное − факт
Под «остальным» спрятаны слагаемые со светофорами, курьерами и сборкой. Нам важно про них одно: от w они не зависят и при изменении этого веса остаются теми же.
Подставим w + h вместо w:
e(w + h) = (w + h) · км + остальное − факт
= e(w) + h · км
Первый вывод: когда вес меняется на h, промах меняется на h · км.
Именно это означала фраза «промах отзывается на изменение веса в «км» раз».
Теперь посмотрим на квадрат промаха. Чтобы запись была короче, обозначим текущий промах просто через e. После изменения веса он станет e + h · км:
(e + h · км)²
=
e² + 2 · e · км · h + км² · h²
Слева — квадрат промаха после изменения веса, справа — то же выражение после раскрытия скобок.
Чтобы узнать, насколько изменился квадрат промаха, вычтем его старое значение e²:
прирост = 2 · e · км · h + км² · h²
Теперь поделим прирост на величину изменения веса h. Получим наклон секущей — приближённый наклон графика:
(2 · e · км · h + км² · h²) ÷ h
=
2 · e · км + км² · h
Первое слагаемое от h вообще не зависит. Второе становится всё меньше, когда мы уменьшаем h.
При h → 0 оно исчезает, и остаётся:
производная квадрата промаха по весу
=
2 · e · км
Поскольку e = прогноз − факт, можем записать:
производная квадрата промаха по весу
=
2 · (прогноз − факт) · км
Или чуть более общо, для нашей линейной модели:
производная квадрата промаха по весу
=
2 · (прогноз − факт) · признак
Здесь хорошо видно, откуда взялась каждая часть формулы:
2 появилась при раскрытии квадрата;
промах показывает, насколько модель сейчас ошибается;
признак показывает, насколько сильно прогноз реагирует на изменение именно этого веса.
Для веса при километрах признак — это число километров. Для веса при светофорах на этом месте будет число светофоров. Для веса при числе курьеров — число курьеров.
Это уже почти правило цепочки. Сначала изменение веса влияет на промах, а затем изменение промаха влияет на его квадрат. Пока зависимость простая и линейная, поэтому всё сводится к произведению 2 · промах · признак. Позже, когда операций станет больше, тот же принцип приведёт нас к обратному распространению ошибки.
Отброшенное слагаемое — не абстракция. Выражение км² · h и есть тот самый систематический снос односторонней разности, который мы видели в таблице выше.
Усреднённый по четырём заказам, он даёт 7.5 · h — то самое среднее квадратов километров. Сверьте: при h = 1 односторонняя разность дала −37.5 вместо −45, и разница оказалась ровно 7.5.
Для любознательных. Строго производная определяется через предел:
f′(x) = lim (f(x + h) − f(x)) / h
h → 0
То есть мы берём наклоны секущих для всё меньших h и смотрим, к какому числу они приближаются. В нашем выводе именно поэтому слагаемое км² · h исчезло.
Теперь перейдём от одного заказа ко всей таблице. MSE — среднее квадратов промахов, значит её производная равна среднему производных этих квадратов:
производная MSE
=
среднее от 2 · (прогноз − факт) · км
В нашем примере промах равен:
промах = (w − 3) · км
Поэтому:
производная MSE
=
среднее от 2 · (w − 3) · км · км
=
2 · (w − 3) · среднее(км²)
=
2 · (w − 3) · 7.5
=
15 · (w − 3)
Та самая пятнадцатка из наших таблиц — это 2 · 7.5:
| w |
0 |
1 |
3 |
5 |
| производная |
−45 |
−30 |
0 |
30 |
От одной производной к градиенту
До сих пор мы меняли только один вес. Но у настоящей модели параметров несколько, и по каждому из них есть своя производная.
Частная производная — производная по одной переменной при фиксированных остальных. Она отвечает на вопрос: насколько изменится функция потерь, если чуть изменить только один параметр модели, а все остальные оставить как есть.
Градиент — вектор всех частных производных. Он направлен в сторону наиболее быстрого локального роста функции.
Записывают градиент так:
∇L = ( ∂L/∂w₁, ∂L/∂w₂, ∂L/∂w₃, ∂L/∂b )
Запись плотная, поэтому разберём её по знакам.
L — функция потерь, у нас это MSE. Буква происходит от английского loss. Писать L короче, чем каждый раз писать «функция потерь».
∂ — специальный знак частной производной. Его используют вместо обычной d, когда функция зависит сразу от нескольких переменных.
∂L/∂w₁ — частная производная L по весу w₁. Проще говоря: насколько изменится ошибка, если чуть тронуть только w₁.
w₁, w₂, w₃ — веса модели: первый стоит при километрах, второй при светофорах, третий при числе курьеров.
b — свободный член, в нашей модели это время сборки.
∇ — знак градиента, читается «набла». Он обозначает сразу весь вектор частных производных.
Главное в этой записи: ∇L — не одно число, а столько чисел, сколько у модели параметров.
У нашей полной модели четыре параметра, поэтому её градиент состоит из четырёх компонент.
Пока мы рассматриваем только одномерный срез функции потерь: разрешаем изменяться весу при километрах, а остальные параметры считаем фиксированными. Поэтому сейчас нам нужна только одна компонента градиента.
Например, в точке w = 0 она равна:
∂L/∂w = −45
Все четыре компоненты градиента одновременно посчитаем в пятом разделе.
Градиент показывает направление наиболее быстрого локального роста функции потерь. Поэтому противоположный вектор −∇L показывает направление её локального уменьшения.
Именно поэтому при градиентном спуске мы движемся против градиента.
Сверка двух способов
Формулу производной легко вывести с ошибкой в множителе или знаке. Такая ошибка часто не приводит к падению программы: код работает, числа считаются, но обучение идёт неправильно.
Поэтому аналитический градиент полезно сверять с численным.
Разберём такую проверку целиком в точке w = 2.
По формуле. Промахи при w = 2 равны:
(2 − 3) · км
То есть для четырёх заказов получаем −1, −2, −3 и −4.
Для каждого заказа считаем:
2 · промах · км
| заказ |
км |
промах |
2 · промах · км |
| 1 |
1 |
−1 |
−2 |
| 2 |
2 |
−2 |
−8 |
| 3 |
3 |
−3 |
−18 |
| 4 |
4 |
−4 |
−32 |
Сумма равна −60. Делим на четыре заказа и получаем:
−60 / 4 = −15
Аналитическая производная равна −15.
Теперь численно. Возьмём центральную разность с h = 0.25.
Нужны два значения MSE:
MSE(2.25) = 7.5 · 0.5625 = 4.21875
MSE(1.75) = 7.5 · 1.5625 = 11.71875
Подставляем их в центральную разность:
(MSE(2.25) − MSE(1.75)) / (2 · 0.25)
=
(4.21875 − 11.71875) / 0.5
=
−7.5 / 0.5
=
−15
Численная производная тоже равна −15.
Два способа сошлись.
Причём здесь они совпали совершенно точно. Это особенность нашей квадратичной функции: у центральной разности симметричные слагаемые с h² сокращаются.
У настоящей нейронной сети функции будут сложнее, а вычисления будут выполняться числами с плавающей точкой, поэтому аналитический и численный градиенты обычно совпадают не идеально, а лишь с небольшой погрешностью.
Если они заметно различаются, значит где-то есть ошибка: в формуле, в знаке или в коде.
В учебных задачах этого раздела все числа небольшие, поэтому будем использовать простую проверку:
|аналитический градиент − численный градиент| < 1e-5
Если условие выполняется, будем считать проверку пройденной.
В реальных программах часто используют относительную погрешность, потому что сами значения градиентов могут быть очень большими или очень маленькими. К этому мы ещё вернёмся позже.
Что такая сверка умеет ловить.
Допустим, при выводе формулы потерялась двойка. Вместо:
2 · промах · км
мы случайно написали:
промах · км
Тогда та же таблица даст сумму −30, а после деления на четыре:
−7.5
То есть аналитическая производная окажется ровно вдвое меньше правильной.
При обычном градиентном спуске это приведёт к тому, что шаг изменения веса тоже окажется вдвое меньше ожидаемого. Это похоже на случай, когда мы случайно уменьшили скорость обучения в два раза.
А теперь рассмотрим другую ошибку. Если перепутать порядок вычитания и определить промах как:
факт − прогноз
вместо:
прогноз − факт
получим производную с противоположным знаком:
верная производная: −15
ошибочная производная: +15
Программа при этом тоже не упадёт. Но направление движения изменится на противоположное: вместо спуска по функции потерь модель начнёт двигаться вверх по склону.
И в случае потерянной двойки, и в случае неправильного знака численная производная сразу разойдётся с аналитической. Поэтому проверка градиента — простой и полезный способ убедиться, что математические формулы реализованы в коде правильно.