Перейти к содержимому
ПереводыАндрей Карпатый

Zero to Hero, лекция 2: языковая модель на биграммах (makemore, часть 1)

Подробный конспект лекции Карпатого: символьная модель имён, построенная двумя способами — подсчётом биграмм и однослойной нейросетью. Сэмплирование, отрицательное лог-правдоподобие, коварство broadcasting и сглаживание, которое оказывается регуляризацией.

  • makemore «делает ещё» того, что ему дали: обучается на 32 тысячах имён и придумывает новые. Внутри — символьная языковая модельязыковая модельEN language model (LM)Распределение вероятностей над последовательностями токенов. На практике — функция «контекст → вероятности следующего токена».: по предыдущим символам она предсказывает следующий. За серию лекций Карпатый проводит её от биграмм до трансформера уровня GPT-2.
  • Первая модель — биграммнаябиграммаEN bigramПара соседних токенов. Биграммная модель оценивает P(x_t | x_{t−1}) — таблица V × V.: смотрит только на один предыдущий символ. Её можно «обучить» обычным подсчётом: сколько раз за буквой ii шла буква jj. Потом строки таблицы нормируются в вероятности.
  • Качество модели меряется одним числом — средним отрицательным лог-правдоподобием (NLLотрицательное лог-правдоподобие (NLL)EN negative log-likelihood (NLL)−log P(данные | модель), обычно усреднённое по токенам. Для языковой модели совпадает с cross-entropy.). Для биграмм на всём наборе это около 2.45. Модель, где все 27 символов равновероятны, дала бы ln⁡27≈3.30\ln 27 \approx 3.30.
  • Вторая половина — та же модель как нейросеть: one-hot вход, один линейный слой 27×2727 \times 27, softmax и градиентный спуск по NLL. Сеть приходит к той же таблице и тому же loss. Сглаживание счётчиков оказывается L2-регуляризацией весов.
  • Главный посыл на всю серию: дальше меняться будет только прямой проход — как из прошлых символов получаются логиты. Softmax, NLL и градиентное обучение останутся прежними вплоть до трансформеров.

Курс повторяет эту лекцию почти шаг в шаг в уроке 4.1.2 «Биграммная модель на русских именах». Смотреть лучше так: урок 4.1.1 «Что такое языковая модель», потом лекция, потом урок 4.1.2. Он добавляет то, чего в лекции нет: честную проверку на отложенных именах и подбор силы сглаживания. Математика правдоподобия и softmax разобрана в уроке 1.3.2, а broadcasting — в уроке 0.3.2, его стоит освежить перед главой про эффективность (36:20).

Новичку не нужно запоминать детали отрисовки таблицы (18:21) и ветку про torch.tensor против torch.Tensor — достаточно усвоить вывод. Главы про broadcasting и про NLL пропускать нельзя.

Код лекции — ноутбук makemore_part1_bigrams.ipynb (MIT), файл names.txt — в репозитории karpathy/makemore (MIT). Нужны PyTorch и matplotlib, хватит CPU. Китайская учебная версия с переведёнными пояснениями — 002_makemore_Bigrams.

Таймкоды — из глав в описании видео.

ВремяГлава в оригиналеО чём
0:00introчто такое makemore и план серии
3:06reading and exploring the dataset32 033 имени, длины от 2 до 15
6:25exploring the bigrams in the datasetпары соседних символов, маркеры начала и конца
9:24counting bigrams in a python dictionaryподсчёт в словаре
12:48counting bigrams in a 2D torch tensor (“training the model”)таблица N в тензоре — это и есть «обучение»
18:21visualizing the bigram tensorкартинка таблицы через matplotlib
20:56deleting spurious (S) and (E) tokens in favor of a single . tokenодин служебный символ . вместо двух
24:03sampling from the modelгенерация имён через torch.multinomial
36:20efficiency! vectorized normalization of the rows, tensor broadcastingнормировка строк и ловушка broadcasting
50:14loss function (the negative log likelihood of the data under our model)правдоподобие, логарифм, NLL
1:00:50model smoothing with fake countsсглаживание «фальшивыми» счётчиками
1:03:00PART 2: the neural network approach: introта же задача в рамках нейросетей
1:05:27creating the bigram dataset for the neural netпары (вход, ответ)
1:10:01feeding integers into neural nets? one-hot encodingsone-hot кодирование
1:13:54the “neural net”: one linear layer of neurons implemented with matrix multiplicationслой как матричное умножение
1:18:46transforming neural net outputs into probabilities: the softmaxлогиты → вероятности
1:26:17summary, preview to next steps, reference to microgradсводка и связь с micrograd
1:35:52vectorized lossloss одной строкой
1:38:40backward and update, in PyTorchобратный проход и шаг
1:42:59putting everything togetherобучение на всех 228 тыс. биграммах
1:47:54note 1: one-hot encoding really just selects a row of the next Linear layer’s weight matrixone-hot × W = строка W
1:50:20note 2: model smoothing as regularization lossсглаживание = регуляризация
1:54:36sampling from the neural netгенерация из сети
1:56:20conclusionитоги

