Как стать автором
Обновить

Как адаптировать языковые модели Kaldi? (со смешными животными)

Время на прочтение14 мин
Количество просмотров9.8K


«Как научить русскоязычную модель распознавать речь геймеров?» Подобными вопросами задаются те, кто увлекается и занимается NLP. В частности, NLP-специалистов интересует, как можно адаптировать модель Kaldi под свою предметную область, чтобы улучшить качество распознавания. Это мы и разберём в данной статье.


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

Сейчас можно легко заставить компьютер распознавать обычную устную речь, благо, есть пакет vosk, который является «человечной обёрткой» (wrapper’ом) к предобученным моделям Kaldi. Alphacephei и Николай Шмырёв проделали колоссальную работу по продвижению опен-сорса в распознавании русскоязычной речи, и vosk, пожалуй, венец всего их труда. Большая модель vosk-ru для распознавания устной русской речи без всяких доработок может решать множество задач распознавания речи.

По умолчанию большие модели vosk-а предназначены для распознавания обычных разговорных слов и синтаксических конструкций. Однако, когда появляется необходимость распознавать другие слова и другие языковые конструкции, которые не предусмотрены моделью vosk-а по умолчанию, качество распознавания заметно ухудшается. Если таких конструкций немного, то можно выстроить соответствие между тем, что нужно распознать, и тем, что распознаётся на самом деле. Например, текущая модель vosk-model-ru-0.10 не умеет распознавать слово «коронавирус», но распознаёт отдельные слова: «корона» и «вирус». В подобных случаях нам будет предоставлен своеобразный ребус, который нам со своей стороны нужно будет решить программно. К сожалению, на ребусах далеко не уехать.

Собственно, как здорово, что все мы здесь сегодня собрались научиться адаптировать модели Kaldi, которые находятся под капотом vosk-а. Прежде чем проводить адаптацию, убедитесь, что поставляемая разработчиками модель поддерживает обновление графа. Для этого существуют пути адаптации:



Чтобы понять, какие именно компоненты нам нужно будет модифицировать или заменять, определимся, какая проблема перед нами стоит. Есть несколько ситуаций, при которых следует по-разному действовать при работе с Kaldi:



Словарь в реалиях Kaldi – это список слов, которые мы хотим уметь распознавать, с соответствующими им фонетическими транскрипциями. Словарь является связующим звеном между акустической и языковой моделями.

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

Бывает и обратная ситуация: мы знаем и ожидаем фразу определённой структуры от человека, в таком случае используются вручную построенные грамматики. Одна из распространённых разновидностей грамматик – грамматика «речевых команд», когда человек может сказать только одну из «n» фраз в определённый момент времени с одинаковой вероятностью.

Соответственно, в Kaldi есть два основных способа проектирования языковых моделей: ARPA LM и грамматика FST:



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


Мопс выделяет частоты в сигнале, на которых находится речь

СТРУКТУРА МОДЕЛЕЙ KALDI

Начнём работу с разбора того, как устроена предобученная модель Kaldi. То, что нам НЕ понадобится при файн-тюнинге, выделено курсивом.

model
\__ am – сокращённо от Acoustic Model. Содержит модель распознавания звуков (фонем)
\__ conf – папка с файлами конфигураций для запуска модуля
\__ graph – графы для описания вероятностей переходов от одной фонемы к другой. Содержит информацию о заученных переходах фонем, а также переходы с учётом языковой модели
\__ ivector – папка с сохранёнными голосовыми слепками из обучающей выборки
\__ rescore – n-граммная языковая модель для переопределения цепочек слов
\__ rnnlm – языковая модель на основе рекуррентной нейронной сети для дополнительного переопределения цепочек слов
\__ decode.sh – исполняемый файл для запуска моделей с помощью инструментов Kaldi
\__ decoder-test.scp, decoder-test.utt2spk – служебные файлы для распознавания пробного файла
\__ decoder-test.wav– пробный файл
\__ README – документация


