Глава 2 из 14 35 мин
Мера удивления
Как понять, хороша ли языковая модель? Показать ей настоящий текст и измерить, насколько он её удивил, — так мы придём к битам и натам, энтропии и главному числу всего обучения: функции потерь.
В этой главе
- измерить, насколько модель удивлена каждой буквой настоящего текста
- понять, почему вероятности не перемножают, а складывают их логарифмы, и что такое биты и наты
- посчитать функцию потерь (перекрёстную энтропию) и перплексию — и узнать, ниже какого предела она не опустится
Таблица из прошлой главы пишет «Frthendd, aced. tre sedsck». Это хорошо? По сравнению с чем? Посмотреть на результат и сказать «вроде похоже на английский» — мало. Чтобы улучшать модель — а обучение и есть улучшение, повторённое тысячи раз, — нужно одно число, которое говорит, насколько она хороша. У модели получше это число должно быть лучше, и считаться оно должно быстро.
Идея простая. Хорошая модель — та, которую настоящий текст редко удивляет. Берём текст, написанный человеком, скармливаем модели по букве и каждый раз спрашиваем: какую вероятность ты дала той букве, что пришла на самом деле? Если модель раз за разом ставит высокие вероятности тому, что действительно случается, она хорошо понимает язык. Если её всё время застают врасплох — плохо.
Удивлённый Росток
Попробуем пока без формул. Ниже — Росток в его нынешнем виде, буквенная таблица из прошлой главы. Напишите что угодно по-английски. Каждая буква окрашена по тому, насколько она удивила таблицу: бледная — её ждали, тёмная — как снег на голову. Стрелка показывает среднее по всему тексту. Нажмите на любую букву, чтобы увидеть, чего таблица ждала вместо неё.
Поиграйте с заготовками. Обычная фраза из сказки даёт в среднем около 2,3; самая тёмная буква в ней — заглавная L в «Lily»: имена предсказывать трудно. Буква «u» после «q» почти ничего не стоит. Панграмма про лису заметно труднее: в ней редкие «q», «x» и «z». Абракадабра уходит к 6,4 — далеко за отметку «наугад»: таблица тут не просто не уверена, она уверенно ошибается. КАПСЛОК ещё хуже, 7,8: в детских сказках заглавная почти никогда не идёт за заглавной. Русский текст латиницей где-то посередине, около 3,5: буквы настоящие, а вот пары для английского непривычные.
Цвета уже многое рассказывают. Теперь превратим их в математику.
Вероятность всего текста
Самый естественный способ оценить модель на тексте — спросить, какую вероятность она даёт всему тексту. Для нашей таблицы это легко посчитать. Шанс, что история начнётся с «O», равен $p(\text{O} \mid \blacklozenge) = 0{,}253$. Шанс, что следом придёт «n», если была «O», — $0{,}730$. Буквы идут одна за другой, поэтому вероятности перемножаются:
$$\begin{aligned} p(\text{текст}) &= p(x_1 \mid \blacklozenge) \cdot p(x_2 \mid x_1) \cdots \\ &= \prod_{t=1}^{N} p(x_t \mid x_{t-1}), \qquad x_0 = \blacklozenge \end{aligned}$$(Большая $\prod$ значит «перемножить всё от $t = 1$ до $N$» — так же, как $\sum$ значит «сложить всё».) Это число называется правдоподобием текста при данной модели. Если сравнить две модели на одном и том же настоящем тексте, лучше та, что даёт ему большую вероятность: она этот текст предвидела.
Посчитаем для «Once upon a time». Шестнадцать вероятностей: 0,253, 0,730, 0,023, 0,156, 0,313, 0,008 (это «u» из «upon»: слова после пробела редко начинаются с u)… и их произведение — около $8{,}0 \cdot 10^{-17}$. Для фразы из шестнадцати букв это уже очень маленькое число. Для 45 букв «Once upon a time, there was a dog named Spot.» — $8{,}0 \cdot 10^{-47}$. А для целой истории?
Вот в чём беда. Компьютер хранит числа с ограниченной точностью. Обычное число двойной точности (float64) не бывает меньше примерно $5 \cdot 10^{-324}$: ниже оно превращается в честный ноль. На этой истории это случается на 324-й букве. Видеокарты чаще считают во float32, и там произведение обнуляется уже на 45-й букве. А ноль бесполезен: все тексты получают одну и ту же оценку «невозможно», и хорошую модель от плохой больше не отличить.
Логарифм спешит на помощь
Выход даёт инструмент из школьной программы — логарифм. Его самое полезное свойство: он превращает умножение в сложение.
$$\log(a \cdot b) = \log a + \log b$$Значит, вместо крошечного произведения можно хранить сумму логарифмов вероятностей:
$$\log p(\text{текст}) = \sum_{t=1}^{N} \log p(x_t \mid x_{t-1})$$Слагаемые — обычные умеренные числа, и сумма тоже. Для «Once upon a time» это −37,1 вместо $8{,}0 \cdot 10^{-17}$; для всей истории из 706 букв — около −1618 (само произведение было бы около $3 \cdot 10^{-703}$), а −1618 помещается в любой компьютер. И ничего не теряется: логарифм — возрастающая функция, у большей вероятности всегда больший логарифм, так что сравнение моделей по сумме логарифмов даёт тот же вердикт, что и по вероятностям. Это розовая линия в виджете выше: она спокойно идёт вниз и после того, как оба произведения упёрлись в дно.
Остался косметический шаг. Вероятности не больше единицы, так что их логарифмы — ноль или отрицательные числа. Говорить об отрицательных неудобно, поэтому поменяем знак и дадим результату имя. Удивление от события с вероятностью $p$ — это
$$s = -\log p$$Оно ведёт себя ровно так, как положено удивлению. Событие, в котором мы были уверены ($p = 1$), удивляет на ноль. Чем событие менее вероятно, тем удивление больше, а то, что мы считали невозможным ($p \to 0$), удивляет бесконечно. И удивление от двух событий подряд — сумма их удивлений: логарифм превратил произведение в сумму. (В теории информации ту же величину называют собственной информацией.)
Биты и наты
Какой логарифм брать? Годится любой — они отличаются лишь постоянным множителем, — но по традиции берут один из двух, и у каждого своя единица удивления.
- По основанию 2 удивление измеряется в битах. Один бит — удивление от того, что честная монетка упала орлом: $-\log_2 \tfrac12 = 1$. Три орла подряд, вероятность $\tfrac18$, — 3 бита.
- С натуральным логарифмом $\ln$ (основание $e \approx 2{,}718$) единица называется нат. PyTorch и почти всё машинное обучение пользуются натуральным логарифмом, поэтому функцию потерь почти всегда указывают в натах.
Переводить просто: $1\ \text{нат} = 1/\ln 2 \approx 1{,}443$ бита, а $1\ \text{бит} \approx 0{,}693$ ната. Виджет ниже помогает почувствовать цену неожиданности: ведите точку по кривой и смотрите, сколько орлов подряд удивили бы вас так же.
Несколько ориентиров, которые стоит запомнить. Угадать один из 97 символов совсем наугад стоит $\log_2 97 \approx 6{,}6$ бита, или $\ln 97 \approx 4{,}575$ ната, — это отметка «наугад» на счётчике. У «x» после «e» вероятность 0,87%, это 6,8 бита — даже больше, чем стоит догадка наугад. А «u» после «q» — 98,8% в сглаженной таблице Ростка — стоит всего 0,017 бита.
Среднее удивление — это и есть функция потерь
Теперь у нас есть всё. Сумма удивлений растёт вместе с длиной текста, а тексты бывают разной длины, поэтому делим на число букв. Среднее удивление на букву — та самая оценка, которую мы искали:
$$L = \frac{1}{N} \sum_{t=1}^{N} -\ln p(x_t \mid x_{t-1})$$В машинном обучении это число называется функцией потерь (loss), и всё обучение — и в этом курсе, и в любой большой модели — сводится к тому, чтобы его уменьшить. Раз оно сравнивает распределение настоящего текста с распределением, в которое верит модель, у него есть и более формальное имя — перекрёстная энтропия. Откуда здесь «энтропия», разберёмся в конце главы.
У нашей биграммы, измеренной на 4,9 миллиона букв текста, которого она никогда не видела (его заранее отложили в сторону), функция потерь равна 2,364 ната на букву, или 3,41 бита. Вот тот же расчёт в нескольких строчках Python — на одной фразе:
Последняя строка — наш старый враг: 400 букв средней вероятности перемножаются в точный ноль. А вот как функцию потерь настоящей таблицы считает код курса — последние две строчки bigram() из snapshots.py, которые мы в прошлой главе оставили без объяснений:
probs[ids[:-1], ids[1:]] выбирает для каждой позиции вероятность той буквы, что на самом деле пришла следом; -np.log превращает каждую в удивление; .mean() усредняет. NLL — negative log-likelihood, отрицательный логарифм правдоподобия, ещё одно имя того же самого. На своих 41 миллионе обучающих букв таблица получает 2,3650, на незнакомом тексте — 2,364: практически одно и то же. Таблица из 9 409 чисел слишком мала, чтобы запомнить 41 миллион букв, поэтому «списать» на знакомом тексте она не может. Модели побольше могут, и в главе 5 эта разница станет очень важной.
Потери в битах — это ещё и размер архива
У функции потерь в битах есть красивый практический смысл. Имея модель, можно методом арифметического кодирования (его придумали в 1970-х) сжать текст почти ровно в столько бит, сколько составляет суммарное удивление модели на нём, — и разжать обратно той же моделью. Значит, модель с потерями 3,41 бита на букву — это архиватор, который хранит английский текст примерно в 3,4 бита на букву вместо 8 бит обычного текстового файла. Хорошо предсказывать и хорошо сжимать — одно и то же умение. Поэтому и говорят, наполовину в шутку, что языковая модель — очень умный архиватор.
Перплексия: сколько вариантов на самом деле
Наты не слишком наглядны, поэтому то же число подают ещё одним способом. Снимем логарифм: $e^{L}$ называется перплексией (perplexity, «растерянность»). А смысл у неё очень простой. Модель, которая выбирает равномерно из $k$ вариантов, каждый раз удивляется на $\ln k$, и её перплексия ровно $k$. Перплексия — число равновероятных вариантов, из которых модель по сути выбирает на каждом шаге.
Угадывание наугад из 97 символов: перплексия 97. Наша биграмма: $e^{2{,}364} \approx 10{,}6$. Знание одной предыдущей буквы сужает выбор с 97 символов примерно до десяти с половиной. Для 9 409 чисел — очень неплохо.
Функция потерь — это среднее удивление: минус логарифм вероятности, которую модель дала тому, что пришло на самом деле, усреднённый по настоящему тексту. Чем меньше, тем лучше, а $e^{\text{loss}}$ — из скольких вариантов модель по сути выбирает.
Лестница моделей
Число что-то значит только в сравнении. Вот три модели, которые легко построить; каждая знает чуть больше предыдущей:
- Наугад: не знает ничего и даёт каждому из 97 символов $1/97$. Её потери — $\ln 97 \approx 4{,}575$ на любом тексте.
- Частоты букв (униграмма): знает, какие буквы часты («e» — часто, «z» — редко), но никогда не смотрит назад. Это наша таблица, свёрнутая в одну строку.
- Биграмма: наша таблица, память на одну букву.
Все три измерены на одном и том же отложенном тексте в 4,9 миллиона букв — вместе с двумя моделями из будущих глав:
| модель | потери, наты/букву | биты/букву | перплексия |
|---|---|---|---|
| наугад из 97 символов | 4,575 | 6,60 | 97 |
| частоты букв (униграмма) | 3,084 | 4,45 | 21,9 |
| биграмма (эта модель) | 2,364 | 3,41 | 10,6 |
| нейросеть, 3 буквы контекста (глава 4) | 1,479 | 2,13 | 4,4 |
| нейросеть, 8 букв контекста (глава 5) | 1,103 | 1,59 | 3,0 |
Каждая ступенька вниз — модель, которая поняла про английский ещё немного больше. Проверьте свой текст сразу на всех — и позовите большого, настоящего Ростка. Он читает текст не по буквам, а токенами — кусочками примерно по четыре буквы. Чтобы поставить его на ту же шкалу, мы складываем его удивление по всему тексту и делим на число букв. (На отложенном тексте на один токен приходится 4,085 буквы, так что потери $L$ на токен — это примерно $L / 4{,}085$ на букву.)
Посмотрите, как перемещаются тёмные пятна. Модель «наугад» удивлена всем одинаково. Униграмма удивляется редким буквам, где бы они ни стояли. Биграмма прощает редкие буквы в привычной компании («u» после «q»), зато спотыкается на первой букве каждого слова. А большая модель, которая помнит сотни токенов, в идеале удивляется только тому, чего не предскажет никто: имени, выбору слова, повороту сюжета. Судите сами, насколько к этому приблизился Росток.
На одном и том же тексте у модели A потери 2,0 ната на букву, у модели B — 2,5. Какая лучше и что это значит в «вариантах выбора»?
Потери — это удивление, а хорошая модель удивляется меньше. Полната на букву — большая разница: на тексте в 1000 букв это 500 натов, то есть B даёт настоящему тексту в $e^{500}$ раз меньшую вероятность, чем A.
Сколько удивления не избежать
Может ли функция потерь опуститься до нуля? Только если текст полностью предсказуем, а язык не такой. После «Once upon a» слово «time» почти гарантировано, но после «She picked up the» подходят десятки слов. Какая-то доля удивления заложена в самом тексте, и ни одна модель, даже огромная, от неё не избавится.
У этой заложенной доли есть имя — энтропия. Это среднее удивление идеальной модели, которая точно знает настоящие вероятности $p$:
$$H(p) = -\sum_i p_i \log p_i$$У честной монетки энтропия 1 бит. У честного шестигранного кубика — $\log_2 6 \approx 2{,}58$ бита. У монетки, погнутой так, что орёл выпадает в 90% случаев, — всего 0,47 бита: она редко удивляет. У достоверного события — ноль. Энтропия измеряет неопределённость самого источника — неважно, кто и как его угадывает.
А теперь самый важный поворот. Пусть мир бросает кубик с вероятностями $p$, а модель верит в другие вероятности $q$. Тогда её среднее удивление —
$$H(p, q) = -\sum_i p_i \log q_i \ \ge\ H(p),$$и равно $H(p)$ оно только при $q = p$. Это и есть перекрёстная энтропия — ровно то, что считает наша функция потерь, где в роли мира выступает настоящий текст. Разница $H(p, q) - H(p)$ — плата за неверные представления. Поиграйте: настройте кубик мира, потом кубик модели и следите за числами.
В нашей таблице 97 маленьких кубиков, и энтропии у них очень разные. После «q» энтропия всего 0,16 бита — угадывать нечего. После пробела — 4,55 бита: новое слово может начаться почти с чего угодно. В самом начале истории — 4,0 бита. Ячейка ниже считает это для любой строки.
Так какова энтропия самого английского — предел, ниже которого не опустится ни одна модель? Точно этого никто не знает. В 1951 году Клод Шеннон оценил её, предлагая людям угадывать текст по одной букве, и получил где-то от 0,6 до 1,3 бита на букву, когда угадывающий видит длинный кусок текста. Наша биграмма тратит 3,41 бита. Спускаться ещё долго, и каждая следующая глава проведёт нас на несколько ступенек ниже.
В PyTorch вся формула — одна функция, F.cross_entropy. Она принимает «логиты» и правильные ответы. С логитами и softmax мы познакомимся в следующей главе, а пока достаточно того, что логиты нашей таблицы — просто логарифмы её вероятностей, ровно то, что bigram() сохранила в файл модели.
2,3586 — то же среднее удивление, что напечатала запускаемая ячейка выше (там оно округлено до 2,359), только теперь его посчитала функция, которая будет обучать все модели начиная с главы 4.
Модель дала какой-то букве вероятность ровно 0, а потом эта буква встретилась в тексте. Что станет с функцией потерь?
Одно бесконечное слагаемое делает бесконечным всё среднее. Поэтому таблица Ростка добавляет единицу в каждую клетку, а настоящие нейросети никогда не выдают точный ноль: softmax, с которым мы встретимся в следующей главе, всегда оставляет каждому токену хоть немного вероятности.
Росток сейчас
Росток — та же буквенная таблица, что и в прошлой главе, но теперь мы умеем её измерить: 2,364 ната удивления на букву на тексте, которого она никогда не видела, — как будто каждую букву она выбирает из десяти с половиной вариантов. Это число и надо побить. В следующей главе мы выбросим подсчёт: начнём с таблицы случайных чисел и будем учить её, спускаясь с горы функции потерь. И вы увидите, как она придёт ровно к тому числу, которое подсчёт даёт на обучающем тексте, — 2,365. А пока проверьте, насколько Ростка удивит ваша собственная фраза: