- PVSM.RU - https://www.pvsm.ru -

Pocketsphinx. Распознавание речи и голосовое управление в Linux

— Всё в порядке, Лёня?
Динамики отрегулированы на максимум, я морщусь, отвечаю:
— Да. Тише звук.
— Звук — тише, — соглашается «Виндоус-Хоум», — тише, тише…
— Хватит, Вика
С.Лукьяненко, «Лабиринт отражений»

Введение

В 1997-ом году Лукьяненко пророчил для десктопа сочетание CLI и голосового управления. Однако сейчас голосовое управление — достаточно узкая ниша.
Голосовое управление — взаимодействие с устройством при помощи звуковых команд. Не путайте это понятие с распознаванием речи. Для голосового управления достаточно, чтобы устройство реагировало на единственную нужную команду (ведь ваша собака не может работать машинисткой?). Распознавание речи — гораздо более глобальная проблема: в этом случае устройство должно преобразовывать в текстовый формат все слова, произнесенные вами. Как легко догадаться, распознавание речи на данный момент реализовано поверхностно относительно человеческих возможностей.
Функционал, рассмотренный в статье, может быть применен, к примеру, для организации модного сейчас «умного дома» или просто управления компьютером. Честно говоря, для описания управления компьютером хватило бы пары абзацев, но я попытаюсь показать вам основы работы с CMU Sphinx.
Кстати, процентов 70 описанного здесь подойдет и пользователям Windows.

Для Linux чаще всего упоминаются два развитых проекта распознавания речи: CMU Sphinx [1] и Julius [2].

В данной статье я не буду касаться Julius, поскольку гайдов по его использованию (в том числе и в Рунете) хватает. Речь будет идти о CMU Sphinx.

Описание и установка

На официальном сайте [8] последняя версия pocketsphinx и sphinxbase — 0.8. В репозитории моего Debian Squeeze есть только устаревшая ветка sphinx2. Значит, будем собирать (проверьте репозитории своих дистрибутивов: в последних версиях Ubuntu и Fedora должны быть актуальные версии). При необходимости вместо pocketsphinx, написанного на C, вы можете использовать Sphinx4 на Java (подробнее [9]).
Скачиваем отсюда [10] исходники pocketsphinx и sphinxbase («support library required by Pocketsphinx and Sphinxtrain») и собираем.

Проблем возникнуть не должно

./configure
make
checkinstall

Не забудьте после установки выполнить:

ldconfig

Готовые пакеты для Debian Squeeze x86

Для работы с /dev/dsp установим согласно FAQ [15] пакет oss-compat.

Базовое использование

Проект предлагает нам проверить работоспособность на базовом примере: распознать фразу на английском «go forward ten meters».
Что ж, пробуем.
Мы воспользуемся утилитой пакетного распознавания pocketsphinx_batch (перед использованием почитайте её man). Также есть утилита распознавания «с микрофона» — pocketsphinx_continuous (синтаксис её схож с pocketsphinx_batch).
Работать будем в отдельной директории, к примеру ~/sphinx.
Синтаксис нашей команды таков:

pocketsphinx_batch -argfile argfile 2>./errors

-argflie: имя файла в текущей директории, содержащего все аргументы.
stderr для удобства перенаправим в файл.
Содержимое argfile:

-hmm /usr/local/share/pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k
-lm /usr/local/share/pocketsphinx/model/lm/en/turtle.DMP
-dict /usr/local/share/pocketsphinx/model/lm/en/turtle.dic
-cepdir /home/saint/sphinx
-ctl ctlfile
-cepext .raw
-adcin true
-hyp outname

