Это туториал по использованию библиотеки pocketsphinx на Python. Надеюсь он поможет вам
побыстрее разобраться с этой библиотекой и не наступать на мои грабли.


Началось все с того, что захотел я сделать себе голосового ассистента на python. Изначально для распознавания решено было использовать библиотеку speech_recognition. Как оказалось, я не один такой. Для распознавания я использовал Google Speech Recognition, так как он единственный не требовал никаких ключей, паролей и т.д. Для синтеза речи был взят gTTS. В общем получился почти клон этого ассистента, из-за чего я не мог успокоиться.


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


Первым усовершенствованием был синтез речи при помощи yandex speechkit cloud:


URL = 'https://tts.voicetech.yandex.net/generate?text='+text+'&format=wav&lang=ru-RU&speaker=ermil&key='+key+'&speed=1&emotion=good'
response=requests.get(URL)
if response.status_code==200:
    with open(speech_file_name,'wb') as file:
        file.write(response.content)

Затем настала очередь распознавания. Меня сразу заинтересовала надпись "CMU Sphinx (works offline)" на странице библиотеки. Я не буду рассказывать об основных понятиях pocketsphinx, т.к. до меня это сделал chubakur(за что ему большое спасибо) в этом посте.


Установка Pocketsphinx


Сразу скажу, так просто pocketsphinx установить не получится(по крайней мере у меня не получилось), поэтому pip install pocketsphinx не сработает, упадет с ошибкой, будет ругаться на wheel. Установка через pip будет работать только если у вас стоит swig. В противном случае чтобы установить pocketsphinx нужно перейти вот сюда и скачать установщик(msi). Обратите внимание: установщик есть только для версии 3.5!


Распознавание речи при помощи pocketsphinx


Pocketsphinx может распознавать речь как с микрофона, так и из файла. Также он может искать горячие фразы(у меня не очень получилось, почему-то код, который должен выполняться когда находится горячее слово выполняется несколько раз, хотя произносил его я только один). От облачных решений pocketsphinx отличается тем, что работает оффлайн и может работать по ограниченному словарю, вследствие чего повышается точность. Если интересно, на странице библиотеки есть примеры. Обратите внимание на пункт "Default config".


Русская языковая и акустическая модель


Изначально pocketsphinx идет с английской языковой и акустической моделями и словарем. Скачать русские можно по этой ссылке. Архив нужно распаковать. Затем надо папку <your_folder>/zero_ru_cont_8k_v3/zero_ru.cd_cont_4000 переместить в папку C:/Users/tutam/AppData/Local/Programs/Python/Python35-32/Lib/site-packages/pocketsphinx/model, где <your_folder> это папка в которую вы распаковали архив. Перемещенная папка — это акустическая модель. Такую же процедуру надо проделать с файлами ru.lm и ru.dic из папки <your_folder>/zero_ru_cont_8k_v3/. Файл ru.lm это языковая модель, а ru.dic это словарь. Если вы все сделали правильно, то следующий код должен работать.


import os
from pocketsphinx import LiveSpeech, get_model_path

model_path = get_model_path()

speech = LiveSpeech(
    verbose=False,
    sampling_rate=16000,
    buffer_size=2048,
    no_search=False,
    full_utt=False,
    hmm=os.path.join(model_path, 'zero_ru.cd_cont_4000'),
    lm=os.path.join(model_path, 'ru.lm'),
    dic=os.path.join(model_path, 'ru.dic')
)

print("Say something!")

for phrase in speech:
    print(phrase)

Предварительно проверьте чтобы микрофон был подключен и работал. Если долго не появляется надпись Say something! — это нормально. Большую часть этого времени занимает создание экземпляра LiveSpeech, который создается так долго потому, что русская языковая модель весит более 500(!) мб. У меня экземпляр LiveSpeech создается около 2 минут.


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


JSGF


Вместо языковой модели можно заставить pocketsphinx работать по упрощенной грамматике. Для этого используется jsgf файл. Его использование ускоряет создание экземпляра LiveSpeech. О том как создавать файлы граматики написано здесь. Если языковая модель есть, то jsgf файл будет игнорироваться, поэтому если вы хотите использовать собственный файл грамматики, то нужно писать так:


speech = LiveSpeech(
    verbose=False,
    sampling_rate=16000,
    buffer_size=2048,
    no_search=False,
    full_utt=False,
    hmm=os.path.join(model_path, 'zero_ru.cd_cont_4000'),
    lm=False,
    jsgf=os.path.join(model_path, 'grammar.jsgf'),
    dic=os.path.join(model_path, 'ru.dic')
)

Естественно файл с грамматикой надо создать в папке C:/Users/tutam/AppData/Local/Programs/Python/Python35-32/Lib/site-packages/pocketsphinx/model. И еще: при использовании jsgf придется четче говорить и разделять слова.


Создаем свой словарь


Словарь — это набор слов и их транскрипций, чем он меньше, тем выше точность распознавания. Для создания словаря с русскими словами нужно воспользоваться проектом ru4sphinx. Качаем, распаковываем. Затем открываем блокнот и пишем слова, которые должны быть в словаре, каждое с новой строки, затем сохраняем файл как my_dictionary.txt в папке text2dict, в кодировке UTF-8. Затем открываем консоль и пишем: C:\Users\tutam\Downloads\ru4sphinx-master\ru4sphinx-master\text2dict> perl dict2transcript.pl my_dictionary.txt my_dictionary_out.txt. Открываем my_dictionary_out.txt, копируем содержимое. Открываем блокнот, вставляем скопированный текст и сохраняем файл как my_dict.dic (вместо "текстовый файл" выберите "все файлы"), в кодировке UTF-8.