Когда мы хотим адаптировать модель для конкретной задачи распознавания речи, наша главная цель – корректно подменить файл ./graph/HCLG.fst. Но как именно подменить и какие файлы использовать для конечной генерации этого графа, целиком зависит от поставленной задачи. Примеры задач представлены выше, таким образом вы можете соотнести свою задачу с представленным пулом задач и понять, какие от вас требуются действия для эффективной адаптации.

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

Модификация словаря
Замена словаря
Модификация ЯМ
Замена ЯМ

Ну что ж, приступим?


Этот хороший мальчик готов адаптировать модель распознавания речи, а вы?

УСТАНОВКА KALDI

Прежде всего нам нужно поставить Kaldi на нашу рабочую машину (Linux или Mac). Благо, делается это весьма просто:

git clone https://github.com/kaldi-asr/kaldi.git
cd kaldi/tools/
./extras/check_dependencies.sh
make -j 4 # тут в качестве параметра указываете количество параллельных процессов при установке
cd ../src/
./configure --shared
make depend -j 4 # аналогично
make -j 4 # аналогично

В результате установки на машине компилируются зависимости и непосредственно сам Kaldi. Если что-то идёт не так, смотрите логи, гуглите и обращайтесь за помощью в комментарии.

УСТАНОВКА KENLM

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

git clone https://github.com/kpu/kenlm.git
mkdir -p kenlm/build
cd kenlm/build
cmake ..
make -j 4


НАСТРОЙКА ДИРЕКТОРИИ

Чтобы начать процедуру по адаптации модели, нужно организовать нашу рабочую директорию. Создайте папку в любом удобном месте, папка будет содержать модель, на которую, в том числе, можно будет ссылаться из вашего Python-приложения.

# Создаём рабочую директорию
mkdir your_asr_project/
cd your_asr_project/

# Копируем необходимые файлы из папки модели
cp -R /path/to/your/model/am .
cp -R /path/to/your/model/conf/ .
cp -R /path/to/your/model/graph/ .
cp -R /path/to/your/model/ivector/ .

# Копируем необходимые скрипты из рецептов Kaldi
cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/steps/ .
cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/utils/ .
cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/path.sh .
cp -R /path/to/your/kaldi/egs/mini_librispeech/s5/cmd.sh .

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

НАСТРОЙКА ОКРУЖЕНИЯ

Предыдущим шагом мы установили все зависимости. Теперь необходимо прописать пути к нашим зависимостям. Это делается через файл path.sh, который мы только что скопировали:

./path.sh
export KALDI_ROOT=/path/to/your/kaldi # Здесь указываете путь до вашего Kaldi