-hmm: путь к каталогу, содержащему файлы акустической модели (шаблоны отдельных звуков).
-lm: путь к файлу триграммной языковой модели (можете почитать здесь [16]).
-dict: путь к файлу словаря произношения.
-cepdir: путь к каталогу со звуковыми файлами. Будьте внимательны: если вы вносите -cepdir в файл аргументов, то сокращенный путь ~/sphinx обрабатывается неправильно: приходится писать полный путь. Если вы будете прописывать аргумент после команды, то можете использовать сокращенный путь.
-ctl: файл с именами обрабатываемых файлов. Файл goforward.raw мы возьмем из комплекта исходников pocketsphinx (там есть еще пару файлов *.raw — можете распознать и их).
-cepext: расширение обрабатываемых файлов
-adcin: указатель принадлежности обрабатываемого файла к raw.
-hyp: имя файла, в который будет выведен распознанный текст.
Аргументы с путями к файлам моделей указывать обязательно. Помните, что многие параметры заданы по умолчанию (смотрите stderr). Поэтому для работы с файлом *.raw необходимо принудительно указать расширение, иначе будет использован параметр по умолчанию — расширение .mfc (а таких файлов у нас в базовом примере, естественно, нету — будут сыпаться ошибки).
В результате исполнения у нас в файле outname будет следующее содержимое:

go forward ten meters (goforward -26532)

Параллельно можете посмотреть, откомпилировать и запустить в каталоге с файлом goforward.raw программку аналогичного назначения [17] на C (пример от разработчиков).
Для проверки на своих примерах я решил не мудрствовать и воспользовался sox (проверьте, установлен ли этот пакет у вас).
Писать звук будем следующим образом (можете почитать man sox):
— для raw

rec -r 16k -e signed-integer -b 16 -c 1 filename.raw

— для wav

rec -r 16k -e signed-integer -b 16 -c 1 filename.wav

Окончание записи по Ctrl+C.
У меня sox при этом ругался на невозможность использования частоты дискретизации: can't set sample rate 16000; using 48000. Учтите: нагло лжет — на самом деле все в порядке.
Я писал и распознавал raw и wav на различных примерах из подключенных словарей — все распознавалось вполне приемлимо.

Адаптация звуковой модели

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

Скачиваем по первой ссылке предлагаемые файлы в отдельную директорию, в которой и будем работать.
Теперь надиктуем предложения из файла arctic20.txt по образцу: у вас должно получиться двадцать файлов, названных по порядку согласно схеме arctic_0001.wav...arctic_0020.wav.
Чтобы упростить запись, воспользуемся предложенным скриптом:

for i in `seq 1 20`; do 
       fn=`printf arctic_%04d $i`; 
       read sent; echo $sent; 
       rec -r 16000 -e signed-integer -b 16 -c 1 $fn.wav 2>/dev/null; 
done < arctic20.txt

Соответственно, чтобы прослушать полученное, выполним:

for i in *.wav; do play $i; done

Скопируем акустическую модель (с которой мы и работали) из /usr/local/share/pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k в нашу рабочую директорию.
Теперь создадим файлы акустических особенностей (напоминаю: работаем в директории с файлами *.wav).

sphinx_fe -argfile hub4wsj_sc_8k/feat.params -samprate 16000 -c arctic20.listoffiles -di . -do . -ei wav -eo mfc -mswav yes

В результате получаем файлы *.mfc.
Скачиваем экстра-пак [20] (89,0 МБ); файл под названием mixture_weights из него, расположенный в pocketsphinx-extra/model/hmm/en_US/hub4_wsj_sc_3s_8k.cd_semi_5000 помещаем в каталог с акустической моделью.
Также необходимо конвертировать mdef-файл акустической модели в текстовый формат:

pocketsphinx_mdef_convert -text hub4wsj_sc_8k/mdef hub4wsj_sc_8k/mdef.txt

Теперь, согласно терминологии гайда по адаптации, соберем накопленные данные. Скопируем утилиту bw из /usr/local/libexec/sphinxtrain/bw в рабочий каталог (перед этим не забудьте установить sphinxtrain!).

./bw -hmmdir hub4wsj_sc_8k -moddeffn hub4wsj_sc_8k/mdef.txt -ts2cbfn .semi. -feat 1s_c_d_dd -svspec 0-12/13-25/26-38 -cmn current -agc none -dictfn arctic20.dic -ctlfn arctic20.fileids -lsnfn arctic20.transcription -accumdir .

