В нескольких последующих постах выложу обзор статей о современных неавторегрессионных моделях генерации текста -- т.е. моделей, предлагающих генерировать текст не по одному токену слева направо, а как-то иначе, более хитрым образом. Подробно опишу пять статей на этот счёт, ещё пару упомяну. Изложение будет в хронологической последовательности, и сложность моделей будет постепенно нарастать.
The Importance of Generation Order in Language Modeling
Nicolas Ford, Daniel Duckworth, Mohammad Norouzi, George E. Dahl
Google Brain, 2018
#nlp #nlg #nonautoregressive #transformer
Статья: https://arxiv.org/abs/1808.07910
Основная часть NLG моделей авторегрессионна, т.е. они пишут текст слева направо, по одному токену за итерацию. Как можно отойти от такой схемы? Авторы придумали использовать двухпроходный алгоритм — общий словарь можно разделить на две части, а затем обучить две независимые трансформерные модели: первая генерирует текст только из токенов первой части словаря + специального placeholder-токена, затем вторая модель получает этот текст с плейсхолдерами на вход и заполняет пропуски словами из второго словаря. Работа описывает различные стратегии разбиения словаря на две части и сравнения их эффективности.
Какие стратегии рассматривались?
* Common First (частотные в первом проходе) / Rare First (редкие в первом проходе) -- разбиение словаря токенов по частоте в корпусе, отсечка выбиралась так, чтобы матожидание числа "частых" и "редких" в предложении было примерно одинаковым.
* Function First (сначала функциональные) and Content First (сначала содержательные) -- разбивали по роли слова, в функциональные попали также предлоги, союзы, модальные глаголы, вопросительные слова и прочее. Омонимию по роли разрешали просто выбирая наиболее частотную роль слова в корпусе (согласно Parsey McParseface).
* Odd First -- контрольный вариант, где в первую половину словаря попали токены с нечётными индексами (по эффекту это должно быть экививалентно равномерно случайному разбиению)
Эксперименты гоняли на датасете LM1B, lowercase, со общим словарём в 64К токенов, только целые слова, без subword tokens. В качестве бейзлайна использовали просто Transformer без энкодера, т.е. простую LM. В качестве “enhanced baseline” его же, но по числу параметров подогнанного до размера анализируемых архитектур. Сравнивали модели по perplexity.
Человеческая интуиция подсказывает, что разумнее сначала передать главную мысль, записать ключевые смысловые слова, а затем уже вокруг них выстраивать предложение (многие люди так делают конспекты и быстрые записки в блокнотах). Здесь, однако, вышло, что наиболее эффективно писать сначала все служебные слова, т.е. фиксировать структуру предложения, а потом уже заполнять её смыслом. Хуже всех при этом отработал Odd First, он оказался заметно слабее всех остальных, это, в целом, показывает, что сама генерация в два прохода — задача существенно более сложная, чем обычная генерация. Вероятно, поэтому ни один из двухпроходных вариантов не смог обойти “enhanced baseline” (хотя Function First почти сравнялся с ним). Другая возможная причина — из требований равенства параметров в моделях каждая из трансформер-сетей в двухпроходной архитектуре заметно меньше чем трансформер в “enhanced baseline”, и качество может падать из-за недостаточного размера.
Обсуждая причины победы Function First над остальными вариантами авторы формулируют две мысли:
* для написания корректного правдоподобного предложения проще сначала задать его синтаксическую структуру, а потом уже заполнять её смыслом
* выгоднее до последнего избегать генерации редких токенов, потому что последующая за ними генерация будет обусловлена маловероятным контекстом (поэтому, кстати, на втором месте после Function First стоит Common First)
Я бы добавил третью мысль — у второй сетки нет возможности вставлять столько слов, сколько ей надо, каждый плейсхолдер она может заполнять только одним словом. Поэтому сценарий "накидать ключевые смыслом слова, а потом выписать вокруг них структуру" тут просто не реализуем в чистом виде.
Nicolas Ford, Daniel Duckworth, Mohammad Norouzi, George E. Dahl
Google Brain, 2018
#nlp #nlg #nonautoregressive #transformer
Статья: https://arxiv.org/abs/1808.07910
Основная часть NLG моделей авторегрессионна, т.е. они пишут текст слева направо, по одному токену за итерацию. Как можно отойти от такой схемы? Авторы придумали использовать двухпроходный алгоритм — общий словарь можно разделить на две части, а затем обучить две независимые трансформерные модели: первая генерирует текст только из токенов первой части словаря + специального placeholder-токена, затем вторая модель получает этот текст с плейсхолдерами на вход и заполняет пропуски словами из второго словаря. Работа описывает различные стратегии разбиения словаря на две части и сравнения их эффективности.
Какие стратегии рассматривались?
* Common First (частотные в первом проходе) / Rare First (редкие в первом проходе) -- разбиение словаря токенов по частоте в корпусе, отсечка выбиралась так, чтобы матожидание числа "частых" и "редких" в предложении было примерно одинаковым.
* Function First (сначала функциональные) and Content First (сначала содержательные) -- разбивали по роли слова, в функциональные попали также предлоги, союзы, модальные глаголы, вопросительные слова и прочее. Омонимию по роли разрешали просто выбирая наиболее частотную роль слова в корпусе (согласно Parsey McParseface).
* Odd First -- контрольный вариант, где в первую половину словаря попали токены с нечётными индексами (по эффекту это должно быть экививалентно равномерно случайному разбиению)
Эксперименты гоняли на датасете LM1B, lowercase, со общим словарём в 64К токенов, только целые слова, без subword tokens. В качестве бейзлайна использовали просто Transformer без энкодера, т.е. простую LM. В качестве “enhanced baseline” его же, но по числу параметров подогнанного до размера анализируемых архитектур. Сравнивали модели по perplexity.
Человеческая интуиция подсказывает, что разумнее сначала передать главную мысль, записать ключевые смысловые слова, а затем уже вокруг них выстраивать предложение (многие люди так делают конспекты и быстрые записки в блокнотах). Здесь, однако, вышло, что наиболее эффективно писать сначала все служебные слова, т.е. фиксировать структуру предложения, а потом уже заполнять её смыслом. Хуже всех при этом отработал Odd First, он оказался заметно слабее всех остальных, это, в целом, показывает, что сама генерация в два прохода — задача существенно более сложная, чем обычная генерация. Вероятно, поэтому ни один из двухпроходных вариантов не смог обойти “enhanced baseline” (хотя Function First почти сравнялся с ним). Другая возможная причина — из требований равенства параметров в моделях каждая из трансформер-сетей в двухпроходной архитектуре заметно меньше чем трансформер в “enhanced baseline”, и качество может падать из-за недостаточного размера.
Обсуждая причины победы Function First над остальными вариантами авторы формулируют две мысли:
* для написания корректного правдоподобного предложения проще сначала задать его синтаксическую структуру, а потом уже заполнять её смыслом
* выгоднее до последнего избегать генерации редких токенов, потому что последующая за ними генерация будет обусловлена маловероятным контекстом (поэтому, кстати, на втором месте после Function First стоит Common First)
Я бы добавил третью мысль — у второй сетки нет возможности вставлять столько слов, сколько ей надо, каждый плейсхолдер она может заполнять только одним словом. Поэтому сценарий "накидать ключевые смыслом слова, а потом выписать вокруг них структуру" тут просто не реализуем в чистом виде.
👍1
Sequence Modeling with Unconstrained Generation Order
Dmitrii Emelianenko, Elena Voita, Pavel Serdyukov
Yandex, 2019
Статья: https://arxiv.org/abs/1911.00176
Код: https://github.com/TIXFeniks/neurips2019_intrus
#nlp #nlg #nonautoregressive #transformer #nips
Авторы решают задачу обучения модели, которая генерирует последовательность по одному токену, но в произвольном порядке. На каждом шагу сеть предсказывает позицию и токен для неё. Такая задача затруднена тем, что для последовательности из N токенов существует N! различных путей последовательных вставок из пустой строки до таргета. Вместо того, чтобы учить модель, награждая за все правильные последовательности и штрафуя за неправильные (что просто нереально), делается следующий трюк — давайте скажем, что для каждой строки есть некоторый оптимальный путь, тогда задача модели — сопоставить ему наибольшую вероятность. Это позволяет перейти к оптимизации lower bound оценки на исходный страшный log-likelihood. Затем делается замена оценки оптимального пути на матожидание по сэмплу путей из текущей модели с весами соответствующими вероятности пути, что даёт следующую lower bound оценку, которую уже не так сложно оптимизировать.
Архитектура предлагаемой модели INTRUS -- encoder/decoder трансформер со следующими модификациями:
- отключаем маскирование "будущих" токенов в декодере, т.к. у нас больше нет явной последовательности, декодер надо полностью пересчитывать после каждой вставки.
- последний слой предсказывает матрицу вероятностей размера (число слотов Х число токенов), для этого делается отдельная проекция в вероятности слотов и отдельная проекция в условные вероятности p(токен|слот), а затем из них собирается итоговая вероятность.
Тестировали на нескольких задачах (NMT, Image-To-Latex, Image captioning). На всех тестах мерили BLEU, для MSCOCO дополнительно CIDEr. Почти везде обогнали бейзлайны, но при этом увеличение времени работы относительно бейзлайна надлинейно растёт от длины строки.
Изучение последовательностей сэмплинга, предлагаемых, обученной моделью показало, что часто модель выбирает сэмплирование слева направо или справа налево, но иногда отходит от этой последовательности при обработке связанных групп токенов (приводят пример со связью открывающих и закрывающих кавычек или скобок). Другая стратегия — генерация из середины в стороны. Авторы отмечают, что чаще модель старается сначала сэмплить высокочастотные токены (например, пунктуацию и союзы), что совпадает с результатами из предыдущей описанной статьи. Для анализа этого эффекта приводятся распределения частот сэмплинга разных частей речи от номера шага для такой модели и для обычного сэмплинга слева направо.
Здесь же, дополнительно, упомяну статью Insertion-based Decoding with automatically Inferred Generation Order от Facebook AI, 2019. Она очень похожа по логике на INTRUS, решает ту же задачу генерации по одному токену в произвольном порядке. Из основных отличий я отметил два -- они используют другую ELBo оценку, и они используют относительное а не абсолютное позиционное кодирование. Детали читайте в [arxiv:1902.01370].
Dmitrii Emelianenko, Elena Voita, Pavel Serdyukov
Yandex, 2019
Статья: https://arxiv.org/abs/1911.00176
Код: https://github.com/TIXFeniks/neurips2019_intrus
#nlp #nlg #nonautoregressive #transformer #nips
Авторы решают задачу обучения модели, которая генерирует последовательность по одному токену, но в произвольном порядке. На каждом шагу сеть предсказывает позицию и токен для неё. Такая задача затруднена тем, что для последовательности из N токенов существует N! различных путей последовательных вставок из пустой строки до таргета. Вместо того, чтобы учить модель, награждая за все правильные последовательности и штрафуя за неправильные (что просто нереально), делается следующий трюк — давайте скажем, что для каждой строки есть некоторый оптимальный путь, тогда задача модели — сопоставить ему наибольшую вероятность. Это позволяет перейти к оптимизации lower bound оценки на исходный страшный log-likelihood. Затем делается замена оценки оптимального пути на матожидание по сэмплу путей из текущей модели с весами соответствующими вероятности пути, что даёт следующую lower bound оценку, которую уже не так сложно оптимизировать.
Архитектура предлагаемой модели INTRUS -- encoder/decoder трансформер со следующими модификациями:
- отключаем маскирование "будущих" токенов в декодере, т.к. у нас больше нет явной последовательности, декодер надо полностью пересчитывать после каждой вставки.
- последний слой предсказывает матрицу вероятностей размера (число слотов Х число токенов), для этого делается отдельная проекция в вероятности слотов и отдельная проекция в условные вероятности p(токен|слот), а затем из них собирается итоговая вероятность.
Тестировали на нескольких задачах (NMT, Image-To-Latex, Image captioning). На всех тестах мерили BLEU, для MSCOCO дополнительно CIDEr. Почти везде обогнали бейзлайны, но при этом увеличение времени работы относительно бейзлайна надлинейно растёт от длины строки.
Изучение последовательностей сэмплинга, предлагаемых, обученной моделью показало, что часто модель выбирает сэмплирование слева направо или справа налево, но иногда отходит от этой последовательности при обработке связанных групп токенов (приводят пример со связью открывающих и закрывающих кавычек или скобок). Другая стратегия — генерация из середины в стороны. Авторы отмечают, что чаще модель старается сначала сэмплить высокочастотные токены (например, пунктуацию и союзы), что совпадает с результатами из предыдущей описанной статьи. Для анализа этого эффекта приводятся распределения частот сэмплинга разных частей речи от номера шага для такой модели и для обычного сэмплинга слева направо.
Здесь же, дополнительно, упомяну статью Insertion-based Decoding with automatically Inferred Generation Order от Facebook AI, 2019. Она очень похожа по логике на INTRUS, решает ту же задачу генерации по одному токену в произвольном порядке. Из основных отличий я отметил два -- они используют другую ELBo оценку, и они используют относительное а не абсолютное позиционное кодирование. Детали читайте в [arxiv:1902.01370].
GitHub
GitHub - TIXFeniks/neurips2019_intrus
Contribute to TIXFeniks/neurips2019_intrus development by creating an account on GitHub.
Insertion Transformer: Flexible Sequence Generation via Insertion Operations
Mitchell Stern, William Chan, Jamie Kiros, Jakob Uszkoreit
Google Brain, 2019
Статья: https://arxiv.org/abs/1902.03249
#nlp #nlg #nonautoregressive #transformer #icml #2019
Итак, раз уж мы научились сэмплировать токены в произвольном порядке, зачем делать это по одному за раз, нельзя ли в этом месте оптимизировать процесс? Авторы предлагают архитектуру Insertion Transformer, которая довольно-таки похожа на INTRUS из предыдущей статьи, но позволяет сэмплировать по нескольку токенов за шаг.
Начинаем всегда с пустой строки = единственного слота. На каждом шаге модель выдаёт на выходе совместное распределение по доступным слотам (= число уже имеющихся токенов + 1) и словарю токенов. И тут допускается одновременная вставка нескольких токенов -- максимально по числу имеющихся слотов.
Модификации по сравнению с обычным трансформером:
- Как и ранее, в декодере снимается стандартная для декодеров трансформеров casual self-attn mask, запрещающая видеть токены правее текущего.
- Пусть вход у нас из n токенов — добавляем на вход два дополнительных токена в начало и в конец, это даёт нам на выходе n+2 позиционных вектора, дальше конкатенацией каждой пары соседних выходных векторов мы получаем n+1 векторов, каждый из которых соответствует одному из возможных слотов
- Дальше из матрицы этих векторов H, соответствующих слотам, вычисляются вероятности совместного распределения (слот, токен). Тут в базовом варианте делается просто проекция+софтмакс в вероятности совместного распределения (слот, токен).
- Дополнительно рассматривают три модификации расчёта вероятностей слотов и токенов: Joint (через условную вероятность, как в INTRUS), Context (с добавлением общего для всех слотов bias, построенного max-polling-ом из H) и Mixture (Mixture-of-Softmaxes Output Layer из [arxiv:1711.03953]).
Далее эту архитектуру тренировали на нескольких фиксированных последовательностях вставок (не оптимизируя lower bound как в INTRUS, а явно задавая стратегию сэмплинга). Использовалось 4 стратегии:
- left-to-right: слева направо, имитация стандартной авторегрессионной модели, на каждом шаге таргет на угадывание крайнего правого из доступных слотов и правильного следующего токена в нём, остановка по <EOS>.
- binary-tree: сбалансированное бинарное дерево -- сначала предсказываем средний токен, потом средние в левой и правой половине, на k-ом шаге дописываем сразу 2^k токенов, т.е. в идеале число шагов ~ логарифм от длины строки. Чтобы учить такое, мы сначала на каждом шаге обучения выбираем случайную подпоследовательность токенов полного таргета (тут есть тонкости в том, чтобы сделать подпоследовательности разной длины равновероятными), а затем считаем таргет следующего шага для каждого слота как каждый отсутствующий там токен с вероятностью, взвешенной на его расстояние до центра бреши (с температурой, которая — гиперпараметр).
- uniform: равномерно случайная генерация -- с одинаковой вероятностью каждого возможного верного шага, технически это вариант binary-tree с бесконечной температурой.
Как устроено сэмплирование:
- Жадное -- выбираем 1 пару (слот, токен) с максимальной вероятностью, её вставляем, итерируем.
- Параллельное -- для каждого слота считаем вероятности токенов, если там топ1 по вероятности это end-of-slot, ничего не сэмплируем; в остальные слоты вставляем их топ1 по вероятности; итерируем (пока все не предскажут end-of-slot) + есть возможность распараллелить вычисления этих распределений для токенов в разных слотах.
- Чтобы уметь как-то останавливаться в стратегиях binary-tree и uniform, добавляем служебный токен end-of-slot, который говорит, что ничего в этот слот сейчас сэмплить не надо. Таким образом, признаков для остановки два: когда строка дописана и пришёл <EOS>, или когда все имеющиеся слоты голосуют за end-of-slot.
Mitchell Stern, William Chan, Jamie Kiros, Jakob Uszkoreit
Google Brain, 2019
Статья: https://arxiv.org/abs/1902.03249
#nlp #nlg #nonautoregressive #transformer #icml #2019
Итак, раз уж мы научились сэмплировать токены в произвольном порядке, зачем делать это по одному за раз, нельзя ли в этом месте оптимизировать процесс? Авторы предлагают архитектуру Insertion Transformer, которая довольно-таки похожа на INTRUS из предыдущей статьи, но позволяет сэмплировать по нескольку токенов за шаг.
Начинаем всегда с пустой строки = единственного слота. На каждом шаге модель выдаёт на выходе совместное распределение по доступным слотам (= число уже имеющихся токенов + 1) и словарю токенов. И тут допускается одновременная вставка нескольких токенов -- максимально по числу имеющихся слотов.
Модификации по сравнению с обычным трансформером:
- Как и ранее, в декодере снимается стандартная для декодеров трансформеров casual self-attn mask, запрещающая видеть токены правее текущего.
- Пусть вход у нас из n токенов — добавляем на вход два дополнительных токена в начало и в конец, это даёт нам на выходе n+2 позиционных вектора, дальше конкатенацией каждой пары соседних выходных векторов мы получаем n+1 векторов, каждый из которых соответствует одному из возможных слотов
- Дальше из матрицы этих векторов H, соответствующих слотам, вычисляются вероятности совместного распределения (слот, токен). Тут в базовом варианте делается просто проекция+софтмакс в вероятности совместного распределения (слот, токен).
- Дополнительно рассматривают три модификации расчёта вероятностей слотов и токенов: Joint (через условную вероятность, как в INTRUS), Context (с добавлением общего для всех слотов bias, построенного max-polling-ом из H) и Mixture (Mixture-of-Softmaxes Output Layer из [arxiv:1711.03953]).
Далее эту архитектуру тренировали на нескольких фиксированных последовательностях вставок (не оптимизируя lower bound как в INTRUS, а явно задавая стратегию сэмплинга). Использовалось 4 стратегии:
- left-to-right: слева направо, имитация стандартной авторегрессионной модели, на каждом шаге таргет на угадывание крайнего правого из доступных слотов и правильного следующего токена в нём, остановка по <EOS>.
- binary-tree: сбалансированное бинарное дерево -- сначала предсказываем средний токен, потом средние в левой и правой половине, на k-ом шаге дописываем сразу 2^k токенов, т.е. в идеале число шагов ~ логарифм от длины строки. Чтобы учить такое, мы сначала на каждом шаге обучения выбираем случайную подпоследовательность токенов полного таргета (тут есть тонкости в том, чтобы сделать подпоследовательности разной длины равновероятными), а затем считаем таргет следующего шага для каждого слота как каждый отсутствующий там токен с вероятностью, взвешенной на его расстояние до центра бреши (с температурой, которая — гиперпараметр).
- uniform: равномерно случайная генерация -- с одинаковой вероятностью каждого возможного верного шага, технически это вариант binary-tree с бесконечной температурой.
Как устроено сэмплирование:
- Жадное -- выбираем 1 пару (слот, токен) с максимальной вероятностью, её вставляем, итерируем.
- Параллельное -- для каждого слота считаем вероятности токенов, если там топ1 по вероятности это end-of-slot, ничего не сэмплируем; в остальные слоты вставляем их топ1 по вероятности; итерируем (пока все не предскажут end-of-slot) + есть возможность распараллелить вычисления этих распределений для токенов в разных слотах.
- Чтобы уметь как-то останавливаться в стратегиях binary-tree и uniform, добавляем служебный токен end-of-slot, который говорит, что ничего в этот слот сейчас сэмплить не надо. Таким образом, признаков для остановки два: когда строка дописана и пришёл <EOS>, или когда все имеющиеся слоты голосуют за end-of-slot.
Экспериментировали на WMT 2014 English-German, на тестах заметили, что модель рано начинает сэмплить end-of-slot и end-of-sequence, и быстро останавливается на коротком выхлопе. Заткнули это место костылём, вычитая везде β из логпробов обоих этих токенов, β подбирали перебором на интервале [0,7] отдельно для каждой версии модели, оптимальное значение β даёт до +4 на BLEU у слабых моделек и поменьше у сильных. Ещё 3-4 пункта BLEU дожали на дистилляции модели.
На сравнении модификаций архитектуры (Joint/Context/Mixture) показали, что хотя из них и можно выжать чуть-чуть профита, но очень мало (и при подстройке β к оптимуму уходит и этот профит). В целом, по качеству получили оптимум на варианте с бинарным деревом и температурой 2 (почти неотличимый от варианта с температурой 1).
Параллельное декодирование (что с деревом, что с uniform) действительно позволяет генерировать результат за число вставок, близкое к логарифму от числа токенов, что является оптимальным для такой схемы.
На сравнении модификаций архитектуры (Joint/Context/Mixture) показали, что хотя из них и можно выжать чуть-чуть профита, но очень мало (и при подстройке β к оптимуму уходит и этот профит). В целом, по качеству получили оптимум на варианте с бинарным деревом и температурой 2 (почти неотличимый от варианта с температурой 1).
Параллельное декодирование (что с деревом, что с uniform) действительно позволяет генерировать результат за число вставок, близкое к логарифму от числа токенов, что является оптимальным для такой схемы.