makemore — репозиторий Карпатого. Как и micrograd из первой лекции, он собирается в видео с нуля. Пример данных — names.txt: около 32 тысяч имён, которые Карпатый, по его словам, взял с правительственного сайта. Обученная модель выдаёт что-то похожее на имена, но новое. Карпатый в шутку предлагает искать так необычное имя для ребёнка.

Модель символьная: каждое имя — последовательность букв, и задача — угадать следующую. План серии, как его обозначает Карпатый: биграммы, «мешок слов» (bag of words), многослойный перцептрон, рекуррентные сети и трансформер, по сути эквивалентный GPT-2. Дальше он собирался перейти от символов к словам и к картинкам, в духе DALL·E и Stable Diffusion. Серия в итоге свернула к GPT и токенизатору, но начало именно такое.

Имён 32 033, самое короткое — 2 буквы, самое длинное — 15. Важное наблюдение: одно слово — это не один, а много обучающих примеров. Имя isabella сообщает, что имя часто начинается с i, что после i бывает s, после is — a и так далее. Ещё один пример спрятан в конце: после isabella имя заканчивается.

БиграммабиграммаEN bigramПара соседних токенов. Биграммная модель оценивает P(x_t | x_{t−1}) — таблица V × V. — два символа подряд. Биграммная модель забывает всю остальную историю и предсказывает следующий символ только по текущему. Модель очень слабая, но с неё удобно начать. Чтобы не потерять информацию о начале и конце, к имени приписываются служебные символы: сначала <S> и <E>, а потом один общий символ ..

Пары соседей Карпатый перебирает изящным приёмом: zip(w, w[1:]). Функция zip идёт по двум последовательностям параллельно и останавливается, когда короче из них кончится. Счётчики сначала живут в словаре. Самые частые биграммы в наборе: «n в конце имени» — 6763 раза, «a в конце» — 6640, a→n — 5438, «имя начинается с a» — 4410.

3. Таблица счётчиков в тензоре — это и есть обучение

Заголовок раздела «3. Таблица счётчиков в тензоре — это и есть обучение»

Словарь неудобен, поэтому счётчики переезжают в двумерный массив PyTorch: строка — первый символ, столбец — второй. Попутно Карпатый показывает основы torch.tensor: создание нулями, тип данных (dtype=torch.int32 для целых счётчиков вместо float32 по умолчанию), индексацию N[1, 3] += 1.

Сначала таблица была 28×2828 \times 28 — 26 букв плюс <S> и <E>. На картинке видно, что это расточительно: строка <E> целиком нулевая (после конца ничего не идёт), столбец <S> тоже (начало не бывает вторым символом). Поэтому остаётся один служебный символ . с номером 0, буквы получают номера 1–26, а таблица становится 27×2727 \times 27. Код из ноутбука лекции (MIT), комментарии наши:

import torch
words = open('names.txt', 'r').read().splitlines() # 32 033 имени, по одному в строке
chars = sorted(list(set(''.join(words)))) # 26 букв в алфавитном порядке
stoi = {s: i + 1 for i, s in enumerate(chars)} # буква → номер 1…26
stoi['.'] = 0 # служебный символ начала и конца
itos = {i: s for s, i in stoi.items()} # номер → буква
N = torch.zeros((27, 27), dtype=torch.int32)
for w in words:
chs = ['.'] + list(w) + ['.']
for ch1, ch2 in zip(chs, chs[1:]): # пары соседних символов
N[stoi[ch1], stoi[ch2]] += 1 # «обучение» — просто подсчёт

Нулевая строка N[0] — сколько имён начинается с каждой буквы. Последний столбец, N[:, 0], — сколько раз каждая буква стоит в конце. Всё остальное — какие буквы за какими следуют. В этой таблице уже есть всё, что нужно, чтобы генерировать имена.