Запускаем и видим:
SYSTEM_ERROR: "corpus.c", line 339: Unable to open arctic20.fileids for reading: No such file or directory
Очевидно, правая рука у разработчиков не ведает, что творит левая (про неактуальность части документации я уже не говорю).
Переименовываем в рабочем каталоге файл arctic20.listoffiles в arctic20.fileids
Теперь все работает.
Произведем MLLR-адаптацию (эффективна для ограниченного объема данных в модели):

cp /usr/local/libexec/sphinxtrain/mllr_solve /your/work/dir/mllr_solve

./mllr_solve -meanfn hub4wsj_sc_8k/means -varfn hub4wsj_sc_8k/variances -outmllrfn mllr_matrix -accumdir .

Эта команда создаст файл адаптационных данных mllr_matrix.
Теперь при распознавании с адаптированной моделью можно добавлять параметр -mllr /path/to/mllr_matrix.
Параллельно произведем другой метод адаптации: MAP.

cp /usr/local/libexec/sphinxtrain/map_adapt /your/work/dir/map_adapt

Сделаем копию модели:

cp -a hub4wsj_sc_8k hub4wsj_sc_8kadapt

И произведем MAP-адаптацию:

./map_adapt -meanfn hub4wsj_sc_8k/means -varfn hub4wsj_sc_8k/variances -mixwfn hub4wsj_sc_8k/mixture_weights -tmatfn hub4wsj_sc_8k/transition_matrices -accumdir . -mapmeanfn hub4wsj_sc_8kadapt/means -mapvarfn hub4wsj_sc_8kadapt/variances -mapmixwfn hub4wsj_sc_8kadapt/mixture_weights -maptmatfn hub4wsj_sc_8kadapt/transition_matrices

Теперь создадим sendump файл, отличающийся меньшим размером:

cp /usr/local/libexec/sphinxtrain/mk_s2sendump /your/work/dir/mk_s2sendump

./mk_s2sendump -pocketsphinx yes -moddeffn hub4wsj_sc_8kadapt/mdef.txt -mixwfn hub4wsj_sc_8kadapt/mixture_weights -sendumpfn hub4wsj_sc_8kadapt/sendump

Адаптация закончена.

Тестирование адаптации

Суть эксперимента: мы записываем несколько образцов, на которых оригинальная акустическая модель спотыкается, и обрабатываем их с помощью адаптированных акустических моделей.
Создадим в рабочем каталоге поддиректорию test. В ней создаем поддиректорию wav, в которой будут наши тестовые записи.
Здесь для подтверждения результата эксперимента я выкладываю свои образцы и две адаптированные акустические модели.

Проверяем (помните, что адаптация не приведет к стопроцентно верному результату: адаптированные модели будут точно так же ошибаться; плюс в том, что они будут делать это реже. Мои вполне наглядные записи были сделаны далеко не с первой попытки: было достаточно записей, где ошибались все модели):
1. Распознавание с помощью базовой модели:

pocketsphinx_batch -hmm /usr/local/share/pocketsphinx/model/hmm/en_US/hub4wsj_sc_8k -lm /usr/local/share/pocketsphinx/model/lm/en/turtle.DMP -dict /usr/local/share/pocketsphinx/model/lm/en/turtle.dic -cepdir wav -ctl adaptation-test.fileids -cepext .wav -adcin yes -hyp adaptation-test.hyp

Результат:

hello halt say forty (test1 -27391)
go forward ten meter (test2 -35213)
hall doing home (test3 -30735)

2. Распознавание с помощью модели с MLLR-адаптацией: при указании параметром -mllr пути к моей матрице происходила ошибка сегментирования (копаться я не стал). При распознавании без этой опции результат полностью идентичен результату оригинальной модели.
Впрочем, в мануале заявлено, что MLLR-адаптация лучше всего подходит для непрерывной модели (т.е. для Sphinx4).
3. Распознавание с помощью модели с MAP-адаптацией:

