- PVSM.RU - https://www.pvsm.ru -
В настоящее время бурно развивается индустрия чат-ботов. Сначала они были достаточно глупыми и могли вести диалог с пользователем, являясь ведущими и предлагая возможные ответы. Потом боты слегка поумнели и начали требовать от пользователя текстового ввода, чтобы из ответов вытаскивать ключевые слова. Развитие машинного обучения привело к появлению возможности общаться с ботом еще и голосом. Однако, большая часть решений не сильно далеко ушла от все того же построения графа диалогов и перехода между его узлами по ключевым словам.
Недавно в Parallels мы решили оптимизировать ряд внутренних процессов и сделать в качестве эксперимента бота для собственных нужд. После недолгих поисков мы решили попытать счастья на проекте с открытым исходным кодом RASA [1]. По утверждениям самих разработчиков они сделали чат бот третьего поколения. То есть этот бот не просто ходит по графу состояний, а умеет сохранять и использовать контекст предыдущего диалога. На сегодняшний день лучшая иллюстрация для современных чат-ботов выглядит примерно так:
То есть чат боты — это просто набор выверенных правил перехода из одной точки графа в другую. Если посмотреть на существующие решения от гигантов рынка, по сути ничего сильно отличающегося от набора правил там нет. Грубо говоря, выглядит этот набор примерно так:
Диалог в точке ХХХ.
Если пользователь ввел предложение со словами [‘купить’,’билет’], перейти в точку «СПРОСИТЬ КУДА»
Если пользователь ввел предложение со словами [‘купить’,’котлет’], перейти в точку «СПРОСИТЬ ИЗ ЧЕГО»
Сразу видно, что тут получается фигня, если пользователь ввел: «Я хотел бы купить билет в Порто», — его все равно спросят, — «А куда вы хотите поехать?». Чтобы сделать диалог более человечным, придется добавлять новые правила о том, что делать, если есть указание места.
Потом добавлять правила о том, что делать, если есть указание места и времени, и так далее.
Этот набор правил будет разрастаться достаточно быстро, но и это не самое страшное, все «правильные» пути можно описать, улучшить и вылизать.
Самое неприятное в том, что человек — существо непредсказуемое, в отличие от бота, и может в произвольный момент начать спрашивать совершенно другое. То есть в момент, когда бот уже готов забронировать билет, человек может спросить «кстати, а что там с погодой?» или «хотя нет, я хотел бы поехать на своей машине, сколько займет дорога?».
Впрочем, он может это спросить и в момент после выбора города, но до выбора времени вылета или вообще до выбора места, куда он хочет отправиться. Бота, основанного на конечных автоматах, заклинит и его механические ложноножки будут грустно подергиваться, а пользователь фрустрировать.
Тут можно (и нужно) использовать машинное обучение. Но тогда возникают новые проблемы: к примеру, если использовать обучение с подкреплением, чтобы предсказывать переходы в точки графа, то возникают вопросы: а где брать данные для этого обучения, и кто будет выставлять оценки за качество ответов?
Пользователи вряд ли согласятся учить вашего бота, да и, как показывает практика, сообщество пользователей может научить бота совсем не тому, что вам хочется, и что общество считает приличным. К тому же бот на начальном этапе будет отвечать совсем невпопад, что заставит пользователей понервничать и не связываться с такой поддержкой в принципе.
Проанализировав и подумав над всеми недостатками существующих ботов, разработчики RASA постарались решить проблемы следующим образом:
Рассмотрим механизмы работы более подробно.
Начнем с первого кита, на котором покоится бот. Это 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», что позволяет строить более человечные диалоги с ботом.
К слову, перед обучением можно из примеров создать искусственные предложения, путем перемешивания существующих, для увеличения числа примеров для обучения.
Вторая часть NLU — это выделение сущностей из текста. Например, пользователь пишет «Я хочу сходить в китайский ресторан с двумя друзьями», бот должен выделить не только намерение, но и данные ему соответствующие. То есть заполнить в своей памяти, что блюда в ресторане должны быть китайскими, и что число посетителей равно трем.
Для этого используется подход, основанный на Conditional Random Fields, который был уже описан где-то на Хабре [3], так что не буду повторяться. Желающие могут почитать про данный алгоритм на сайте Стэнфорда [4].
Дополнительно отмечу, что можно получать сущности из текста на основе шаблонов, текстов (например названия городов), а также подключить отдельным сервисом Facebook Duckling, про который тоже неплохо бы написать когда-нибудь.
Второй синий кит, на котором основана RASA Core — это истории. Общая суть историй — примеры реальных диалогов с ботом, отформатированные в виде намерение-реакция. На основе этих историй обучается рекуррентная нейросеть (LSTM), которая сопоставляет предыдущую историю сообщений в требуемое действие. Это позволяет не задавать графы диалогов жестко, а также не определять все возможные состояния и переходы между ними.
При достаточном количестве примеров сеть будет адекватно предсказывать следующее состояние для перехода вне зависимости от наличия конкретного примера. К сожалению, точное число историй для этого неизвестно и все, чем можно руководствоваться, — это фраза разработчиков: «чем больше, тем лучше».
Чтобы обучать систему, не просто записывая какие-то там придуманные диалоги, можно использовать интерактивное обучение.
Тут варианта два:
1. Заставить некоторое количество инженеров заниматься разговорами с ботом, корректируя неправильные предсказания, неправильное определение сущностей и затыки с предсказанием действия по историям.
2. Сохранять разговоры в базу данных и дальше специально обученными инженерами просматривать те диалоги, где пользователь не смог решить свою проблему, то есть переключился на человека, или же бот признался в своей беспомощности и не смог ответить.
Для того, чтобы понять механизм историй, проще всего разобрать какой-то простой пример. Допустим бронь столика в ресторане, пример, предоставленный разработчиками в разделе примеров исходного кода. Для начала определим намерения, а дальше сделаем пару историй.
Намерения и их примеры:
Intent_hello
…
Intent_thanks
…
Intent_request
Intent_inform
Далее надо сделать память бота, то есть определить слоты в которые будет записываться то, что требуется пользователю. Определим слоты:
cuisine:
type: unfeaturized
auto_fill: false
num_people:
type: unfeaturized
auto_fill: false
А теперь покажем примеры (небольшую часть) для пропущенных выше намерений. Скобки в примерах — это данные для обучения Ner_CRF, в формате [сущность](имя переменной для хранения: что храним).
intent_request_restaurant
intent_inform
Теперь определяем историю основного пути:
* 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!
Автор: Антон
Источник [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
Нажмите здесь для печати.