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

В этой статье мы расскажем о нашем опыте обучения алгоритма генерации текста, основанного на высказываниях великих личностей. В датасете для обучения модели мы собрали цитаты десяти известных философов, писателей и ученых. Конечный текст будет генерироваться на высказываниях десяти различных мыслителей. А если вы захотите “пообщаться” в кем-то конкретным – например, с Сократом или Ницше, то ноутбук, в котором велась работа, прилагается в конце. С его помощью можно будет поэкспериментировать только с предложениями выбранного вами философа.

Сбор данных

План работы

С задачей генерации текста неплохо справляются модели по типу \textbf{LSTM}, \textbf{GRU}, но все же зачастую лучшего всего работают Трансформеры. У них получается более осмысленный и понятный для человека текст. Проблема в том, что Трансформеры очень “тяжеловесные”, и их нужно долго обучать.

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

Как настоящие исследователи-практики, мы начнем с более простой модели -  \textbf{LSTM}. Вдруг она сразу покажет достойный результат, и нам не придется использовать Трансформеры. 

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

Работа с RNN

Обработка данных

Для \textbf{LSTM} объединим данные из датасета и посмотрим на предложение, токенизируем его. В самом ноутбуке можно увидеть, что получилось слишком большое количество токенов – 8760:

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

  • Отбросить редко встречающиеся слова (токены), и заменить их все на соответствующий токен \<UNK\>, присваиваемый всем токенам, которых нет в словаре. Понятно, что это очень простой и не особо эффективный вариант, так как мы сильно урезаем словарь.

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

  • Использовать \href{https://arxiv.org/pdf/1508.07909.pdf}{Byte Pair Encoding}. Более современный и мощный метод, реализованный ребятами из VK. В прикрепленном ноутбуке кратко описан принцип работы данного метода, а также прикреплены ссылки на смежные статьи и GitHub. Идея решения проблемы заключается в том, что мы находим часто встречающиеся n-gram-ы и заменяем их на какой-то символ, тем самым кодируя их. Очевидно, что в объемных текстах мы сможем найти огромное количество таких замен.

Вот небольшая поясняющая картинка:

Конечно же мы выберем последний метод работы с данными.

Обучение модели

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

После этого необходимо усреднить лосс по полученным предсказаниям. Вкратце, это будет происходить следующим образом: на вход приходит последовательность x_1, x_2, ..., x_{n}. Требуется на каждом этапе времени t по символам x_1, ..., x_{t - 1} предсказывать символ x_t. Для этого на вход нужно подать последовательность \<BOS\>, x_1, x_2..., x_{n} и в качестве таргетов взять последовательность x_1, x_2, ..., x_{n},  \<EOS\>. В данном случае \<BOS\>, \<EOS\> — специальные символы начала и конца предложения (любые новые символы, которых нет в словаре). При обучении мы подаем на вход истинные токены и предсказываем следующий токен, но при тестировании истинных токенов нет. Поэтому при тестировании стоит в качестве следующего символа передавать предыдущий предсказанный символ. Соответственно поясняющая картинка:

Также для дальнейшей работы необходимо уравнять все предложения по длине. Идея решения этой проблемы незамысловатая: выберем нужную длину предложения, и для недостающих по длине предложений добавим специальный токен  \<PAD\>, означающий, что предложение расширилось, и не нужно будет после эмбеддинга пускать по таким токенам градиент. Слишком длинные предложения мы просто обрежем. Все тонкости реализации можно посмотреть в ноутбуке. 

В качестве модели была взята следующая архитектура:

В качестве первого слоя используется эмбеддинг, который будет переводить токены в удобный для машины вид. Мы специально передаем индекс токена \<PAD\>, чтобы, как уже отмечалось, по таким словам не протекал градиент. Слои свертки и нормализации используются в виде feature extactor – некоторые эксперименты с моделью показали, что это дает неплохой результат. Далее идет сама рекуррентная сеть, и в конце – линейный слой в виде классификатора. 

В итоге после 30 эпох обучения получились следующие результаты:

Вполне неплохое значение метрики. Интересно посмотреть на саму генерацию текста.

Генерация текста

Есть несколько способов генерации текста. Вот некоторые из них:

  • Жадная генерация текста: на каждом шаге берем токен с наибольшей вероятностью. Очевидны проблемы с данным подходом: выбрав наиболее вероятный токен, мы можем потерять осмысленность предложения, ведь не всегда наиболее вероятное слово является самым подходящим.

  • Top k sampling: для предсказания следующего токена посмотрим на распределение вероятностей. Выбираем k токенов с максимальной вероятностью, и из них сэмплируем один токен с вероятностью, пропорциональной предсказанной для этого токена вероятности. Понятно, что это частично решает проблему жадной генерации, но не полностью.

  • Beam search: смотрим на несколько вариантов построения предложения, и в некоторые моменты времени отсекаем предложения с наименьшей в целом вероятностью, и так разрастаемся. В конце берем предложение с наибольшей вероятностью. Вот картинка поясняющая данный метод:

В данном случае мы будем использовать первые два метода из-за простоты их реализации. 

  • Жадная генерация

Выясним, что выдает наша модель в случае жадной генерации. В качестве проверки возьмем начало предложения в виде: "Красота есть во всем, но". Посмотрим, как модель продолжит высказывание. Результаты получились следующими:

Видно, что текст получился бессвязным и не осмысленным.

  • Top k sampling

Попробуем с тем же предложением продолжить с методом генерации \textit{Top k sampling}. Результаты получились следующими:

Видно, что текст аналогично получился бессмысленным.

Работа с Transformers

RNN справились плохо, поэтому попробуем более тяжеловесные решения, а именно Трансформеры. Они почти всегда выдают неплохой результат, особенно в данной задаче. Почему мы, в таком случае, сразу не воспользовались этой моделью? Дело в том, что при всей мощности данной модели, ее необходимо долго обучать. Даже на одну эпоху некоторые модели учатся часами. Поэтому чаще используют уже обученные модели, и для специфических случаев дообучают их. Так мы и сделаем, возьмем модель с известного сайта \href{https://huggingface.co}{huggingface}. После недолгих поисков, можно найти следующую модель, которая хорошо подходит для нашей задачи:

Это уже обученная модель от Сбербанка, основанная на известном Трансформере \textbf{GPT-2}. 

Проверка предобученной модели

Трансформеры славятся тем, что хорошо работают прямо из коробки. Поэтому сразу посмотрим, как дополняет предложение из предыдущего пункта данная модель:

Текст выглядит намного более осмысленным, можно даже зачитать своей даме сердца. Но все же интересно: если дообучить модель на нашем датасете, то сможет ли она смоделировать корректные высказывания великих людей?

Обработка данных

Обработка данных во многом повторяет методы, использованные в работе с  \textbf{RNN}. Добавляем токены конца и начала предложения. Приводим все предложения к одной длине с помощью обрезки, либо же дополнения с помощью все того же символа \<PAD\>. Только в данном случае есть небольшое различие, а именно в том, что для обработки данных символов Трансформеру необходимо также передавать так называемую “маску”, в которой стоят нули там, где градиент не должен течь. То есть там, где стоят токены \<PAD\>, в остальных местах стоят единички.

Дообучение модели и проверка результатов

Дообучение модели происходит аналогично обучению для модели \textbf{RNN}, только намного дольше, даже несмотря на то, что эпох здесь меньше. Зато результаты получились впечатляющими, а именно продолжение все того же высказывания:

Получилось очень похоже на высказывание великого человека с именем "Не знаю". 

В целом результат неплох. 

Выводы

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

Литература

Ноутбук в котором велась работа URL: \url{https://colab.research.google.com/drive/1kbzCidpjvpI0AmAKPoamP8xn1jYzAecI?usp=sharing};

Комментарии (0)