«Персональный голосовой бот, основанный на искусственном интеллекте, для изучения языков»

Personal voice bot for language learning, based on artificial intelligence

Создано с помощью Stable Diffusion XL — Изображение автора

Какой самый эффективный способ освоить новый язык? Говорить на нем! Но мы все знаем, как страшно пробовать новые слова и фразы перед другими людьми. А что, если у вас будет терпеливый и понимающий друг для практики, свободный от суждений, свободный от стыда?

Тот терпеливый и понимающий друг, которого вы ищете, может оказаться виртуальным языковым репетитором, работающим на основе LLM! Это может стать революционным способом освоения языка, все это с комфортом вашего собственного пространства.

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

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

В этой статье я поделюсь выбранными мной инструментами, объясню процесс и представлю концепцию практики разговора с помощью чат-бота на основе искусственного интеллекта через голосовые команды и голосовые ответы. Проект состоит из трех основных разделов: транскрипция речи в текст, использование языковой модели и преобразование текста в речь. Все это будет объяснено в следующих трех заголовках.

1. Транскрипция речи в текст

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

Точная транскрипция критически важна для плавного взаимодействия с чат-ботом, особенно в контексте изучения языка, где произношение, акцент и грамматика являются ключевыми факторами. Существует несколько инструментов распознавания речи, которые могут быть использованы для транскрибации голосового ввода на Python, такие как Whisper от OpenAI и Speech-to-Text от Google Cloud.

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

У Google есть Python API, который требует подключения к интернету и предлагает 60 минут бесплатной транскрипции в месяц. В отличие от Google, OpenAI опубликовала свою модель Whisper, и вы можете запускать ее локально, не завися от скорости интернета, если у вас есть достаточно вычислительной мощности. Вот почему я выбрал Whisper, чтобы минимизировать задержку при транскрипции.

2. Языковая модель

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

Чтобы увеличить последовательность ответов и иметь их в определенном шаблоне, вы также можете донастроить языковые модели (например, как донастроить ChatGPT под ваш случай использования). Вам нужно сгенерировать примерные предложения и соответствующие оптимальные ответы, и подать их на тренировку с донастройкой. Однако базовому репетитору, который я хочу создать, не требуется донастройка, и я буду использовать обобщенную модель GPT3.5-turbo в своем проекте.

Я приведу пример вызова API для облегчения разговора между пользователем и ChatGPT через его API на Python ниже. Во-первых, если у вас его еще нет, вам потребуется открыть учетную запись OpenAI и установить ключ API для взаимодействия с ChatGPT. Инструкции можно найти здесь.

После настройки ключа API вы можете начать генерировать текст, используя метод openai.ChatCompletion.create. Этот метод требует два параметра: параметр model, который указывает конкретную модель GPT для доступа через API, и параметр messages, который включает структуру для разговора с ChatGPT. Параметр messages состоит из двух основных компонентов: role и content.

Вот фрагмент кода, иллюстрирующий процесс:

# Инициализация сообщений с установкой поведения(s).messages = [{"role": "system", "content": "Введите поведение(я) здесь."}]# Начало бесконечного цикла для продолжения разговора с пользователем.while True:    content = input("Пользователь: ") # Получение ввода от пользователя для ответа.    messages.append({"role": "user", "content": content})# Добавление ввода пользователя в сообщения.    # Использование модели OpenAI GPT-3.5 для генерации ответа на ввод пользователя.    completion = openai.ChatCompletion.create(        model="gpt-3.5-turbo",        messages=messages    )    chat_response = completion.choices[0].message.content # Извлечение ответа на чат из ответа API.    print(f'ChatGPT: {chat_response}') # Печать ответа.    # Добавление ответа в сообщения с ролью "assistant" для хранения истории чата.    messages.append({"role": "assistant", "content": chat_response})
  • Роль system определяется для определения поведения ChatGPT, добавляя инструкцию в начало списка сообщений.
  • Во время чата сообщение user получается от пользователя с помощью указанной модели распознавания речи, чтобы получить ответ от ChatGPT.
  • Наконец, ответы ChatGPT добавляются в список сообщений с ролью assistant для регистрации истории разговора.

3. Преобразование текста в речь

В разделе о транскрипции речи в текст я объяснил, как пользователь использует голосовые команды, чтобы создать впечатление разговора, как с реальным человеком. Чтобы улучшить это ощущение и создать более динамичный и интерактивный опыт обучения, следующий шаг заключается в преобразовании текстового вывода GPT в слышимую речь с использованием инструмента преобразования текста в речь, такого как gTTS. Это не только помогает создать более увлекательный и легко читаемый опыт, но и решает критический аспект изучения языка: вызов понимания через слушание, а не чтение. Интегрируя этот аудиальный компонент, мы облегчаем более всестороннюю практику, более точно отражающую использование языка в реальном мире.

