# Строим GPT с нуля: пошаговое руководство по архитектуре Transformer

## Метаданные

- **Спикер:** Андрей Карпати
- **Канал:** Andrej Karpathy
- **Тема:** Пошаговая реализация GPT-модели на Python с нуля — от биграммной языковой модели до полноценного Transformer с self-attention, multi-head attention и генерацией текста.
- **Длительность:** 1:56:20
- **YouTube:** https://www.youtube.com/watch?v=kCc8FmEb1nY
- **Источник:** https://ekstraktznaniy.ru/workbook/460

## Ключевые тезисы

1. **ChatGPT — это языковая модель, которая предсказывает следующий токен** — В основе ChatGPT лежит простой принцип: модель получает последовательность токенов и предсказывает, какой токен наиболее вероятен следующим. Это вероятностная система — для одного и того же запроса она может давать разные ответы. Всё «волшебство» — результат обучения на огромных объёмах текста.
2. **Transformer — единственная архитектура, которая имеет значение** — Архитектура Transformer из статьи «Attention Is All You Need» (2017) с минимальными изменениями была скопирована практически во все области ИИ за последние годы. GPT расшифровывается как Generatively Pre-trained Transformer — именно Transformer выполняет всю тяжёлую работу внутри модели.
3. **Токенизация — перевод текста в числа и обратно** — Перед обработкой текст нужно превратить в последовательность целых чисел. Существуют разные схемы: посимвольная (65 токенов для Шекспира), subword-уровень (SentencePiece, tiktoken с ~50 000 токенов). Компромисс: маленький словарь — длинные последовательности, большой словарь — короткие последовательности.
4. **Данные подаются батчами из случайных чанков фиксированной длины** — Нельзя подать весь текст в Transformer целиком. Вместо этого из обучающей выборки случайно выбираются чанки длиной block_size. В одном чанке из 9 символов упаковано 8 обучающих примеров — каждая позиция учит модель предсказывать следующий символ по контексту разной длины от 1 до block_size.
5. **Биграммная модель — простейший, но работающий baseline** — Самая простая языковая модель: каждый токен предсказывает следующий только на основе собственной идентичности, без учёта контекста. Реализуется через таблицу эмбеддингов vocab_size × vocab_size. Даже такая модель даёт осмысленные результаты после обучения — loss снижается с 4.87 до ~2.5.
6. **Self-attention — механизм коммуникации между токенами через Query, Key, Value** — Каждый токен генерирует три вектора: Query (что я ищу), Key (что я содержу) и Value (что я сообщу). Скалярное произведение Query с Key всех предыдущих токенов даёт веса внимания — насколько каждый токен «интересен» текущему. Затем по этим весам агрегируются Value-векторы. Это data-dependent взвешенное усреднение.
7. **Матричное умножение с нижнетреугольной маской — ключевой трюк реализации** — Вместо наивного цикла усреднения предыдущих токенов используется умножение на нижнетреугольную матрицу. Маскирование будущих позиций через -∞ перед softmax гарантирует, что токены не получают информацию из будущего. Это делает вычисления параллельными и эффективными на GPU.
8. **Multi-head attention — несколько каналов коммуникации параллельно** — Вместо одного большого attention используется несколько «голов» с меньшей размерностью, результаты которых конкатенируются. Это позволяет токенам одновременно искать разные типы информации: гласные, согласные, позиции и т.д. Аналогично групповым свёрткам в CNN.
9. **Transformer-блок = коммуникация (attention) + вычисление (feedforward)** — Полный блок Transformer чередует два типа операций: multi-head self-attention для обмена информацией между токенами и feedforward MLP для индивидуальной обработки каждого токена. Attention — «собрать данные», feedforward — «подумать над собранным». Блоки повторяются N раз.
10. **Residual connections и LayerNorm — необходимы для обучения глубоких сетей** — Residual connections (skip connections) создают «магистраль» для градиентов через глубокую сеть: x = x + block(x). LayerNorm нормализует активации перед каждым подблоком (pre-norm формулировка). Без этих техник глубокие Transformer-сети просто не обучаются.
11. **Scaled attention: деление на √d_k предотвращает схлопывание softmax** — Без масштабирования дисперсия скалярных произведений Q·K растёт пропорционально размерности head_size. Большие значения делают softmax «острым» — модель фокусируется на одном токене вместо мягкого распределения. Деление на √head_size сохраняет дисперсию около 1 при инициализации.
12. **Dropout — регуляризация через случайное «выключение» нейронов** — Dropout случайным образом зануляет часть активаций во время обучения. Применяется после attention-весов и в feedforward-слоях. Это предотвращает переобучение и действует как ансамблирование подсетей. При инференсе dropout отключается.