pocketsphinx_batch -hmm ../hub4wsj_sc_8kadapt -lm /usr/local/share/pocketsphinx/model/lm/en/turtle.DMP -dict /usr/local/share/pocketsphinx/model/lm/en/turtle.dic -cepdir wav -ctl adaptation-test.fileids -cepext .wav -adcin yes -hyp adaptation-test.hyp

Результат:

hello rotate display (test1 -28994)
go forward ten meters (test2 -33877)
lost window (test3 -29293)

Как можно убедиться, результат полностью идентичен записи. Адаптация реально работает!

Русский язык в Pocketsphinx

Скачаем отсюда [26] русские модели, созданные на voxforge [27]. На разнообразие моделей можно посмотреть здесь [28] и просто в интернетах.

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

Создание собственной статической языковой модели

Вообще, для малого количества слов вместо статической языковой модели можно использовать jsgf-словарь. Однако, это частный случай и мы его рассмотрим чуть ниже.
Руководство по созданию языковой модели находится здесь [29].
Создавать будем с помощью CMUCLMTK.
Скачиваем [30], собираем.
Для начала создадим текстовый файл с предложениями для нашей языковой модели.

lmbase.txt

<s> предлагаю свернуть окно браузера </s>
<s> нужно развернуть окно и радоваться </s>
<s> тише едешь громче падаешь </s>
<s> открыть терминал и закрыть окно </s>
<s> вперёд вперёд плешивые стада </s>
<s> играй и не смотри назад </s>
<s> пора проверить почту </s>
<s> почта находится за углом </s>
<s> сделай паузу выключи компьютер </s>
<s> стоп проход запрещен </s>
<s> пауза это небольшой перерыв </s>
<s> замолчи а потом говори </s>
<s> компьютер разверни окно </s>
<s> открой браузер и покажи мне интернет </s>
<s> сделай шаг вперёд и два шага назад </s>
<s> запустить браузер можно голосом </s>
<s> открой окно давай вперед </s>
<s> запустить окно несложно </s>
<s> давай запусти терминал </s>
<s> можно запустить компьютер </s>
<s> сделай тише </s>
<s> можно играть тише </s>
<s> нужно проверить компьютер </s>
<s> компьютер прошу громче </s>
<s> делай громче </s>
<s> пора все закрыть </s>
<s> закрыть все окна </s>
<s> пора свернуть все к черту </s>
<s> нужно свернуть окна </s>
<s> сверни окно </s>
<s> браузер необходимо срочно свернуть </s>
<s> пора все развернуть </s>
<s> нужно развернуть окно </s>
<s> разверни окно </s>

Далее создаем словарный файл:

text2wfreq <lmbase.txt | wfreq2vocab> lmbase.tmp.vocab
cp lmbase.tmp.vocab  lmbase.vocab

Создаем языковую модель в arpa-формате:

text2idngram -vocab lmbase.vocab -idngram lmbase.idngram < lmbase.txt
idngram2lm -vocab_type 0 -idngram lmbase.idngram -vocab lmbase.vocab -arpa lmbase.arpa

И создаем DMP-модель.

sphinx_lm_convert -i lmbase.arpa -o lmbase.lm.DMP
Создание собственного словаря

Тащим с гитхаба утилиты:

git clone https://github.com/zamiron/ru4sphinx/ yourdir

Переходим в каталог ./yourdir/text2dict и создаем там текстовый файл my_dictionary с вашим списком слов (каждое новое слово — с нового абзаца).

Пример my_dictionary

браузер
громче
закрыть
запустить
окно
почта
развернуть
свернуть
свернуть
терминал
тише

Затем выполняем:

perl dict2transcript.pl my_dictionary my_dictionary_out

И вот ваш словарь создан.

Теперь пробуем распознавать слова, присутствующие в словаре (благо, в нашем примере их немного). Не забудьте указать в аргументах собственную языковую модель и словарь — все должно работать. При желании можно произвести адаптацию акустической модели (сразу предупреждаю: при использовании утилиты bw в процессе адаптации для большинства акустических моделей опция -svspec не нужна ).

