Вниз по склону: Gradient Descent и два шага, которые все пропускают
Вычисляем точный потолок learning rate и видим, как перебор 3 600 направлений заново находит градиент.
На этой странице
Предыдущая глава закончилась долиной.
Не метафорической: настоящей кривой, где функция потерь отложена против одного параметра, опускается вниз и снова поднимается. И функция потерь под ней была выбрана не потому, что так аккуратнее, — она была выведена из утверждения о шуме в измерениях, а квадратичная ошибка появилась на выходе как следствие, а не как условность.
Итак, у нас есть ландшафт с дном и причина считать, что дно — правильное место. Чего у нас нет, так это способа туда добраться.
Эта глава строит такой способ, и это алгоритм, который обучает каждую модель во всей оставшейся части курса — каждую без исключения, вплоть до моделей с сотнями миллиардов параметров. Он помещается примерно в двадцать строк. Две трудные части находятся не в этих двадцати строках, и именно их почти каждое объяснение пропускает:
- Почему знак минус. Обновление вычитает градиент. Каждый tutorial это пишет; очень немногие объясняют, почему градиент — это направление вверх, а это единственный факт, который делает знак минус чем-то иным, чем акт веры.
- Насколько большой шаг. «Слишком большой — расходится, слишком маленький — медленно» — правда и бесполезность. Есть точное число, его можно вычислить из функции потерь, и эта глава вычисляет его дважды — один раз для игрушечной параболы и один раз для реальных данных.
Постановка задачи и почему нельзя просто искать
Ссылка на раздел: Постановка задачи и почему нельзя просто искатьПовторим, чтобы эта глава читалась самостоятельно: восемь деталей с конвейера из главы 1, но с другим вопросом. Не принять или отклонить — это вернётся позже, — а предсказать вес детали по её ширине.
import numpy as np
WIDTH = np.array([18.0, 19.5, 20.2, 21.0, 24.0, 25.5, 23.0, 26.0])
WEIGHT = np.array([47.0, 52.0, 49.0, 55.0, 61.0, 66.0, 70.0, 58.0])
x = WIDTH - WIDTH.mean() # 22.15 mm
y = WEIGHT - WEIGHT.mean() # 57.25 gИзмерения центрированы, ровно как в главе 1 и по причине, которая с процентами вернётся до конца этой главы. Модель — прямая, , а функция потерь — среднеквадратичная ошибка, которую вывела предыдущая глава:
Два параметра. Почему бы просто не попробовать много значений? Давайте правда попробуем — сетку от до и от до с шагом :
grid 501 x 1001 = 501,501 evaluations in 3.67 s
best found: a = 2.1000, b = -0.0000, L = 24.592450Полмиллиона вычислений, чтобы зафиксировать два числа с точностью до двух знаков после запятой, — и эта секунда является временем на одной конкретной машине, так что повторный запуск окажется где-то между тремя и шестью; воспроизводятся число вычислений и минимум. Gradient descent к концу этой главы получает четыре знака после запятой за восемь шагов и полный ответ float64 за тридцать шесть.
Но скорость — не главный аргумент, и именно этот пункт решает судьбу всего курса. Grid search стоит вычислений для параметров при значениях каждого. Если взять тысячу значений на ось:
| model | parameters | grid evaluations |
|---|---|---|
| эта прямая | 2 | |
| XOR-сеть из главы 5 | 9 | |
| небольшая многослойная сеть | 20,000 |
Третья строка — не большое число, а бессмысленное: в наблюдаемой Вселенной примерно атомов. Поиск не становится медленнее по мере роста моделей; он перестаёт существовать. Всё, что дальше, существует из-за этой таблицы.
Производная — это измерение, которое можно сделать
Ссылка на раздел: Производная — это измерение, которое можно сделатьНа мгновение зафиксируйте , чтобы остался один параметр и одна кривая — та самая картинка, которой закончилась прошлая глава. Возьмите на ней точку, , и спросите: если я сдвину на небольшую величину , насколько изменится функция потерь на единицу сдвига?
Это отношение — подъём к пробегу, наклон прямой через две точки на кривой. Когда уменьшается, две точки сходятся, и прямая становится касательной. Её наклон — производная : скорость, с которой функция потерь меняется на единицу изменения . Не приближение к чему-то и не бесконечно малая величина. Предел обычных отношений.
Стоит это запустить, потому что числа говорят то, чего не говорит определение:
def loss1(a):
return np.mean((a * x - y) ** 2)
for h in [1.0, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]:
q = (loss1(1.0 + h) - loss1(1.0)) / h
print(f"h = {h:<8.0e} slope estimate = {q:.10f} error = {abs(q + 16.385):.3e}")h = 1e+00 slope estimate = -8.9400000000 error = 7.445e+00
h = 1e-02 slope estimate = -16.3105500000 error = 7.445e-02
h = 1e-04 slope estimate = -16.3842555001 error = 7.445e-04
h = 1e-06 slope estimate = -16.3849925556 error = 7.444e-06
h = 1e-08 slope estimate = -16.3850003787 error = 3.787e-07
h = 1e-10 slope estimate = -16.3850444324 error = 4.443e-05
h = 1e-12 slope estimate = -16.3851154866 error = 1.155e-04
h = 1e-14 slope estimate = -17.0530256582 error = 6.680e-01Здесь происходят две вещи, и обе несущие.
Ошибка не смутно пропорциональна — она равна ровно . Разделите на сто, ошибка разделится на сто, каждый раз до четырёх значащих цифр. Эта константа не украшение: это половина второй производной функции потерь, и это первое появление идеи, к которой мы придём через два раздела, — что кривая рядом с точкой выглядит как прямая плюс поправка, пропорциональная .
А затем закономерность ломается. Ниже оценка становится хуже, а при она неверна уже во второй цифре. Ничего математического не произошло; сработала коробка с floating point из прошлой главы. и совпадают в первых десяти цифрах, их вычитание уничтожает эти цифры, а деление обломков на крошечное число усиливает то, что осталось. Есть лучший — здесь около , примерно квадратный корень из машинного epsilon, — и брать меньше не значит быть осторожнее, а значит быть менее осторожным. Запомните это: функция в конце главы от этого зависит.
Точный наклон, из calculus, а не из измерения, равен . Так что можно перестать измерять и начать выводить.
Композиция и chain rule
Ссылка на раздел: Композиция и chain ruleВот идея, на которой построена вся оставшаяся часть курса, сформулированная один раз и прямо.
Скомпоновать две функции — значит подать одну на вход другой: . И всё.
Глубокая сеть не похожа на композицию. Она и есть композиция. Слой — это функция; складывать слои стопкой — значит компоновать их; «глубина» — это число функций в цепочке. Когда глава 5 строит сеть, она строит и ничего больше. А значит, самое важное для нас правило calculus — то, которое дифференцирует композицию:
Скорости перемножаются. Если меняется в три раза быстрее, чем , а меняется в два раза быстрее, чем , то меняется в шесть раз быстрее, чем . В этом всё содержание, и поэтому сигнал, проходя назад через десять слоёв, умножается на десять чисел — вот почему глава 6 выделяет раздел на то, что происходит, когда все эти числа чуть меньше единицы.
Применим это к нашей функции потерь. Запишем residual , так что . Каждый зависит от через внутреннюю функцию , производная которой равна . Chain rule, слагаемое за слагаемым:
Эти фигурные символы обозначают частную производную: дифференцируйте по одной переменной, а все остальные считайте константами. Ничего нового не происходит — это тот же предел, что и раньше, только взятый вдоль одной оси. Соберите частные производные в вектор, и у вас получится градиент:
В точке этот вектор равен . Два числа. Вопрос в том, что они означают, и это первый шаг, который все пропускают.
Почему градиент указывает в гору
Ссылка на раздел: Почему градиент указывает в горуГрадиент — это вектор наклонов вдоль осей. Это всё, что мы доказали. Не очевидно — и не должно быть очевидно, — что если собрать их в вектор, получится что-то, указывающее в каком-то конкретном направлении.
Поэтому определим то, что нам на самом деле нужно. Выберите единичный вектор , направление. Производная по направлению — это скорость, с которой меняется функция потерь, когда вы идёте в эту сторону:
Chain rule превращает это во что-то вычислимое. Движение вдоль меняет со скоростью и со скоростью , а вклады складываются:
Скорость изменения в любом направлении — это скалярное произведение градиента с этим направлением. А теперь развязка, одна строка геометрии. Запишем скалярное произведение через угол между векторами:
поскольку имеет длину 1. Единственное, чем вы управляете, — это , максимальный при и минимальный при половине оборота, градусах. Поэтому:
- Самый крутой подъём идёт вдоль самого , и наклон там ровно .
- Самый крутой спуск идёт вдоль , и наклон там равен .
- Перпендикулярно градиенту функция потерь вообще не меняется. Поэтому линии на карте уровней пересекают градиент под прямым углом.
Вот откуда знак минус. Не условность, не выбранная кем-то смена знака: направление самого быстрого уменьшения — отрицательный градиент, потому что минимизируется при половине оборота, и ни по какой другой причине.
Поскольку это утверждение обо всех направлениях, проверим его на всех направлениях. Возьмём 3 600 из них, по одному на каждую десятую градуса, и измерим каждое небольшим сдвигом:
theta = np.array([1.0, 4.0])
g = grad(theta)
print("gradient ", g)
print("its length ", np.linalg.norm(g))
print("its angle ", np.degrees(np.arctan2(g[1], g[0])) % 360, "degrees")
best = max(
((loss(theta + 1e-6 * u) - loss(theta - 1e-6 * u)) / 2e-6, np.degrees(ang))
for ang, u in (
(a, np.array([np.cos(a), np.sin(a)])) for a in np.arange(3600) * 2 * np.pi / 3600
)
)
print("steepest slope", best[0], "at", best[1], "degrees")gradient [-16.385 8. ]
its length 18.23371122399386
its angle 153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degreesПоиск, который ничего не знает о градиентах, среди 3 600 направлений находит самый крутой подъём на 154.0 градусах — в собственном направлении градиента, с точностью до 0,1 градуса, заданной поиском. И наклон, найденный там, 18.2337, совпадает с длиной градиента до шести значащих цифр. Теорема — не история о том, что означают градиенты; это измеримый факт, и вот его измерение.
Почему маленький шаг вниз действительно помогает
Ссылка на раздел: Почему маленький шаг вниз действительно помогаетТеперь второй пропущенный шаг. Мы знаем, где низ. Из этого не следует, что если пойти туда, функция потерь уменьшится, потому что «низ» — утверждение о бесконечно малом сдвиге, а шаг не бесконечно мал.
Мост — линеаризация. Рядом с точкой гладкая функция — это её касательная плюс поправка:
Это разложение Тейлора первого порядка. Отброшенный — кривизна, тот самый член, который делал оценку в таблице наклонов неверной ровно на . Подставим шаг, который собираемся сделать, :
Функция потерь падает на . Каждая часть здесь неотрицательна, так что обещание настоящее — для достаточно малого , потому что отброшенный член растёт как и в конце концов его съедает. Вот и вся теория. Ниже обещание выполняется, а затем ломается:
eta = 0.2 promised 66.49364500 delivered -16.01619240 ratio -0.240868
eta = 0.1 promised 33.24682250 delivered 12.61936315 ratio 0.379566
eta = 0.01 promised 3.32468225 delivered 3.11840766 ratio 0.937957
eta = 0.001 promised 0.33246822 delivered 0.33040548 ratio 0.993796
eta = 0.0001 promised 0.03324682 delivered 0.03322620 ratio 0.999380
eta = 1e-05 promised 0.00332468 delivered 0.00332448 ratio 0.999938Читайте снизу. Когда уменьшается, полученное падение сходится к обещанному — отношение 0.99938, затем 0.99994, — то есть теорема Тейлора оказывается права. Читайте сверху, и при полученное «падение» равно минус шестнадцати. Шаг пошёл вниз, а функция потерь выросла.
Итак, правило обновления такое:
и у него есть условие, которое никто не проговаривает: должно быть достаточно малым. Достаточно малым по сравнению с чем именно — следующий раздел.
У learning rate есть потолок, и его можно вычислить
Ссылка на раздел: У learning rate есть потолок, и его можно вычислитьНачнём с самой простой долины, , где . Один шаг gradient descent:
Позиция умножается на на каждом шаге. Это геометрическая прогрессия, а у геометрических прогрессий ровно одно правило: они уменьшаются, когда множитель меньше 1 по модулю, и растут иначе. Значит, , то есть .
Граница ровно при . Не «около 1», не «1 обычно слишком много». При множитель равен , и точка вечно отскакивает между и , не приближаясь и не убегая. Ниже — сходимость; выше — расходимость. Интервал снова делится при , где множитель меняет знак: ниже подход монотонный, выше точка перескакивает и чередует стороны, а ровно при множитель равен 0, и один-единственный шаг попадает в минимум.
Четыре режима из четырёх строк алгебры. Пересеките границы сами:
А теперь интересный случай:
Теперь общее правило, которое выпадает из того же аргумента. Множитель на самом деле был , а рядом с минимумом многопараметрическая функция потерь имеет по одному такому числу на направление — собственные значения матрицы вторых производных. Все направления должны быть устойчивы одновременно, поэтому потолок задаёт самое большое:
Для , , потолок равен 1, как мы только что вывели. Для нашего конвейера матрица вторых производных — , где — двухколоночная матрица входов, а её собственные значения равны 2 и 14.89, так что потолок равен . Это прогноз с пятью значащими цифрами. Проверим:
lr=0.1343 -> L = 24.5924
lr=0.13431 -> L = 24.5924
lr=0.13432 -> L = 4707.8 BLEW UP
lr=0.13433 -> L = 4.00452e+16 BLEW UP
lr=0.1344 -> L = 1.18229e+107 BLEW UPПять знаков после запятой совпадения между одной строкой линейной алгебры и ста тысячами итераций цикла for.
И здесь возвращается глава 1. Всё выше использовало центрированные измерения. Запустите тот же код на сырых миллиметрах и граммах, и собственные значения будут 0.0298 и 998.1 вместо 2 и 14.89. Потолок рушится с 0.134 до 0.002004 — столь же точно: сходимость при lr=0.002003 и взрыв при lr=0.002004.
Хуже потолка — отношение собственных значений. Число обусловленности измеряет, насколько долина далека от круглой: длинная тонкая траншея заставляет взять rate, достаточно маленький для крутых стен, а затем по дну траншеи приходится ползти с той же скоростью. У нас оно меняется с 7.44 для центрированных данных до 33 452 для сырых. С лучшим rate, который может взять каждая версия:
| features | condition number | best rate | steps to within 1% of the optimum |
|---|---|---|---|
| центрированные | 7.44 | 0.1184 | 10 |
| сырые миллиметры и граммы | 33,452 | 0.0020037 | 79,513 |
Те же данные, тот же код, тот же ответ в конце — и в восемь тысяч раз больше работы, потому что никто не вычел среднее. В главе 1 тот же пропуск стоил персептрону фактора в шесть тысяч эпох, и диагноз там был геометрическим: данные плавали далеко от начала координат. Здесь та же геометрия в костюме оптимизации, и поэтому нормализация входов — не совет по гигиене, а арифметика.1
Двадцать строк
Ссылка на раздел: Двадцать строкВсё выше не требовало библиотеки. Вот весь оптимизатор.
def loss(theta):
a, b = theta
return np.mean((a * x + b - y) ** 2)
def grad(theta):
a, b = theta
residual = a * x + b - y
return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])
def descend(theta, lr, steps):
theta = np.array(theta, dtype=float)
for _ in range(steps):
theta = theta - lr * grad(theta)
return theta
theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984Закрытая least-squares-формула для этих восьми точек даёт , , с функцией потерь . Цикл нашёл это до восьми значащих цифр, не зная, что закрытая форма существует, — а это важно, потому что начиная с главы 5 её уже не будет.
Траектория, потому что смотреть на неё и есть смысл:
0 a=0.000000 b=0.000000 L=57.437500
1 a=1.563750 b=0.000000 L=26.736582
2 a=1.963288 b=-0.000000 L=24.732418
5 a=2.098116 b=-0.000000 L=24.592488
10 a=2.100400 b=-0.000000 L=24.592449
60 a=2.100403 b=-0.000000 L=24.592449Большая часть расстояния пройдена за первые два шага, потому что градиент максимален, когда вы дальше всего от дна, и уменьшается по мере приближения. Gradient descent автоматически замедляется рядом с минимумом. Это feature, и в главе 6 это также проблема.
Где ещё наклон равен нулю
Ссылка на раздел: Где ещё наклон равен нулюВ аргументе до сих пор есть дыра. Шаг останавливается, когда , и мы называли это «минимумом». Точка с нулевым градиентом — критическая точка, и быть минимумом — лишь один из способов быть такой точкой:
- локальный минимум: вверх во всех направлениях, но, возможно, это не самая низкая такая точка вообще;
- локальный максимум: вниз во всех направлениях;
- седловая точка: вверх в одних направлениях и вниз в других. Поверхность имеет , что равно нулю в начале координат, где функция одновременно является минимумом вдоль оси и максимумом вдоль оси .
Gradient descent не умеет их различать, потому что он всегда смотрит только на градиент, а градиент равен нулю во всех трёх случаях.
У нашей прямой одна критическая точка, и она является ответом: функция потерь с квадратичной ошибкой для линейной модели выпукла, это одна чаша, и descent по ней не может не найти глобальный минимум. Это свойство не переживает встречи с этим курсом. Функция потерь нейронной сети не выпукла, и начиная с главы 5 «минимум» — не вещь, которая существует: их много, разной глубины, и то, какой получите вы, зависит от начальной точки. Это одно предложение и останется одним предложением, потому что теория большая, а практическое следствие маленькое.
Всё следствие можно увидеть на одной кривой. Возьмите , у которой две долины разной глубины:
x = -1.046681 f(x) = -0.352386 minimum
x = 0.101031 f(x) = 0.005026 maximum
x = 0.945649 f(x) = -0.152639 minimumПопадание в мелкую долину даёт функцию потерь на 56.7% хуже, и алгоритм никак не может это узнать, потому что изнутри долины любое направление ведёт вверх. В gradient descent для этого нет исправления, и оно не появится. На практике есть другое: оказывается, это куда менее важно, чем подсказывает картинка, — в очень высокой размерности реальной сети большинство критических точек оказываются седлами, а не ловушками,2 и глава 5 измеряет, как часто маленькая сеть действительно застревает.
Более дешёвые шаги: stochastic, minibatch, momentum
Ссылка на раздел: Более дешёвые шаги: stochastic, minibatch, momentumОдна вещь в grad выше должна вас беспокоить: он суммирует по всему набору данных на каждом шаге. Восемь деталей — ничто. Миллион — это миллион вычислений градиента, чтобы один раз сдвинуть параметры.
Выход в том, что градиент — это среднее, а среднее можно оценить по выборке. Вычислите его на случайной горстке — minibatch — и шагните по нему. Оценка шумная; она также несмещённая, а сотни дешёвых шумных шагов побеждают один дорогой точный. На ста тысячах синтетических деталей, считая градиенты по примерам, а не шаги:
| method | steps to within 0.1% of the optimum | per-example gradients |
|---|---|---|
| full batch | 7 | 700,000 |
| minibatch из 32 | 100 | 3,200 |
| по одному примеру | 17,580 | 17,580 |
В 219 раз меньше арифметики, чтобы прийти туда же. А крайний случай — по одному примеру за раз, исходная stochastic approximation Роббинса и Монро3, — не победитель: он в пять раз хуже batch размером 32, потому что 32 примера стоят почти не дороже одного на железе, которое перемножает матрицы, а шум падает как квадратный корень из размера batch. Из-за этого компромисса в каждом training script, который вы когда-либо прочитаете, есть batch_size.
Momentum — другое дешёвое исправление, и оно нацелено прямо на траншею. В плохо обусловленной долине шаги зигзагом пересекают узкое направление, пока ползут вдоль длинного. Momentum хранит скользящее среднее прошлых градиентов, так что колеблющиеся компоненты гасят друг друга, а согласованная накапливается:4
Две дополнительные строки. На сыром нецентрированном конвейере — число обусловленности 33 452, худший случай из имеющихся, — при лучшем rate, который может взять обычный descent:
momentum beta=0.0 -> 79,513 steps to 1%
momentum beta=0.9 -> 1,609 steps to 1%
momentum beta=0.99 -> 461 steps to 1%Фактор 172 за две строки кода. Глава 6 превращает это в Adam; механизм уже здесь.
Проверка, которая понадобится в главе 5
Ссылка на раздел: Проверка, которая понадобится в главе 5Каждый градиент в этой главе был выведен вручную и поэтому мог быть ошибочным. Исправление — таблица наклонов из начала: измерьте производную численно и сравните. Используйте центральную разность, , которая сокращает главный член ошибки и намного точнее при том же .
def numeric_grad(f, theta, h=1e-5):
theta = np.asarray(theta, dtype=float)
out = np.zeros_like(theta)
for i in range(theta.size):
bump = np.zeros_like(theta)
bump[i] = h
out[i] = (f(theta + bump) - f(theta - bump)) / (2 * h)
return out
def gradcheck(f, df, theta, h=1e-5):
analytic = np.asarray(df(theta), dtype=float)
numeric = numeric_grad(f, theta, h)
return np.max(np.abs(analytic - numeric) / np.maximum(1e-8, np.abs(analytic) + np.abs(numeric)))Относительная форма сравнения важна: абсолютная разница — катастрофа для градиента размера и ерунда для градиента размера .
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744Первая строка — выведенный вручную градиент выше. Вторая — та же функция, но в одной компоненте забыли множитель 2, опечатка в один символ, и проверка ловит её мгновенно. Всё ниже примерно — совпадение; всё выше — bug. Сохраните эту функцию: глава 5 использует её для отладки automatic differentiation engine, и только благодаря ей неправильный градиент вообще можно найти.
Куда это ведёт дальше
Ссылка на раздел: Куда это ведёт дальшеВсё в этой главе держалось на одном неозвученном предположении: что вы можете записать .
Для прямой с двумя параметрами это была одна строка алгебры. Почти сразу она перестаёт быть одной строкой. Попросите систему символьной алгебры найти производную функции потерь сети по одному весу первого слоя для одного примера и посчитайте арифметические операции в ответе:
| network | operations in one partial derivative |
|---|---|
| четыре скрытых нейрона, один слой | 40 |
| четыре скрытых нейрона, два слоя | 301 |
| четыре скрытых нейрона, три слоя | 1,717 |
Третья строка — сеть с 57 параметрами, настолько маленькая, что в главе 6 она была бы сноской, — и выписать её градиент вручную означает примерно 97 869 операций для одного обучающего примера. Нет нотации, которая это спасёт. Спасает наблюдение, что chain rule, применённый к композиции, имеет огромную структуру, что одни и те же промежуточные величины появляются снова и снова, и что вычисление их в правильном порядке даёт все производные примерно по цене одного forward pass. Это глава 5.
Но сначала есть проблема поменьше, и она ждёт прямо сейчас.
Теперь у нас есть машина, которая скатится вниз по любой дифференцируемой функции потерь. Направьте её на исходный вопрос конвейера — принять или отклонить, target равен 1 или 0, — поставьте sigmoid на выход, чтобы она предсказывала вероятность, и минимизируйте квадратичную ошибку. Она запустится. Но она также почти не будет двигаться тогда, когда ошибается сильнее всего, и градиент объясняет почему:
| output | prediction | truth | gradient with squared error | gradient with cross-entropy |
|---|---|---|---|---|
| 0.5000 | 1 | |||
| 0.1192 | 1 | |||
| 0.0025 | 1 | |||
| 1 |
Модель, которая уверенно, катастрофически ошибается — предсказывает 0.0000454, когда ответ равен 1, — выдаёт градиент квадратичной ошибки . Она понятия не имеет, что попала в беду. Другой столбец, из функции потерь, которую мы ещё не вывели, сообщает 1.0: максимальная срочность ровно там, где она заслужена.
Отсюда вопрос, с которого начинается следующая глава. Прошлая глава сказала, что функция потерь — это предположение о шуме, а квадратичная ошибка предполагает гауссов шум. Какая модель шума у ответа «да или нет» — и какая функция потерь получается, если провести с ней тот же вывод?
Источники и метод
Ссылка на раздел: Источники и методМетод старше всех этих работ: Коши описал его в заметке для Académie des Sciences в 1847 году как способ решать системы уравнений, двигаясь вниз по сумме квадратов их residuals. Также вместе с этой главой стоит прочитать обзор Sebastian Ruder An overview of gradient descent optimization algorithms (arXiv:1609.04747), где momentum до Adam разобран на четырнадцати понятных страницах; главу 3 книги Nocedal and Wright Numerical Optimization (2nd ed., Springer, 2006), где теорема 3.3 даёт скорость сходимости steepest descent на квадратичной функции через число обусловленности — это теория за тем, почему обусловленность определяет число шагов, хотя она рассматривает line search, а не фиксированный шаг и потолок , измеренный выше; или §5.8 и §7.1 книги Deisenroth, Faisal and Ong Mathematics for Machine Learning для того же материала с меньшим аппаратом; §6.1 книги Prince Understanding Deep Learning и §4.3 книги Goodfellow, Bengio and Courville Deep Learning; Dive into Deep Learning §12.1–12.3, где minibatch-анализ дан с большим числом измерений, чем здесь поместилось; и главу 4 книги Géron Hands-On Machine Learning (3rd ed.), самое практичное изложение learning rate как вещи, которую настраивают, а не выводят. Конспекты MIT 6.390 ставят gradient descent перед классификацией, как и этот курс, и по той же причине.
Сноски
Ссылка на раздел: Сноски-
LeCun, Y., Bottou, L., Orr, G. B. and Müller, K.-R. Efficient BackProp, in Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. Раздел 4.3 даёт рекомендацию, а раздел 5.1 — аргумент, использованный в блоке подробностей выше: центрирование и масштабирование входов меняет собственные значения матрицы вторых производных, а значит, число шагов, а не просто численное удобство. ↩
-
Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. and Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). Аргумент о том, что в высокой размерности критические точки подавляющим образом являются седлами, а не локальными минимумами, поскольку минимум требует, чтобы каждое из тысяч направлений одновременно изгибалось вверх. ↩
-
Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). Статья, которая установила, что шумной оценки градиента достаточно, если размер шага уменьшается правильным образом. ↩
-
Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), pp. 1–17 (1964). Метод heavy-ball, то есть обновление с momentum выше, за двадцать два года до того, как backpropagation пришла в эту область. ↩