Глава 3 из 14 35 мин
Спуск с горы
Хорошие вероятности можно не только подсчитать: можно начать с любых чисел и шаг за шагом спускаться туда, где ошибка меньше. Так учатся все нейросети — от биграммы до Ростка.
В этой главе
- понять, что такое производная и градиент и почему шагают против градиента
- вывести градиент softmax с перекрёстной энтропией: p − onehot
- обучить биграммную нейросеть у себя в браузере и прийти к тем же вероятностям, что дал подсчёт
В первой главе мы построили модель, ничего не обучая: посчитали, как часто одна буква идёт за другой, и поделили. Во второй измерили, насколько она хороша: 2,365 ната удивления на букву на том самом тексте, по которому считали (и 2,364 на тексте, которого она не видела). Для таблицы 97 × 97 лучше не придумаешь. Но у подсчёта есть потолок, и мы очень скоро в него упрёмся.
Допустим, мы хотим смотреть не на одну предыдущую букву, а на три. Разных троек букв 97³ = 912 673, и каждой нужна строка из 97 вероятностей — почти 89 миллионов клеток. В нашем корпусе 41 миллион букв, так что большинство троек не встретится ни разу, и их строки останутся пустыми. А Росток оглядывается на 512 токенов назад. Таблица для такого контекста не поместилась бы ни в одну вселенную.
Нужен другой способ находить хорошие числа — такой, который годится для любой модели, а не только для таблицы. Идея простая: взять числа наугад, измерить ошибку и чуть-чуть подвинуть каждое число туда, где ошибка меньше. Повторить тысячи раз. Это и есть обучение, а его сердце — градиентный спуск. В этой главе мы освоим его на знакомой задаче: заново получим биграмму, только не подсчётом, а спуском. И проверим, что спуск придёт ровно туда же.
В тумане
Представьте: вы в горах, и опустился такой туман, что не видно собственных ботинок. Нужно спуститься в долину. Карты нет, тропы не видно. Что остаётся? Почувствовать ногами, куда наклонена земля прямо под вами, и шагнуть туда, где она круче всего уходит вниз. Потом снова прислушаться к склону — и снова шагнуть.
Сыграйте пару раз с разных мест, а потом снимите туман. Вы заметите три вещи, и все три — про обучение нейросетей.
- Карта не нужна. Чтобы идти вниз, достаточно знать наклон там, где стоишь. Модели учатся именно так: всю «местность» ошибки не видит никто, но наклон в текущей точке посчитать можно всегда.
- Шаги сами укорачиваются. Кнопка шагает на расстояние, пропорциональное крутизне: на крутом склоне шаг широкий, у дна — мелкий. На самом дне наклон почти нулевой, и путник почти замирает.
- Низина — не обязательно самая глубокая. С неудачного старта спуск приводит в мелкое озеро, откуда во все стороны дорога ведёт вверх. Такое место называют локальным минимумом.
Переведём метафору на язык моделей. Высота — это ошибка модели, та самая функция потерь из главы 2. Место на карте — значения параметров. У путника их два: запад–восток и юг–север. У нашей биграммы их 9 409, у Ростка — 17 миллионов. Такую местность не нарисуешь, но наклон в ней по-прежнему можно вычислить, а значит, можно и шагать так же. Осталось понять, что такое «наклон» на языке чисел.
Производная — это наклон
Начнём с одной ручки. Пусть ошибка зависит от единственного числа $x$ и выглядит как чаша: $f(x) = \tfrac12 (x-2)^2$. Дно — при $x = 2$. Как, стоя в точке $x$, понять, куда идти?
Сдвинемся от $x$ на крошечное $h$ и посмотрим, насколько изменилась высота. Отношение подъёма к шагу и есть наклон:
$$\text{наклон} \approx \frac{f(x+h) - f(x)}{h}.$$Чем меньше $h$, тем точнее. То, к чему стремится это отношение при $h \to 0$, называют производной и пишут $f'(x)$ или $\frac{df}{dx}$. Для нашей чаши её можно вывести в одну строчку:
$$\frac{f(x+h)-f(x)}{h} = \frac{\tfrac12 (x-2+h)^2 - \tfrac12 (x-2)^2}{h} = \frac{(x-2)h + \tfrac12 h^2}{h} = (x-2) + \tfrac12 h.$$Когда $h$ стремится к нулю, остаётся $f'(x) = x - 2$. Знак производной говорит, в какую сторону склон поднимается: при $x = -1$ производная равна $-3$, значит, влево — вверх, вправо — вниз. Величина говорит, насколько круто. Правило спуска получается само: шагнуть против производной на небольшую её долю.
$$x \leftarrow x - \eta \, f'(x)$$Число $\eta$ (греческая «эта») — шаг обучения; его ещё называют скоростью обучения, по-английски learning rate. Это единственная настройка спуска, и от неё зависит всё. Проверьте сами:
В чаше всё считается точно. Расстояние до дна равно $x - 2$, и после шага оно становится таким:
$$x - \eta(x-2) - 2 = (1-\eta)(x-2).$$Каждый шаг умножает расстояние до дна на $1 - \eta$. При $\eta = 0{,}1$ это $0{,}9$ — мяч ползёт. При $\eta = 1$ множитель равен нулю: ровно на дно за один шаг. При $\eta = 1{,}5$ множитель $-0{,}5$: мяч перепрыгивает дно, но каждый прыжок вдвое короче прежнего. При $\eta = 2$ получаются вечные качели, а при $\eta > 2$ каждый прыжок длиннее предыдущего, и мяч улетает. Про такое говорят «обучение разошлось»: ошибка не падает, а растёт до бесконечности.
Теперь переключите кривую на «Две ямы» и «Плато». В двух ямах мяч скатывается в ту, что ближе, даже если соседняя глубже. На плато наклон почти нулевой, поэтому и шаги почти нулевые: мяч еле движется, хотя впереди глубокая яма. Обе беды настоящие, и с обеими мы ещё встретимся — с плато уже в следующей главе.
Всё это умещается в несколько строк Python. Производную считаем «в лоб», по определению: делим подъём на крошечный шаг.
При $\eta = 0{,}5$ наклон с каждым шагом уменьшается вдвое: −3; −1,5; −0,75… — а с ним и расстояние до дна. Поменяйте lr на 2,5 и запустите снова.
Много ручек: градиент
У путника в тумане две координаты, и высота зависит от обеих: $f(x, y)$. Как измерить наклон, если направлений бесконечно много? Хитрость в том, чтобы сначала крутить ручки по одной.
Заморозим $y$ и будем двигать только $x$. Получится обычная функция одной переменной с обычной производной. Её называют частной производной по $x$ и пишут $\frac{\partial f}{\partial x}$: закруглённая «д» напоминает, что остальные переменные на это время застыли. Точно так же получается $\frac{\partial f}{\partial y}$. Вместе эти два числа и есть градиент:
$$\nabla f = \left( \frac{\partial f}{\partial x},\ \frac{\partial f}{\partial y} \right).$$Градиент — это стрелка. Она показывает направление, в котором функция растёт быстрее всего, а её длина — насколько быстро. Нам нужно вниз, поэтому шагаем против стрелки, и правило выглядит так же, как для одной ручки:
$$(x, y) \leftarrow (x, y) - \eta \, \nabla f(x, y).$$Посмотрите на стрелки у мяча: шаг действительно складывается из двух независимых кусочков. Каждая частная производная говорит, как повернуть свою ручку, а вместе они дают направление самого крутого спуска. И это работает при любом числе ручек: у биграммы их 9 409, значит, градиент — это 9 409 чисел, по одному на ручку. У Ростка — 17 миллионов. Один шаг спуска сдвигает все ручки разом.
Поднимите шаг η до 0,1, и мяч начнёт метаться между берегами озера, а то и вылетит из него — как в чаше при $\eta > 2$. В узких долинах большой шаг особенно опасен: поперёк долины склоны крутые, и мяч перелетает дно, почти не продвигаясь вдоль неё.
Почему градиент смотрит именно в сторону самого крутого подъёма?
Шагнём на крошечное расстояние в направлении единичного вектора $u = (u_x, u_y)$. Вблизи точки поверхность почти плоская, поэтому высота изменится примерно на $\frac{\partial f}{\partial x} u_x + \frac{\partial f}{\partial y} u_y$. Это скалярное произведение $\nabla f \cdot u$, а оно равно $|\nabla f| \cdot |u| \cdot \cos\alpha$, где $\alpha$ — угол между векторами. Оно наибольшее при $\cos\alpha = 1$, то есть когда $u$ смотрит туда же, куда градиент, и наименьшее, когда смотрит строго против. Поперёк градиента ($\cos\alpha = 0$) высота не меняется: это линия уровня, горизонталь на карте. Поэтому стрелки спуска всегда перпендикулярны горизонталям.
Вероятности из любых чисел
Вернёмся к биграмме. Чтобы учить её спуском, нужны числа, которые можно двигать куда угодно. Вероятности так не подвинешь: они не бывают отрицательными, а в каждой строке обязаны давать в сумме единицу. Шаг спуска об этих правилах ничего не знает и легко их нарушит.
Выход — хранить не вероятности, а свободные числа и превращать их в вероятности в последний момент. Свободные числа называются логитами (обозначим их $z$), а превращение — softmax:
$$p_i = \frac{e^{z_i}}{\sum_j e^{z_j}}.$$Тут два действия. Экспонента $e^{z}$ делает из любого числа положительное: большие логиты превращаются в очень большие числа, отрицательные — в крошечные, но не нулевые. Деление на сумму нормирует их, так что вместе они дают ровно 1. Поиграйте с логитами пяти самых частых букв после «t»:
Нажмите «+1 ко всем»: логиты выросли, а вероятности не сдвинулись ни на волос. Softmax смотрит только на разницу между логитами: $e^{z_i + c} = e^c \cdot e^{z_i}$, и множитель $e^c$ сокращается в числителе и знаменателе. А «×2» удваивает все разницы, и распределение становится острее, увереннее. (Температура при генерации текста делает то же самое — к ней мы вернёмся в главе 11.)
В нейросетевом варианте биграмма — это просто таблица $W$ размером 97 × 97, только в клетках не вероятности, а логиты. В строке $a$ лежат логиты для буквы, которая идёт после $a$. Чтобы предсказать следующую букву, берём строку и пропускаем её через softmax. Почему это называют нейросетью? Запишем текущую букву как вектор из 97 нулей с единицей на её месте — такая запись называется one-hot. Тогда «взять строку $a$» — это умножить этот вектор на матрицу $W$. Одна матрица, один слой, ни одного скрытого — самая маленькая нейросеть на свете. В главе 4 у неё появятся скрытые слои.
Куда тянуть логиты
Для спуска нужен градиент ошибки по каждому логиту. Ошибка на одном примере — это удивление из главы 2 при виде правильной буквы $y$: $L = -\ln p_y$. Распишем её через логиты, подставив softmax:
$$L = -\ln \frac{e^{z_y}}{\sum_j e^{z_j}} = -z_y + \ln \sum_j e^{z_j}.$$Возьмём частную производную по какому-нибудь логиту $z_i$. У первого слагаемого она равна $-1$, если $i = y$, и нулю в остальных случаях. У второго:
$$\frac{\partial}{\partial z_i} \ln \sum_j e^{z_j} = \frac{e^{z_i}}{\sum_j e^{z_j}} = p_i.$$Складываем:
$$\frac{\partial L}{\partial z_i} = p_i - [i = y].$$Квадратные скобки равны 1, если условие выполнено, и 0, если нет. Весь градиент — это вектор вероятностей минус one-hot правильного ответа: $p - \text{onehot}(y)$. Формулы проще в машинном обучении почти не бывает, и её стоит запомнить.
Прочтите формулу глазами спуска, который шагает против градиента. Логит правильной буквы получает толчок вверх размером $1 - p_y$: чем меньше модель верила в правильный ответ, тем сильнее толчок. Каждый неправильный логит получает толчок вниз размером $p_i$: чем увереннее модель ошиблась, тем больше ей достаётся. Вернитесь к виджету выше, выберите верную букву и понажимайте «Шаг спуска». Последний столбец — это и есть $p - \text{onehot}$, а удивление с каждым шагом тает.
Формулу стоит проверить честно: пошевелить каждый логит и посмотреть, как меняется ошибка. Как с мячом, только ручек теперь пять.
Теперь от одного примера — ко всему корпусу. Ошибка биграммы — среднее удивление по всем $N$ = 41,3 миллиона пар букв. Пара «$a$, потом $b$» встречается $n_{ab}$ раз, и каждая такая встреча толкает строку $a$ по формуле выше. Суммируем все толчки и делим на $N$:
$$\frac{\partial L}{\partial W_{ab}} = \frac{n_a}{N}\left(p_{ab} - \frac{n_{ab}}{n_a}\right),$$где $n_a$ — сколько раз встретилась буква $a$, а $\frac{n_{ab}}{n_a}$ — доля $b$ среди букв после $a$, то есть в точности вероятность из подсчёта в главе 1. Посмотрите, когда градиент обращается в ноль: когда $p_{ab} = \frac{n_{ab}}{n_a}$ для всех пар. Спуск остановится ровно там, где вероятности сети совпадут с подсчётом.
Подсчёт и спуск ведут в одну точку. Но подсчёт умеет строить только таблицы, а спуск — обучить любую модель, у которой можно вычислить градиент.
Модель дала правильной букве вероятность 0,3, а одной из неправильных — 0,5. Какие градиенты у их логитов?
Градиент равен $p - \text{onehot}$: у правильной буквы $0{,}3 - 1 = -0{,}7$, у неправильной $0{,}5 - 0 = 0{,}5$. Спуск шагает против градиента, так что логит правильной буквы растёт, а неправильной — падает.
Обучаем биграмму
Всё готово. Таблица $W$ из 9 409 логитов, в начале сплошь нули — значит, каждый из 97 символов получает вероятность 1/97, а удивление равно $\ln 97 \approx 4{,}575$ ната. Градиент берём из формулы выше, счётчики $n_{ab}$ — из той же таблицы подсчётов, что в главе 1. Нажмите «Обучать».
Кривая сначала падает круто, потом всё медленнее ползёт к зелёному пунктиру. За первые 100 шагов удивление опускается с 4,575 до 2,49, за 1 000 шагов — до 2,381, за 20 000 — до 2,366, всего на тысячную выше подсчёта. Таблица под графиком тоже проявляется: в строке «q» загорается «u», после пробела — «t», «a», «s». Это те же узоры, что в главе 1, только теперь сеть нашла их сама.
Нажмите на строку «q». Даже когда кривая почти легла на пунктир, эта строка ещё заметно отстаёт. После 300 шагов строка пробела расходится с подсчётом меньше чем на 1% вероятности, а строка «q» — на целых 60%. Всё дело в множителе $\frac{n_a}{N}$: чем реже буква, тем слабее толчок её строке. Пробел — 18% всех букв, «q» — три сотых процента. Частые буквы учатся первыми, редкие — последними. Эта несправедливость — одна из причин, по которым настоящие модели учат не голым спуском, а оптимизаторами вроде Adam, которые подбирают шаг для каждого числа отдельно (глава 10).
Почему последние тысячные даются так тяжело? В таблице 7 051 клетка с нулём: эти пары ни разу не встретились, и подсчёт даёт им вероятность ровно 0. Сеть так не умеет: чтобы $e^{z}$ стало нулём, логит должен уйти в минус бесконечность. Спуск честно толкает такие логиты вниз, но толчок пропорционален самой вероятности $p$, а она с каждым шагом меньше. Сеть бесконечно приближается к подсчёту, но никогда его не достигает.
Теперь поиграйте с шагом. При η = 5 спуск идёт, но вяло: через 1 000 шагов удивление всё ещё 2,49. При η = 150 кривая сначала падает, а потом начинает дрожать около 2,5 и ниже не опускается: для строк самых частых букв такой шаг велик — как $\eta > 2$ в чаше, — и они раскачиваются. Одной строке пробела достаётся почти пятая часть всех толчков, да и строки «e», «t», «h» отстают не так уж сильно. При η = 500 обучение не сходится вовсе: удивление скачет где-то между 6 и 9,5 — хуже, чем при угадывании наугад. Ровно как мяч в чаше, только в 9 409 измерениях.
И последний опыт: начните со случайных логитов вместо нулей. Удивление стартует выше, около 5,0 (случайная модель уверенно ошибается), но приходит в ту же точку. У этой задачи одна-единственная долина без ложных ям (математики говорят, что функция потерь выпуклая), и откуда ни начни, спуск найдёт её дно. У настоящих нейросетей долина не одна, и всё же на практике спуск раз за разом находит хорошие места. Почему так выходит, до конца не понимают даже исследователи.
Вот тот же спуск на numpy: 600 шагов, пара секунд прямо в браузере.
В настоящих проектах градиент руками не выводят: библиотека вроде PyTorch находит его сама. Проверим, что она получает ровно нашу формулу (этот код — для вашего компьютера, в браузере PyTorch не запустится):
Как loss.backward() справляется с любой, даже очень запутанной формулой, — тема следующей главы.
Сглаживание — это штраф
У подсчитанной таблицы есть неприятная черта: пары, которых не было в корпусе, получают вероятность ноль. А удивление при нулевой вероятности бесконечно: одна непредвиденная пара — и оценка всего текста рушится. Поэтому к каждому счётчику обычно добавляют понемногу, например по единице. Это сглаживание; модель bigram-char, которая лепетала в конце главы 1, сглажена именно так.
У нейросети есть свой способ сделать то же самое — добавить к ошибке штраф за большие логиты:
$$L_{\text{всего}} = L + \lambda \sum_{a,b} W_{ab}^2.$$Градиент штрафа равен $2\lambda W_{ab}$: он тянет каждый логит к нулю, а значит, всю строку — к равномерному распределению. Данные тянут к подсчёту, штраф — к «все буквы равны», и сеть останавливается где-то посередине. Где данных много, побеждают данные; где мало — штраф. Сглаживание ведёт себя точно так же: лишняя единица почти ничего не меняет в строке пробела с её 7,5 миллиона пар, но сильно размывает строку редкой буквы.
Попробуйте штраф λ в виджете выше (после «Сначала»). Вот что выходит за 20 000 шагов:
| λ | удивление | p(u | q) | p(u | Q) |
|---|---|---|---|
| 0 | 2,366 | 0,994 | 0,93 |
| 10⁻⁶ | 2,371 | 0,949 | 0,57 |
| 10⁻⁵ | 2,403 | 0,667 | 0,03 |
| 10⁻⁴ | 2,577 | 0,045 | 0,01 |
Чем сильнее штраф, тем выше удивление на обучающем тексте: сеть сознательно отказывается подгонять таблицу под данные до последней цифры. Строка «Q» (958 раз в корпусе) размывается уже от слабого штрафа, строка «q» (13 061 раз) держится дольше. Зачем это нужно, если удивление растёт? Затем, что нам важен не обучающий текст, а тот, которого модель ещё не видела. Биграмме, обученной на 41 миллионе букв, заучивать почти нечего: на новом тексте подсчитанная таблица удивляется на 2,364 ната — практически как на старом. А моделям с миллионами параметров штраф помогает не заучивать случайные особенности обучающих данных. Такой приём называют регуляризацией, а этот её вид — L2-регуляризацией, или затуханием весов (weight decay); у оптимизатора AdamW, которым учат большие модели, для него есть отдельная ручка.
Росток сейчас
Росток по-прежнему видит только одну предыдущую букву, поэтому лепечет так же, как в главе 1. Но по духу это уже другая модель: её числа не посчитаны, а найдены спуском — тем же способом, каким обучены все 17 миллионов чисел настоящего Ростка. Ниже три текста, написанные с одинаковыми бросками кубика: до обучения, вашей сетью и подсчитанной таблицей. Если вы ещё не обучали сеть, это можно сделать прямо здесь.
Чтобы видеть дальше одной буквы, модели нужны скрытые слои, а градиент через них уже не выведешь на листочке, как $p - \text{onehot}$. В следующей главе мы научимся находить его автоматически — обратным распространением ошибки.