GPT для самых маленьких: как машина учится говорить — Блог
$ cat learn/blogopost/popular-microgpt-guide.md

GPT для самых маленьких: как машина учится говорить

Введение: Задача

Представьте: вы хотите научить ребёнка писать имена. Даёте ему 3200 имён — «Антон», «Мария», «Дмитрий». После этого ребёнок должен сам придумывать новые имена, которые похожи на настоящие.

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

Эта статья — прогулка от простого к сложному. Каждый шаг появится потому, что он необходим для следующего. Никаких терминов вперёд их определения.


Шаг 1. Превращаем буквы в числа

Проблема: Компьютер понимает только числа. Как сказать ему «буква а»?

Решение: Дадим каждой букве номер. «а» = 0, «б» = 1, … «я» = 32. Это токенизация — превращение текста в последовательность чисел.

Теперь имя «мария» — это [15, 0, 17, 9, 33, 11]. Удобно для компьютера.

Но одного номера недостаточно. Нужно знать ещё кое-что…


Шаг 2. Представляем каждую букву как набор чисел

Проблема: Одним номером не передать «характер» буквы. Чем «а» отличается от «о» кроме номера?

Решение: Заменим один номер на вектор — набор из нескольких чисел. Например, 16 чисел для каждой буквы.

Буква «а» = [0.1, -0.3, 0.5, ...] (16 чисел) Буква «о» = [0.8, 0.2, -0.1, ...] (16 чисел)

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


Шаг 3. Компьютер должен знать, где буква стоит

Проблема: Буква «с» в начале слова и в конце — разные. «Слон» и «олос» — разные слова, хотя буквы те же.

Решение: Добавим к эмбеддингу буквы ещё и позицию. Первая буква = один вектор, вторая = другой.

Если «а» на позиции 1: [эмбеддинг_а] + [позиция_1] Если «а» на позиции 2: [эмбеддинг_а] + [позиция_2]

Теперь программа знает и что (буква), и где (позиция).


Шаг 4. Как сделать из букв «мнение» о следующей букве?

Проблема: У нас есть векторы для букв. Как превратить их в предсказание «какая буква будет следующей»?

Решение: Пропустим вектор через простейшую нейросеть — набор математических операций:

вектор → [умножение на весы] → [сложение] → [функция активации] → ... → [предсказание]

Это MLP (Multilayer Perceptron) — «мозг» из двух слоёв. Первый слой расширяет вектор (даёт больше «пространства для мысли»), второй — сжимает обратно к размеру словаря (32 буквы = 32 числа, логиты).


Шаг 5. Как учесть все предыдущие буквы, а не только последнюю?

Проблема: Чтобы предсказать следующую букву, нужно знать все предыдущие. Одной последней буквы недостаточно.

Решение: Используем механизм внимания — способ «посмотреть» на все предыдущие буквы и решить, на какие обратить внимание.

Это как читатель, который держит в голове предыдущие страницы романа и понимает, что «Иван» в главе 5 — это тот же персонаж, что и Иван из главы 1.

Как работает внимание (упрощённо):

  1. Для каждой предыдущей буквы вычисляем: «насколько она релевантна для текущей позиции»
  2. Чем больше релевантность, тем больше вес при усреднении
  3. Суммируем все предыдущие буквы с этими весами

Это self-attention — механизм, который сделал трансформеры такими мощными.


Шаг 6. Зачем нужно несколько «голов» внимания?

Проблема: Одно внимание думает только об одном. А нужно учитывать разные аспекты: грамматику, смысл, стиль.

Решение: Делаем несколько вниманий параллельно. Каждое — head. Одно смотрит на соседние буквы, другое — на удалённые, третье — на гласные/согласные.

Это multi-head attention — «мнение» нескольких экспертов, которые потом объединяются.


Шаг 7. Как из логитов получить вероятности?

Проблема: MLP выдаёт 32 числа (логиты). Как превратить их в «наиболее вероятную следующую букву»?

Решение: Используем softmax — функция, которая превращает любые числа в вероятности (сумма = 1).

Логиты: [3.2, 1.5, 0.1, ...]
Softmax: [0.85, 0.12, 0.03, ...]  (сумма = 1)