Существует множество инструментов для преобразования текста в речь, таких как Text-to-Speech от Google (gTTS) и Text to Speech от IBM Watson. В этом проекте я предпочел gTTS, поскольку он очень прост в использовании, обладает естественным качеством голоса и не требует дополнительных затрат. Для использования библиотеки gTTS вам понадобится подключение к Интернету, поскольку библиотека требует доступа к серверу Google для преобразования текста в речь.

Подробное объяснение конвейера

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

На рисунке ниже показана схема рабочего процесса AI-поддерживаемого виртуального языкового учителя, предназначенного для создания обучающего опыта реального времени на основе голосового общения:

Схема конвейера — Изображение автора
  • Пользователь начинает разговор, инициируя запись своей речи, временно сохраняя ее в виде файла .wav. Это достигается нажатием и удерживанием клавиши пробела, и запись останавливается, когда клавиша пробела отпускается. Разделы кода на Python, которые обеспечивают эту функциональность нажатия и разговора, объясняются ниже.

Для управления состоянием процесса записи используются следующие глобальные переменные:

recording = False       # указывает, происходит ли в данный момент запись аудио done_recording = False  # указывает, что пользователь завершил запись голосовой команды stop_recording = False  # указывает, что пользователь хочет завершить разговор

Функция listen_for_keys служит для проверки нажатий и отпускания клавиш. Она устанавливает глобальные переменные на основе состояния клавиши пробела и клавиши esc.

def listen_for_keys():    # Функция для прослушивания нажатий клавиш для управления записью    global recording, done_recording, stop_recording    while True:        if keyboard.is_pressed('space'):  # Начать запись при нажатии клавиши пробела            stop_recording = False            recording = True            done_recording = False        elif keyboard.is_pressed('esc'):  # Остановить запись при нажатии клавиши 'esc'            stop_recording = True            break        elif recording:  # Остановить запись при отпускании клавиши пробела            recording = False            done_recording = True            break        time.sleep(0.01)

Функция callback используется для обработки аудиоданных при записи. Она проверяет флаг recording, чтобы определить, нужно ли записывать входящие аудиоданные.

def callback(indata, frames, time, status):    # Функция, вызываемая для каждого аудиоблока во время записи.    if recording:        if status:            print(status, file=sys.stderr)        q.put(indata.copy())

Функция press2record является основной функцией, ответственной за обработку голосовой записи при нажатии и удерживании пробела пользователем.

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

Затем функция открывает объект SoundFile для записи аудиоданных и объект InputStream для захвата аудио с микрофона с использованием ранее упомянутой функции callback. Запускается поток для прослушивания нажатий клавиш, в частности пробела для записи и клавиши ‘esc’ для остановки. Внутри цикла функция проверяет флаг записи и записывает аудиоданные в файл, если запись активна. Если запись остановлена, функция возвращает -1, в противном случае она возвращает имя файла записанного аудио.

def press2record(filename, subtype, channels, samplerate):    # Функция для обработки записи при нажатии клавиши    global recording, done_recording, stop_recording    stop_recording = False    recording = False    done_recording = False    try:        # Определение частоты дискретизации, если не указано        if samplerate is None:            device_info = sd.query_devices(None, 'input')            samplerate = int(device_info['default_samplerate'])            print(int(device_info['default_samplerate']))        # Создание временного имени файла, если не указано        if filename is None:            filename = tempfile.mktemp(prefix='captured_audio',                                       suffix='.wav', dir='')        # Открытие звукового файла для записи        with sf.SoundFile(filename, mode='x', samplerate=samplerate,                          channels=channels, subtype=subtype) as file:            with sd.InputStream(samplerate=samplerate, device=None,                                channels=channels, callback=callback, blocksize=4096) as stream:                print('нажмите Пробел, чтобы начать запись, отпустите для остановки или нажмите Esc для выхода')                listener_thread = threading.Thread(target=listen_for_keys)  # Запуск прослушивателя в отдельном потоке                listener_thread.start()                # Запись аудиоданных в файл                while not done_recording and not stop_recording:                    while recording and not q.empty():                        file.write(q.get())        # Возвращение -1, если запись остановлена        if stop_recording:            return -1    except KeyboardInterrupt:        print('Прервано пользователем')    return filename

Наконец, функция get_voice_command вызывает press2record для записи голосовой команды пользователя.

def get_voice_command():    # ...    saved_file = press2record(filename="input_to_gpt.wav", subtype = args.subtype, channels = args.channels, samplerate = args.samplerate)    # ...
  • После того, как голосовая команда была записана и сохранена во временном файле .wav, мы переходим к этапу транскрипции. На этом этапе записанное аудио преобразуется в текст с помощью Whisper. Соответствующий сценарий для простого запуска задачи транскрипции для файла .wav приведен ниже:
def get_voice_command():    # ...    result = audio_model.transcribe(saved_file, fp16=torch.cuda.is_available())    # ...

Эта функция принимает два параметра: путь к записанному аудиофайлу, saved_file, и необязательный флаг использования FP16-точности, если CUDA доступна для повышения производительности на совместимом оборудовании. Она просто возвращает транскрибированный текст.

  • Затем транскрибированный текст отправляется в ChatGPT для генерации соответствующего ответа в функции interact_with_tutor(). Соответствующий фрагмент кода выглядит следующим образом:
