Статья Автор: Деникина Н.В., Деникин А.В.

Наклон: численно и по формуле

Вернёмся к hot and cold. Из двух чисел, «слева» и «справа», он извлекал ровно один ответ: какое из них меньше. То есть на выходе — «влево» или «вправо», одно из двух, и ничего больше.

А насколько одно меньше другого, он не использовал никак. Между тем это разные положения: бывает, что одна сторона лучше другой в разы, а бывает — что почти не отличается. Hot and cold в обоих случаях делает одинаковый шаг заданной длины.

Вот это «насколько» и выбрасывалось. Достать его просто: взять разность двух чисел.

Шаг по-прежнему 1.0, то есть пробы стоят в точках w − 1 и w + 1. Обратите внимание: расстояние между ними равно двум шагам, а не одному — от одной пробы до другой идти сначала до w, потом столько же дальше. Поэтому в последнем столбце делим на 2.

точка wслева, w − 1справа, w + 1разностьрасстояниеразность ÷ расстояние
012030−902−45
2300−302−15
37.57.5020

Знак разности — это и есть направление, которым пользовался hot and cold: минус означает, что ошибка убывает вправо. Но величина разности тоже не случайна. В нуле она −90, в двойке −30: у нуля спуск втрое круче. А ровно в минимуме разность обращается в ноль.

Выходит, эти два числа описывают не только куда идти, но и насколько круто падает график.

Деление на расстояние нужно затем, чтобы результат не зависел от выбранного шага. Проверьте на графике ниже: при шаге 1.0 разность равна −90 и делится на 2, при шаге 0.5 разность равна −45 и делится на 1 — в обоих случаях выходит одно и то же −45. Хорды при этом параллельны.


Разность, поделённая на расстояние, от величины шага не зависитшаг 1−1012расстояние 2разность −90«слева» 120 «справа» 30−90 ÷ 2 = −45шаг 0.5−1012расстояние 1разность −45«слева» 91.875 «справа» 46.875−45 ÷ 1 = −45зелёные хорды параллельны: у обеих наклон −45

Вот эта величина — куда и насколько круто идёт график в данной точке — и есть тот самый наклон, про который шла речь в первом разделе. Получается, hot and cold его уже считал, просто пользовался одним знаком, а само число выбрасывал.

Дальше нам нужно научиться считать наклон как следует. У него есть точное имя.

Производная функции в точке — наклон касательной к её графику в этой точке.

Численная производная — отношение (f(w + h) − f(w)) / h при малом h. Геометрически это наклон секущей через две близкие точки графика. Есть и второй вариант, центральная разность: (f(w + h) − f(w − h)) / (2h).

Во второй формуле стоит вглядеться: это ровно то, что мы посчитали в начале раздела. f(w + h) — это «справа», f(w − h) — «слева», а 2h — расстояние между ними. Центральная разность и есть разность «справа» и «слева», поделённая на расстояние между ними, — только записанная формулой и с маленьким h вместо целого шага.


Две разности против настоящей касательной, шаг h = 1односторонняя: «здесь» и «справа»−1012«здесь»«справа»−37.5 — мимонаклон получается заметно положецентральная: «слева» и «справа»−1012«слева»«справа»−45 — точнонаклон совпал с касательнойпунктиром на обеих панелях — касательная в точке «здесь», её наклон −45

Обе разности в точке w = 0

hодносторонняяцентральная
1−37.5−45
0.5−41.25−45
0.25−43.125−45
0.125−44.0625−45
0.0625−44.53125−45

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

Как выбирать h. Если h слишком велик, секущая заметно отличается от касательной. Если h слишком мал, в разности двух почти равных чисел начинает преобладать ошибка округления. Поэтому h берут достаточно малым, но не стремящимся к нулю буквально. В наших примерах удобно начать с h = 10⁻⁵ и проверить, устойчив ли результат при небольшом изменении h.


Программа считает численную производную, чтобы сверить её с формулой, выведенной вручную. Числа хранятся в обычных вещественных переменных двойной точности. Какое h взять?

Наклон по формуле

Теперь выведем формулу производной напрямую. Возьмём один заказ и посмотрим, что с ним происходит, когда вес меняется с 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

Программа при этом тоже не упадёт. Но направление движения изменится на противоположное: вместо спуска по функции потерь модель начнёт двигаться вверх по склону.

И в случае потерянной двойки, и в случае неправильного знака численная производная сразу разойдётся с аналитической. Поэтому проверка градиента — простой и полезный способ убедиться, что математические формулы реализованы в коде правильно.


Реши практическую задачу

Печать