## Практические задания

### Задание 1: Реализовать посимвольный токенизатор
**Цель:** Напишите с нуля функции encode() и decode() для посимвольной токенизации. Возьмите любой текстовый файл, постройте словарь уникальных символов, создайте маппинг символ→число и число→символ. Проверьте, что decode(encode(text)) == text. Сравните длину закодированной последовательности с результатом tiktoken.

### Задание 2: Реализовать взвешенное усреднение через матричное умножение
**Цель:** Создайте случайный тензор X размера (B, T, C) = (4, 8, 2). Реализуйте три версии усреднения предыдущих токенов: 1) наивный цикл for, 2) умножение на нижнетреугольную матрицу с нормализацией строк, 3) через masked_fill(-inf) + softmax. Убедитесь с помощью torch.allclose, что все три версии дают одинаковый результат.

### Задание 3: Построить и обучить биграммную модель
**Цель:** Реализуйте BigramLanguageModel как подкласс nn.Module с одной таблицей эмбеддингов. Обучите на tiny Shakespeare с batch_size=32 и learning_rate=1e-3 на 10 000 итераций. Достигните loss ~2.5. Сгенерируйте 500 символов и оцените качество. Сравните начальный loss с теоретическим -ln(1/65) ≈ 4.17.

### Задание 4: Реализовать single-head self-attention
**Цель:** Добавьте к биграммной модели один head самовнимания: создайте линейные проекции для Q, K, V с head_size=32. Реализуйте масштабирование (÷√head_size), маскирование будущих позиций и softmax. Обучите модель и убедитесь, что loss снизился с ~2.5 до ~2.4. Визуализируйте матрицу attention-весов для одного примера.

### Задание 5: Собрать полный Transformer-блок
**Цель:** Соберите TransformerBlock, включающий: MultiHeadAttention (4 головы по 8 измерений), FeedForward (линейный слой с расширением ×4 и ReLU), residual connections и LayerNorm. Сложите 4 таких блока. Обучите на tiny Shakespeare — целевой val_loss ~1.48. Сгенерируйте 500 символов и сравните с биграммной моделью.

### Задание 6: Эксперименты с гиперпараметрами
**Цель:** Проведите серию экспериментов: измените количество голов (1, 4, 8), глубину (2, 4, 6 блоков), n_embd (32, 64, 128), dropout (0.0, 0.2, 0.4). Для каждого варианта зафиксируйте train_loss, val_loss и качество генерации. Постройте таблицу результатов и определите, какой параметр влияет сильнее всего.

### Задание 7: Перенести модель на собственный датасет
**Цель:** Возьмите текст на русском языке (минимум 500 КБ — например, произведения из lib.ru). Адаптируйте токенизатор под новый набор символов (кириллица + знаки препинания). Обучите Transformer и сгенерируйте текст. Оцените, насколько модель уловила структуру русского языка по сравнению с английским Шекспиром.

## Ключевые цитаты

> «Attention is really just a communication mechanism. You can think about it as nodes in a directed graph, where every node has some vector of information and it gets to aggregate information via a weighted sum from all of the nodes that point to it.»
> — Андрей Карпати