Генерация идёт по шагам. Начинаем с ., берём её строку, превращаем счётчики в вероятности (делим на сумму строки) и случайно выбираем следующий символ по этим вероятностям. Затем берём строку выбранного символа, и так до тех пор, пока не выпадет . — конец имени.

Случайный выбор делает torch.multinomial: даёшь ему вероятности, он возвращает номера. Карпатый обращает внимание на две детали:

  • чтобы у всех получались одинаковые числа, используется генератор с фиксированным зерном: torch.Generator().manual_seed(2147483647);
  • аргумент replacement=True нужно указывать явно — по умолчанию он почему-то False.

На игрушечном примере из трёх вероятностей [0.61,0.30,0.09][0.61, 0.30, 0.09] в выборке видно примерно то же соотношение нулей, единиц и двоек. Первой буквой в ноутбуке выпадает m. Это правдоподобно: с m начинаются 2538 имён из 32 033, почти 8%.

g = torch.Generator().manual_seed(2147483647) # одинаковые случайные числа у всех
for _ in range(5):
out, ix = [], 0 # начинаем со служебного '.'
while True:
p = N[ix].float()
p = p / p.sum() # строка счётчиков → распределение
ix = torch.multinomial(p, num_samples=1, replacement=True, generator=g).item()
out.append(itos[ix])
if ix == 0: # выпал '.', имя закончилось
break
print(''.join(out))

В ноутбуке Карпатого получаются mor., axx., minaymoryles., kondlaisah., anchshizarie.. В свежих версиях PyTorch тот же код с тем же зерном выдаёт другие имена: у нас на PyTorch 2.14 вышли cexze., momasurailezitynn., konimittain., llayn., ka.. Важна не конкретика, а общий вид. Карпатый признаётся, что сам несколько минут проверял, не ошибся ли: результат выглядит ужасно. Но всё верно, просто биграмма — очень слабая модель. Когда она генерирует имя из одной буквы h, она не знает, что h стоит первой. Она видит только «был символ h», а после h конец имени вполне вероятен. Для сравнения Карпатый генерирует из необученной модели, где все 27 символов равновероятны. Получается полная каша, так что биграмма всё-таки чему-то научилась.

Подробнее о выборе следующего токена — жадном, с температурой, top-k и top-p — в уроке 4.1.3.

В цикле генерации строка нормируется заново на каждом шаге. Это расточительно, поэтому Карпатый один раз строит матрицу вероятностей P, где каждая строка уже нормирована. Заодно у него есть повод потренироваться с тензорами — дальше, до трансформеров, без этого не обойтись.

Нужно разделить каждую строку 27×2727 \times 27-матрицы на её сумму. P.sum(1, keepdim=True) даёт суммы строк формы 27×127 \times 1 — столбец. Можно ли делить матрицу 27×2727 \times 27 на столбец 27×127 \times 1? Это решают правила broadcastingbroadcasting (растягивание форм)EN broadcastingПравило, по которому NumPy и PyTorch выполняют операции над массивами разных форм без копирования данных.: формы выравниваются по правому краю, и в каждой позиции размеры должны совпадать, или один из них равен 1, или его нет вовсе. Здесь всё в порядке: единичная ось растягивается (копируется) 27 раз, и каждая строка делится на свою сумму.

Дальше Карпатый, по его словам, хочет «немного напугать». Уберите keepdim=True, и сумма станет вектором формы 2727. Правила снова выполнены: недостающая ось дописывается слева, вектор превращается в строку 1×271 \times 27 и копируется вниз. Код работает без ошибок, но делит каждый столбец, а не строку. Строки перестают суммироваться в единицу:

P = N.float()
good = P / P.sum(1, keepdim=True) # (27, 27) / (27, 1): каждая строка делится на свою сумму
bad = P / P.sum(1) # (27, 27) / (27,) → (1, 27): делятся столбцы!
print(good[0].sum().item()) # 1.0
print(bad[0].sum().item()) # около 7 — нулевая строка не нормирована

Его совет — относиться к broadcasting с уважением: читать документацию, проверять, в какую сторону идёт растягивание, иначе появятся очень тонкие, трудно находимые баги. В конце главы — мелочь про память: P /= ... делит на месте, без создания нового тензора, и это может быть быстрее. В курсе та же ловушка разобрана в предупреждении «Ловушка broadcasting: keepdim» урока 4.1.2.

6. Как оценить модель: отрицательное лог-правдоподобие