def interact_with_tutor():    # Определение роли системы для задания поведения чат-ассистента    messages = [        {"role": "system", "content" : "Вы Анна, мой немецкий учебный партнер.                                         Вы будете общаться со мной. Ваши ответы будут краткими.                                        Мой уровень - B1, настраивайте свою сложность предложений на мой уровень.                                         Постоянно старайтесь заставить меня говорить, задавая вопросы, и углубляйте чат."}    ]    while True:        # Получение голосовой команды пользователя        command = get_voice_command()          if command == -1:            # Сохранение журналов чата и выход, если запись остановлена            save_response_to_pkl(messages)            return "Чат был остановлен."        # Добавление команды пользователя в историю сообщений        messages.append({"role": "user", "content": command})          # Генерация ответа от чат-ассистента        completion = openai.ChatCompletion.create(            model="gpt-3.5-turbo",            messages=messages        )          # Извлечение ответа из завершения        chat_response = completion.choices[0].message.content  # Извлечение ответа из завершения        print(f'ChatGPT: {chat_response} \n')  # Печать ответа ассистента        messages.append({"role": "assistant", "content": chat_response})  # Добавление ответа ассистента в историю сообщений        # ...

Функция interact_with_tutor начинается с определения роли системы ChatGPT для формирования его поведения на протяжении всего разговора. Поскольку моя цель – практика немецкого языка, я устанавливаю соответствующую системную роль. Я назвал своего виртуального репетитора “Анна” и установил уровень владения языком для того, чтобы она могла адаптировать свои ответы. Кроме того, я инструктирую ее поддерживать интересный разговор, задавая вопросы.

Затем голосовая команда пользователя переносится в список сообщений с ролью “пользователь”. Это сообщение затем отправляется в ChatGPT. При продолжении разговора в цикле while весь исторический контекст команд пользователя и ответов GPT записывается в список сообщений.

  • После каждого ответа ChatGPT мы преобразуем текстовое сообщение в речь с помощью gTTS.
def interact_with_tutor():  # ...  # Преобразование текстового ответа в речь  speech_object = gTTS(text=messages[-1]['content'],tld="de", lang=language, slow=False)  speech_object.save("GPT_response.wav")  current_dir = os.getcwd()  audio_file = "GPT_response.wav"  # Воспроизведение аудио-ответа  play_wav_once(audio_file, args.samplerate, 1.0)  os.remove(audio_file) # Удаление временного аудио-файла

Функция gTTS() принимает 4 параметра: text, tld, lang и slow. Параметру text присваивается содержимое последнего сообщения в списке messages (указывается [-1]), которое нужно преобразовать в речь. Параметр tld указывает домен верхнего уровня для сервиса Google Translate. Установка его на "de" означает, что используется немецкий домен, что может быть важным для обеспечения правильного произношения и интонации на немецком языке. Параметр lang указывает язык, на котором должен быть произнесен текст. В этом коде переменная language установлена на 'de', что означает, что текст будет произнесен на немецком языке. slow=False: параметр slow управляет скоростью речи. Установка его на False означает, что речь будет произнесена с нормальной скоростью. Если бы он был установлен на True, речь была бы произнесена медленнее.

  • Преобразованная речь ответа ChatGPT затем сохраняется как временный файл .wav, воспроизводится пользователю, а затем удаляется.
  • Функция interact_with_tutor выполняется повторно, когда пользователь продолжает разговор, нажимая клавишу пробела.
  • Если пользователь нажимает “esc”, разговор заканчивается и весь разговор сохраняется в файл pickle chat_log.pkl. Вы можете использовать его позже для анализа.

Использование командной строки

Для запуска скрипта просто выполните код python в терминале следующим образом:

sudo python chat.py

Необходимо использовать sudo, так как скрипт требует доступ к микрофону и использование библиотеки клавиатуры. Если вы используете Anaconda, вы также можете запустить терминал Anaconda с правами администратора, чтобы получить полный доступ.

Вот видеодемонстрация, показывающая, как код работает на моем ноутбуке. Вы сможете оценить его производительность:

Видеодемонстрация, созданная автором

Заключительные замечания

Я установил язык репетитора на немецкий, просто установив системную роль ChatGPT и настроив параметры в функции gTTs для соответствия немецкому языку. Однако вы можете легко переключить его на другой язык. Это займет всего несколько секунд для настройки под целевой язык.

Если вы хотите поговорить о конкретной теме, вы также можете добавить ее в системную роль ChatGPT. Например, практика для собеседований может быть хорошим примером использования. Вы также можете указать уровень владения языком, чтобы адаптировать его ответы.

Одно важное замечание заключается в том, что общая скорость чата зависит от вашего интернет-соединения (из-за API ChatGPT и gTTS) и от вашего оборудования (из-за локальной установки Whisper). В моем случае, общее время отклика после моих вводов составляет от 4 до 10 секунд.