В наше время голосовые роботы набирают огромную популярность, от банального заказа такси, до продаж клиентам. Создание голосового бота сводится к трем базовым этапам.
- Распознавание голоса ASR.
- Выяснение смысла сказанного и поиск необходимых сущностей в тексте(к примеру адрес, сумма, ФИО итд )
- Генерация ответа, преобразование текста в речь TTS. Мы пройдем от пути создания простого текстового бота до интеграции с системой телефонии freeswitch с распознаванием голоса и озвучиванием подготовленных ответов. Данная статья описывает используемые инструменты и путь по их интеграции вместе для создания голосового робота.
В первой части поговорим о создании простого текстового бота, которого можно встроить в чат.
Пример разговора Б-бот Ч-человек
Б:привет я могу посоветовать тебе фильм или сериал
Ч:привет
Б:здорово дружище
Ч:какой фильм про космос посмотреть?
Б:аполлон тринадцать
Ч:ты любишь алису?
Б:алиса? я лучше алисы это точно
Ч:ты
Б:не понял вас
Немного теории
Бот работает на принципе понятия намерения пользователя. На каждое намерение есть список заготовленных ответов. Чтобы бот мог понять намерение пользователя, необходимо обучить модель на датасете с намерениями и фразами, которые могут активировать это намерение
К примеру
Намерение: Поздороваться
Возможные фразы: привет, добрый день, дратути…
Ответ: Привет
Намерение: Попрощаться
Возможные фразы: Пока, До свидания, Прощай…
Ответ: Пока
Шаг 1: Предобработка датасета
За основу взят датасет из открытого обучения skillbox по написанию чат бота в телеграмм, который может пообщаться с вами на тему кино. Выложить не могу по понятным причинам.
Предобработка является очень важным этапом.
Первым делом уберем все символы и цифры из текста и приведем все к нижнему регистру.
Далее необходимо исправить опечатки и ошибки в словах.
Сервара - сервера
Эта задача не самая простая, есть хороший инструмент от Yandex под названием Speller, но он ограничен по числу запросов в день, поэтому будем искать бесплатные альтернативы
Для python есть замечательная библиотека jamspell, которая неплохо исправляет опечатки. Для нее есть хорошая предобученная модель для русского языка. Прогоним все входные данные через эту библиотеку. Для голосового бота этот шаг не так актуален, так как система распознавания речи не должна выдавать слова с ошибками, она может выдать не то слово. Для чат бота этот процесс необходим. Так же для минимизации влияния опечаток можно обучать сеть не на словах, а на n-граммах.
N-граммы, это части слов, состоящие из n букв. к примеру 3-граммы для слова привет будут при, рив, иве, вет. Это поможет быть менее зависимым от опечаток и повысит точность распознавания.
Далее необходимо привести слова к нормальной форме, так называемый процесс лемматицизии слов.
Доброе утро - добрый утро
Для данной задачи хорошо подходит библиотека rulemma .
Так же можно из фраз убрать стоп слова, которые несут мало смысловой нагрузки, но увеличивают размер нейронной сети (я брал из библиотеки nltk stopwords.words(«russian»)), но в нашем случае лучше их не убирать, так как пользователь может ответить роботу только одним словом, а оно может быть из списка стоп слов.
Шаг 2: Преобразование датасета в понятный для NN вид
Для начала необходимо сформировать словарь из всех слов датасета.
Для обучения модели понадобится перевести все слова в oneHotVector
Это массив, которые равен длине словаря слов, в котором все значения равны 0 и только одно равно 1 в позиции слова в словаре.
Далее все входные фразы преобразовываются в 3-х мерный массив, который содержит все фразы, фраза содержит список слов в формате oneHotVector — это и будет наш входной датасет X_train.
Каждой входной фразе нужно сопоставить подходящее к ней намерение так же в формате oneHotVector — это наш выход y_train.
Шаг 3: Создание модели
Для небольшого бота достаточно маленькой модели с двумя слоями lstm и двумя полносвязными слоями:
model = Sequential()
model.add(LSTM(64,return_sequences=True,input_shape=(description_length, num_encoder_tokens)))
model.add(LSTM(32))
model.add(Dropout(0.25))
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(len(set(y)), activation='softmax'))
Компилируем модель и подбираем оптимизитор, я выбрал adam, так как он давал лучший результат.
Шаг 4: Обучение модели
После подготовки датасета и компиляции модели можно запустить ее обучение. Так как датасет небольшой, пришлось обучать модель на 250-500 эпохах, после чего происходило переобучение.
Шаг 5: Пытаемся поговорить с нашим ботом
Чтобы поговорить с нашим ботом, надо на вход обученной модели подать правильно подготовленные данные. Пользовательский ввод необходимо обработать так же как и датасет из первого шага. Дальше преобразовать его в вид, понятный NN как во втором шаге используя тот же словарь слов и их индексы, чтобы входные слова соответствовали словам, на которых проводилось обучение.
Обработанный ввод подаем в модель и получаем массив значений, в котором присутствуют вероятности попадания нашей фразы в то или иное намерение, нам же необходимо выбрать то намерение, у которого самая высокая вероятность, это можно сделать через библиотеку numpy
np.argmax(results)
Необходимо оценить уверенность сети в этом ответе и подобрать порог, при котором выдавать пользователю failure phrases, типа — я вас не понял. Для своих целей я установил порог в 50% уверенности, ниже которого бот скажет, что не понял вас.
Далее из списка наших намерений выбираем подходящий ответ и выдаем его пользователю
P.S.: Модель можно обучить не только на основе слов но и разделяя фразы на буквы или n-граммы, в таком случае понадобится модель посерьезнее.
model = Sequential()
model.add(LSTM(512,return_sequences=True,input_shape=(description_length, num_encoder_tokens)))
model.add(LSTM(256))
model.add(Dropout(0.25))
model.add(Dense(len(set(y)), activation='softmax'))