Задний план

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

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

Возможно, вы слышали о таких терминах, как Transformer, Attention, Encoder-Decoder и т. Д. В RNN, что кажется подавляющим. На самом деле все они связаны, и есть способ связать их вместе. В этой статье будет объяснен этот путь развития интуиции до концепции внимания.

Начнем с «базовой» нейронной сети.

Нейронная сеть с прямой связью (FNN)

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

Рекуррентная нейронная сеть (RNN)

Способ обработки входных данных рекуррентной нейронной сетью (RNN) отличается от FNN. В FNN мы потребляем все входные данные за один временной шаг, тогда как в RNN мы потребляем один вход для каждого временного шага. . Этот один ввод может быть одним символом (если мы потребляем ввод по символам), одним словом (если мы потребляем ввод по словам), одним сэмплом / одним аудиокадром (для аудиовхода) и т. Д.

Особенностью RNN является то, что вход имеет временную зависимость. Это означает, что входные данные состоят из последовательности значений с временным порядком, например, сначала идет это значение, затем другое значение и так далее. Данные временных рядов являются одним из примеров. Еще один пример - предложение (оно начинается с первого слова, затем второе слово следует за первым словом и так далее).

Следовательно, вход RNN - это последовательность или список значений. В нейронной сети значение представлено в виде вектора. Другими словами, входом RNN является последовательность векторов (список векторов).

В реальном коде мы обрабатываем ввод в пакетном режиме, чтобы сделать вычисления более эффективными. Различные входные данные из одного и того же пакета независимы друг от друга, поэтому модель будет вести себя так же, как если бы мы обрабатывали входные данные один за другим).

Подробнее о состоянии в РНС

Во время обработки ввода мы можем передавать информацию от одного временного шага к другому. Это делается путем сохранения состояния (часто называемого скрытым состоянием). Состояние имеет ту же роль, что и память (RAM) в компьютерах.

Например, когда вы добавляете два числа цифра за цифрой, вы отслеживаете перенос (если сумма цифр больше 9) до следующей позиции цифры. Это состояние переноса сохраняется в некоторой памяти.

Как и в случае с ОЗУ, мы можем читать из или писать в состояние. Короче говоря, на каждом временном шаге RNN он выполняет все эти операции:

  1. Считать значение (я) из состояния (с предыдущего временного шага) и входное значение (на текущем временном шаге)
  2. Произведите вычисления, используя эти значения, чтобы получить результат.
  3. Запишите результат в Состояние (на текущем временном шаге). Конечно, мы можем хранить и историю предыдущих государств.

Варианты на этапе (2) и (3) определяют разные ароматы RNN (ванильный RNN, LSTM, GRU и т. Д.).

В ванильных RNN и GRU мы используем только один вектор для представления состояния. В LSTM мы используем два вектора для представления состояния (называемые состоянием ячейки и скрытым состоянием соответственно, каждый из которых имеет разную роль). В нейронной машине Тьюринга (NMT) мы используем N векторов (мы можем определить их количество) для представления состояния.

С этого момента, когда мы упоминаем RNN, он может относиться к любой вариации RNN (может быть LSTM, GRU и т. Д.).

В чистом RNN (без добавления дополнительного слоя после RNN) скрытое состояние является выходом. Мы можем передать скрытое состояние на выходной слой, чтобы преобразовать его в желаемую выходную форму. Например, мы можем использовать слой linear + softmax в качестве слоя вывода, если мы хотим иметь категориальный вывод (также известный как классификатор). В RNN термины вывод и скрытое состояние могут использоваться как синонимы.

Интересный факт: Softmax - это непрерывная или плавная версия hardmax (широко известная как argmax). Softmax также является обобщением сигмоида для измерения N.

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

Второй - использовать результат всех временных шагов. Например, при распознавании сущности имени мы можем иметь каждое слово с вероятностью быть частью сущности имени. В этом случае мы будем использовать все выходные данные каждого временного шага (потому что на каждом временном шаге мы вычисляем вероятность для каждого слова).

Укладка RNN