> «The query vector roughly speaking is 'what am I looking for' and the key vector roughly speaking is 'what do I contain', and then we get affinities between tokens by doing a dot product between the keys and the queries.»
> — Андрей Карпати

> «If you have unit Gaussian inputs, and you just do the dot product naively, your variance will be on the order of head_size. But if you multiply by one over square root of head_size, the variance will be preserved at one.»
> — Андрей Карпати

> «These tokens have a lot to talk about — they want to find the consonants, the vowels, vowels from certain positions — and so it helps to create multiple independent channels of communication.»
> — Андрей Карпати

> «Self-attention does the communication, the feedforward does the computation. The tokens look at each other, but then they need to think about what they found.»
> — Андрей Карпати

> «Residual connections are these skip connections — instead of just going through the block, you fork off, do some computation, and come back. This is extremely important for training deep networks because gradients flow directly through the residual pathway.»
> — Андрей Карпати

> «In a chunk of nine characters there's actually eight individual examples packed in there. We train on all these examples with context between one all the way up to block_size.»
> — Андрей Карпати

> «We don't want these values to be too extreme especially at initialization, otherwise softmax will be way too peaky and you're basically aggregating information from a single node — that's not what we want.»
> — Андрей Карпати

## Полный текст экстракта

# Строим GPT с нуля: пошаговое руководство по архитектуре Transformer

> Спикер: Андрей Карпати | Длительность: 1:56:20

## Ключевые идеи

1. **ChatGPT — это языковая модель** — система предсказывает следующий токен в последовательности. Это вероятностная модель: один и тот же запрос может дать разные ответы.

2. **Transformer — единственная архитектура, которая имеет значение** — из статьи «Attention Is All You Need» (2017), с минимальными изменениями захватила все области ИИ.

3. **Токенизация: текст → числа** — существуют разные схемы (посимвольная, subword, BPE). Компромисс между размером словаря и длиной последовательности.

4. **Данные подаются батчами случайных чанков** — в одном чанке длиной T+1 упаковано T обучающих примеров с контекстами от 1 до T символов.

5. **Биграммная модель — простейший baseline** — предсказание только по последнему токену, без контекста. Loss ~2.5 на Shakespeare.

6. **Self-attention через Query, Key, Value** — каждый токен генерирует Q (что ищу), K (что содержу), V (что сообщу). Скалярное произведение Q·K даёт веса, по которым агрегируются V.

7. **Матричный трюк: нижнетреугольная маска** — маскирование будущих позиций через -∞ + softmax. Параллельные вычисления вместо циклов.

8. **Multi-head attention** — несколько параллельных каналов коммуникации. 4 головы по 8 измерений вместо 1 головы на 32.

9. **Transformer-блок = attention + feedforward** — attention собирает информацию, feedforward обрабатывает собранное. Блоки повторяются N раз.

10. **Residual connections + LayerNorm** — обязательны для обучения глубоких сетей. Residual создают «магистраль» для градиентов, LayerNorm стабилизирует активации.

11. **Scaled attention** — деление на √d_k сохраняет дисперсию ~1 при инициализации, предотвращая схлопывание softmax в one-hot.

12. **Dropout** — регуляризация через случайное зануление активаций. Применяется в attention-весах и feedforward.

## Транскрипт

### Введение: ChatGPT и Transformer

Андрей Карпати начинает с демонстрации ChatGPT — системы, которая генерирует текст слева направо, токен за токеном. Это языковая модель: она моделирует последовательности слов и знает, как слова следуют друг за другом в языке. Под капотом работает архитектура Transformer из статьи «Attention Is All You Need» (2017).

Цель лекции — построить Transformer-модель с нуля, обучить её на корпусе Шекспира (tiny Shakespeare, ~1 МБ) и научить генерировать «шекспироподобный» текст посимвольно. Весь код доступен в репозитории nanoGPT.

### Подготовка данных и токенизация

Датасет — все произведения Шекспира в одном файле (~1 млн символов). Словарь — 65 уникальных символов (буквы, пробелы, знаки препинания). Создаются функции encode (строка → список чисел) и decode (обратно).

