Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dbklim/voice_chatbot

Chatbot in russian with speech recognition using PocketSphinx and speech synthesis using RHVoice. The AttentionSeq2Seq model is used. Imlemented using Python3+TensorFlow+Keras.
https://github.com/dbklim/voice_chatbot

attention-model bot chatbot flask gensim keras lstm natural-language-processing nlp pocketsphinx restful-api rhvoice russian seq2seq speech-recognition speech-synthesis tensorflow text-processing word2vec

Last synced: 2 days ago
JSON representation

Chatbot in russian with speech recognition using PocketSphinx and speech synthesis using RHVoice. The AttentionSeq2Seq model is used. Imlemented using Python3+TensorFlow+Keras.

Awesome Lists containing this project

README

        

# Voice ChatBot

Проект состоит из двух частей - голосовой бот и RESTful сервер для взаимодействия с ним.

Для запуска бота локально нужно выполнить `python3 bot.py` (или `run_bot.sh`) и в предложенном меню выбрать желаемый вариант работы ([подробнее тут](https://github.com/Desklop/Voice_ChatBot#бот)).

Для запуска RESTful сервера, предоставляющего интерфейс для взаимодействия с модулями голосового бота, нужно выполнить `python3 rest_server.py` (или `run_rest_server.sh`) ([подробнее тут](https://github.com/Desklop/Voice_ChatBot#restful-сервер)).

Для сборки docker-образа на основе RESTful сервера выполните `sudo docker build -t voice_chatbot:0.1 .` ([подробнее тут](https://github.com/Desklop/Voice_ChatBot#docker-образ-с-restful-сервером)).

**ВНИМАНИЕ!** Это был мой дипломный проект, по этому архитектура и код тут не очень хорошие, я это понимаю и как появится время - всё обновлю.

## Зависимости

Полный список всех необходимых для работы зависимостей:
1. Для Python3.5-3.6: decorator, Flask (>=1.0.2), Flask-HTTPAuth (>=3.2.4), gensim, gevent (>=1.3.7), h5py, Keras (>=2.2.4), matplotlib, numpy, pocketsphinx, pydub, simpleaudio, [recurrentshop](https://github.com/datalogai/recurrentshop), requests, [seq2seq](https://github.com/Desklop/seq2seq), tensorflow[-gpu].
2. Для Ubuntu: ffmpeg, x264, x265, make, git, scons, gcc, pkg-config, pulseaudio, libpulse-dev, portaudio19-dev, libglibmm-2.4-dev, libasound-dev, libao4, libao-dev, sonic, sox, swig, flite1-dev, net-tools, zip, unzip.
3. Данные для обучения и готовые модели: необходимо вручную загрузить из Google Drive архив [`Voice_ChatBot_data.zip`(3Gb)](https://drive.google.com/uc?export=download&id=1_hUmsu1d2bigS2Lpkm0VRNiL7GFF_AvE) и распаковать в корень проекта (папки `data` и `install_files`).

Если вы используете Ubuntu 16.04 или выше, для установки всех пакетов можно воспользоваться `install_packages.sh` (проверено в Ubuntu 16.04 и 18.04). По умолчанию будет установлен [TensorFlow](https://www.tensorflow.org/install/) для CPU. Если у вас есть видеокарта nvidia с утановленным официальным драйвером версии 410, вы можете установить [TensorFlowGPU](https://www.tensorflow.org/install/gpu). Для этого необходимо при запуске `install_packages.sh` передать параметр `gpu`. Например:
```
./install_packages.sh gpu
```
В этом случае из моего Google Drive будет загружено 2 архива:
1. [`Install_CUDA10.0_cuDNN_for410.zip`(2.0Gb)](https://drive.google.com/uc?export=download&id=19kFTwkF7BOXmu1mYBTfOMoxhWDXfOytS) с CUDA 10.0 и cuDNN 7.5.0 (если был передан параметр `gpu`). Установка будет выполнена автоматически, но если что-то пошло не так, есть инструкция `Install.txt` в загруженном архиве.
2. [`Voice_ChatBot_data.zip`(3Gb)](https://drive.google.com/uc?export=download&id=1_hUmsu1d2bigS2Lpkm0VRNiL7GFF_AvE) с данными для обучения и готовыми моделями. Он будет автоматически распакован в папки `data` и `install_files` в корне проекта.

Если вы не можете или не хотите воспользоваться скриптом для установки всех необходимых пакетов, нужно вручную установить [RHVoice](https://github.com/Olga-Yakovleva/RHVoice) и [CMUclmtk_v0.7](https://cmusphinx.github.io/wiki/cmuclmtkdevelopment/), используя инструкции в `install_files/Install RHVoice.txt` и `install_files/Install CMUclmtk.txt`. Так же необходимо скопировать файлы языковой, акустической модели и словаря для PocketSphinx из `temp/` в `/usr/local/lib/python3.6/dist-packages/pocketsphinx/model` (у вас путь к `python3.6` может отличаться). Файлы языковой модели `prepared_questions_plays_ru.lm` и словаря `prepared_questions_plays_ru.dic` необходимо переименовать в `ru_bot_plays_ru.lm` и `ru_bot_plays_ru.dic` (либо изменить их название в `speech_to_text.py`, если у вас есть своя языковая модель и словарь).

# Бот

Основа бота - рекуррентная нейронная сеть, модель AttentionSeq2Seq. В текущей реализации она состоит из 2 двунаправленных LSTM ячеек в кодировщике, слоя внимания и 2 LSTM ячеек в декодировщике. Использование модели внимания позволяет установить "мягкое" соответствие между входными и выходными последовательностями, что повышает качество и производительность. Размерность входа в последней конфигурации равна 500 и длина последовательности 26 (т.е. максимальная длина предложений в обучающей выборке). Слова переводятся в вектора с помощью кодировщика word2vec (со словарём на 445.000 слов) из бибилотеки gensim. Модель seq2seq реализована с помощью Keras и RecurrentShop. Обученная модель seq2seq (веса которой находятся в `data/plays_ru/model_weights_plays_ru.h5`) с параметрами, которые указаны в исходных файлах, имеет точность 99.19% (т.е. бот ответит на 1577 из 1601 вопросов правильно).

На данный момент предусмотрено 3 набора данных для обучения бота: 1601 пара вопрос-ответ из различных пьес (`data/plays_ru`), 136.000 пар из различных произведений (`data/conversations_ru`, спасибо [NLP Datasets](https://github.com/Koziev/NLP_Datasets)) и 2.500.000 пар из субтитров к 347 сериалам (`data/subtitles_ru`, подробнее в [Russian subtitles dataset](https://github.com/Desklop/Russian_subtitles_dataset)). Модели word2vec обучены для всех наборов данных, но нейронная сеть обучена только на наборе данных из пьес.

Обучение модели word2vec и нейронной сети на наборе данных из пьес без изменения параметров длится примерно 7.5 часов на nvidia gtx1070 и intel core i7. Обучение на наборах данных из произведений и субтитров на данном железе будет длиться минимум нескольких суток.

Бот умеет работать в нескольких режимах:
1. Обучение модели seq2seq.
2. Работа с обученной моделью seq2seq в текстовом режиме.
3. Работа с обученной моделью seq2seq с озвучиванием ответов с помощью [RHVoice](https://github.com/Olga-Yakovleva/RHVoice).
4. Работа с обученной моделью seq2seq с распознаванием речи с помощью [PocketSphinx](https://github.com/cmusphinx/pocketsphinx).
5. Работа с обученной моделью seq2seq с озвучиванием ответов и распознаванием речи.

---

## 1. Обучение модели seq2seq

Обучающая выборка состоит из 1600 пар вопрос %% ответ, взятых из различных русских пьес. Она хранится в файле `data/plays_ru/plays_ru.txt`. Каждая пара вопрос %% ответ пишется с новой строки, т.е. на одной строке только одна пара.

Все необходимые для обучения этапы выполняются методами `prepare()` или `load_prepared()` и `train()` класса `TextToText` из модуля `text_to_text.py` и метод `build_language_model()` класса `LanguageModel` из модуля `preparing_speech_to_text.py`. Или можно использовать функцию `train()` модуля `bot.py`.

Для запуска бота в режиме обучения нужно запустить `bot.py` с параметром `train`. Например, так:
```
python3 bot.py train
```

Или можно просто запустить `bot.py` (или выполнить `run_bot.sh`) и в предложенном меню выбрать режим 1 и 1.

Процесс обучения состоит из нескольких этапов:

**1. Подготовка обучающей выборки.**

Для подготовки обучающей выборки предназначен модуль `source_to_prepared.py`, состоящий из класса `SourceToPrepared`. Данный класс считывает обучающую выборку из файла, разделяет вопросы и ответы, удаляет неподдерживаемые символы и знаки препинания, преобразует полученные вопросы и ответы в последовательности фиксированного размера (с помощью слов-наполнителей ``). Так же этот класс осуществляет подготовку вопросов к сети и обработку её ответов. Например:

Вход: `"Зачем нужен этот класс? %% Для подготовки данных"`

Выход: `[['', ..., '', '?', 'класс', 'этот', 'нужен', 'Зачем', ''], ['Для', 'подготовки', 'данных', '', '', ..., '']]`

Обучающая выборка считывается из файла `data/plays_ru/plays_ru.txt`, преобразованные пары [вопрос,ответ] сохраняются в файл `data/plays_ru/prepared_plays_ru.pkl`. Так же при этом строится гистограмма размеров вопросов и ответов, которая сохраняется в `data/plays_ru/histogram_of_sizes_sentences_plays_ru.png`.

Для подготовки обучающей выборки из набора данных на основе пьес достаточно передать методу `prepare_all()` имя соответствующего файла. Что бы подготовить обучающую выборку из набора данных на основе произведений или субтитров, нужно вначале вызвать `combine_conversations()` или `combine_subtitles()`, а после вызывать `preapre_all()`.

**2. Перевод слов в вещественные вектора.**

За этот этап отвечает модуль `word_to_vec.py`, состоящий из класса `WordToVec`. Данный класс кодирует последовательности фиксированного размера (т.е. наши вопросы и ответы) в вещественные вектора. Использутся кодировщик word2vec из библиотеки gensim. В классе реализованы методы для кодирования сразу всех пар [вопрос,ответ] из обучающей выборки в вектора, а так же для кодирования вопроса к сети и декодирования её ответа. Например:

Вход: `[['', ..., '', '?', 'класс', 'этот', 'нужен', 'Зачем', ''], ['Для', 'кодирования', 'предложений', '', '', ..., '']]`

Выход: `[[[0.43271607, 0.52814275, 0.6504923, ...], [0.43271607, 0.52814275, 0.6504923, ...], ...], [[0.5464854, 1.01612, 0.15063584, ...], [0.88263285, 0.62758327, 0.6659863, ...], ...]]` (т.е. каждое слово кодируется вектором с длинной 500 (это значение можно изменить, аргумент `size` в методе `build_word2vec()`))

Пары [вопрос,ответ] считываются из файла `data/plays_ru/prepared_plays_ru.pkl` (который был получен на предыдущем этапе, для расширения и повышения качества модели рекомендуется дополнительно передать методу `build_word2vec()` предобработанный набор данных из субтитров `data/subtitles_ru/prepared_subtitles_ru.pkl`), закодированные пары сохраняются в файл `data/plays_ru/encoded_plays_ru.npz`. Так же в процессе работы строится список всех используемых слов, т.е. словарь, который сохраняется в файле `data/plays_ru/w2v_vocabulary_plays_ru.txt`. Также сохраняется обученная модель word2vec в `data/plays_ru/w2v_model_plays_ru.bin`.

Для перевода слов из обучающей выборки в вектора достаточно передать методу `build_word2vec()` имя соответствующего файла и задать желаемые параметры.

**3. Обучение сети.**

На этом этапе выполняется обучение модели seq2seq на уже подготовленных ранее данных. За это отвечает модуль `text_to_text.py`, состоящий из класса `TextToText`. Данный класс осуществляет обучение сети, сохранение модели сети и весовых коэффициентов, и позволяет удобно взаимодействовать с обученной моделью.

Для обучение необходим файл `data/plays_ru/encoded_plays_ru.npz`, содержащий пары [вопрос,ответ], закодированные в вектора, которые были получены на предыдущем этапе. В процессе обучения после каждой 5-ой эпохи (это значение можно изменить) сохраняется крайний промежуточный результат обучения сети в файл `data/plays_ru/model_weights_plays_ru_[номер_итерации].h5`, а на последней итерации в файл `data/plays_ru/model_weights_plays_ru.h5` (итерация - один цикл обучения сети, некоторое число эпох, после которых происходит сохранение весов в файл и можно например оценить точность работы сети или вывести другие её параметры. По умолчанию число эпох равно 5, а общее число итераций 200). Модель сети сохраняется в файле `data/plays_ru/model_plays_ru.json`.

После обучения сети выполняется оценка качества обучения путём подачи на вход обученной сети всех вопросов и сравнения ответов сети с эталонными ответами из обучающей выборки. Если точность оцениваемой модели получается выше 75%, то неправильные ответы сети сохраняются в файл `data/plays_ru/wrong_answers_plays_ru.txt` (что бы их можно было потом проанализировать).

Для обучения сети достаточно передать методу `train()` имя соответствующего файла и задать желаемые параметры.

**4. Построение языковой модели и словаря для PocketSphinx.**

Этот этап нужен в случае, если будет использоваться распознавание речи. На этом этапе осуществляется создание статической языковой модели и фонетического словаря для PocketSphinx на основе вопросов из обучающей выборки (осторожно: чем больше вопросов в обучающей выборке, тем дольше PocketSphinx будет распознавать речь). Для этого используется метод `build_language_model()` (которая обращается к `text2wfreq, wfreq2vocab, text2idngram` и `idngram2lm` из CMUclmtk_v0.7) класса `LanguageModel` из модуля `preparing_speech_to_text.py`. Данный метод использует вопросы из файла с исходной обучающей выборкой (до их подготовки модулем `source_to_prepared.py`), сохраняет языковую модель в файл `temp/prepared_questions_plays_ru.lm`, а словарь в `temp/prepared_questions_plays_ru.dic` (`plays_ru` может меняться, в зависимости от того, какая обучающая выборка была использована). В конце работы языковая модель и словарь будут скопированы в `/usr/local/lib/python3.х/dist-packages/pocketsphinx/model` с именами `ru_bot_plays_ru.lm` и `ru_bot_plays_ru.dic` (`plays_ru` может меняться так же, как и на предыдущем этапе, потребуется ввод пароля root-пользователя).

---

## 2. Работа с обученной моделью seq2seq в текстовом режиме

Для взаимодействия с обученной моделью seq2seq предназначена функция `predict()` (которая является обёрткой над методом `predict()` класса `TextToText` из модуля `text_to_text.py`) модуля `bot.py`. Данная функция поддерживает несколько режимов работы. В текстовом режиме, т.е. когда пользователь вводит вопрос с клавиатуры и сеть отвечает текстом, используется только метод `predict()` класса `TextToText` из модуля `text_to_text.py`. Данный метод принимает строку с вопросом к сети и возвращает строку с ответом сети. Для работы необходимы: файл `data/plays_ru/w2v_model_plays_ru.bin` с обученной моделью word2vec, файл `data/plays_ru/model_plays_ru.json` с параметрами модели сети и файл `data/plays_ru/model_weights_plays_ru.h5` с весами обученной сети.

Для запуска бота в данном режиме нужно запустить `bot.py` с параметром `predict`. Например, так:
```
python3 bot.py predict
```

Так же можно просто запустить `bot.py` (или выполнить `run_bot.sh`) и в предложенном меню выбрать режим 2 и 1.

---

## 3. Работа с обученной моделью seq2seq с озвучиванием ответов с помощью [RHVoice](https://github.com/Olga-Yakovleva/RHVoice)

Данный режим отличается от предыдущего тем, что функции `predict()` модуля `bot.py` передаётся параметр `speech_synthesis = True`. Это означает, что взаимодействие с сетью будет проходить так же, как и в режиме 2, но ответ сети дополнительно будет озвучиваться.

Озвучивание ответов, т.е. синтез речи, реализован в методе `get()` класса `TextToSpeech` из модуля `text_to_speech.py`. Данный класс требует установленного RHVoice-client и с помощью аргументов командной строки передаёт ему необходимые параметры для синтеза речи (об установке RHVoice и примеры обращения к RHVoice-client можно посмотреть в `install_files/Install RHVoice.txt`). Метод `get()` принимает на вход строку, которую нужно преобразовать в речь, и, если требуется, имя .wav файла, в который будет сохранена синтезированная речь (с частотой дискретизации 32 кГц и глубиной 16 бит, моно; если его не указывать - речь будет воспроизводиться сразу после синтеза). При создании объекта класса `TextToSpeech` можно указать имя используемого голоса. Поддерживается 4 голоса: мужской Aleksandr и три женских - Anna, Elena и Irina (подробнее в [RHVoice Wiki](https://github.com/Olga-Yakovleva/RHVoice/wiki/Latest-version-%28Russian%29)).

Для запуска бота в данном режиме нужно запустить `bot.py` с параметрами `predict -ss`. Например, так:
```
python3 bot.py predict -ss
```

Так же можно просто запустить `bot.py` (или выполнить `run_bot.sh`) и в предложенном меню выбрать режим 3 и 1.

---

## 4. Работа с обученной моделью seq2seq с распознаванием речи с помощью [PocketSphinx](https://github.com/cmusphinx/pocketsphinx)

Для работы в этом режиме необходимо функции `predict()` модуля `bot.py` передать параметр `speech_recognition = True`. Это означает, что взаимодействие с сетью, а точнее ввод вопросов, будет осуществляться с помощью голоса.

Распознавание речи реализовано в методе `get()` класса `SpeechToText` модуля `speech_to_text.py`. Данный класс использует PocketSphinx и языковую модель со словарём (`ru_bot_plays_ru.lm` и `ru_bot_plays_ru.dic`), которые были построены в режиме обучения сети. Метод `get()` может работать в двух режимах: `from_file` - распознавание речи из .wav или .opus файла с частотой дискретизации >=16кГц, 16bit, моно (имя файла передаётся в качестве аргумента функции) и `from_microphone` - распознавание речи с микрофона. Режим работы задаётся при создании экземпляра класса `SpeechRecognition`, т.к. загрузка языковой модели занимает некоторое время (чем больше модель, тем дольше она загружается).

Для запуска бота в данном режиме нужно запустить `bot.py` с параметрами `predict -sr`. Например, так:
```
python3 bot.py predict -sr
```

Так же можно просто запустить `bot.py` (или выполнить `run_bot.sh`) и в предложенном меню выбрать режим 4 и 1.

---

## 5. Работа с обученной моделью seq2seq с озвучиванием ответов и распознаванием речи

Это комбинация режимов 3 и 4.

Для работы в этом режиме необходимо функции `predict()` модуля `bot.py` передать параметры `speech_recognition = True` и `speech_synthesis = True`. Это означает, что ввод вопросов будет осуществляться с помощью голоса, а ответы сети будут озвучиваться. Описание используемых модулей можно найти в описании режимов 3 и 4.

Для запуска бота в данном режиме нужно запустить `bot.py` с параметрами `predict -ss -sr`. Например, так:
```
python3 bot.py predict -sr -ss
```
или
```
python3 bot.py predict -ss -sr
```

Так же можно просто запустить `bot.py` (или выполнить `run_bot.sh`) и в предложенном меню выбрать режим 5 и 1.

# RESTful сервер

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

Сервер реализован с помощью [Flask](http://flask.pocoo.org/), а многопоточный режим (production-версия) с помощью [gevent.pywsgi.WSGIServer](http://flask.pocoo.org/docs/1.0/deploying/wsgi-standalone/). Также сервер имеет ограничение на размер принимаемых данных в теле запроса равное 16 Мб. Реализация находится в модуле `rest_server.py`.

Запустить WSGI сервер можно выполнив `run_rest_server.sh` (запуск WSGI сервера на `0.0.0.0:5000`).

Сервер поддерживает аргументы командной строки, которые немного упрощают его запуск. Аргументы имеют следующую структуру: `[ключ(-и)] [адрес:порт]`.

Возможные ключи:
1. `-d` - запуск тестового Flask сервера (если ключ не указывать - будет запущен WSGI сервер)
2. `-s` - запуск сервера с поддержкой https (используется самоподписанный сертификат, получен с помощью openssl)

Допустимые варианты `адрес:порт`:
1. `host:port` - запуск на указанном `host` и `port`
2. `localaddr:port` - запуск с автоопределением адреса машины в локальной сети и указанным `port`
3. `host:0` или `localaddr:0` - если `port = 0`, то будет выбран любой доступный порт автоматически

Список возможных комбинаций аргументов командной строки и их описание:
1. без аргументов - запуск WSGI сервера с автоопределением адреса машины в локальной сети и портом `5000`. Например: ```python3 rest_server.py```
2. `host:port` - запуск WSGI сервера на указанном `host` и `port`. Например: ```python3 rest_server.py 192.168.2.102:5000```
3. `-d` - запуск тестового Flask сервера на `127.0.0.1:5000`. Например: ```python3 rest_server.py -d```
4. `-d host:port` - запуск тестового Flask сервера на указанном `host` и `port`. Например: ```python3 rest_server.py -d 192.168.2.102:5000```
5. `-d localaddr:port` - запуск тестового Flask сервера с автоопределением адреса машины в локальной сети и портом `port`. Например: ```python3 rest_server.py -d localaddr:5000```
6. `-s` - запуск WSGI сервера с поддержкой https, автоопределением адреса машины в локальной сети и портом `5000`. Например: ```python3 rest_server.py -s```
7. `-s host:port` - запуск WSGI сервера с поддержкой https на указанном `host` и `port`. Например: ```python3 rest_server.py -s 192.168.2.102:5000```
8. `-s -d` - запуск тестового Flask сервера с поддержкой https на `127.0.0.1:5000`. Например: ```python3 rest_server.py -s -d```
9. `-s -d host:port` - запуск тестового Flask сервера с поддержкой https на указанном `host` и `port`. Например: ```python3 rest_server.py -s -d 192.168.2.102:5000```
10. `-s -d localaddr:port` - запуск тестового Flask сервера с поддержкой https, автоопределением адреса машины в локальной сети и портом `port`. Например: ```python3 rest_server.py -s -d localaddr:5000```

Сервер может сам выбрать доступный порт, для этого нужно указать в `host:port` или `localaddr:port` порт `0` (например: ```python3 rest_server.py -d localaddr:0```).

Всего поддерживается 5 запросов:
1. GET-запрос на `/chatbot/about`, вернёт информацию о проекте
2. GET-запрос на `/chatbot/questions`, вернёт список всех поддерживаемых вопросов
3. POST-запрос на `/chatbot/speech-to-text`, принимает .wav/.opus-файл и возвращает распознанную строку
4. POST-запрос на `/chatbot/text-to-speech`, принимает строку и возвращает .wav-файл с синтезированной речью
5. POST-запрос на `/chatbot/text-to-text`, принимает строку и возвращает ответ бота в виде строки

---

## Описание сервера

**1.** Сервер имеет базовую http-авторизацию. Т.е. для получения доступа к серверу надо в каждом запросе добавить заголовок, содержащий логин:пароль, закодированный с помощью `base64` (логин: `bot`, пароль: `test_bot`). Пример на python:
```
import requests
import base64

auth = base64.b64encode('testbot:test'.encode())
headers = {'Authorization' : "Basic " + auth.decode()}
```

Выглядеть это будет так:
```
Authorization: Basic dGVzdGJvdDp0ZXN0
```

**2.** В запросе на распознавание речи (который под номером 3) сервер ожидает .wav или .opus файл (>=16кГц 16бит моно) с записанной речью, который так же передаётся в json с помощью кодировки `base64` (т.е. открывается .wav/.opus-файл, читается в массив байт, потом кодирутеся `base64`, полученный массив декодируется из байтовой формы в строку `utf-8` и помещается в json), в python это выглядит так:
```
# Формирование запроса
auth = base64.b64encode('testbot:test'.encode())
headers = {'Authorization' : "Basic " + auth.decode()}

with open('test.wav', 'rb') as audio:
data = audio.read()
data = base64.b64encode(data)
data = {'wav' : data.decode()}

# Отправка запроса серверу
r = requests.post('http://' + addr + '/chatbot/speech-to-text', headers=headers, json=data)

# Разбор ответа
data = r.json()
data = data.get('text')
print(data)
```

**3.** В запросе на синтез речи (который под номером 4) сервер пришлёт в ответе json с .wav-файлом (16бит 32кГц моно) с синтезированной речью, который был закодирован так, как описано выше (что бы обратно его декодировать нужно из json получить нужную строку в массив байт, потом декодировать его с помощью `base64` и записать в файл или поток, что бы потом воспроизвести), пример на python:
```
# Формирование запроса
auth = base64.b64encode('testbot:test'.encode())
headers = {'Authorization' : "Basic " + auth.decode()}
data = {'text':'который час'}

# Отправка запроса серверу
r = requests.post('http://' + addr + '/chatbot/text-to-speech', headers=headers, json=data)

# Разбор ответа
data = r.json()
data = base64.b64decode(data.get('wav'))
with open('/home/vladislav/Проекты/Voice chat bot/temp/answer.wav', 'wb') as audio:
audio.write(data)
```

---

## Передаваемые данные в каждом запросе

Все передаваемые данные обёрнуты в json (в том числе и ответы с ошибками).
1. Сервер передаёт клиенту:
```
{
"text" : "Информация о проекте."
}
```
2. Сервер передаёт клиенту:
```
{
"text" : ["Вопрос 1",
"Вопрос 2",
"Вопрос 3"]
}
```
3. Клиент в теле запроса должен отправить:
```
{
"wav" : "UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."
}
```
или
```
{
"opus" : "ZFZm10IBUklQVZFZm10IBARLASBAAEOpH..."
}
```
Сервер ему передаст:
```
{
"text" : "который час"
}
```
4. Клиент в теле запроса должен отправить:
```
{
"text" : "который час"
}
```
Сервер ему передаст:
```
{
"wav" : "UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."
}
```
5. Клиент в теле запроса должен отправить:
```
{
"text" : "прощай"
}
```
Сервер ему передаст:
```
{
"text" : "это снова я"
}
```

---

## Примеры запросов

**1. GET-запрос на `/chatbot/about`**

Пример запроса, который формирует `python-requests`:
```
GET /chatbot/about HTTP/1.1
Host: 192.168.2.83:5000
Connection: keep-alive
Accept-Encoding: gzip, deflate
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: python-requests/2.9.1
```

Пример запроса, который формирует curl (`curl -v -u testbot:test -i http://192.168.2.83:5000/chatbot/about`):
```
GET /chatbot/about HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: curl/7.47.0
```

В обоих случаях сервер ответил:
```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 305
Date: Fri, 02 Nov 2018 15:13:21 GMT

{
"text" : "Информация о проекте."
}
```

---

**2. GET-запрос на `/chatbot/questions`**

Пример запроса, который формирует `python-requests`:
```
GET /chatbot/questions HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: python-requests/2.9.1
Connection: keep-alive
Accept-Encoding: gzip, deflate
```

Пример запроса, который формирует curl (`curl -v -u testbot:test -i http://192.168.2.83:5000/chatbot/questions`):
```
GET /chatbot/questions HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: curl/7.47.0
```

В обоих случаях сервер ответил:
```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1086
Date: Fri, 02 Nov 2018 15:43:06 GMT

{
"text" : ["Что случилось?",
"Срочно нужна твоя помощь.",
"Ты уезжаешь?",
...]
}
```

---

**3. POST-запрос на `/chatbot/speech-to-text`**

Пример запроса, который формирует `python-requests`:
```
POST /chatbot/speech-to-text HTTP/1.1
Host: 192.168.2.83:5000
User-Agent: python-requests/2.9.1
Accept: */*
Content-Length: 10739
Connection: keep-alive
Content-Type: application/json
Authorization: Basic dGVzdGJvdDp0ZXN0
Accept-Encoding: gzip, deflate

{
"wav" : "UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."
}
```

Пример запроса, который формирует curl (`curl -v -u testbot:test -i -H "Content-Type: application/json" -X POST -d '{"wav":"UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."}' http://192.168.2.83:5000/chatbot/speech-to-text`):
```
POST /chatbot/speech-to-text HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: curl/7.47.0
Accept: */*
Content-Type: application/json
Content-Length: 10739

{
"wav" : "UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."
}
```

Сервер ответил:
```
HTTP/1.1 200 OK
Content-Length: 81
Date: Fri, 02 Nov 2018 15:57:13 GMT
Content-Type: application/json

{
"text" : "Распознные слова из аудиозаписи"
}
```

---

**4. POST-запрос на `/chatbot/text-to-speech`**

Пример запроса, который формирует `python-requests`:
```
POST /chatbot/text-to-speech HTTP/1.1
Host: 192.168.2.83:5000
Connection: keep-alive
Accept: */*
User-Agent: python-requests/2.9.1
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 73
Authorization: Basic dGVzdGJvdDp0ZXN0

{
"text" : "который час"
}
```

Пример запроса, который формирует curl (`curl -v -u testbot:test -i -H "Content-Type: application/json" -X POST -d '{"text":"который час"}' http://192.168.2.83:5000/chatbot/text-to-speech`):
```
POST /chatbot/text-to-speech HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: curl/7.47.0
Accept: */*
Content-Type: application/json
Content-Length: 32

{
"text" : "который час"
}
```

Сервер ответил:
```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 78151
Date: Fri, 02 Nov 2018 16:36:02 GMT

{
"wav" : "UklGRuTkAABXQVZFZm10IBAAAAABAAEAAH..."
}
```

---

**5. POST-запрос на `/chatbot/text-to-text`**

Пример запроса, который формирует `python-requests`:
```
POST /chatbot/text-to-text HTTP/1.1
Host: 192.168.2.83:5000
Accept-Encoding: gzip, deflate
Content-Type: application/json
User-Agent: python-requests/2.9.1
Connection: keep-alive
Content-Length: 48
Accept: */*
Authorization: Basic dGVzdGJvdDp0ZXN0

{
"text" : "прощай"
}
```

Пример запроса, который формирует curl (`curl -v -u testbot:test -i -H "Content-Type: application/json" -X POST -d '{"text":"прощай"}' http://192.168.2.83:5000/chatbot/text-to-text`):
```
POST /chatbot/text-to-text HTTP/1.1
Host: 192.168.2.83:5000
Authorization: Basic dGVzdGJvdDp0ZXN0
User-Agent: curl/7.47.0
Accept: */*
Content-Type: application/json
Content-Length: 23

{
"text" : "прощай"
}
```

Сервер ответил:
```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 68
Date: Fri, 02 Nov 2018 16:41:22 GMT

{
"text" : "это снова я"
}
```

---

## Предполагаемый алгоритм работы с сервером

1. Запросить список вопросов у сервера (запрос 2) и отобразить его
2. В зависимости от выбранного режима:
* Записать речь с микрофона клиента
* Отправить на сервер (запрос 3) и получить ответ с распознанным текстом
* Отобразить текст в поле ввода
* Отправить текст на сервер (запрос 5) и получить ответ бота
* Отправить ответ бота на сервер (запрос 4) и получить аудиофайл с синтезированной речью
* Воспроизвести аудиофайл
3. Если клиент хочет узнать информацию о данном проекте, послать запрос 1 на сервер и отобразить полученные данные

---

## Docker-образ с RESTful сервером

В проекте содержится Dockerfile, который позволяет собрать docker образ на основе данного проекта. Если для установки всех зависимостей вы использовали `install_packages.sh` и ранее Docker вы не устанавливали, вам необходимо установить его вручную. Например, так (проверено в Ubuntu 16.04-18.04):
```
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main' -y
sudo apt-get -y update
sudo apt-get install -y docker-engine
```
После установки выполните `sudo systemctl status docker` что бы убедиться, что всё установилось и работает (в выводе данной команды вы найдёте строку с зелёным текстом `active (running)`).

Для сборки образа необходимо перейти в терминале в папку с проектом и выполнить (`-t` — запуск терминала, `.` — директория, из которой вызывается docker build (точка — значит в текущей директории находятся все файлы для образа), `voice_chatbot:0.1` — метка образа и его версия):
```
sudo docker build -t voice_chatbot:0.1 .
```

После успешного выполнения данной операции вы можете вывести список имеющихся образов, выполнив:
```
sudo docker images
```
В полученном списке вы увидите наш образ — `voice_chatbot:0.1`.

Теперь вы можете запустить этот образ (`-t` — запуск терминала, `-i` — интерактивный режим, `--rm` — удалить контейнер после завершения его работы, `-p 5000:5000` — пробросить все подключения на порт 5000 к машине-хосту в контейнер на порт 5000 (вы так же можете явно указать другой адрес, к которому нужно будет подключиться извне, например: `-p 127.0.0.1:5000:5000`)):
```
sudo docker run -ti --rm -p 5000:5000 voice_chatbot:0.1
```

В результате запустится RESTful сервер на `0.0.0.0:5000` и можно к нему обращаться по указанному в терминале адресу (если вы не указали другой при запуске образа).

**Примечание**: собранный docker-образ весит 5.2Гб. Исходные файлы проекта так же включают файл `.dockerignore`, в котором находятся имена файлов, которые не нужно добавлять в образ. Для минимизации размера итогового образа, из него были исключены все файлы, относящиеся к наборку данных из рассказов и из субтитров, файлы с промежуточными результатами обработки данных и обучения нейронной сети. Это значит, что образ содержит только файлы обученной сети и сырые исходные наборы данных.

На всякий случай, в исходных файлах проекта есть файл `command_for_docker.txt`, содержащий минимально необходимый набор команд для работы с docker.

---

Если у вас возникнут вопросы или вы хотите сотрудничать, можете написать мне на почту: [email protected] или в [LinkedIn](https://www.linkedin.com/in/vladklim/).