После того, как мы получим основную часть RNN, мы можем обновить ее дальше. Пока мы говорим только об однослойной RNN. Как насчет объединения нескольких RNN? Когда дело доходит до укладки RNN, есть два способа их укладки: вертикальный или горизонтальный.

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

Конечный результат - это скрытое состояние на последнем слое RNN (самом дальнем от входа).

При горизонтальном наложении один уровень RNN должен обрабатывать все входные данные перед передачей вывода на следующий уровень RNN. Это часто называют кодировщиком-декодером.

Кажется, нет смысла складывать более двух слоев по горизонтали. Может быть, в этом есть какое-то применение, но термин «горизонтальное наложение» просто пытается обобщить между вертикальным и горизонтальным.

Кодер-декодер

Когда вход - это последовательность, а выход - последовательность, мы часто также называем это от последовательности к последовательности (seq2seq). Мы можем использовать архитектуру кодировщика-декодера для решения проблем seq2seq.

В самой простой форме кодировщика-декодера декодер - это еще одна RNN, которая получает начальное состояние из последнего скрытого состояния кодировщика.

Знаменитым примером архитектуры кодировщика-декодера является машинный перевод, где у нас есть входное предложение с одного языка (например, английского), и мы хотим, чтобы на выходе было переведенное предложение (то же предложение, но, например, на китайском). В приведенном выше примере анимации переводится английское предложение «Я люблю тебя» на французское предложение «Je t’aime». Обратите внимание, что длина исходного и целевого предложений различается (3 слова против 2 слов, не считая токенов ‹start› и ‹end›).

В кодировщике-декодере исходный ввод (последовательность векторов, представляющих слова в предложении) потребляется только кодировщиком (первым RNN). Поскольку Decoder получает информацию только из скрытых состояний Encoder (а не исходного ввода), Decoder может создавать последовательность с длиной, отличной от исходной входной последовательности. Это обеспечивает более гибкий конечный вывод, поскольку длина вывода не привязана к длине ввода.

Как мы видим, единственная информация о вводе, которую получает Decoder, - это последнее скрытое состояние Encoder. Из этого одного вектора декодеру необходимо сгенерировать всю выходную последовательность. Это довольно тяжелая ответственность. Хотя в некоторых случаях это может работать нормально, это, безусловно, очень ограничивает.

Почему мы учитываем только последнее скрытое состояние кодировщика? Почему бы не учитывать все скрытые состояния кодировщика? Перейдем к вниманию.

Внимание

В Decoder мы можем получить более точную информацию о вводе, рассматривая все скрытые состояния кодировщика (а не только последнее). Эта информация (называемая вектором контекста) используется для улучшения скрытого состояния декодера для создания лучшего конечного вывода.

Уровень внимания вычисляет вектор контекста из всех скрытых состояний кодировщика; и скрытое состояние декодера текущего временного шага. Он повторно вычисляет вектор контекста для каждого временного шага в Decoder.

Мы можем увидеть уровень внимания как: «Хорошо, с текущим скрытым состоянием декодера, какая часть ввода важна?». Одним из способов вычисления этой релевантности является вычисление оценки сходства между скрытым состоянием декодера и каждым из скрытых состояний кодера. Вот почему при вычислении внимания часто наблюдаются операции скалярного произведения (скалярное произведение можно использовать для вычисления сходства).

Мы также можем видеть внимание как поиск в памяти (содержащей информацию из скрытых состояний кодировщика). Если у нас есть скрытое состояние Decoder как Query; скрытое состояние кодировщика как Key; то операция поиска ищет ключи, относящиеся к запросу. Значение внутри памяти - это само скрытое состояние кодировщика. Это называется вниманием, основанным на содержании.

Когда сами ключи и ценности исходят из текущей последовательности, это называется самовниманием.

Надеюсь, вы сформировали базовое понимание, которое поможет вам следить за более подробными ресурсами.

Эта статья в основном объясняет эти архитектуры в режиме прогнозирования. Мы не говорили о том, как его тренируют. Обучение связано с предсказанием, но это отдельная история.

В следующей статье мы поговорим о том, как можно использовать внимание без повторяющейся сети, что и делает Transformer.

Ресурсы