Заголовок раздела «6. Как оценить модель: отрицательное лог-правдоподобие»

Таблица P — это и есть параметры модели. Как одним числом сказать, хороша ли она? Карпатый смотрит, какую вероятность модель даёт каждой биграмме из обучающих имён. Ориентир: если бы модель ничего не знала, каждому из 27 символов досталось бы 1/27≈4%1/27 \approx 4\%. Всё, что выше, — выученное. В наборе встречаются биграммы с вероятностями 35–40%.

Дальше рассуждение из статистики, по шагам:

  1. Правдоподобие — произведение вероятностей всех биграмм из данных. Чем оно больше, тем лучше модель объясняет данные. Но произведение сотен тысяч чисел меньше единицы — исчезающе малое число.
  2. Поэтому берут логарифм: log⁡(abc)=log⁡a+log⁡b+log⁡c\log(abc) = \log a + \log b + \log c, произведение превращается в сумму. Логарифм монотонен, значит, максимизировать его — то же самое, что максимизировать правдоподобие. Логарифм вероятности равен 0 при вероятности 1 и уходит в минус бесконечность при вероятности 0.
  3. Функцию потерь принято минимизировать, поэтому знак меняют — получается отрицательное лог-правдоподобие.
  4. Для удобства его усредняют по числу биграмм.
NLL=−1N∑k=1Nlog⁡P(ck+1∣ck).\text{NLL} = -\frac{1}{N} \sum_{k=1}^{N} \log P(c_{k+1} \mid c_k).

Читается так: для каждой из NN биграмм берём вероятность, которую модель дала правильному следующему символу, логарифмируем, усредняем и меняем знак. Минимум NLL — ноль, тогда модель всегда уверенно права. На всех 228 146 биграммах набора получается около 2.454. Можно оценить и отдельное слово: имя andrej стоит в среднем около 3.04 на символ — дорого, потому что ej в данных редкость.

Допишите к имени букву: andrejq. Loss становится бесконечным. Биграмма jq в данных не встречалась ни разу, модель даёт ей вероятность ноль, а log⁡0=−∞\log 0 = -\infty. Совершенно правдоподобная строка объявлена невозможной.

Лечение простое — сглаживание: ко всем счётчикам добавляют одно и то же число, например единицу, P = (N + 1).float(). Чем больше добавка, тем ближе распределения к равномерным. Чем меньше, тем «острее» модель. С добавкой 1 у jq вероятность около 0.03%, и andrejq стоит конечные 3.48 на символ. На общем loss по набору сглаживание почти не сказывается: данных много.

Эта приём известен как сглаживание Лапласасглаживание Лапласа (add-one)EN Laplace (add-one) smoothingДобавить константу ко всем счётчикам, чтобы невиданные n-граммы не получали нулевую вероятность. Классический вариант — +1 (add-one), с произвольным α > 0 — сглаживание Лидстоуна (add-α); силу α подбирают на валидации.. Курс подбирает силу сглаживания на отложенных именах и показывает, почему без этого обойтись нельзя: в маленьком наборе русских имён невиданных биграмм очень много.

Подсчёт работает, но такой подход не масштабируется. С контекстом в два символа строк было бы 272=72927^2 = 729, с тремя — почти 20 тысяч, и счётчиков на каждую не хватит. Поэтому Карпатый переформулирует ту же биграммную задачу в терминах нейросетей. На вход — текущий символ, на выход — распределение следующего, качество — тот же NLL, обучение — градиентный спуск.

Данные. Из каждого имени получаются пары (вход, ответ). Из emma — пять примеров: . → e, e → m, m → m, m → a, a → ., в номерах xs = [0, 5, 13, 13, 1], ys = [5, 13, 13, 1, 0]. По пути Карпатый предупреждает про API: есть torch.tensor с маленькой буквы и класс torch.Tensor с большой. Первый сохраняет тип данных (целые останутся целыми), второй всегда делает float. Разницу пришлось искать в обсуждениях на форумах, документация её толком не объясняет. Совет — пользоваться маленьким tensor и привыкать читать документацию и обсуждения.

One-hot. Подать номер символа прямо в нейрон бессмысленно: веса умножаются на вход, и «13» не больше «5» ни в каком полезном смысле. Поэтому номер превращают в one-hotone-hot кодирование (унитарный код)EN one-hot encodingВектор длины V из нулей с единицей в позиции номера токена. Все one-hot векторы попарно ортогональны.-вектор длины 27: все нули, одна единица на месте номера. F.one_hot возвращает целые числа, их нужно явно привести к float.

