Машинное обучение
Seq2Seq и механизм Attention в NLP
Машинный перевод оставался мечтой с 1950-х годов - десятилетия rule-based подходов и статистических моделей давали посредственное качество. В 2014 году архитектура Seq2Seq с механизмом Attention достигла качества, близкого к человеческому. Ключевое открытие оказалось простым: нужно позволить модели самой решать, на какие части входного предложения смотреть при генерации каждого выходного слова.
- **Google Translate** - до 2016 года использовал Seq2Seq с Attention (GNMT), обрабатывая 140 миллиардов слов в день на 100+ языковых парах, и показал скачок качества на 60% по сравнению с предыдущей статистической системой
- **Автозамена на смартфонах** - клавиатуры Google Gboard и Apple iOS используют легковесные Seq2Seq модели для предсказания следующего слова и автокоррекции в real-time, работая локально без интернета
- **Чат-боты и диалоговые системы** - Seq2Seq стал основой для генерации ответов в диалоговых AI, от простых FAQ-ботов до голосовых ассистентов, где входная последовательность (вопрос) преобразуется в выходную (ответ)
Предварительные знания
Обучение последовательность-в-последовательность и рождение внимания
В 2014 году команда Google Brain в составе Ильи Суцкевера, Ориола Виньялса и Куок Ле опубликовала работу "Sequence to Sequence Learning with Neural Networks". Идея была обманчиво простой: одна LSTM читает входное предложение и сжимает его в один вектор фиксированной длины, а вторая LSTM разворачивает из него выход по одному токену. Это работало достаточно хорошо, чтобы соперничать со статистическим машинным переводом тех лет, и шаблон энкодер-декодер быстро распространился на перевод, суммаризацию и диалог. В том же году Дмитрий Богданов вместе с Кёнхён Чо и Йошуа Бенжио в Монреале нашёл слабое место: упаковка целого предложения в один вектор теряла информацию на длинных входах. Их ответом стал механизм внимания, позволивший декодеру смотреть на все состояния энкодера и взвешивать их на каждом шаге. Внимание убрало бутылочное горлышко, а через три года легло в основу Трансформера.
Encoder-Decoder для NLP
Задачи вроде машинного перевода имеют фундаментальную особенность: длина входной последовательности **не совпадает** с длиной выходной. "Я люблю кошек" (3 слова) переводится как "I love cats" (3 слова), но "Мне нравится гулять" (3 слова) становится "I like to walk" (4 слова). Обычная RNN принимает и выдаёт последовательности одинаковой длины. Архитектура **Encoder-Decoder** (Seq2Seq) решает эту проблему, разделяя модель на две части: encoder читает вход целиком, а decoder генерирует выход произвольной длины.
**Encoder** - это RNN (обычно LSTM или GRU), которая обрабатывает входную последовательность слово за словом. Каждое слово сначала проходит через embedding-слой (из урока про Word Embeddings), затем попадает в рекуррентную ячейку. Финальное скрытое состояние encoder называется **context vector** - это вектор фиксированного размера (например, 256 или 512 чисел), в который сжато *всё содержание* входного предложения.
**Decoder** - вторая RNN, которая принимает context vector как начальное скрытое состояние и генерирует выходную последовательность по одному токену за шаг. На каждом шаге decoder получает предыдущее сгенерированное слово и своё текущее состояние, и предсказывает следующее слово. Генерация продолжается до тех пор, пока decoder не выдаст специальный токен `<END>`.
**Проблема бутылочного горлышка (information bottleneck):** Context vector - это вектор фиксированной длины, скажем 256 чисел. В него нужно упаковать *всё содержание* входного предложения. Для короткого предложения из 5 слов это работает. Но для абзаца из 50 слов? Или для целого документа? Эксперименты показали, что качество Seq2Seq резко падает на длинных предложениях (более 20-30 слов). Информация о первых словах "размывается" при прохождении через десятки ячеек RNN. Эту проблему решает механизм **Attention** - но об этом в следующем концепте.
Обратите внимание, что у encoder и decoder могут быть **разные словари** (vocab_size). При переводе с русского на английский encoder работает с русскими словами (10000 токенов), а decoder - с английскими (8000 токенов). Размер hidden_dim должен совпадать, потому что context vector передаётся из encoder в decoder как начальное скрытое состояние.
Почему качество Seq2Seq модели (без Attention) резко падает на длинных предложениях?
Механизм Attention
В 2014 году Dzmitry Bahdanau предложил механизм **Attention** - решение проблемы бутылочного горлышка. Идея: вместо того чтобы сжимать весь вход в один вектор, позволить decoder на каждом шаге генерации **смотреть на все скрытые состояния encoder** и самому решать, на какие части входного предложения обратить внимание. При переводе слова "cats" decoder может посмотреть на encoder-состояние слова "кошек", игнорируя "Я" и "люблю".
Как вычисляются attention weights? Нужна **функция оценки** (score function), которая измеряет, насколько текущее состояние decoder `s_t` "совместимо" с каждым состоянием encoder `h_i`. Bahdanau предложил **additive attention**: score(s_t, h_i) = v * tanh(W1 * s_t + W2 * h_i), где W1, W2, v - обучаемые матрицы. Luong в 2015 году предложил более простой вариант - **multiplicative (dot-product) attention**: score(s_t, h_i) = s_t * h_i (скалярное произведение). Оба подхода работают, но dot-product attention быстрее и стал основой для Transformer.
**Три шага вычисления Attention:** 1. **Score** - вычислить совместимость decoder-состояния s_t с каждым encoder-состоянием h_i: - Bahdanau: score_i = v * tanh(W1 * s_t + W2 * h_i) - Luong: score_i = s_t^T * h_i (dot-product) 2. **Normalize** - превратить score в вероятности через softmax: - alpha_i = exp(score_i) / sum(exp(score_j)) - Сумма всех alpha = 1.0 3. **Context** - взвешенная сумма encoder-состояний: - context_t = sum(alpha_i * h_i) - Результат: вектор того же размера, что и h_i
Attention дал два ключевых улучшения. Во-первых, **устранил бутылочное горлышко** - теперь decoder на каждом шаге имеет прямой доступ ко всем encoder-состояниям, а не только к сжатому context vector. Во-вторых, attention weights создают **интерпретируемое выравнивание** (alignment) между словами входа и выхода - можно визуализировать, на какие слова модель смотрела при генерации каждого выходного слова. Эта визуализация помогает понять и отлаживать модель.
Чем Attention отличается от базового Seq2Seq при генерации каждого выходного слова?
Beam Search
Decoder генерирует по одному слову за шаг, выбирая из словаря в тысячи токенов. Простейшая стратегия - **greedy decoding**: на каждом шаге выбирать слово с максимальной вероятностью. Быстро, но часто приводит к плохим переводам. Почему? Потому что локально лучшее слово на шаге 2 может привести к тупику на шаге 5. Пример: greedy выберет "Я есть ученик" (каждое слово по отдельности вероятно), хотя "Я - студент" звучит естественнее.
**Beam Search** - компромисс между greedy (проверяем 1 путь) и полным перебором (проверяем все пути, но это экспоненциально). Beam Search держит **top-k** (beam width) лучших частичных переводов на каждом шаге. На каждом шаге для каждого из k кандидатов рассматриваются все возможные следующие слова, вычисляются вероятности полных путей, и снова отбираются top-k. Типичный beam width: 4-10 для машинного перевода.
**Length normalization - важная деталь:** Log-вероятность предложения = сумма log-вероятностей слов. Чем длиннее предложение, тем больше отрицательных слагаемых, тем меньше общий score. Без коррекции Beam Search предпочитает **короткие** предложения. Решение - **нормализация по длине:** score_normalized = (1/L^alpha) * sum(log P(w_t)) где L - длина предложения, alpha - гиперпараметр (обычно 0.6-0.7). alpha = 0: без нормализации (предпочитает короткие) alpha = 1: полная нормализация (средний log-prob на слово) alpha = 0.6: золотая середина (используется в Google Translate)
На практике Beam Search с beam width 4-5 даёт основной прирост качества по сравнению с greedy. Увеличение beam width до 10-20 приносит незначительное улучшение, но удваивает-утраивает время генерации. Интересно, что **слишком большой** beam width (100+) может даже *ухудшить* качество - модель начинает находить вероятные, но неестественные переводы. Поэтому в продакшн-системах обычно используют beam width 4-8.
Зачем в Beam Search применяется length normalization?
Teacher Forcing
При обучении Seq2Seq модели decoder должен генерировать последовательность слово за словом. Возникает вопрос: что подавать decoder как "предыдущее слово" на каждом шаге - его собственное предсказание или правильное слово из обучающих данных? Если подавать предсказание модели, ранняя ошибка (неправильное первое слово) приведёт к каскаду ошибок - каждое следующее предсказание будет опираться на неверный контекст. **Teacher Forcing** решает эту проблему: во время обучения decoder получает **правильное предыдущее слово** (ground truth) вместо своего предсказания.
Teacher Forcing значительно **ускоряет обучение** - модель сходится в 2-5 раз быстрее, потому что каждый шаг decoder получает качественный контекст. Кроме того, Teacher Forcing позволяет **параллелизацию**: поскольку все входные токены decoder известны заранее (это ground truth), все шаги можно вычислить параллельно вместо последовательного. Это особенно полезно на GPU.
**Exposure Bias - обратная сторона Teacher Forcing:** При обучении decoder всегда видит правильные предыдущие слова (ground truth). При inference (реальном использовании) - свои собственные предсказания, которые содержат ошибки. Модель никогда не тренировалась восстанавливаться после ошибок! Это называется **exposure bias** - расхождение между условиями обучения и использования. Последствия: - На обучающих данных модель работает отлично - На реальных данных ошибки накапливаются: одна ошибка влияет на все последующие слова - Чем длиннее генерируемая последовательность, тем сильнее degradation
**Scheduled Sampling** (Bengio, 2015) - практическое решение exposure bias. Идея: в начале обучения использовать 100% Teacher Forcing (быстрая сходимость), затем постепенно увеличивать долю собственных предсказаний модели. К концу обучения модель полностью работает в autoregressive режиме, как при реальном использовании. Типичные расписания: линейное (от 1.0 до 0.0 за N эпох), экспоненциальное (ratio = k^epoch). Teacher Forcing и его варианты остаются стандартной техникой обучения не только в Seq2Seq, но и в Transformer-архитектурах (GPT, BERT), где Teacher Forcing реализован через **causal masking** - каждая позиция видит только предыдущие токены ground truth.
Seq2Seq устарел после появления Transformer, его нигде не используют
Легкие Seq2Seq модели на основе GRU активно используются на мобильных устройствах и в real-time приложениях, где Transformer слишком тяжёлый
Transformer требует значительных вычислительных ресурсов из-за self-attention с квадратичной сложностью O(n^2). На мобильных устройствах, в IoT и в задачах с жёстким ограничением latency (автозамена на клавиатуре, голосовые команды, потоковый перевод субтитров) компактные Seq2Seq на GRU работают в 10-100 раз быстрее при приемлемом качестве. Кроме того, концепции из Seq2Seq (Encoder-Decoder, Attention, Teacher Forcing, Beam Search) являются фундаментом для понимания Transformer.
В чём проблема exposure bias при использовании Teacher Forcing?
Итоги
- **Encoder-Decoder:** Encoder (RNN) читает вход и сжимает его в context vector фиксированного размера, Decoder (отдельная RNN) генерирует выход слово за словом - это позволяет работать с последовательностями разной длины, но создаёт bottleneck на длинных предложениях
- **Attention:** на каждом шаге decoder вычисляет attention weights для всех encoder-состояний через score + softmax и создаёт взвешенный context vector - это устраняет bottleneck и даёт интерпретируемое выравнивание (alignment) между входом и выходом
- **Beam Search:** вместо жадного выбора одного лучшего слова на каждом шаге, хранит top-k кандидатов (beam width = 4-8), а length normalization предотвращает предпочтение коротких предложений
- **Teacher Forcing:** при обучении decoder получает правильные предыдущие слова вместо своих предсказаний, что ускоряет сходимость, но создаёт exposure bias - Scheduled Sampling решает это, постепенно переводя модель в autoregressive режим. Эти техники стали фундаментом: от мечты 1950-х до Transformer и GPT
Связанные темы
Seq2Seq связывает рекуррентные сети с современными архитектурами NLP и является фундаментом для понимания Transformer:
- RNN и LSTM — Encoder и Decoder в Seq2Seq построены на RNN/LSTM/GRU - рекуррентных сетях, которые обрабатывают последовательности. Без понимания скрытых состояний и vanishing gradient невозможно понять, почему нужен Attention
- Transformer — Transformer (2017) заменил RNN в Seq2Seq на self-attention, но сохранил Encoder-Decoder структуру, механизм Attention и Teacher Forcing. Seq2Seq - прямой предшественник Transformer
- Word Embeddings — Embedding-слой в encoder и decoder преобразует слова в плотные векторы. Качество embeddings напрямую влияет на качество Seq2Seq модели - предобученные word2vec или GloVe ускоряют сходимость
- BERT и GPT — BERT использует encoder из Seq2Seq (bidirectional), GPT - decoder (autoregressive). Обе архитектуры наследуют Teacher Forcing, Attention и идею Encoder-Decoder, доведённые до масштаба миллиардов параметров
Вопросы для размышления
- Attention вычисляет взвешенную сумму всех encoder-состояний на каждом шаге decoder. Это значит, что decoder имеет доступ ко всему входу. Тогда зачем вообще нужен encoder - почему нельзя подать сырые embeddings входных слов напрямую в Attention?
- Beam Search с beam width 100+ может ухудшить качество перевода по сравнению с beam width 5. Почему нахождение более вероятной последовательности не гарантирует лучший перевод? Что это говорит об обученной модели?
- Teacher Forcing используется не только в Seq2Seq, но и в GPT (через causal masking). Можно ли полностью избавиться от exposure bias? Какие альтернативные подходы к обучению генеративных моделей вы могли бы предложить?
Связанные уроки
- ml-35-word-embeddings — Вход энкодера - эмбеддинги слов
- ml-30-rnn-lstm — Классический seq2seq на рекуррентных энкодерах
- ml-31-transformers — Attention обобщил декодирование seq2seq
- ml-37-bert-gpt — Encoder-decoder вырос в LLM
- alg-14-dijkstra — Beam search похож на ограниченный поиск пути
- aie-03-llm-fundamentals