Вы прочитали 1 из 3 бесплатных методичек сегодня
Экстракт 17 января 2023

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

Андрей Карпати · Andrej Karpathy Верифицирован 1:56:20

Пошаговая реализация GPT-модели на Python с нуля — от биграммной языковой модели до полноценного Transformer с self-attention, multi-head attention и генерацией текста.

12 тезисов 7 заданий 8 цитат ⏱ 7 мин чтения 🎯 12 тезисов
YouTube Транскрипт Сохранить
Поделиться: TG WA VK X

Для AI-агентов и LLM

Экстракт доступен в структурированном Markdown. Скачать .md · JSON API · Site index

💡 Ключевые тезисы (12)

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 отключается.

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

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

Транскрипт

Введение: 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.» — Андрей Карпати

🏋️ Практикум

0 / 7 выполнено

Реализовать посимвольный токенизатор

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

Реализовать взвешенное усреднение через матричное умножение

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

Построить и обучить биграммную модель

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

Реализовать single-head self-attention

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

Собрать полный Transformer-блок

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

Эксперименты с гиперпараметрами

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

Перенести модель на собственный датасет

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

🎉
Все задания выполнены!
Отлично — знания превращены в навыки

💬 Цитаты (8)

«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.» #

Читать далее

Как использовать LLM на максимум: практическое руководство от Андрея Карпати

Andrej Karpathy

Как использовать LLM на максимум: практическое руководство от Андрея Карпати

Андрей Карпати

Понравился экстракт?
Подписывайтесь — лучшие материалы каждую неделю.
Telegram Дайджест →

Поделитесь с коллегами

Telegram ВКонтакте X / Twitter
Открыть в Telegram

Экстракт Знаний в Telegram

Экстракты и дистилляты из лучших YouTube-каналов — сразу после публикации.

Подписаться

Дайджест Экстрактов

Лучшие методички за неделю — каждый понедельник