Слой. Веса WW — матрица 27×2727 \times 27 из нормального распределения: 27 нейронов, у каждого 27 входов, без смещения и без нелинейности. Умножение xenc @ W формы (5,27)×(27,27)(5, 27) \times (27, 27) за один раз считает выходы всех 27 нейронов на всех пяти примерах. Элемент [3,13][3, 13] — скалярное произведение третьего примера на тринадцатый столбец WW.

Softmax. Выход слоя — произвольные числа со знаком, а нужны вероятности: положительные и с суммой 1. Карпатый предлагает считать эти числа логарифмами счётчиков. Их называют логитамилогитEN logitСтрого — логарифм шансов log(p / (1 − p)). В нейросетях логитами называют любые выходы до softmax или сигмоиды.. Экспонента делает их положительными «как бы счётчиками», а нормировка по строке — вероятностями. Две последние операции вместе и есть softmax.

flowchart LR
A["символ<br/>номер 5"] --> B["one-hot<br/>длина 27"]
B --> C["× W (27×27)<br/>логиты"]
C --> D["exp<br/>«счётчики»"]
D --> E["÷ сумма строки<br/>вероятности"]
E --> F["−log p[ответ]<br/>среднее = loss"]
Биграммная модель как нейросеть: всё, кроме выбора строки и логарифма, дифференцируемо.

Loss и обучение. Нужные вероятности выдёргиваются одной индексацией: probs[torch.arange(5), ys] — в строке kk взять столбец ys[k]. Дальше всё как в micrograd: обнулить градиент (в PyTorch можно присвоить None, это эффективнее нуля), loss.backward(), шаг против градиента. PyTorch, как и micrograd, при прямом проходе записывает граф операций. Не забудьте requires_grad=True у весов. Код из ноутбука лекции (MIT), комментарии наши:

import torch.nn.functional as F
# все биграммы всех имён как пары (вход, ответ)
xs, ys = [], []
for w in words:
chs = ['.'] + list(w) + ['.']
for ch1, ch2 in zip(chs, chs[1:]):
xs.append(stoi[ch1])
ys.append(stoi[ch2])
xs, ys = torch.tensor(xs), torch.tensor(ys)
num = xs.nelement() # 228 146 примеров
g = torch.Generator().manual_seed(2147483647)
W = torch.randn((27, 27), generator=g, requires_grad=True)
for k in range(100):
xenc = F.one_hot(xs, num_classes=27).float() # вход: one-hot, приводим к float
logits = xenc @ W # «логарифмы счётчиков»
counts = logits.exp() # «счётчики», аналог N
probs = counts / counts.sum(1, keepdim=True) # две последние строки — softmax
loss = -probs[torch.arange(num), ys].log().mean() + 0.01 * (W**2).mean()
W.grad = None # обнулить градиент
loss.backward()
W.data += -50 * W.grad # да, learning rate = 50
print(loss.item()) # около 2.49 после 100 шагов (вместе со штрафом)

Сначала Карпатый учит сеть на пяти примерах из emma. Потом без единого изменения кода — на всех 228 тысячах биграмм. Задача настолько простая, что подходит огромный learning rate 50. Loss сходится к 2.45–2.46, то есть к тому же, что дал подсчёт. Так и должно быть: информации на входе столько же — один предыдущий символ. Просто для биграмм вероятности можно посчитать напрямую и хранить в таблице, а градиентный подход гибче. Его можно расширять: подавать больше символов и усложнять сеть, а таблица с длинным контекстом разрослась бы до бесконечности.

9. Две заметки, которые связывают обе половины

Заголовок раздела «9. Две заметки, которые связывают обе половины»

One-hot × W — это просто выбор строки. Если в one-hot векторе единица стоит на пятом месте, то умножение на WW вытаскивает пятую строку WW. Ровно это делала и модель на счётчиках: брала строку таблицы по номеру символа. Значит, после обучения WW — это логарифмы счётчиков, а eWe^W после нормировки — та же таблица P. Только получена она не подсчётом, а постепенно, следуя за градиентом loss.

