Перейти к содержимому
ПереводыStanford

CS224N: языковые модели, RNN, seq2seq и внимание (конспект)

Пересказ конспектов Stanford CS224N о языковых моделях и машинном переводе: n-граммы и оконная нейронная модель, RNN и перплексия, затухающие и взрывающиеся градиенты, отсечение градиента, GRU и LSTM, двунаправленные сети, seq2seq, внимание Богданау и Луонга, стратегии декодирования, BLEU, большой словарь, копирование и подслова.

Часть 1. Языковые модели и рекуррентные сети

Заголовок раздела «Часть 1. Языковые модели и рекуррентные сети»

Языковая модель приписывает вероятность последовательности слов. По цепному правилу вероятностей

P(w1,…,wT)=∏t=1TP(wt∣w1,…,wt−1),P(w_1, \ldots, w_T) = \prod_{t=1}^{T} P(w_t \mid w_1, \ldots, w_{t-1}),

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

n-граммы. Классический приём — считать, что слово зависит только от n−1n - 1 предыдущих (марковское предположение), и оценивать вероятности частотами: P(w3∣w1,w2)=count(w1w2w3)count(w1w2)P(w_3 \mid w_1, w_2) = \frac{\text{count}(w_1 w_2 w_3)}{\text{count}(w_1 w_2)}. Две проблемы: разреженность (если тройка ни разу не встречалась, вероятность нулевая — спасает сглаживание, добавление малого числа к счётчикам; если не встречался даже префикс — откат к более короткому контексту, backoff) и память (число n-грамм растёт с корпусом и с nn, поэтому nn больше 5 почти не берут).

Оконная нейронная модель (Бенжио и др., 2003): склеить векторы n−1n - 1 предыдущих слов, пропустить через скрытый слой и softmax по словарю. Разреженности нет — похожие слова получают похожие векторы, — но окно по-прежнему фиксированное, и каждое место в окне имеет свои веса.

Рекуррентная сеть читает текст по одному слову и хранит скрытое состояние hth_t, в которое «сворачивается» вся история:

ht=σ(W(hh)ht−1+W(hx)xt),y^t=softmax⁡(W(S)ht).h_t = \sigma\big(W^{(hh)} h_{t-1} + W^{(hx)} x_t\big), \qquad \hat y_t = \operatorname{softmax}\big(W^{(S)} h_t\big).

Веса W(hh),W(hx)W^{(hh)}, W^{(hx)} одни и те же на каждом шаге — поэтому число параметров не зависит от длины текста, а сеть применима к последовательностям любой длины.

flowchart LR
X1["x₁"] --> H1["h₁"]
X2["x₂"] --> H2["h₂"]
X3["x₃"] --> H3["h₃"]
H0["h₀"] -->|"W(hh)"| H1 -->|"W(hh)"| H2 -->|"W(hh)"| H3
H1 --> Y1["ŷ₁"]
H2 --> Y2["ŷ₂"]
H3 --> Y3["ŷ₃"]
Развёрнутая во времени RNN: одни и те же веса на каждом шаге; каждое состояние зависит от текущего слова и предыдущего состояния и даёт распределение следующего слова.

Функция потерь и перплексия. На каждом шаге — кросс-энтропия между предсказанным распределением и настоящим следующим словом; по корпусу — среднее JJ. Модели сравнивают по перплексии: в конспекте она записана как 2J2^J (при логарифме по основанию 2; с натуральным логарифмом — eJe^J). Перплексия — это «эффективное число вариантов», из которых модель выбирает следующее слово: чем меньше, тем увереннее и точнее модель.

Плюсы и минусы RNN. Плюсы: вход любой длины, размер модели не растёт с длиной, в принципе учитывается вся история. Минусы: вычисления последовательны (медленно, плохо параллелятся), и на практике информацию с далёких шагов сеть удерживает плохо. Память тоже велика: для обратного прохода нужно хранить состояния всех шагов.

Градиент потерь на шаге tt по состоянию на шаге kk содержит произведение якобианов ∏j=k+1t∂hj∂hj−1\prod_{j=k+1}^{t} \frac{\partial h_j}{\partial h_{j-1}}. Каждый множитель включает W(hh)W^{(hh)} и производную нелинейности, и если их нормы стабильно меньше единицы, произведение затухает экспоненциально (сеть «не чувствует» далёкие слова), а если больше — взрывается (обучение срывается, появляются NaN). Конспект приводит пример: в коротком тексте «Jane walked into the room. John walked in too. Jane said hi to ___» модель легко подставит «John», а если между фразами вставить ещё одно длинное предложение, информация о Джоне доходит до места предсказания уже слишком ослабленной.

