RNN и LSTM: нейросети с памятью

Чтобы учитывать порядок и контекст, нужна сеть, которая читает текст последовательно и помнит прочитанное; это рекуррентные сети (RNN).

Рекуррентная нейросеть (RNN) — сеть, которая обрабатывает последовательность по одному элементу, передавая на каждом шаге «память» (скрытое состояние) о ранее увиденном.

Зачем вообще читать последовательно

Bag-of-words терял порядок, n-граммы видели 1-2 слова, эмбеддинги описывали отдельные слова. Но смысл предложения — в порядке и связях. «Собака укусила человека» ≠ «человек укусил собаку». Хочется сеть, которая читает слова по очереди, как человек, и держит в уме контекст. Это и есть RNN.

Идея скрытого состояния (памяти)

RNN идёт по словам слева направо. На каждом шаге у неё есть скрытое состояние — вектор-«память», сжатый итог всего прочитанного. Получив новое слово, сеть обновляет память: новое состояние = функция(прошлое состояние, текущее слово). Так информация о начале предложения «протекает» до конца.

# Схематичная RNN: показываем, как обновляется память (без обучения)
def step(hidden, word_value):
    # очень упрощённо: новая память = смесь старой и нового слова
    return round(0.5 * hidden + 0.5 * word_value, 3)

# закодируем слова числами (как будто это их эмбеддинги-скаляры)
sentence = [("я", 0.2), ("очень", 0.6), ("рад", 0.9)]
hidden = 0.0
for word, value in sentence:
    hidden = step(hidden, value)
    print("прочитали '%s' -> память:" % word, hidden)
print("Итоговая память кодирует всё предложение:", hidden)

Вывод:

прочитали 'я' -> память: 0.1
прочитали 'очень' -> память: 0.35
прочитали 'рад' -> память: 0.625
Итоговая память кодирует всё предложение: 0.625

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

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

У простой RNN есть серьёзная беда. Информация о ранних словах с каждым шагом «размывается» — её перезаписывают новые слова. К концу длинного предложения память о начале почти исчезает. Технически это затухание градиента: при обучении сигнал, идущий назад через много шагов, становится исчезающе мал, и сеть не может выучить связь между далёкими словами.

Пример: «Кошка, которую я завёл прошлым летом в деревне у бабушки, … была чёрной». Чтобы согласовать «была чёрной» с «кошка», нужно помнить начало через десяток слов. Простая RNN это теряет.

LSTM: управляемая память

LSTM (Long Short-Term Memory) — улучшенная RNN с механизмом «ворот» (gates). Это маленькие обучаемые регуляторы, которые решают: что из новой информации запомнить, что из старой памяти забыть, что выдать наружу. Благодаря отдельной «ячейке памяти», которая обновляется аккуратно, LSTM держит важную информацию на много шагов и почти не страдает от затухания градиента.

ВоротаЧто решает
Forget gateчто из старой памяти забыть
Input gateчто из нового запомнить
Output gateчто выдать как результат шага

Близкий родственник — GRU: похожая идея с меньшим числом ворот, быстрее. Долгое время LSTM/GRU были стандартом для текста: перевод, распознавание речи, генерация.

Что осталось нерешённым

LSTM смягчила проблему длинных зависимостей, но не убрала полностью: на очень длинных текстах память всё равно ограничена. И главное — RNN обрабатывает слова строго по очереди, шаг за шагом, поэтому её нельзя эффективно распараллелить на современных видеокартах. Эти два ограничения и подтолкнут к механизму внимания и трансформерам.

Итог

  • RNN читает текст по словам, неся скрытое состояние — память о прошлом.
  • Итоговое состояние — сжатое представление всей последовательности.
  • Простая RNN забывает далёкое прошлое (затухание градиента).
  • LSTM с воротами управляет памятью и держит длинные зависимости лучше.
  • Остаются пределы: длина и невозможность распараллеливания.
Проверьте себя
1. Что такое скрытое состояние в RNN?
AСписок всех слов словаря
BВектор-память, который сжато хранит информацию о прочитанной части последовательности
CМатрица ошибок классификатора
DНомер текущего слова
2. В чём проблема длинных зависимостей у простой RNN?
AСеть читает текст слишком быстро
BИнформация о ранних словах размывается, и связь между далёкими словами теряется (затухание градиента)
CRNN не умеет работать с эмбеддингами
DRNN путает регистр букв
3. Зачем в LSTM нужны ворота (gates)?
AЧтобы ускорить токенизацию
BЧтобы управлять памятью: что забыть, что запомнить, что выдать — и держать длинные зависимости
CЧтобы перевести текст на другой язык
DЧтобы уменьшить размер словаря
Поддержать проект