Обсуждаются альтернативные токенизаторы: SentencePiece (Google), tiktoken (OpenAI, ~50 000 токенов для GPT-2). Компромисс: маленький словарь = длинные последовательности и наоборот.

Данные разбиваются на train (90%) и val (10%) для контроля переобучения.

### DataLoader: батчи чанков данных

Вводятся ключевые параметры: block_size (максимальная длина контекста, начинаем с 8) и batch_size (количество параллельных последовательностей, 4). В чанке из 9 символов упаковано 8 обучающих примеров — модель учится предсказывать по контексту от 1 до 8 символов.

Функция get_batch генерирует случайные смещения в массиве данных, вырезает чанки и складывает в тензор (B, T). Итого 32 независимых примера в одном батче.

### Биграммная языковая модель

Простейшая модель: таблица эмбеддингов vocab_size × vocab_size. Каждый токен по своему индексу извлекает строку из таблицы — это logits (оценки) для следующего символа. Токены не взаимодействуют.

Loss измеряется через cross-entropy. Начальный loss ~4.87 (теоретический при равномерном распределении: -ln(1/65) ≈ 4.17). После 10 000 шагов обучения с AdamW loss падает до ~2.5. Генерация — авторегрессивная: softmax → multinomial sampling → конкатенация.

### Математический трюк: матричное умножение как взвешенное усреднение

Три версии одной операции — усреднение предыдущих токенов:

**Версия 1 (цикл for):** Для каждой позиции t вычисляем среднее всех предыдущих векторов. Работает, но медленно.

**Версия 2 (матричное умножение):** Создаём нижнетреугольную матрицу (T×T), нормализуем строки, умножаем на X. Результат идентичен, но параллельный.

**Версия 3 (softmax):** Начинаем с нулей, заполняем верхний треугольник значением -∞, применяем softmax. Получаем те же нормализованные веса. Этот вариант обобщается: нули можно заменить на data-dependent значения — и это уже self-attention.

### Self-Attention: ключевой механизм

Каждый токен генерирует три вектора через линейные проекции:
- **Query (Q):** «что я ищу»
- **Key (K):** «что я содержу»
- **Value (V):** «что я сообщу, если вы меня найдёте»

Афинность между токенами = Q · Kᵀ. Масштабирование: делим на √head_size, чтобы сохранить дисперсию ~1. Маскирование будущих позиций через -∞. Softmax → нормализованные веса. Агрегация: weights · V.

Важные замечания:
- Attention — механизм коммуникации на направленном графе
- Нет понятия пространства — нужны позиционные эмбеддинги
- Батчи обрабатываются независимо
- Encoder-блоки (полная связность) vs decoder-блоки (маскирование будущего)
- Self-attention (Q, K, V из одного источника) vs cross-attention (K, V из внешнего источника)

### Multi-Head Attention

Вместо одной «головы» с head_size=32 используются 4 головы по 8. Каждая голова ищет свой тип паттернов. Результаты конкатенируются обратно в 32-мерный вектор. Loss снижается с 2.4 до 2.28.

### FeedForward и Transformer-блок

FeedForward — это MLP: Linear → ReLU → Linear. Применяется к каждому токену независимо (per-node computation). Attention собрал информацию, feedforward её обработал.

Полный блок: MultiHeadAttention → residual + LayerNorm → FeedForward → residual + LayerNorm. Блоки повторяются N раз (в финальной модели — 4-6).

### Residual Connections и LayerNorm

Residual connections: x = x + block(x). Создают прямой путь для градиентов через глубокую сеть. Без них глубокие Transformer-ы не обучаются.

LayerNorm нормализует активации по последнему измерению. Используется pre-norm формулировка (LayerNorm до attention/feedforward), что отличается от оригинальной статьи.

### Dropout и финальная модель

Dropout добавляется после softmax в attention и в feedforward-слоях. Регуляризация через случайное зануление нейронов во время обучения.

