- PVSM.RU - https://www.pvsm.ru -

Чат-бот на RASA: опыт Parallels

Чат-бот на RASA: опыт Parallels - 1

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

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

Чат-бот на RASA: опыт Parallels - 2

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

Диалог в точке ХХХ.
Если пользователь ввел предложение со словами [‘купить’,’билет’], перейти в точку «СПРОСИТЬ КУДА»
Если пользователь ввел предложение со словами [‘купить’,’котлет’], перейти в точку «СПРОСИТЬ ИЗ ЧЕГО»

Сразу видно, что тут получается фигня, если пользователь ввел: «Я хотел бы купить билет в Порто», — его все равно спросят, — «А куда вы хотите поехать?». Чтобы сделать диалог более человечным, придется добавлять новые правила о том, что делать, если есть указание места.
Потом добавлять правила о том, что делать, если есть указание места и времени, и так далее.

Этот набор правил будет разрастаться достаточно быстро, но и это не самое страшное, все «правильные» пути можно описать, улучшить и вылизать.

Самое неприятное в том, что человек — существо непредсказуемое, в отличие от бота, и может в произвольный момент начать спрашивать совершенно другое. То есть в момент, когда бот уже готов забронировать билет, человек может спросить «кстати, а что там с погодой?» или «хотя нет, я хотел бы поехать на своей машине, сколько займет дорога?».

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

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

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

Проанализировав и подумав над всеми недостатками существующих ботов, разработчики RASA постарались решить проблемы следующим образом:

  • Любой ввод от пользователя проходит через «определение намерения», то есть вводимый текст при помощи машинного обучения сопоставляется одному (или нескольким) намерениям. Также из текста при необходимости вычленяются сущности и складываются в память бота.
  • Этот процесс аналогичен другим ботам, за исключением используемой модели определения намерения.
  • Следующее действие бота предсказывается при помощи машинного обучения на основе контекста, то есть предыдущих действий, намерений и состояния памяти бота.
  • При этом данных для первоначального обучения надо не очень много, и бот может вполне себе предсказывать какое действие совершать даже без конкретных примеров и правил.

Рассмотрим механизмы работы более подробно.

RASA NLU

Начнем с первого кита, на котором покоится бот. Это Natural Language Understanding, состоящий из двух основных частей: определение намерения и распознавание сущностей.

Intent detection
Определение намерения строится на модифицированном алгоритме под названием StarSpace [2] от Facebook, реализованном на Tensorflow. При этом не используются предобученные модели векторных представлений слов, что позволяет обойти ограничения данных представлений.

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

Алгоритм работает на основе векторизации предложений через bag of words и сравнения их «похожести». Примеры намерений и сами намерения преобразуются в вектора при помощи bag of words и подаются на вход соответствующим нейросетям. На выходе нейросети получается вектор для именно этого набора слов (тот самый embedding).

Обучение происходит таким образом, чтобы минимизировать функцию потерь в виде суммы попарных расстояний (либо косинусных, либо векторных произведений) между двумя похожими векторами и k-непохожими. Таким образом, после обучения каждому намерению будет поставлен в соответствие некий вектор.

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

Дополнительно к вышеперечисленным плюшкам этот подход позволяет автоматически выделять более одного намерения из предложения. К примеру: «да, я это понял. А как же мне теперь доехать до дома?» распознается как «intent_confirm+intent_how_to_drive», что позволяет строить более человечные диалоги с ботом.

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

RASA Entity recognition

Вторая часть NLU — это выделение сущностей из текста. Например, пользователь пишет «Я хочу сходить в китайский ресторан с двумя друзьями», бот должен выделить не только намерение, но и данные ему соответствующие. То есть заполнить в своей памяти, что блюда в ресторане должны быть китайскими, и что число посетителей равно трем.

Для этого используется подход, основанный на Conditional Random Fields, который был уже описан где-то на Хабре [3], так что не буду повторяться. Желающие могут почитать про данный алгоритм на сайте Стэнфорда [4].

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

RASA Stories

Второй синий кит, на котором основана RASA Core — это истории. Общая суть историй — примеры реальных диалогов с ботом, отформатированные в виде намерение-реакция. На основе этих историй обучается рекуррентная нейросеть (LSTM), которая сопоставляет предыдущую историю сообщений в требуемое действие. Это позволяет не задавать графы диалогов жестко, а также не определять все возможные состояния и переходы между ними.

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