Использование JavaScript Grammar File вместо статической языковой модели

Читать тут. [31]
Синтаксис:

#JSGF V1.0;
grammar test;
public <test> = ( <a> | <a> <b> );
<a> = ( тише | громче | закрыть | свернуть );
<b> = [ окно ];

"|" обозначает условие выбора. Т.е. мы можем сказать «тише» или «закрыть окно». Правда, по сравнению с использованием языковой модели есть один минус: говорить нужно гораздо членораздельнее.
Созданный jsgf-файл указываем с параметром -jsgf (параметр -lm в таком случае не нужен).

Реализация голосового управления

Моей целью не было реализовать крутой интерфейс управления: здесь все будет очень примитивно (если есть желание и возможность, можете посмотреть на заброшенный проект Gnome Voice Control [32]).
Действовать будем следующим образом:
1. Пишем команду, распознаем её.
2. Передаем распознанный текст в файл, в соответствии с ним выполняем команду.
В качестве тестовых команд будем использовать уменьшение и увеличение громкости звука.

Внимательно почитав мануал к sox, я решил оканчивать запись по истечении секунды тишины с порогом тишины в 3.8% (порог явно является сугубо индивидуальным значением и зависит от вашего микрофона и окружающей обстановки).
К сожалению, я не нашел в pocketsphinx_batch параметр вывода только для распознанных слов, поэтому я воспользуюсь инструментом sed:

cat ~/sphinx/rus/outname  | sed 's/( (.*)//'

Эта конструкция удалит из строки вида «наша команда (audio -4023)» пробел перед открывающей скобкой, её саму и все последующее содержимое. В результате мы получим строку вида «наша команда», что нам и нужно.
Вот сам скрипт:

#!/bin/bash
rec -r 16k -e signed-integer -b 16 -c 1 test.wav silence 0 1 00:01 3.8%
pocketsphinx_batch -argfile argfile 2>./errors
a=$(cat ~/sphinx/rus/outname  | sed 's/( (.*)//')
case $a in
тише)
amixer -q sset Master 10-
a=$(amixer sget Master | grep "Mono: Playback")
notify-send "$a" 
;;
громче)
amixer -q sset Master 10+
a=$(amixer sget Master | grep "Mono: Playback")
notify-send "$a"
;;
*)
;;
esac

Скрипт в ответ на команды «тише» или «громче» выполняет соответствующие действия с сигнализацией через notify-send.
К сожалению, работать он будет только при запуске из терминала (иначе звук не пишется). Впрочем, представление о голосовом управлении он дает (возможно, вы предложите лучший метод).

Пару слов в качестве заключения

Когда я начинал писать эту статью, я просто хотел рассказать вам, что со свободным распознаванием сейчас далеко не всё так глухо, как кажется. Есть движки, ведется работа над акустическими моделями на www.voxforge.org [33] (вы можете посодействовать им начитыванием лексики). Вместе с тем, работа с распознаванием не является чем-то сложным для простого пользователя.
Несколько дней назад было объявлено [34], что в Ubuntu для планшетов будет использоваться Pocketsphinx или Julius. Надеюсь, в этом свете данная тема выглядит немного актуальнее и интереснее.
В статье я пытался рассмотреть основные моменты работы с Pocketsphinx, ведя речь скорее о теории, чем о практике. Однако вы могли убедиться, что распознавание не фикция, оно работает.
Помните, что я затронул только поверхностные аспекты: в документации на официальных сайтах и различных форумах описано намного больше. Пробуйте, рассказывайте в комментариях о своем опыте, делитесь наработками, удачными мыслями и готовыми решениями для голосового управления.

Автор: kedobear

Источник [35]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/linux/28030

Ссылки в тексте:

[1] CMU Sphinx: http://en.wikipedia.org/wiki/CMU_Sphinx