Финальная модель: n_embd=384, 6 блоков, 6 голов, dropout=0.2. Val_loss ~1.48. Генерация выдаёт текст, стилистически похожий на Шекспира с правильной структурой диалогов.

### Масштабирование до GPT и ChatGPT

Путь от nanoGPT до ChatGPT:
1. **Pre-training:** обучение на интернет-данных (предсказание следующего токена)
2. **Fine-tuning:** обучение на примерах «вопрос → ответ»
3. **RLHF:** обучение с подкреплением на основе человеческих оценок

nanoGPT — это только первый этап. Fine-tuning и RLHF превращают «генератор текста» в «полезного ассистента».

## Практические задания

### Задание 1: Реализовать посимвольный токенизатор
Напишите с нуля функции encode() и decode() для посимвольной токенизации. Возьмите любой текстовый файл, постройте словарь уникальных символов, создайте маппинг символ→число и число→символ. Проверьте, что decode(encode(text)) == text. Сравните длину закодированной последовательности с результатом tiktoken.

### Задание 2: Реализовать взвешенное усреднение через матричное умножение
Создайте случайный тензор X размера (B, T, C) = (4, 8, 2). Реализуйте три версии усреднения предыдущих токенов: наивный цикл, умножение на нижнетреугольную матрицу и masked_fill + softmax. Убедитесь через torch.allclose, что все три версии дают идентичный результат.

### Задание 3: Построить и обучить биграммную модель
Реализуйте BigramLanguageModel как подкласс nn.Module. Обучите на tiny Shakespeare с batch_size=32 и lr=1e-3 на 10 000 итераций. Достигните loss ~2.5. Сгенерируйте 500 символов и оцените качество.

### Задание 4: Реализовать single-head self-attention
Добавьте head самовнимания с линейными проекциями Q, K, V. Реализуйте масштабирование, маскирование, softmax и агрегацию V. Обучите и убедитесь в снижении loss с ~2.5 до ~2.4.

### Задание 5: Собрать полный Transformer-блок
Объедините MultiHeadAttention + FeedForward + residual + LayerNorm в TransformerBlock. Сложите 4 блока, добавьте dropout. Целевой val_loss ~1.48.

### Задание 6: Эксперименты с гиперпараметрами
Измените количество голов (1, 4, 8), глубину (2, 4, 6), n_embd (32, 64, 128), dropout (0.0, 0.2, 0.4). Постройте таблицу результатов.

### Задание 7: Перенести на собственный датасет
Возьмите текст на русском языке (≥500 КБ). Адаптируйте токенизатор, обучите Transformer, оцените качество генерации.

## Лучшие цитаты

> «Attention is really just a communication mechanism. You can think about it as nodes in a directed graph, where every node has some vector of information and it gets to aggregate information via a weighted sum from all of the nodes that point to it.» — Андрей Карпати

> «The query vector roughly speaking is 'what am I looking for' and the key vector roughly speaking is 'what do I contain'.» — Андрей Карпати

> «These tokens have a lot to talk about — they want to find the consonants, the vowels, vowels from certain positions — and so it helps to create multiple independent channels of communication.» — Андрей Карпати

> «Self-attention does the communication, the feedforward does the computation. The tokens look at each other, but then they need to think about what they found.» — Андрей Карпати

> «We don't want these values to be too extreme especially at initialization, otherwise softmax will be way too peaky and you're basically aggregating information from a single node — that's not what we want.» — Андрей Карпати

> «Residual connections are these skip connections — instead of just going through the block, you fork off, do some computation, and come back. This is extremely important for training deep networks.» — Андрей Карпати

> «In a chunk of nine characters there's actually eight individual examples packed in there.» — Андрей Карпати

> «If you have unit Gaussian inputs and you just do the dot product naively, your variance will be on the order of head_size. But if you multiply by one over square root of head_size, the variance will be preserved at one.» — Андрей Карпати