Теперь «а» с вероятностью 85%, «б» — 12%, и т.д.


Шаг 8. Как измерить ошибку?

Проблема: Компьютер предсказал букву «б» с вероятностью 85%, а настоящая следующая буква — «а». Как сказать компьютеру «ты ошибся»?

Решение: Используем loss — функцию потерь. Для ��ероятностей удобно использовать -log(вероятность_правильной_буквы):

  • Предсказал правильно с p=0.85 → loss = -log(0.85) = 0.16 (маленькая ошибка)
  • Предсказал правильно с p=0.01 → loss = -log(0.01) = 4.6 (большая ошибка)
  • Предсказал неправильно → loss огромный

Шаг 9. Как компьютер понимает, какие веса менять?

Проблема: Мы знаем ошибку (loss). Но как сказать конкретным весам в MLP, насколько они виноваты?

Решение: Используем автоматическое дифференцирование (autograd) — техника, которая автоматически вычисляет, как каждый параметр влияет на ошибку.

Это как:

  1. Пройти от входа к выходу, запоминая все операции (forward pass)
  2. Пройти от ошибки обратно к входу, вычисляя производные (backward pass)
  3. Каждый параметр получает градиент — «насколько увеличить/уменьшить»

В MicroGPT это класс Value на 40 строк кода.


Шаг 10. Как обновлять веса?

Проблема: У нас есть градиенты для всех параметров. Как правильно изменить веса, чтобы уменьшить ошибку?

Решение: Используем Adam — популярный оптимизатор. Его идея:

  • Запоминает, каким был градиент в среднем (первый момент)
  • Запоминает, насколько большими были градиенты (второй момент)
  • Большие градиенты → маленький шаг, маленькие → большой шаг

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


Шаг 11. Как генерировать новые имена?

Проблема: Модель обучена. Как заставить её придумывать новые имена?

Решение: Начинаем с специального токена BOS (начало последовательности):

  1. Предсказываем следующую букву (softmax)
  2. Выбираем букву по вероятностям (семплирование)
  3. Добавляем к последовательности
  4. Повторяем, пока не получим BOS или лимит длины

Температура контролирует случайность:

  • Низкая (0.1) — всегда выбираем самую вероятную букву, текст повторяется
  • Высокая (1.0) — больше случайности, интересные но странные имена

Полная картина: Как это работает вместе

Теперь соберём всё вместе:

Вход: "мария" → [15, 0, 17, 9, 33, 11, BOS]

1. Токенизация: числа вместо букв
2. Эмбеддинги: числа → векторы
3. Позиционные эмбеддинги: добавляем позицию
4. RMSNorm: нормализуем (стабилизируем)
5. Multi-head attention: смотрим на все предыдущие буквы
6. Остаточная связь: добавляем исходный вектор (чтобы не терять информацию)
7. MLP: ��реобразуем в «мнение» о следующей букве
8. Softmax: получаем вероятности
9. Loss: измеряем ошибку
10. Autograd: вычисляем градиенты
11. Adam: обновляем веса

Повторяем 1000 раз — и модель выучивает статистику имён.


Заключение

Вот и весь GPT — несколько компонентов, собранных вместе:

  1. Токенизация → превращаем текст в числа
  2. Эмбеддинги → превращаем числа в векторы
  3. Позиционные эмбеддинги → добавляем информацию о месте
  4. Multi-head attention → «смотрим» на предыдущий контекст
  5. MLP → «думаем» о том, что увидели
  6. Softmax → превращаем в вероятности
  7. Loss → измеряем ошибку
  8. Autograd → вычисляем градиенты
  9. Adam → обновляем параметры
  10. Генерация → создаём новое

Каждый компонент необходим для следующего. Это не магия — это последовательная архитектура, построенная на ясных идеях.

Что изучить дальше:

  • Попробуйте запустить microgpt.py и посмотреть, какие имена генерируются
  • Поизменяйте параметры: n_embd, n_layer, learning_rate
  • Прочитайте «Neural Networks: Zero to Hero» Карпати

Статья написана для полного понимания архитектуры GPT с нуля.