Средства, перечисленные в конспекте:

  • Отсечение градиента (gradient clipping, Миколов): если норма градиента больше порога, его масштабируют до этого порога. Просто и надёжно против взрыва.
  • Против затухания — инициализация W(hh)W^{(hh)} единичной матрицей и ReLU вместо сигмоиды (производная ReLU — 0 или 1, она не «сжимает» градиент).
  • Глубокие и двунаправленные RNN: две сети читают текст слева направо и справа налево, их состояния склеиваются, так что каждое слово видит и прошлый, и будущий контекст; слои можно складывать стопкой.

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

GRU (gated recurrent unit): вентиль обновления ztz_t решает, сколько взять от старого состояния, вентиль сброса rtr_t — сколько прошлого учитывать при вычислении нового кандидата:

zt=σ(W(z)xt+U(z)ht−1),rt=σ(W(r)xt+U(r)ht−1),h~t=tanh⁡(Wxt+rt∘Uht−1),ht=(1−zt)∘h~t+zt∘ht−1.\begin{aligned} z_t &= \sigma(W^{(z)} x_t + U^{(z)} h_{t-1}), \qquad r_t = \sigma(W^{(r)} x_t + U^{(r)} h_{t-1}), \\ \tilde h_t &= \tanh\big(W x_t + r_t \circ U h_{t-1}\big), \qquad h_t = (1 - z_t) \circ \tilde h_t + z_t \circ h_{t-1}. \end{aligned}

Когда ztz_t близок к единице, состояние почти копируется без изменений — и градиент проходит через много шагов, не затухая.

LSTM (long short-term memory) хранит отдельную ячейку памяти ctc_t и управляет ею тремя вентилями — входным iti_t, забывания ftf_t и выходным oto_t:

ct=ft∘ct−1+it∘c~t,c~t=tanh⁡(W(c)xt+U(c)ht−1),ht=ot∘tanh⁡(ct),\begin{aligned} c_t &= f_t \circ c_{t-1} + i_t \circ \tilde c_t, \qquad \tilde c_t = \tanh(W^{(c)} x_t + U^{(c)} h_{t-1}), \\ h_t &= o_t \circ \tanh(c_t), \end{aligned}

где каждый вентиль — сигмоида от линейной функции xtx_t и ht−1h_{t-1}. Ячейка обновляется сложением, а не умножением на матрицу, — это и есть «шоссе» для градиента. Та же идея позже появилась в остаточных связях трансформеров (урок 3.4.2).

До нейросетей перевод делали статистические системы из многих частей (модели выравнивания, фразовые таблицы, языковые модели). Seq2seq (Суцкевер и др., 2014; Чо и др., 2014) — одна сквозная сеть из двух частей:

  • кодировщик читает исходное предложение (часто задом наперёд или двунаправленно, несколькими слоями LSTM) и сжимает его в вектор контекста — последнее скрытое состояние;
  • декодировщик — языковая модель, обусловленная этим вектором: начиная с токена начала, генерирует перевод слово за словом, подавая на вход предыдущее слово, пока не выдаст токен конца.

Обучение — кросс-энтропия на правильных переводах (на вход декодировщику подают правильное предыдущее слово, а не своё предсказание).

Проблема seq2seq — бутылочное горлышко: всё предложение, каким бы длинным оно ни было, должно уместиться в один вектор фиксированного размера. Качество перевода падает на длинных предложениях. При этом человек-переводчик, выбирая очередное слово, смотрит на определённые слова оригинала.

Внимание (Богданау, Чо, Бенжио, 2014) даёт декодировщику на каждом шаге ii свой вектор контекста — взвешенную сумму состояний кодировщика hjh_j:

eij=a(si−1,hj),αij=exp⁡(eij)∑kexp⁡(eik),ci=∑jαijhj,e_{ij} = a(s_{i-1}, h_j), \qquad \alpha_{ij} = \frac{\exp(e_{ij})}{\sum_k \exp(e_{ik})}, \qquad c_i = \sum_j \alpha_{ij} h_j,

где si−1s_{i-1} — предыдущее состояние декодировщика, а aa — небольшая сеть, оценивающая, насколько слово jj оригинала важно для шага ii. Веса αij\alpha_{ij} — мягкое выравнивание слов перевода со словами оригинала, которое модель выучивает сама, без разметки; их удобно рисовать картой и смотреть, что с чем сопоставилось. С вниманием качество на длинных предложениях перестаёт падать.

flowchart LR
subgraph enc["Кодировщик"]
  H1["h₁"] --- H2["h₂"] --- H3["h₃"] --- H4["h₄"]