[2] Julius: http://en.wikipedia.org/wiki/Julius_(software)

[3] Использование google-сервиса: http://habrahabr.ru/post/157333/

[4] Статья на Хакере об использовании Julius: http://www.xakep.ru/magazine/xa/133/082/1.asp

[5] Список систем распознавания под Linux: http://isaleksey.blogspot.com/2011/05/blog-post.html

[6] Небольшой обзор от LinuxFormat. Часть 1: http://wiki.linuxformat.ru/index.php/LXF116:Компьютер_слушает%21

[7] Небольшой обзор от LinuxFormat. Часть 2: http://wiki.linuxformat.ru/index.php/LXF117:Компьютер_слушает

[8] официальном сайте: http://cmusphinx.sourceforge.net/

[9] подробнее: http://cmusphinx.sourceforge.net/wiki/tutorialbeforestart

[10] отсюда: http://cmusphinx.sourceforge.net/wiki/download/

[11] sphinxbase: https://dl.dropbox.com/u/22130570/cmusphinx/sphinxbase_0.8-1_i386.deb

[12] pocketsphinx: https://dl.dropbox.com/u/22130570/cmusphinx/pocketsphinx_0.8-1_i386.deb

[13] sphinxtrain: https://dl.dropbox.com/u/22130570/cmusphinx/sphinxtrain_1.0.8-1_i386.deb

[14] cmuclmtk: https://dl.dropbox.com/u/22130570/cmusphinx/cmuclmtk_0.7-1_i386.deb

[15] FAQ: http://cmusphinx.sourceforge.net/wiki/faq

[16] здесь: http://www.intsys.msu.ru/invest/speech/articles/rus_lm.htm

[17] программку аналогичного назначения: http://cmusphinx.sourceforge.net/wiki/tutorialpocketsphinx

[18] Официальный сайт: http://cmusphinx.sourceforge.net/wiki/tutorialadapt

[19] Примерный перевод: http://forum.sources.ru/index.php?showtopic=367628

[20] экстра-пак: http://cmusphinx.svn.sourceforge.net/viewvc/cmusphinx/trunk/pocketsphinx-extra/?view=tar

[21] dl.dropbox.com/u/22130570/cmusphinx/wav/test1.wav: https://dl.dropbox.com/u/22130570/cmusphinx/wav/test1.wav

[22] dl.dropbox.com/u/22130570/cmusphinx/wav/test2.wav: https://dl.dropbox.com/u/22130570/cmusphinx/wav/test2.wav

[23] dl.dropbox.com/u/22130570/cmusphinx/wav/test3.wav: https://dl.dropbox.com/u/22130570/cmusphinx/wav/test3.wav

[24] dl.dropbox.com/u/22130570/cmusphinx/hub4wsj_sc_8k.7z: https://dl.dropbox.com/u/22130570/cmusphinx/hub4wsj_sc_8k.7z

[25] dl.dropbox.com/u/22130570/cmusphinx/hub4wsj_sc_8kadapt.7z: https://dl.dropbox.com/u/22130570/cmusphinx/hub4wsj_sc_8kadapt.7z

[26] отсюда: http://sourceforge.net/projects/cmusphinx/files/Acoustic%20and%20Language%20Models/Russian%20Voxforge/

[27] voxforge: http://www.voxforge.org/

[28] здесь: http://www.repository.voxforge1.org/downloads/Russian/Trunk/Audio/

[29] здесь: http://cmusphinx.sourceforge.net/wiki/tutoriallm

[30] Скачиваем: http://sourceforge.net/projects/cmusphinx/files/cmuclmtk/0.7

[31] Читать тут.: http://homepages.abdn.ac.uk/k.vdeemter/pages/teaching/NLP/practicals/JSGFGrammar.html

[32] Gnome Voice Control: https://live.gnome.org/GnomeVoiceControl

[33] www.voxforge.org: http://www.voxforge.org

[34] было объявлено: http://vasilisc.com/hud-2

[35] Источник: http://habrahabr.ru/post/167479/