Сглаживание — это регуляризация. Если все веса WW равны нулю, все логиты нулевые, экспоненты — единицы, и распределение равномерное. Значит, если тянуть WW к нулю, распределения будут ровнее, как при большом сглаживании. Это делает добавка к loss — штраф λ⋅mean(W2)\lambda \cdot \text{mean}(W^2), регуляризациярегуляризацияEN regularization. Карпатый описывает её как пружину или гравитацию, которая тянет веса к нулю, пока данные тянут их к своим вероятностям. Сила штрафа λ\lambda играет ту же роль, что величина фальшивых счётчиков. Курс разбирает обе заметки в разделе «Шаг 6» урока 4.1.2 и проверяет первую одной строкой: torch.allclose(X @ W, W[xs]).

Генерация меняется в одном месте: строка вероятностей берётся не из P[ix], а из сети — one-hot, умножение на WW, экспонента, нормировка. С тем же зерном генератора получаются те же имена, что из таблицы (mor., axx., minaymoryles.…). Это одна и та же модель, полученная двумя разными путями.

Итог лекции Карпатый формулирует как обещание. Сейчас нейросеть предельно проста: один символ и один линейный слой. Дальше входом станут несколько символов, а сети будут всё сложнее, вплоть до трансформеров. Но на выходе всегда будут логиты, а дальше — тот же softmax, тот же NLL и та же градиентная оптимизация.

Где курс объясняет то же самое и что есть сверх курса

Заголовок раздела «Где курс объясняет то же самое и что есть сверх курса»
Тема лекцииГде в курсеЧто в лекции сверх курса
Языковая модель, авторегрессия, n-граммы4.1.1план серии makemore
Биграммы подсчётом, генерация, NLL, сглаживание4.1.2 (242 русских имени)масштаб: 32 тыс. имён, 228 тыс. биграмм; сравнение с равномерной моделью
Сэмплирование и генераторы случайных чисел4.1.2, 4.1.3ловушка replacement=False по умолчанию
Broadcasting, keepdim0.3.2, 4.1.2разбор правил по документации PyTorch, деление на месте
Правдоподобие, логарифм, softmax, cross-entropy1.3.2—
One-hot, линейный слой, градиентное обучение биграммы4.1.2, 3.2.3torch.tensor против torch.Tensor, приведение типов one-hot
Сглаживание = L2-регуляризация4.1.2, модуль 2.1образ «пружины», которая тянет веса к нулю

Что есть в курсе и чего нет в лекции: разбиение на обучение и проверку. Карпатый считает loss только на обучающих данных, а в курсе сглаживание подбирается по отложенной выборке. В лекции это вынесено в упражнения — см. ниже.

Упражнения из описания видео (их копия с пояснениями на китайском — 002_makemore_Bigrams/bigram_exercises.ipynb в репозитории karpathy-zero-to-hero-zh-transcripts):

  1. Триграммы. Обучите модель, которая по двум символам предсказывает третий, подсчётом или нейросетью. Насколько loss лучше, чем у биграммы?
  2. Честная проверка. Разбейте имена случайно на 80% обучения, 10% валидации и 10% теста. Обучите биграммы и триграммы только на обучающей части и оцените на двух остальных. Что вы видите?
  3. Подбор сглаживания. По валидации подберите силу сглаживания (или регуляризации) для триграммной модели. Как при этом ведут себя loss на обучении и на валидации? Лучшую модель один раз оцените на тесте.
  4. Без one-hot. Раз умножение на one-hot просто выбирает строку, уберите F.one_hot и индексируйте W[xs].
  5. F.cross_entropy. Посчитайте loss через torch.nn.functional.cross_entropy и сравните с ручным. Почему на практике лучше встроенная функция? (Ответ Карпатый даёт в следующей лекции.)
  6. Своя тема. Придумайте и сделайте что-нибудь интересное сами — например, обучите ту же модель на русских именах из урока 4.1.2 и сравните loss с английскими.
ENRU
character-level language modelсимвольная языковая модель
bigramбиграмма
special token (start / end)служебный токен (начала / конца)
counts, normalizeсчётчики, нормировать
sampling, torch.multinomial, generator, seedсэмплирование, генератор, зерно
broadcasting, keepdimbroadcasting (растягивание форм), сохранить ось
in-place operationоперация на месте
likelihood, log likelihoodправдоподобие, лог-правдоподобие
negative log likelihood (NLL)отрицательное лог-правдоподобие
maximum likelihood estimationметод максимального правдоподобия
model smoothing, fake countsсглаживание модели, фальшивые счётчики
one-hot encodingone-hot кодирование
logits (log-counts)логиты («логарифмы счётчиков»)
softmaxsoftmax
regularization lossштраф регуляризации