[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh
export PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$PWD:$PATH
[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path$. $KALDI_ROOT/tools/config/common_path.sh
export LC_ALL=C

# For now, don't include any of the optional dependenices of the main
# librispeech recipe

Не забываем сделать этот файл исполняемым и выполняем его. Каждый раз, когда открывается консоль, нам необходимо запускать path.sh и выполнять адаптацию.

НАСТРОЙКА ОКРУЖЕНИЯ ДЛЯ KENLM

Чтобы kenlm также был доступен из вашей рабочей директории, нужно определить до него путь. Можно отдельно выполнять эту строку в командной строке или прописать в path.sh:

export PATH=$PATH:/path/to/your/kenlm/build/bin # Здесь указываете путь до вашего kenlm

Кроме того, нужно обозначить путь до бинарников по работе с языковыми моделями в рамках Kaldi:

export PATH=$PATH:/path/to/your/kaldi/src/lmbin

Итак, мы закончили настраивать наше окружение, пора приступать к самым важным шагам для того, чтобы сгенерировать новый итоговый граф ./graph/HCLG.fst.


Сколько можно настраиваться, давайте уже что-нибудь предметное делать!

КОНФИГУРАЦИЯ СЛОВАРЯ

Здесь начинается самое сложное и интересное: из тех файлов, что предоставлены вместе с моделью, предстоит идентифицировать те, которые нам нужны, и привести их в соответствующий формат.

При работе с Kaldi графом называется детерминированный конечный автомат (finite state transducer) в формате openfst. Можно выделить 3 основных графа, с которыми так или иначе приходится иметь дело при обучении и адаптации систем распознавания речи, основанных на Kaldi:
  1. L_disambig.fst – граф лексикона, по своей сути фонетический словарь, закодированный в детерминированный конечный автомат.
  2. G.fst – граф языковой модели, представляет собой закодированную в детерминированный конечный автомат языковую модель.
  3. HCLG.fst – объединение графов лексикона, языковой модели и акустической модели.


Нашей задачей по умолчанию является восстановление графа лексикона (и затем графа языковой модели), который используется моделью Kaldi при создании итогового графа ./graph/HCLG.fst. Файл с графом HCLG.fst в папке graph обычно поставляется вместе с моделью Kaldi по умолчанию.

Итак, для генерации графа лексикона нам нужно создать папку ./data/local/dict, в неё необходимо будет добавить несколько файлов:

  1. data/local/dict/lexicon.txt – словарь фонетических транскрипций
  2. data/local/dict/extra_questions.txt – словарь фонетических вариантов
  3. data/local/dict/nonsilence_phones.txt – список «значимых» фонем
  4. data/local/dict/optional_silence.txt – список необязательных обозначений тишины
  5. data/local/dict/silence_phones.txt – словарь обозначений тишины


Сейчас подробно разберём, что должно быть в каждом из указанных выше файлов. Начнём со словаря фонетических транскрипций. Словарь фонетических транскрипций был также указан ранее во вводной части. Повторюсь, в таком словаре через пробел указано сначала само слово, затем поочерёдно фонемы, которые отражают произношение слова. Конкретно в реализации vosk-model-ru можно выделить несколько разновидностей фонем:

  • SIL, GBG – неречевые звуки:
    • SIL – обозначение тишины
    • GBG – обозначение иных любых неречевых звуков
  • a0, e0, i0, … – безударные гласные
  • a1, e1, i1, … – ударные гласные
  • bj, dj, fj, … – мягкие парные согласные
  • c, ch, j,… – остальные непарные согласные.


Для русского языка, например, основа для этого словаря поставляется с моделью vosk-model-ru-0.10 в файле ./extra/db/ru.dic. В таком словаре через пробел указано сначала само слово, а затем поочерёдно фонемы, которые отражают произношение слова. Кроме непосредственного содержания этого словаря надо добавить две строки в начало ru.dic: !SIL и [unk]. Начало файла будет следующее:

./data/local/dict/lexicon.txt
!SIL SIL
[unk] GBG
а a0
а a1
а-а a0 a1
а-а-а a0 a0 a1

Весь дальнейший файл аналогичен ./extra/db/ru.dic, добавлены только две строчки сверху. Изменённый файл нужно сохранить в ./data/local/dict/lexicon.txt.

Затем нужно определить файл extra_questions.txt, который описывает схожести среди разных фонем. Его нужно оформить следующим образом:

./data/local/dict/extra_questions.txt
a0 a1 b bj c ch d dj e0 e1 f fj g gj h hj i0 i1 j k kj l lj m mj n nj o0 o1 p pj r rj s sch sh sj t tj u0 u1 v vj y0 y1 z zh zj
SIL GBG

Также нужно определить другие файлы, описывающие различные фонемы и категории, к которым эти фонемы относятся. ./data/local/dict/nonsilence_phones.txt сформирован на основе файла ./graph/phones.txt, но убрана нумерация после пробела и убраны постфиксы у фонем. С помощью этих же фонем описаны все слова (кроме !SIL и [unk]) в lexicon.txt, то есть это наш основной инструмент по описанию обыкновенных русскоязычных слов с точки зрения их произношения. После того как провели сортировку и убрали дубликаты, у нас получается файл ./data/local/dict/nonsilence_phones.txt, первые пять строк которого указаны ниже:

./data/local/dict/nonsilence_phones.txt
a0
a1
b
bj
c

Ну и, наконец, определяем «мусорные» звуки и звук тишины.

./data/local/dict/optional_silence.txt
SIL

./data/local/dict/silence_phones.txt
SIL
GBG

Следует обратить особое внимание на то, чтобы все строки были однообразно оформлены, чтобы были Linux-овские переносы строк "\n", чтобы все файлы были в кодировке UTF-8. После шагов, обозначенных выше, можем выполнять шаги по адаптации нашей модели.


Читающий эту статью, кот и файлы для генерации графа L_disambig.fst


МОДИФИКАЦИЯ СЛОВАРЯ

На этом этапе нужно дополнить наш словарь транскрипций другими наименованиями. Сделать это можно, вписав дополнительные строки со словами и их транскрипциями и упорядочив словарь. Транскрипции можно написать вручную, проанализировав те закономерности, которые присутствуют в словаре, но когда новых слов очень много, это не представляется возможным. Для русского языка нам на подмогу может прийти пакет russian_g2p_neuro. Устанавливать и пользоваться данным пакетом предельно просто, для этого скачайте его в вашу директорию со сторонними модулями:

git clone https://github.com/DinoTheDinosaur/russian_g2p_neuro.git
cd russian_g2p_neuro/
python setup.py install

Этот модуль предобучен на ru.dic, поэтому он формирует новый словарь по образу и подобию изначального словаря для vosk-model-ru. Чтобы сгенерировать новые транскрипции для списка слов, достаточно запустить команду:

generate_transcriptions extra/db/input.txt extra/db/output.dict

В input.txt перечислены в любом виде слова на кириллице (в том числе целые тексты с повторениями), а в output.dict формируется список всех этих слов с соответствующими транскрипциями. Результат output.dict можно совместить с данными из lexicon.txt и сформировать новый расширенный словарь:

mv data/local/dict/lexicon.txt extra/db/lexicon_old.txt
cat extra/db/lexicon_old.txt extra/db/output.dict | sort | uniq > data/local/dict/lexicon.txt


ЗАМЕНА СЛОВАРЯ

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

Шаги по установке и использованию те же самые:

git clone https://github.com/DinoTheDinosaur/russian_g2p_neuro.git
cd russian_g2p_neuro/
python setup.py install
cd /path/to/your_asr_project/
generate_transcriptions extra/db/input.txt extra/db/output.dict

Однако последний шаг отличается:

mv data/local/dict/lexicon.txt extra/db/lexicon_old.txt
sed “s/^/!SIL SIL\n[unk] GBG\n/” extra/db/output.dict > data/local/dict/lexicon.txt

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

ФОРМИРОВАНИЕ ГРАФА ЛЕКСИКОНА


Это не граф, это кот

Когда все файлы корректно сформированы, директория наконец-то готова к запуску скрипта utils/prepare_lang.sh из корневой директории вашего проекта по адаптации. Запуск этого скрипта создаст нужный нам граф лексикона под названием L_disambig.fst

utils/prepare_lang.sh --phone-symbol-table graph/phones.txt data/local/dict "[unk]" data/tmp/ data/dict/


По итогу выполнения скрипта можно будет найти нужный нам L_disambig.fst в папке data/dict. После этого можно приступать к модификации и замене языковой модели.

ЗАМЕНА ЯЗЫКОВОЙ МОДЕЛИ НА N-ГРАММНУЮ

Работу над языковыми моделями будем вести в новой директории ./data/local/lang. Если у вас есть тексты, по аналогии с которыми вы хотите распознавать какие-то фиксированные ключевые фразы, но при этом не хотите распознавать обычную спонтанную речь, то этот пункт для вас. Обычно имеет смысл использовать такой подход, если есть большой массив примеров команд и каких-то кодовых фраз и нет возможности прописать грамматику, которая бы предусмотрела все варианты.

Допустим, что корпус с вашими примерами реплик вы положили в ./extra/db/your.corpus. Начнём с того, что сформируем новую языковую модель с помощью установленного ранее kenlm:

lmplz -o 3 --limit_vocab_file graph/words.txt < extra/db/your.corpus > data/local/lang/lm.arpa

Проясним немного, что в этой команде обозначает каждый из параметров:

  1. -o – «order», то есть максимальный порядок словесных n-грамм, для которых мы подсчитываем вероятности
  2. --limit_vocab_file – словарь, в соответствии с которым фильтруются входные данные. Мы будем использовать этот параметр, если не хотим добавлять новые слова в словарь. Если мы не используем этот параметр, то необходимо после построения языковой модели также модифицировать словарь и следовать пунктам, отмеченным
  3. -S 30% – не указан, но можно добавить в случае, если в системе не хватает памяти на расчёт модели.


По результату выполнения этой команды получим файл такого формата:

./data/local/lang/lm.arpa
\data\
ngram 1=51515
ngram 2=990559
ngram 3=3056222

\1-grams:
-5.968162       [unk]   0
0       <s>     -2.2876017
-1.5350189      </s>    0
-2.3502047      а       -0.7859633
-3.6979482      банки   -0.42208096
-3.9146104      вторую  -0.46862456
-2.0171714      в       -1.142168

Языковая модель в формате ARPA построена следующим образом:

  1. Вначале указана шапка \data\, в которой указано количество каждой n-граммы
  2. Затем по очереди указаны все униграммы, биграммы и т.п. с соответствующими им заголовками \1-grams, \2-grams и т.п.
  3. Перечисление n-грамм начинается со значения логарифма вероятности появления последовательности (-3.6979482)
  4. Затем через знак табуляции указана сама последовательность (униграмма «банки»)
  5. Через ещё один знак табуляции так называемый «backoff weight» (-0.42208096), который позволяет высчитывать вероятности для последовательностей, которые явным образом не представлены в языковой модели
  6. Заканчивается файл ARPA меткой \end\


Когда у нас готова языковая модель, нужно заменить все "<unk>" обозначения на "[unk]":

sed -i "s/<unk>/[unk]/g" data/local/lang/lm.arpa

Ну и наконец, когда у нас есть готовая ARPA модель, мы можем сгенерировать новый граф языковой модели G.fst и таким образом подготовиться к итоговому объединению всех результатов в HCLG.fst:

arpa2fst --disambig-symbol=#0 --read-symbol-table=data/dict/words.txt data/local/lang/lm.arpa graph/G.fst

В результате выполнения последней команды рядом с графом по умолчанию HCLG.fst мы положили новый граф языковой модели G.fst. Следующим и последним шагом генерируем новый итоговый граф HCLG.fst с помощью нового графа языковой модели G.fst.

МОДИФИКАЦИЯ N-ГРАММНОЙ ЯЗЫКОВОЙ МОДЕЛИ

Когда мы хотим распознавать спонтанную речь и при этом добавить какие-то необычные речевые конструкции, можно расширить языковую модель. Для этого нужны изначальные модели ARPA, которые участвовали в формировании модели Kaldi и конкретно итогового графа HCLG.fst. Для демонстрации воспользуемся ./extra/db/ru-small.lm.gz и ./extra/db/ru.lm.gz из vosk-model-ru-0.10.

Аналогично предыдущему пункту, мы генерируем нашу новую lm.arpa и заменяем в ней символы "<unk>":

lmplz -o 4 --limit_vocab_file graph/words.txt < extra/db/your.corpus > data/local/lang/lm.arpa
sed -i 's/<unk>/[unk]/g' data/local/lang/lm.arpa

Обратим ваше внимание, что здесь мы используем другой максимальный порядок n-грамм (параметр -o). Это мы делаем, чтобы продемонстрировать, как можно объединить две языковых модели в одну, а объединять можно языковые модели только одинакового порядка. Рассмотрим те модели, которые мы имеем на данный момент:

  1. ./extra/db/ru-small.lm.gz — 3-граммная ЯМ
  2. ./extra/db/ru.lm.gz — 4-граммная ЯМ


Как вы могли догадаться, мы для примера будем объединять нашу модель с большой языковой моделью ru.lm. Для объединения языковых моделей можно воспользоваться пакетом SRILM и после установки выполнить следующую команду:

ngram-order <максимальный порядок n-грамм> -lm ru.lm -mix-lm ru-small.lm -lambda <степень смешения новой модели в старую> -write-lm lm_joint.arpa


Пакет SRILM также можно использовать для построения различных n-граммных моделей с целью улучшения качества систем распознавания речи, так как этот пакет предоставляет различные виды сглаживания вероятностей. Различные виды сглаживания подходят для разных задач распознавания речи, так что один из вариантов улучшения получившейся системы распознавания речи — экспериментировать с видами сглаживания.

Теперь результат объединения можно конвертировать в граф с помощью команды arpa2fst:

arpa2fst --disambig-symbol=#0 --read-symbol-table=data/dict/words.txt data/local/lang/lm_joint.arpa graph/G.fst

Аналогично предыдущему пункту, G.fst готов, остался последний шаг – генерация HCLG.fst.


Братья L_disambig.fst и G.fst

ЗАМЕНА ЯЗЫКОВОЙ МОДЕЛИ НА ГРАММАТИКУ

Последний способ адаптации языковой модели – ручное формирование грамматики. Пример грамматики рассматривался выше во введении. Напомню, чтобы не скроллить:

./graph/G.txt
0 1 [unk] [unk]
0 1 да да
0 1 нет нет
1 0.0

Эта грамматика служит способом выявления конкретных речевых событий – команд – и распознаёт только 3 команды:

  1. Слово «да»
  2. Слово «нет»
  3. Иное слово


События эти равновероятны, и все могут повторяться только один раз. Это пример очень простой грамматики, однако с помощью этого подхода можно задавать куда более сложные структуры. У конкретно такой грамматики «0» является начальной вершиной графа, «1» – конечной, но могут быть также промежуточные вершины, может быть несколько конечных состояний, и также можно определять вероятности каждого перехода. Этот граф определяет переходы из начального состояния в конечное по нескольким возможным равновероятным рёбрам: «[unk]», «да» и «нет».

Чтобы сформировать уже знакомый и любимый G.fst, нужно преобразовать эту грамматику из текстового вида в бинарный:

fstcompile --isymbols=data/dict/words.txt --osymbols=data/dict/words.txt --keep_isymbols=false --keep_osymbols=false G.txt | fstarcsort --sort_type=ilabel > G.fst

Ура! Теперь и с помощью этого последнего способа мы смогли сгенерировать тот же самый G.fst. Осталось совсем чуть-чуть.

ФОРМИРОВАНИЕ ИТОГОВОГО ГРАФА

Наконец-то мы можем приступить к финальному и самому ответственному моменту: к генерации итогового графа. Делается это ровно одной строкой:

utils/mkgraph.sh --self-loop-scale 1.0 data/lang/ am/ graph/

Теперь ваше персонализированное распознавание речи готово! Достаточно лишь сослаться на рабочую директорию при инициализации модели vosk-а:

from vosk import Model

model = Model("/path/to/your_asr_project/")

И далее уже в интерфейсе vosk-а реализовывать распознавание.


Вы заслужили

ПРОДОЛЖЕНИЕ СЛЕДУЕТ...

Надеюсь, статья для вас была полезной и увлекательной. Очень хочется, чтобы технология распознавания речи была несколько более доступной для желающих разобраться в этой теме. Давайте развивать опенсорс распознавания речи вместе! :)
Теги:
Хабы:
+7
Комментарии3

Публикации

Изменить настройки темы

Информация

Сайт
team.cft.ru
Дата регистрации
Дата основания
1991
Численность
1 001–5 000 человек
Местоположение
Россия
Представитель
Roman Annenkov

Истории