Чтобы обучать систему, не просто записывая какие-то там придуманные диалоги, можно использовать интерактивное обучение.

Тут варианта два:
1. Заставить некоторое количество инженеров заниматься разговорами с ботом, корректируя неправильные предсказания, неправильное определение сущностей и затыки с предсказанием действия по историям.

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

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

Намерения и их примеры:

Intent_hello

  • Hi
  • Hello
  • Aloha
  • Good morning


Intent_thanks

  • Thanks
  • Nice
  • Thank you


Intent_request

  • Пока пропустим

Intent_inform

  • Пока пропустим

Далее надо сделать память бота, то есть определить слоты в которые будет записываться то, что требуется пользователю. Определим слоты:

cuisine:
type: unfeaturized
auto_fill: false
num_people:
type: unfeaturized
auto_fill: false

А теперь покажем примеры (небольшую часть) для пропущенных выше намерений. Скобки в примерах — это данные для обучения Ner_CRF, в формате [сущность](имя переменной для хранения: что храним).

intent_request_restaurant

  • im looking for a restaurant
  • can i get [swedish](cuisine) food for [six people](num_people:6)
  • a restaurant that serves [caribbean](cuisine) food
  • id like a restaurant
  • im looking for a restaurant that serves [mediterranean](cuisine) food

intent_inform

  • [2](num_people) people
  • for [three](num_people:3) people
  • just [one](num_people:1) person
  • how bout [asian oriental](cuisine)
  • what about [indian](cuisine) food
  • uh how about [turkish](cuisine) type of food
  • um [english](cuisine)

Теперь определяем историю основного пути:

* greet
— utter_greet
* Intent_request
— restaurant_form
— form{«name»: «restaurant_form»}
— form{«name»: null}
— action_book_restaurant
* thankyou
— utter_noworries

Вот и весь идеальный бот для идеального мира. Если пользователь сразу же в первом предложении указал все нужные данные, то столик забронируют. Например он пишет «i want to book table in spanish restaurant for five people». В этом случае num_people будет 5, а cuisine — spanish, что хватит боту для дальнейших действий по бронированию.

Однако, если посмотреть на примеры, видно, что данные не всегда присутствуют в необходимом количестве, а иногда их вообще нет. Так появляются неосновные диалоги.

Допустим в запросе нет данных о кухне, то есть примерно такой диалог:
Hello
Hi
I want to book restaurant for five people

Чтобы он корректно завершился, надо определить историю следующего вида:
* greet
— utter_greet
* Intent_request
— restaurant_form
— form{«name»: «restaurant_form»}
— slot{«requested_slot»: «num_people»}
— utter_ask_cuisine
* form: inform{«cuisine»: «mexican»}
— slot{«cuisine»: «mexican»}
— form: restaurant_form

И самое приятное, что если создать истории для нескольких кухонь, то, встретив незнакомую, бот предскажет следующее действие самостоятельно, хотя и будет не очень уверен. При этом если создать аналогичную историю, но где заполнен слот «cuisine», а не «num_people», то боту станет абсолютно все равно в каком порядке будет предоставлена информация о параметрах бронирования столика.

Всякие попытки увести бота с правильного пути можно пресекать двумя способами: определить возможные истории для разговоров «ни о чем», либо на все попытки начать подобный разговор — отвечать, что стоит вернуться к делу.

Так как наша компания находится в начале удивительного путешествия в мир чат-ботов, то есть шанс, что будут и новые статьи о том, какие грабли мы собирали, и что вообще делали. Stay tuned!

Чат-бот на RASA: опыт Parallels - 3

Автор: Антон

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/parallels/332686

Ссылки в тексте:

[1] RASA: https://rasa.com/

[2] StarSpace: https://arxiv.org/abs/1709.03856

[3] Хабре: https://habr.com/ru/post/241317/

[4] на сайте Стэнфорда: https://nlp.stanford.edu/software/CRF-NER.shtml

[5] Источник: https://habr.com/ru/post/470754/?utm_source=habrahabr&utm_medium=rss&utm_campaign=470754