Включение механизма копирования в модели от последовательности к последовательности

В этом сообщении объясняются детали, лежащие в основе модели CopyNet от Gu et al. (1). Если вы просто хотите увидеть код, вы можете ознакомиться с моей реализацией в AllenNLP. У меня также есть рабочий эксперимент с игрушкой здесь, на github.com/epwalsh/nlp-models.

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

Тем не менее, многие такие задачи требуют, чтобы модель могла создавать токены в целевой последовательности, которые появляются в исходной последовательности, но находятся вне словаря (OOV) по отношению к целевому словарю. Иногда эти токены являются частью исходного словаря, а иногда нет (т.е. OOV по отношению к исходному словарю). Эта концепция называется копированием.

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

Поэтому вместо этого мы собираемся узнать, как включить механизм копирования непосредственно в модель уровня токена, следуя архитектуре CopyNet от Гу и др. (1). На высоком уровне механизм копирования предоставляет модели переключатель для выбора между двумя режимами на каждом этапе декодирования: режим генерации и режим копирования. В режиме генерации декодер создает токен из целевого словаря, в то время как в режиме копирования декодер выбирает токен из исходной последовательности (не обязательно из исходного словаря, поскольку токен может быть OOV).

Математика

В частности, CopyNet работает, вычисляя вероятность по каждому токену в целевом словаре и каждому токену в исходной последовательности на каждом этапе декодирования. Таким образом, CopyNet действует так, как будто данная исходная последовательность расширяет целевой словарь, позволяя модели потенциально генерировать токены, которые являются OOV по отношению к целевому словарю.

Распределение по расширенному словарю

Чтобы формализовать это, пусть V = {v₁,…, vₙ} обозначает целевой словарь, а X обозначает набор уникальных слов в исходной последовательности x = ( x₁,…, xₘ). Также предположим, что у нас есть специальный токен UNK для слов OOV. Сам токен UNK не является частью V.

Тогда расширенный словарь, соответствующий каждой исходной последовательности, имеет вид V ∪ X ∪ {UNK}, т.е. модель сможет создать любой токен, который находится либо в целевом словаре, либо в исходной последовательности, а также токен UNK. В частности, вероятность токена yₜ из расширенного словаря на этапе декодирования t ᵗʰ дается выражением

где «» представляет состояние декодера, которое включает в себя закодированный источник, скрытое состояние RNN и предыдущий прогноз. Вероятность генерации равна

а вероятность копирования равна

куда

Функция оценки генерации производит оценку для каждого токена в целевом словаре, а также для UNK-токена, а функция оценки копирования производит оценку для каждого токена в исходной последовательности. Более подробно они описаны ниже.

Gu et al. (1) обеспечивают хорошую визуализацию, чтобы помочь вам понять, когда оценки копирования и генерации влияют на вероятность:

Таким образом, если yₜ находится в целевом словаре, но не в исходной последовательности, то только оценка поколения влияет на вероятность yₜ. Если yₜ находится в целевом словаре, а также в исходной последовательности, тогда будет учитываться оценка поколения и все соответствующие оценки копии. Если yₜ присутствует только в исходной последовательности, тогда будут учитываться только оценки копии. И если yₜ не входит ни в исходную последовательность, ни в целевой словарь, то вклад будет иметь только оценка генерации UNK-токена.

Создание и копирование партитур

Функция оценки генерации Ψg является функцией состояния декодера на текущем временном шаге. В простейшей форме это может быть просто линейная функция, которая проецирует состояние декодера на целевой словарь (включая токен UNK). Именно так это и делается в реализации AllenNLP:

Точно так же функция оценки копирования Ψc является функцией состояния декодера и полного закодированного источника, который выводит оценку для каждого токена в исходном предложении (кроме специальных токенов START, END или PAD). . В AllenNLP это реализовано следующим образом:

Состояние декодера

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

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

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

Затем декодированное состояние обновляется в зависимости от внимательного чтения, выборочного чтения и целевого токена (или прогнозируемого целевого токена) с предыдущего временного шага. Вот реализация в AllenNLP:

Попробуй это

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

Hi, my name is <NAME>

и сгенерируйте ответ, в котором говорится

Nice to meet you, <NAME>!

где <NAME> - любое мыслимое имя, состоящее из одного или нескольких токенов. Таким образом, очевидно, что токены в <NAME>, вероятно, будут OOV по отношению к целевому словарю, и поэтому модель должна будет полагаться на копирование, чтобы создать правильную целевую последовательность.

Используя такие простые шаблоны, я создал фактический набор данных для этой задачи: https://github.com/epwalsh/nlp-models/blob/master/data/greetings.tar.gz. Набор данных состоит из набора для обучения и проверки, каждый из которых представляет собой файл с разделителями табуляции и двумя столбцами. Первый столбец - это исходное предложение, а второй столбец - это целевое предложение. Например, первые несколько строк train.tsv выглядят так:

Hi, I’m Celeste Busard.<tab>Nice to meet you, Celeste Busard!
Its Juliana Stever<tab>Nice to meet you, Juliana Stever!
Hey, it’s Erna Lundquist<tab>Nice to meet you, Erna Lundquist!
Hey call me Etsuko.<tab>Nice to meet you, Etsuko!
Pura is my name.<tab>Nice to meet you, Pura!

Набор проверки, конечно, похож, но содержит совершенно разные названия.

Чтобы запустить этот пример, начните с клонирования репозитория:

git clone https://github.com/epwalsh/nlp-models.git
cd nlp-models

Теперь распакуйте набор данных и обучите модель:

make data/greetings.tar.gz
make train

При запросе файла модели введите experiments/greetings/copynet.json. Эта конфигурация модели предполагает, что у вас есть один графический процессор. Если у вас нет графического процессора, измените строку "cuda_device": 0 на "cuda_device": -1 в copynet.json.

На моем графическом процессоре GTX 1070 это займет всего пару минут, чтобы достичь 100% точности на наборе для проверки.

Примечания по реализации

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

Чтобы отслеживать, какие токены в целевой последовательности эффективно соответствуют токенам в исходной последовательности, мне нужно было создать поле данных, которое будет содержать эту информацию и передаваться на прямой проход модели вместе с исходными и целевыми токенами. Я решил назвать это поле source_to_target.

Поле source_to_target представляет собой массив беззнаковых целых чисел формы batch_size x source_length, который дает индекс целевого словаря каждого токена в исходном предложении (который является индексом токена UNK, если исходный токен является OOV по отношению к целевому словарю).

Это поле используется в _get_ll_contrib(self, ...) методе CopyNetSeq2Seq, который объединяет оценки генерации и копирования для получения распределения по расширенному словарю:

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

Надеюсь, вы нашли этот пост и мою реализацию CopyNet полезными. Я много работал, чтобы модель была готова к исследованиям и производству, и хотел бы узнать, как вы ее используете!

использованная литература

  1. Цзятао Гу, Чжэндун Лу, Ханг Ли и Виктор О.К. Ли. 2016. Включение механизма копирования в последовательное обучение .
  2. Илья Суцкевер, Ориол Виньялс, Куок В. Ле. 2014. Последовательность для последовательного обучения с помощью нейронных сетей .