Чатбот это набор инструментов, позволяющих организовывать диалоговые сессии пользователя примерно такого вида:
B:> Привет, буду рад поговорить
H:> Как тебя зовут?
B:> кеша
B:> А как тебя зовут?
H:> Меня зовут Илья.
B:> Приятно познакомиться.
H:> Сколько будет 2 плюс 2?
B:> 4
H:> Меня как зовут?
B:> илья
H:> Сколько сейчас времени?
B:> 17 часов 47 минут
В этом примере реплики чатбота отмечены символами B:>, а реплики человека - символами H:>.
Данный чатбот архитектурно сочетает два разных подхода. Во-первых, ответ на заданный вопрос ищется в базе знаний с помощью NLP моделей (retrieval-based архитектура). Во-вторых, чатбот может генерировать реплики без использования базы знаний (см. генеративные smalltalk-правила). База знаний состоит из двух больших частей - база фактов и FAQ. В базе фактов ищется факт, на основе которого можно сформулировать ответ, даже если текст ответа в явном виде не содержится в факте. В FAQ ищутся готовые ответы на типовые вопросы, при этом текст ответа выдается собеседнику без изменений.
Есть два способа запуска чатбота под Linux.
Простой способ - выкачать и запустить docker-образ текущего релиза. Допустим, chatbot.tar.gz это скачанный файл, тогда для запуска нужно выполнить команды:
docker image load -i chatbot.tar.gz
docker run -ti -e PYTHONIOENCODING=utf-8 chatbot
Текущая пре-альфа версия чатбота не оптимизирована и достаточно долго загружает файлы словарей и моделей. После появления приглашения можно ввести тестовые вопросы:
Привет, как тебя зовут?
Наверное, ты робот, да?
Что ты любишь делать?
Ты в шахматы умеешь играть?
А в шашки?
Ты знаешь, что такое белый карлик?
Более сложный способ - выкачать содержимое репозитория, установить ряд необходимых питоновских библиотек, включая:
pip install git+https://github.com/Koziev/rutokenizer
pip install git+https://github.com/Koziev/rupostagger
pip install git+https://github.com/Koziev/ruword2tags
pip install git+https://github.com/Koziev/rusyllab
Затем запустить .../script/console_bot.sh
Используемые базы знаний и FAQ, а также наборы правил ведения диалога указываются в профиле, который загружается при старте экземпляра бота. В скрипте console_bot.sh можно увидеть указание на тестовый профиль [profile_1.json](data/rules.yaml](https://github.com/Koziev/chatbot/blob/master/data/profile_1.json), позволяющий боту отвечать на несколько простых вопросов. В этом профиле в качестве базы знаний указан файл [profile_facts_1.dat]((data/rules.yaml](https://github.com/Koziev/chatbot/blob/master/data/profile_facts_1.dat). Формат этого файла описан в шапке файла. Среди разных фактов там можно увидеть запись об имени бота:
Меня зовут Вика
Когда чатбот обрабатывает вопрос "Как тебя зовут", он определяет, что первый факт наиболее релевантен для ответа на заданный вопрос, и далее запускает процедуру построения ответа. Само имя "Вика" нигде не "зашито" в языковых моделях. Поэтому для его смены не нужно переобучать нейросетки, достаточно отредактировать данную запись и перезапустить бота.
При добавлении новых фраз в вышеуказанные файлы следует по возможности воздерживаться от использования лексики, неизвестной языковым моделям чатбота. С определенными оговорками, список слов в файле tmp/dataset_words.txt известен чатботу и модет использоваться.
Остальные правила для движка чатбота собраны в файле data/rules.yaml.
Правила собраны в файле data/rules.yaml с форматом YAML. Смотрите комментарии в файле, поясняющие структуру правил, а также пояснения в разделе "Порядок применения правил" далее. Привязка набора правил к экземпляру бота выполняется в профиле - текстовом файле типа [profile_1.json](data/rules.yaml](https://github.com/Koziev/chatbot/blob/master/data/profile_1.json).
Знания функционально разделены на 2 части.
FAQ-правила - состоят из пар "вопрос - ответ". Для удобства обработки перефразировок вопросов для одного ответа может быть несколько. Когда движок бота обрабатывает вопрос собеседника, он ищет среди FAQ-правил наиболее близкий опорный вопрос. Если поиск удался, то в качестве ответной реплики бота будет выдан текст из этого FAQ-правила. Сопоставление опорных вопросов и запроса собеседника выполняется с помощью модели синонимичности. В демо-версии чатбота FAQ-правила собраны в файле data/faq2.txt. Для примера, введите вопрос "Что такое белые карлики" и бот выдаст соответствующую инфомацию:
H:> что такое белые карлики
B:> Белые карлики — проэволюционировавшие звёзды с массой, не превышающей
предел Чандрасекара
F-правила, или просто факты, представляют из себя одиночные предложения, описывающие элементарные факты о самом чатботе, собеседнике или окружении. Получив вопрос собеседника, чатбот ищет в этой базе факт, максимально релевантный заданному вопросу (сравни с FAQ-правилами). Если такой факт найден, то он далее поступает в движок генерации ответа. Сопоставление вопроса собеседника и предпосылок производится с помощью модели релевантности. В демо-версии чатбота факты собраны в файле [profile_facts_1.dat]((data/rules.yaml](https://github.com/Koziev/chatbot/blob/master/data/profile_facts_1.dat). К примеру, ответ на вопрос "Как тебя зовут?" подразумевает поиск соответствующего факта - см. абзац про кастомизацию бота.
-
Если есть история диалога (>1 реплики), то реплика собеседника прогоняется через модель интерпретации для восстановления полной фразы, раскрытия анафоры, гэппинга и т.д.
-
Среди comprehension правил ищется достаточно близкий вариант фразы в if блоке. Если нашлось, то вместо исходной фразы дальше будет обрабатываться then-фраза из найденного правила. Таким образом выполняется некоторая нормализация фраз собеседника.
-
Определяется intent с помощью обученного на датасете data/intents.txt классификатора (см. далее).
-
Определяется грамматическая модальность - является ли реплика вопросом, утверждением или приказом.
-
Для приказов: пытаемся найти правило для обработки (секция rules в rules.yaml) и выполняем его. При поиске используется либо определенный intent (if-часть содержит ключевое слово intent), либо проверяется синонимичность с помощью модели синонимичности. Если правило не найдено, то вызывается дефолтный обработчик - пользовательская функция, зарегистрированная в on_process_order. Если и он не обработал приказ, то будет сказана фраза "unknown_order" в rules.yaml
-
Для утверждений: пытаемся найти правило обработки (секция rules в rules.yaml) и выполнить его. Далее, факт сохраняется в базе знаний. Наконец, пытаемся найти smalltalk-правило: это правило в группе rules (rules.yaml), в котором опорная часть (if) и результативная часть (then) заданы с ключевым словом text. Ищется правило, в котором опорная часть максимально синонимична входной фразе, если найдено - чатбот скажет фразу, которая указана в then-ветке.
-
Для вопросов: сначала проверяется, нет ли похожего (модель синонимичности) вопроса среди FAQ-правил (файл faq2.txt). Если есть - выдается содержимое найденного FAQ-правила. Иначе начинается процедура генерации ответа. С помощью модели релевантности (см. отдельный раздел про ее дообучение и валидацию) ищутся максимальной релевантные предпосылки в файлах premises*.txt. Если не найдена достаточно релевантная предпосылка, то выдается фраза "no_relevant_information" из rules.yaml.
В ходе генерации ответа может потребоваться ответить "да" или "нет". Эти фразы находятся в rules.yaml в разделе "answers".
При добавлении новых фактов в базу знаний может возникнуть ситуация, что модель релевантности не знакома с новой лексикой и сильно ошибается при поиске подходящего факта. В этом случае модель релевантности можно переобучить, добавив новые сэмплы в обучающий датасет. Обучающий датасет
- это текстовый tab-separated файл premise_question_relevancy.csv. В колонке premise находятся предпосылки (факты), question - вопросы. Колонка relevance содержит 1 для нелевантных пар, 0 для нерелевантных. Таким образом, чтобы модель считала предпосылку и вопрос релевантными, надо добавить к этому датасету запись с relevance=1. Следует избегать добавления повторов, так как это будет приводить к искажению оценок точности при обучении.
После изменения файла premise_question_relevancy.csv нужно запустить обучение скриптом train_lgb_relevancy.sh. Обучение идет примерно полчаса. В результате в каталоге .../tmp будут созданы новые файлы lgb_relevancy.*, содержащие правила модели релевантности.
Любые ошибки при работе модели релевантности негативно сказываются на общем качестве диалогов, поскольку многие другие части чатбота используют результаты выбора предпосылок из базы знаний в качестве входной информации. Чтобы контролировать качество этой модели, желательно верифицировать ее работу на тестовых вопросах и наборе тестовых предпосылок. Для выполнения этой верификации мы используем простой консольный скрипт query2_lgb_relevancy.sh. Он загружает текущую обученную модель релевантности и список предпосылок из базы знаний (файлы ../data/premises*.txt) и тренировочного датасета premise_question_relevancy.csv. Затем с консоли вводится проверочный вопрос, модель вычисляет его релевантность по всем предпосылкам и выводит список из нескольких самых релевантных. Если в этом списке есть явно нерелевантные предпосылки с высокой оценкой (допустим, выше 0.5), то есть смысл добавить такие предпосылки с вопросом в качестве негативных примеров в датасет premise_question_relevancy.csv и переобучить модель релевантности, запустив скрипт train_lgb_relevancy.sh.
С помощью скрипта scripts/query2_lgb_synonymy.sh можно искать примеры неверной работы модели синонимичности. В консоли вводится фраза, для которой модель вычисляет похожесть к набору фраз из датасетов. Результаты, ранжированные по убыванию похожести, выводятся в консоль. Если среди фраз с похожестью > 0.5 есть явно ошибочные, их можно внести как негативные примеры (в файл paraphrases.txt или в собранный датасет synonymy_dataset.csv), затем переобучить модель скриптом scripts/train_lgb_synonymy.sh
С помощью модели intent'а можно присвоить фразе собеседника одну метку из набора возможных и далее обрабатывать фразу с учетом этой метки правилами (раздел rules в rules.yaml).
Данные для обучения находятся в текстовом файле data/intents.txt Его формат прост и описан в комментарии в начале файла.
После модификации этого файла необходимо собрать датасет с помощью PyModels/preparation/prepare_intents_dataset.py, затем запустить обучение классификатора с помощью scripts/train_intent_classifier.sh
-
Новая информация (факты), сообщаемые пользователем, могут немедленно добавляться в базу знаний и использоваться в последующих ответах наряду с остальными фактами. Именно так в базу знаний бота попадает имя собеседника, которое он сообщает. Переобучение моделей при этом не требуется.
-
Сессионность: сообщаемые пользователем факты сохраняются в отдельном разделе базы знаний, привязанном к пользователю, так что сессии для разных пользователей могут оперировать противоречащей или конфиденциальной информацией.
-
Ориентированность на клиент-серверный механизм; способ доставки сообщений отвязан от ядра системы и может быть реализован в виде текстовой консоли, бота для мессенджера, браузерного клиента для веб-сервиса и т.д.
-
Ответы чатбота не программируются скриптами или аналогичными средствами, а генерируются обучаемыми моделями. В необходимых случаях используются нейросетевые deep learning модели, а также классические NLP алгоритмы.
-
Архитектура движка чатбота полностью отделена от каналов доставки сообщений. Например, есть варианты чатботов для консоли и в виде веб-сервиса.
Благодаря использованию посимвольных встраиваний для слов чатбот можно обучить отвечать даже на вопросы, относящиеся к символьному представлению слов:
B:> Привет, буду рад пообщаться
H:> сколько букв в слове мама?
B:> 4
В более широком смысле посимвольные встраивания для слов позволяют боту лучше учитывать некоторые лексические особенности русского языка, в частности - однокоренные слова.
Специальная модель интерпретации ответов собеседника позволяет чатботу понимать неполные реплики, опираясь на контекст беседы, и далее учитывать их как обычные факты:
B:> Здравствуй
H:> как тебя зовут?
B:> кеша
B:> А как тебя зовут?
H:> илья
B:> Приятно познакомиться.
H:> как меня зовут?
B:> илья
В данном примере чатбот задает вопрос "А как тебя зовут", рассчитывая заполнить пробел в своей базе знаний. Собеседник сообщает свое имя, и его ответ интерпретируется как "Меня зовут Илья". Эта реплика рассматривается как полноценный факт и запоминается в базе знаний, что видно на последних двух репликах диалога, где собеседник спрашивает у бота свое имя и получает его.
Способ генерации ответа выбирается в зависимости от контекста. В частности, чатбот может выполнять простые арифметические операции:
B:> Привет, буду рад поговорить
H:> Чему равно 2 плюс 3?
B:> 5
Реализован в файле console_chatbot.py. Запуск под Linux выполняется скриптом scripts/console_bot.sh
Это отладочная консоль, в которую помимо реплик чатбота выводятся также различные диагностические сообщения.
Список моделей:
Посимвольное встраивание слово в вектор фиксированной длины wordchar2vector_model.py
Определение способа генерации ответа nn_model_selector.py
Определение слов, копируемых из предпосылки в ответ nn_wordcopy3.py
Определение достаточности набора предпосылок для генерации ответа nn_enough_premises_model.py
Генерация ответов yes/no nn_yes_no_model.py
Посимвольная генерация ответа xgb_answer_generator_model.py
Определение релевантности предпосылки и вопроса lgb_relevancy_detector.py
Интерпретация реплики собеседника (раскрытие анафоры, дополнение ответа etc) nn_interpreter.py
Определение синонимии фраз nn_synonymy_detector.py
Набор моделей и конкретная реализация могут сильно меняться по мере развития проекта, поэтому список является не окончательным.
Описание тренировки и использования модели посимвольного встраивания слов смотрите на отдельной странице.
Также доступно описание модели для определения релевантности факта и вопроса.
Модель встраивания слов в векторное пространство тренируется с помощью скрипта https://gist.github.com/Koziev/e39689adec30ae5bf6afaa1ca47c08e5 (Python, gensim) и текстового корпуса размером около 10 Гб, в котором текст предварительно разбит на слова и приведен к нижнему регистру.
Все необходимые файлы моделей, которые тренируются на диалоговых корпусах и используются чатботом, выложены в папке tmp репозитория.
Скрипты запуска бота, например console_bot.sh указывают именно этот подкаталог в качестве местоположения моделей. Таким образом, после скачивания репозитория чатбот должен быть доступен для использования без предварительного обучения.