end
H1 -->|"α₁"| C(("cᵢ = Σ αⱼ hⱼ"))
H2 -->|"α₂"| C
H3 -->|"α₃"| C
H4 -->|"α₄"| C
S["sᵢ₋₁ (декодировщик)"] -.->|"оценки eᵢⱼ → softmax"| C
C --> D["sᵢ → следующее слово"]
Внимание: на каждом шаге декодировщик заново взвешивает все состояния кодировщика и получает свой вектор контекста.

Варианты Луонга (Луонг, Фам, Мэннинг, 2015): глобальное внимание смотрит на все слова оригинала, а оценку eije_{ij} можно считать по-разному — скалярным произведением siThjs_i^T h_j, билинейной формой siTWhjs_i^T W h_j или маленькой сетью; локальное внимание сначала предсказывает позицию выравнивания и смотрит только на окно вокруг неё.

Google NMT (2016) показал, что одна модель может переводить между многими парами языков, если добавить ко входу токен целевого языка, — и даже переводить между парами, которых не было в обучении («перевод с нуля», zero-shot).

Отсюда прямой путь к трансформеру: «Attention Is All You Need» (2017) убрал рекуррентность и оставил только внимание. Это тема урока 4.4.2 и наших переводов Transformer Explainer.

Модель выдаёт распределение следующего слова; как из него получить предложение?

  • Сэмплирование из предков (ancestral sampling) — на каждом шаге случайно выбирать слово по распределению. Несмещённо, но с большой дисперсией: каждый запуск даёт другой перевод.
  • Жадный поиск — каждый раз брать самое вероятное слово. Быстро, но ранняя ошибка уже не исправляется.
  • Поиск лучом (beam search) — держать KK лучших частичных гипотез и на каждом шаге расширять их, оставляя снова KK лучших. На практике в переводе используют именно его.

Подробнее о стратегиях генерации — в уроке 4.1.3.

Оценка людьми — эталон, но дорого и медленно. Оценка через другую задачу (например, ответы на вопросы по переводу) косвенна. Стандарт автоматической оценки — BLEU (Папинени и др., 2002): доля n-грамм перевода (обычно до 4-грамм), встречающихся в эталонных переводах, с геометрическим средним по nn и штрафом за краткость, чтобы нельзя было набрать балл, выдав пару точных слов:

BLEU=β⋅exp⁡(∑nwnlog⁡pn),β=min⁡(1, e1−lenref/lenMT).\text{BLEU} = \beta \cdot \exp\Big(\sum_{n} w_n \log p_n\Big), \qquad \beta = \min\big(1,\, e^{1 - \text{len}_{\text{ref}} / \text{len}_{\text{MT}}}\big).

BLEU хорошо коррелирует с людьми на уровне корпуса, но плохо — на отдельном предложении и не замечает верных перефразирований. О метриках и их ограничениях — в уроке 7.3.1.

Softmax по словарю из сотен тысяч слов дорог. Конспект перечисляет приёмы:

  • Ускорить softmax: оценка через шумовой контраст (NCE, родственник отрицательного сэмплирования из word2vec) и иерархический softmax.
  • Уменьшить словарь: обучать на подмножествах данных с ограниченным целевым словарём (Жан и др.) и при переводе строить «список кандидатов» для каждого предложения — KK самых частых слов плюс K′K' вероятных переводов каждого исходного слова (в конспекте приведены значения KK = 15–50 тыс., K′K' = 10–20).
  • Неизвестные слова: механизм копирования (Гульчехре и др.) — модель решает, сгенерировать слово из словаря или скопировать его из оригинала по распределению внимания. Авторы Google NMT замечали, что это ненадёжно в масштабе и что редкие слова иногда лучше транслитерировать.
  • Подслова и символы: разбивать редкие слова на части — BPE (Зеннрих и др., 2016), символьные модели и гибриды «слова + символы для неизвестных слов» (Луонг и Мэннинг, 2016). Подсловная токенизация победила и стала стандартом всех современных LLM — см. урок 4.2.2 «BPE».
ENRU
language model, n-gram, backoffязыковая модель, n-грамма, откат
hidden state, perplexityскрытое состояние, перплексия
vanishing / exploding gradientsзатухающие / взрывающиеся градиенты
gradient clippingотсечение градиента
GRU, LSTM, gate, memory cellGRU, LSTM, вентиль, ячейка памяти
encoder, decoder, seq2seqкодировщик, декодировщик, seq2seq
attention, alignmentвнимание, выравнивание
greedy search, beam searchжадный поиск, поиск лучом
BLEU, brevity penaltyBLEU, штраф за краткость
copy mechanism, subwordмеханизм копирования, подслово