Перейти к содержанию

Модуль 4. Производные и оптимизация

После этого модуля вы сможете

  • Читать градиент как направление наискорейшего роста и объяснить, почему спуск идёт против него.
  • Применить цепное правило руками — ту самую операцию, из которой в модуле 8 соберётся backprop.
  • Проверить аналитический градиент численным. Это главный приём отладки обучения.
  • Показать на графике, что делает слишком большой шаг и что делает слишком маленький.
  • Объяснить зигзаг спуска через число обусловленности из модуля 3.

Время: около двух недель. Пререквизиты: модуль 3. Ноутбук: notebooks/04-derivatives-and-optimisation.ipynb

Зачем это

Регрессия в модуле 3 решилась точно: одно уравнение, ответ. Это редкая удача. У почти всех остальных задач курса точного решения нет — ни у нейросети, ни у политики в RL, ни у рекомендательной модели.

Остаётся один приём, и он же последний: начать с плохого ответа и улучшать его маленькими шагами, пока улучшать нечего. Всё обучение в этом курсе — это он. Разница между методами сводится к тому, как считать направление шага и какой длины его делать.

Производная

Производная — скорость изменения функции. Насколько вырастет \(f\), если чуть увеличить \(x\).

\[f'(x) = \lim_{h \to 0} \frac{f(x + h) - f(x)}{h}\]

Определение через предел важно для одной вещи: производную всегда можно посчитать численно, подставив маленькое \(h\) вместо предела. Это медленно и неточно, но не требует вывода формулы, и поэтому это идеальный способ проверить формулу, которую вы вывели.

Приём называется gradient check, и в модуле 8 он спасёт вам не один день. Аналитическая формула и численная оценка обязаны совпасть в нескольких знаках. Не совпали — ошибка в выводе, а не в данных.

Что понадобится из таблицы производных:

\(f(x)\) \(f'(x)\)
\(x^n\) \(nx^{n-1}\)
\(e^x\) \(e^x\)
\(\ln x\) \(1/x\)
\(\sigma(x) = \frac{1}{1+e^{-x}}\) \(\sigma(x)(1 - \sigma(x))\)

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

Градиент

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

\[\nabla f = \left(\frac{\partial f}{\partial x_1}, \dots, \frac{\partial f}{\partial x_n}\right)\]

Одно свойство делает его полезным: градиент указывает направление наискорейшего роста, а его длина равна скорости роста в этом направлении.

Отсюда весь метод. Хотите минимум — идите против градиента:

\[x_{t+1} = x_t - \eta \nabla f(x_t)\]

\(\eta\) — длина шага, learning rate. Всё обучение в курсе — эта строка, повторённая миллионы раз.

Почему именно наискорейший рост

Изменение \(f\) при маленьком шаге \(\delta\) равно \(\nabla f \cdot \delta\) — скалярное произведение из модуля 3. При фиксированной длине шага оно максимально, когда \(\delta\) сонаправлен с \(\nabla f\). Максимальный рост — вдоль градиента, максимальное падение — против.

Здесь скалярное произведение из прошлого модуля работает не как метафора, а как доказательство.

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

Функция от функции. Производная — произведение производных:

\[\frac{d}{dx}f(g(x)) = f'(g(x)) \cdot g'(x)\]

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

Разберём на функции потерь

Логистическая регрессия: \(z = w \cdot x\), предсказание \(p = \sigma(z)\), потеря \(L = -\big(y\ln p + (1-y)\ln(1-p)\big)\).

Нужна \(\partial L/\partial w\). Идём по цепочке справа налево:

\[\frac{\partial L}{\partial p} = \frac{p - y}{p(1-p)}, \qquad \frac{\partial p}{\partial z} = p(1-p), \qquad \frac{\partial z}{\partial w} = x\]

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

\[\frac{\partial L}{\partial w} = \frac{p-y}{p(1-p)} \cdot p(1-p) \cdot x = (p - y)\,x\]

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

Градиентный спуск и длина шага

Направление известно. Остаётся длина шага, и она решает всё.

Слишком маленький \(\eta\). Спуск сходится, но медленно. Тысячи шагов там, где хватило бы десятков. Обидно, но не страшно.

Слишком большой \(\eta\). Шаг перелетает минимум и попадает выше, чем был. Следующий перелетает сильнее. Потеря растёт, потом становится nan. Это не «модель не учится» — это арифметика расходящейся последовательности.

Правильный \(\eta\) находится подбором, и подбирают его по графику потерь: потеря должна падать монотонно и быстро. График — не украшение отчёта, а рабочий инструмент.

Первое, что делать, когда обучение не идёт

Уменьшите шаг в десять раз и запустите снова. В большинстве случаев это диагноз: пошло — был слишком велик, не пошло — ищите ошибку в градиенте, и ищите её через gradient check.

Овраг

Теперь почему одного шага не хватает.

Возьмите функцию, у которой минимум лежит на дне длинного узкого оврага. Градиент на склоне указывает поперёк оврага, а не вдоль него. Спуск бьётся от стенки к стенке и продвигается к минимуму медленно.

Крутизна стенок относительно пологости дна — это число обусловленности из модуля 3. Чем оно больше, тем уже овраг и тем сильнее зигзаг.

Отсюда два лечения, и оба встретятся дальше в курсе.

Нормализация признаков. Приводит облако к круглому виду, уменьшает \(\kappa\), распрямляет траекторию. Вот зачем данные масштабируют. Не «так принято», а потому, что иначе спуск идёт зигзагом.

Момент. Шаг накапливает инерцию:

\[v_{t+1} = \beta v_t + \nabla f(x_t), \qquad x_{t+1} = x_t - \eta\, v_{t+1}\]

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

Стохастический спуск

Последний кусок. Считать градиент по всему набору данных дорого: миллион объектов — миллион слагаемых на каждый шаг.

SGD считает градиент по случайному подмножеству — батчу. Оценка получается шумной, зато шагов в секунду делается на порядки больше.

Шум здесь не только цена, но и польза. Точный градиент застревает в первом же локальном минимуме. Шумный из мелкого минимума вытряхивается и уходит дальше.

Размер батча — обмен между качеством оценки градиента и числом шагов. Из модуля 2 известен курс: шум оценки падает как \(\sqrt{\text{размер батча}}\). Батч вчетверо больше даёт вдвое менее шумный градиент и вчетверо более дорогой шаг.

Практика

Часть 1. Ноутбук

Откройте notebooks/04-derivatives-and-optimisation.ipynb.

Что внутри:

  1. Численная производная против аналитической. Подбираем \(h\): слишком большое — ошибка аппроксимации, слишком маленькое — ошибка округления.
  2. Gradient check на логистической потере. Тот самый приём для модуля 8.
  3. Спуск на квадратичной функции, траектория на линиях уровня.
  4. Перебор шага: сходимость, медленная сходимость, расходимость в nan.
  5. Овраг: та же задача с числом обусловленности 50. Зигзаг видно глазами.
  6. Момент на той же задаче. Считаем, во сколько раз меньше шагов.
  7. SGD против полного спуска на регрессии: шаги в секунду против гладкости.

Часть 2. Своя регрессия, но спуском

Возьмите регрессию из модуля 3, где ответ уже известен точно.

  1. Решите её градиентным спуском с нуля, без lstsq.
  2. Сравните веса с точным решением. Насколько близко и за сколько шагов.
  3. Отмасштабируйте признаки и повторите. Сколько шагов теперь.
  4. Посчитайте число обусловленности до и после масштабирования. Соотнесите с числом шагов.

Смысл упражнения в том, что правильный ответ известен заранее. Дальше в курсе так не будет никогда, и опыт «спуск сошёлся туда, куда надо» стоит получить сейчас.

Задание

  1. Выведите производную сигмоиды из определения. Убедитесь, что получается \(\sigma(1-\sigma)\).
  2. Выведите \(\partial L/\partial w\) для линейной регрессии с квадратичной потерей. Сравните с логистической из текста.
  3. Реализуйте gradient check как функцию: принимает функцию, аналитический градиент и точку, возвращает относительное расхождение. Она пригодится в модуле 8.
  4. Найдите наибольший шаг, при котором спуск на \(f(x) = x^2\) ещё сходится. Ответ должен быть точным числом, а не подобранным.
  5. Постройте задачу с числом обусловленности около 1000 и покажите, во сколько раз момент сокращает число шагов.

Проверка себя

  1. Почему спуск идёт против градиента, а не вдоль?
  2. Что происходит при слишком большом шаге и как это выглядит на графике потерь?
  3. Сформулируйте цепное правило и объясните, почему без него не существует обучения сетей.
  4. Почему в градиенте логистической регрессии всё сокращается до \((p-y)x\)?
  5. Что такое gradient check и почему это первое, что нужно сделать при подозрении на ошибку?
  6. Как число обусловленности связано с зигзагом спуска?
  7. Зачем нормализуют признаки — геометрический ответ, а не «так принято».
  8. Чем шум SGD полезен, а не только вреден?

Дальше

Часть I закончена. Теперь есть язык: утверждение и база сравнения, параметр и оценка, вектор и матрица, градиент и шаг. Дальше он применяется.

В части II появляются первые настоящие модели — линейные, деревья, бустинг. Они же будут базовыми линиями для всего, что придёт после. Половину заявленных прорывов обыгрывает правильно настроенный бустинг, и увидеть это своими руками надо до того, как начнутся нейросети.

Обучение — это одна строка: шаг против градиента. Всё остальное в курсе — как считать направление и какой длины делать шаг.