speech = LiveSpeech(
    verbose=False,
    sampling_rate=16000,
    buffer_size=2048,
    no_search=False,
    full_utt=False,
    hmm=os.path.join(model_path, 'zero_ru.cd_cont_4000'),
    lm=os.path.join(model_path, 'ru.lm'),
    dic=os.path.join(model_path, 'my_dict.dic')
)

Некоторые транскрипции может быть нужно подправить.


Использование pocketsphinx через speech_recognition


Использовать pocketsphinx через speech_recognition имеет смысл только если вы распознаете английскую речь. В speech_recognition нельзя указать пустую языковую модель и использовать jsgf, а следовательно для распознавания каждого фрагмента придется ждать 2 минуты. Проверенно.


Итог


Угробив несколько вечеров я понял, что потратил время впустую. В словаре из двух слов(да и нет) сфинкс умудряется ошибаться, причем часто. Отъедает 30-40% celeron'а, а с языковой моделью еще и жирный кусок памяти. А Яндекс почти любую речь распознает безошибочно, при том не ест память и процессор. Так что думайте сами, стоит ли за это браться вообще.


P.S.: это мой первый пост, так что жду советы по оформлению и содержанию статьи.

Комментарии (7)


  1. mbait
    18.03.2018 01:42

    Сразу скажу, так просто pocketsphinx установить не получится(по крайней мере у меня не получилось), поэтому pip install pocketsphinx не сработает, упадет с ошибкой, будет ругаться на wheel.

    pip install pocketsphinx отлично работает. Скорее всего, у вас не установлен swig, который нужен для сборки.


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

    Потому что надо документацию читать.


    В словаре из двух слов(да и нет) сфинкс умудряется ошибаться, причем часто.

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


    Отъедает 30-40% celeron'а, а с языковой моделью еще и жирный кусок памяти. А Яндекс почти любую речь распознает безошибочно, при том не ест память и процессор.

    Сравнение тёплого с мягким. Если ПО для распознавания Яндекса запустить на вашей машине, то она, вероятно, вообще загнётся. Хотите попробовать? — Kaldi


    1. tutam Автор
      18.03.2018 22:31

      pip install pocketsphinx отлично работает. Скорее всего, у вас не установлен swig, который нужен для сборки.

      Насчет swig я знал, решил не заморачиваться с его установкой.
      Потому что надо документацию читать.

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

      Спасибо. Точность увеличилась, правда говорить приходится громче.
      Сравнение тёплого с мягким. Если ПО для распознавания Яндекса запустить на вашей машине, то она, вероятно, вообще загнётся. Хотите попробовать? — Kaldi

      Загнется, не сомневаюсь. Но Яндекс распознает на сервере, так что в итоге по потреблению ресурсов он эффективнее(если нет проблем с интернетом). И при своем огромном словаре он распознает он распознает эти несколько команд точнее чем sphinx, у которого словарь только из этих команд и состоит.


  1. Delneg
    18.03.2018 08:04

  1. kedobear
    18.03.2018 09:54

    Вот вам до кучи:
    Pocketsphinx. Распознавание речи и голосовое управление в Linux
    У меня с двумя словами 5 лет назад работало хорошо.


  1. DollaR84
    18.03.2018 22:06

    Я тоже написал себе голосового помощника на pocketsphinx.
    Через pip устанавливается прекрасно.
    Так как я не задавался целью распознавания произвольной речи, поэтому сразу отбросил вариант использования языковой модели.
    Использовал исключительно набор грамматических правил JSGF и составил свой ограниченный словарь.
    Данная схема позволила получить в итоге:
    быструю загрузку;
    очень высокую точность распознавания, по-моему. Ошибки бывают, но не очень часто.
    И это абсолютно без подстройки к голосу.
    Ну и самое главное конечно оффлайн.
    Так что считаю sphinx наиболее удачным решением именно для помощника, выполняющего определенные команды, а онлайновые сдк яндекса и гугла хороши для распознавания произвольной речи, но это другой круг задач.


    1. tutam Автор
      18.03.2018 22:49

      Через pip устанавливается прекрасно.

      Через pip устанавливается прекрасно потому, что у вас стоит swig.

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

      И еще: поделитесь секретом, как вы сделали активационную фразу?


      1. DollaR84
        19.03.2018 00:22

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

        И не буду говорить, что я его полностью закончил или все идеально. Я постоянно продолжаю что-то добавлять или изменять, много бывает еще багов нахожу, но постепенно допиливаю потихоньку.
        А насчет активационной фразы, так у меня тоже возникли проблемы с ее использованием, я читал документацию про keyphrase, но мне на тот момент не удалось настроить ее порог срабатывания, и я ее оставил до лучших времен, вот думаю скоро снова заняться может быть этой проблемой.
        А на данном этапе пока как временное решение я поступил немного по другому.
        У меня все команды разделены по разделам, плюс основной раздел, плюс активационная фраза.
        Все разделы разделены по разным грамматическим правилам, в том числе фраза для активации представлена также отдельным правилом.
        При инициализации я их всех загружаю функцией set_jsgf_file(), с указанными именами, а затем в цикле крутится распознавание, сначала активационного правила. Когда фраза найдена, происходит смена set_search() на основное правило, которое уже ждет распознавание по разделам, ну и так далее. При окончании или отмене, возвращается правило с активационной фразой.
        Я конечно согласен, что это костыль тот еще, поэтому и настроен на переделку с применением правильного решения по документации, так как бывают ложные срабатывания, но пока не хватило еще времени и желания дойти до этого. И так потрачено было много времени на эту домашнюю поделку =)