Шумоподавление в CMU Sphinx
Можно смело заявить, что на сегодня CMU Sphinx стал лидером среди открытого софта для распознавания речи. Pocketsphinx поставляется вместе с Ubuntu, многообещающий проект Simon построен с широким его использованием, а структура корпуса Voxforge как бы намекает, что создан он для sphinxtrain.Несмотря на бурное развитие самого Sphinx’а и методов распознавания речи вообще, каждый, кто пытался использовать его на практике, знает, насколько сложно получить вменяемый результат даже для простых задач. А все потому, что нельзя просто подключить дефолтные модели и ожидать, что система вас поймет. Требуется адаптировать акустику, построить релевантную языковую модель, найти оптимальные параметры и конфигурацию движка — вобщем, потратить недели времени, кропотливо снижая ошибку процент за процентом. Как человек, потративший эти самые недели, могу заверить, что и в этом случае вам ничего не гарантировано. Особенно, если вы хотите распознавать речь, записанную не гарнитурой, а встроенным микрофоном ноутбука, как это часто бывает.Вообще, фундаментальная причина плохого распознавания — несовпадение обучающих и тестовых условий (немного кривая калька с conditions mismatch). Туда можно отнести все: незнакомых дикторов, несовпадающие характеристики каналов, неадекватную языковую модель, и даже проявление эмоций, которых мы не ожидали от пользователя. В случае с ноутбучным микрофоном имеем различные аддитивные шумы и эхо, которых не было в обучающей базе, и которые могут значительно уронить точность распознавания.
ПредысторияРеализация шумоподавления в CMU Sphinx началась ровно год назад вот с этого поста Николая Шмырева (низкий ему поклон за всё, кстати): Around noise-robust PNCC features. Через два месяца состоялся коммит, но первое упоминание в FAQ появилось только 10 июня 2014. До этого момента с шумами предлагали бороться с помощью адаптации к каналу (весьма дельный совет, кстати, который никто не отменял). Так что для экспериментов вам понадобятся новейшая на сегодня версия 0.8.Описание самого алгоритма приведено в фундаментальной статье и в посте Николая. Вкратце, алгоритм весьма похож на MFCC, а модификации обусловлены исследованиями в области слуховой системы человека. Шумоподавление в системах распознавания речи — весьма обширная облать, в которую я не буду углубляться, поскольку не шарю. Расскажу только, как реализовать ее на практике. Данный пост является обобщением информации, найденной мной в статьях и на форумах. От вас потребуется знакомство со сфинксом. В противном случае, добро пожаловать в вики.
Шумоподавление на практике Если PNCCs — это просто новые признаки такие, логично предположить, что их можно использовать, указав соответствующее значение для -feat. А вот и нет, ха-ха. В данном случае реализация модификацию уже существующего механизма feature extraction. И выглядит это немного по-разному для pocketsphinx и Sphinx4. Но давайте по порядку.Создание акустических моделей Итак, прежде чем приступить к распознаванию, нам нужны акустические модели. Существующие модели нам не подойдут, потому что получены они пока обычным способом, а значит попытка их использования в шумоустойчивой системе породит тот самый фундаментальный mismatch. Поэтому модели нужно будет натренировать заново. Для этого, соответственно, нужен корпус и установленные sphinxbase и sphinxtrain. В качестве корпуса рекомендую voxforge, который нужно будет слегка модифицировать.Вот здесь мы и подходим к самому главному. Как вы, наверное, знаете, sphinxtrain управляется общим конфигом (sphinx_train.cfg), который задает все параметры для обучения (и тестирования) моделей, и дополнительно feat.params, в котором указываются параметры feature extraction. Начиная с версии 0.8, некоторые утилиты сфинкса получили дополнительные параметры, отвечающие за шумоподавление. А именно -remove_noise и -lifter. Для -remove_noise нужно задать значение yes (впрочем, это его дефолтное значение), а обычное значение параметра -lifter — 22. Если задавать его в основном конфиге:
$CFG_LIFTER = »22»; # Cepstrum lifter is smoothing to improve recognition то можно читать его оттуда: -lifter __CFG_LIFTER__ Еще один важный для нас параметр — это -transform. Его дефолтное значение legacy, но нам нужно dct. Итак, чтобы натренировать шумоустойчивые модели, нам нужно задать в feat.params трио параметров:
-transform dct -remove_noise yes -lifter 22 Но все же, лучше перенести их в sphinx_train.cfg, как это делается для других параметров:
$CFG_TRANSFORM = «dct»; # Previously legacy transform is used, but dct is more accurate $CFG_LIFTER = »22»; # Cepstrum lifter is smoothing to improve recognition feat.params: -transform __CFG_TRANSFORM__ -remove_noise yes -lifter __CFG_LIFTER__ Надо понимать, что sphinxtrain — это всего лишь скрипт-обертка для отдельных утилит, таких как fe, поэтому если вы вызываете их отдельно, нужно всегда задавать эти параметры (если они есть).
Вот пример моих конфигов для voxforge-en:
sphinx_train.cfg: # Configuration script for sphinx trainer -*-mode: Perl-*-
$CFG_VERBOSE = 1; # Determines how much goes to the screen.
# These are filled in at configuration time $CFG_DB_NAME = «voxforge_en»; # Experiment name, will be used to name model files and log files $CFG_EXPTNAME = »$CFG_DB_NAME»;
# Directory containing SphinxTrain binaries $CFG_BASE_DIR = »/home/speechdat/voxforge-en»; $CFG_SPHINXTRAIN_DIR = »/usr/local/lib/sphinxtrain»; $CFG_BIN_DIR = »/usr/local/libexec/sphinxtrain»; $CFG_SCRIPT_DIR = »/usr/local/lib/sphinxtrain/scripts»;
# Audio waveform and feature file information $CFG_WAVFILES_DIR = »$CFG_BASE_DIR/wav»; $CFG_WAVFILE_EXTENSION = 'wav'; $CFG_WAVFILE_TYPE = 'mswav'; # one of nist, mswav, raw $CFG_FEATFILES_DIR = »$CFG_BASE_DIR/feat»; $CFG_FEATFILE_EXTENSION = 'mfc'; $CFG_VECTOR_LENGTH = 13;
# Feature extraction parameters $CFG_WAVFILE_SRATE = 16000.0; $CFG_NUM_FILT = 40; # For wideband speech it’s 40, for telephone 8khz reasonable value is 31 $CFG_LO_FILT = 133.33334; # For telephone 8kHz speech value is 200 $CFG_HI_FILT = 6855.4976; # For telephone 8kHz speech value is 3500 $CFG_TRANSFORM = «dct»; # Previously legacy transform is used, but dct is more accurate $CFG_LIFTER = »22»; # Cepstrum lifter is smoothing to improve recognition
$CFG_MIN_ITERATIONS = 1; # BW Iterate at least this many times $CFG_MAX_ITERATIONS = 10; # BW Don’t iterate more than this, somethings likely wrong.
# (none/max) Type of AGC to apply to input files $CFG_AGC = 'none'; # (current/none) Type of cepstral mean subtraction/normalization # to apply to input files $CFG_CMN = 'current'; $CFG_CMNINIT = 10.0; # (yes/no) Normalize variance of input files to 1.0 $CFG_VARNORM = 'no'; # (yes/no) Train full covariance matrices $CFG_FULLVAR = 'no'; # (yes/no) Use diagonals only of full covariance matrices for # Forward-Backward evaluation (recommended if CFG_FULLVAR is yes) $CFG_DIAGFULL = 'no';
# (yes/no) Perform vocal tract length normalization in training. This # will result in a «normalized» model which requires VTLN to be done # during decoding as well. $CFG_VTLN = 'no'; # Starting warp factor for VTLN $CFG_VTLN_START = 0.80; # Ending warp factor for VTLN $CFG_VTLN_END = 1.40; # Step size of warping factors $CFG_VTLN_STEP = 0.05;
# Directory to write queue manager logs to $CFG_QMGR_DIR = »$CFG_BASE_DIR/qmanager»; # Directory to write training logs to $CFG_LOG_DIR = »$CFG_BASE_DIR/logdir»; # Directory for re-estimation counts $CFG_BWACCUM_DIR = »$CFG_BASE_DIR/bwaccumdir»; # Directory to write model parameter files to $CFG_MODEL_DIR = »$CFG_BASE_DIR/model_parameters»;
# Directory containing transcripts and control files for # speaker-adaptive training $CFG_LIST_DIR = »$CFG_BASE_DIR/etc»;
# Decoding variables for MMIE training $CFG_LANGUAGEWEIGHT = »11.5»; $CFG_BEAMWIDTH = »1e-100»; $CFG_WORDBEAM = »1e-80»; $CFG_LANGUAGEMODEL = »$CFG_LIST_DIR/${CFG_DB_NAME}_full.lm.DMP»; $CFG_WORDPENALTY = »0.2»;
# Lattice pruning variables $CFG_ABEAM = »1e-50»; $CFG_NBEAM = »1e-10»; $CFG_PRUNED_DENLAT_DIR = »$CFG_BASE_DIR/pruned_denlat»;
# MMIE training related variables $CFG_MMIE = «no»; $CFG_MMIE_MAX_ITERATIONS = 5; $CFG_LATTICE_DIR = »$CFG_BASE_DIR/lattice»; $CFG_MMIE_TYPE = «best»; # Valid values are «rand», «best» or «ci» $CFG_MMIE_CONSTE = »3.0»; $CFG_NUMLAT_DIR = »$CFG_BASE_DIR/numlat»; $CFG_DENLAT_DIR = »$CFG_BASE_DIR/denlat»;
# Variables used in main training of models $CFG_DICTIONARY = »$CFG_LIST_DIR/$CFG_DB_NAME.dict»; $CFG_RAWPHONEFILE = »$CFG_LIST_DIR/$CFG_DB_NAME.phone»; $CFG_FILLERDICT = »$CFG_LIST_DIR/$CFG_DB_NAME.filler»; $CFG_LISTOFFILES = »$CFG_LIST_DIR/${CFG_DB_NAME}_full.fileids»; $CFG_TRANSCRIPTFILE = »$CFG_LIST_DIR/${CFG_DB_NAME}_full.transcription»; $CFG_FEATPARAMS = »$CFG_LIST_DIR/feat.params»;
# Variables used in characterizing models
$CFG_HMM_TYPE = '.cont.'; # Sphinx 4, PocketSphinx #$CFG_HMM_TYPE = '.semi.'; # PocketSphinx #$CFG_HMM_TYPE = '.ptm.'; # PocketSphinx (larger data sets)
if (($CFG_HMM_TYPE ne ».semi.») and ($CFG_HMM_TYPE ne ».ptm.») and ($CFG_HMM_TYPE ne ».cont.»)) { die «Please choose one CFG_HMM_TYPE out of '.cont.', '.ptm.', or '.semi.',» . «currently $CFG_HMM_TYPE\n»; }
# This configuration is fastest and best for most acoustic models in # PocketSphinx and Sphinx-III. See below for Sphinx-II. $CFG_STATESPERHMM = 3; $CFG_SKIPSTATE = 'no';
if ($CFG_HMM_TYPE eq '.semi.') { $CFG_DIRLABEL = 'semi'; # Four stream features for PocketSphinx $CFG_FEATURE = «s2_4x»; $CFG_NUM_STREAMS = 4; $CFG_INITIAL_NUM_DENSITIES = 256; $CFG_FINAL_NUM_DENSITIES = 256; die «For semi continuous models, the initial and final models have the same density» if ($CFG_INITIAL_NUM_DENSITIES!= $CFG_FINAL_NUM_DENSITIES); } elsif ($CFG_HMM_TYPE eq '.ptm.') { $CFG_DIRLABEL = 'ptm'; # Four stream features for PocketSphinx $CFG_FEATURE = «s2_4x»; $CFG_NUM_STREAMS = 4; $CFG_INITIAL_NUM_DENSITIES = 64; $CFG_FINAL_NUM_DENSITIES = 64; die «For phonetically tied models, the initial and final models have the same density» if ($CFG_INITIAL_NUM_DENSITIES!= $CFG_FINAL_NUM_DENSITIES); } elsif ($CFG_HMM_TYPE eq '.cont.') { $CFG_DIRLABEL = 'cont'; # Single stream features — Sphinx 3 $CFG_FEATURE = »1s_c_d_dd»; $CFG_NUM_STREAMS = 1; $CFG_INITIAL_NUM_DENSITIES = 1; $CFG_FINAL_NUM_DENSITIES = 32; die «The initial has to be less than the final number of densities» if ($CFG_INITIAL_NUM_DENSITIES > $CFG_FINAL_NUM_DENSITIES); }
# Number of top gaussians to score a frame. A little bit less accurate computations # make training significantly faster. Uncomment to apply this during the training # For good accuracy make sure you are using the same setting in decoder # In theory this can be different for various training stages. For example 4 for # CI stage and 16 for CD stage # $CFG_CI_TOPN = 4; # $CFG_CD_TOPN = 16;
# (yes/no) Train multiple-gaussian context-independent models (useful # for alignment, use 'no' otherwise) in the models created # specifically for forced alignment $CFG_FALIGN_CI_MGAU = 'no'; # (yes/no) Train multiple-gaussian context-independent models (useful # for alignment, use 'no' otherwise) $CFG_CI_MGAU = 'no'; # Number of tied states (senones) to create in decision-tree clustering $CFG_N_TIED_STATES = 3000; # How many parts to run Forward-Backward estimatinon in $CFG_NPART = 1;
# (yes/no) Train a single decision tree for all phones (actually one # per state) (useful for grapheme-based models, use 'no' otherwise) $CFG_CROSS_PHONE_TREES = 'no';
# Use force-aligned transcripts (if available) as input to training $CFG_FORCEDALIGN = 'no';
# Use a specific set of models for force alignment. If not defined, # context-independent models for the current experiment will be used. $CFG_FORCE_ALIGN_MDEF = »$CFG_BASE_DIR/model_architecture/$CFG_EXPTNAME.falign_ci.mdef»; $CFG_FORCE_ALIGN_MODELDIR = »$CFG_MODEL_DIR/$CFG_EXPTNAME.falign_ci_$CFG_DIRLABEL»;
# Use a specific dictionary and filler dictionary for force alignment. # If these are not defined, a dictionary and filler dictionary will be # created from $CFG_DICTIONARY and $CFG_FILLERDICT, with noise words # removed from the filler dictionary and added to the dictionary (this # is because the force alignment is not very good at inserting them)
# $CFG_FORCE_ALIGN_DICTIONARY = »$ST: CFG_BASE_DIR/falignout$ST: CFG_EXPTNAME.falign.dict»;; # $CFG_FORCE_ALIGN_FILLERDICT = »$ST: CFG_BASE_DIR/falignout/$ST: CFG_EXPTNAME.falign.fdict»;;
# Use a particular beam width for force alignment. The wider # (i.e. smaller numerically) the beam, the fewer sentences will be # rejected for bad alignment. $CFG_FORCE_ALIGN_BEAM = 1e-60;
# Calculate an LDA/MLLT transform? $CFG_LDA_MLLT = 'yes'; # Dimensionality of LDA/MLLT output $CFG_LDA_DIMENSION = 29;
# This is actually just a difference in log space (it doesn’t make # sense otherwise, because different feature parameters have very # different likelihoods) $CFG_CONVERGENCE_RATIO = 0.1;
# Queue: POSIX for multiple CPUs on a local machine # Queue: PBS to use a PBS/TORQUE queue $CFG_QUEUE_TYPE = «Queue: POSIX»;
# Name of queue to use for PBS/TORQUE $CFG_QUEUE_NAME = «workq»;
# (yes/no) Build questions for decision tree clustering automatically $CFG_MAKE_QUESTS = «yes»; # If CFG_MAKE_QUESTS is yes, questions are written to this file. # If CFG_MAKE_QUESTS is no, questions are read from this file. $CFG_QUESTION_SET = »${CFG_BASE_DIR}/model_architecture/${CFG_EXPTNAME}.tree_questions»; #$CFG_QUESTION_SET = »${CFG_BASE_DIR}/linguistic_questions»;
$CFG_CP_OPERATION = »${CFG_BASE_DIR}/model_architecture/${CFG_EXPTNAME}.cpmeanvar»;
# This variable has to be defined, otherwise utils.pl will not load. $CFG_DONE = 1;
return 1; feat.params: -alpha 0.97 -dither yes -doublebw no -nfilt __CFG_NUM_FILT__ -ncep __CFG_VECTOR_LENGTH__ -lowerf __CFG_LO_FILT__ -upperf __CFG_HI_FILT__ -samprate __CFG_WAVFILE_SRATE__ -nfft 512 -wlen 0.0256 -transform __CFG_TRANSFORM__ -feat __CFG_FEATURE__ -agc __CFG_AGC__ -cmn __CFG_CMN__ -varnorm __CFG_VARNORM__ -remove_noise yes -lifter __CFG_LIFTER__ Конечно, тренировка акустических моделей — это тот еще геморрой труд. Помимо специфических знаний, она требует установки sphinxbase и sphinxtrain и длится около суток. Поэтому я расшарил свои модели, натренированные на voxforge-en по вышеприведенному рецепту: dropbox.
Использование акустических моделей Имея модели, мы можем, наконец, свободно вздохнуть и подключить их в свою систему. Здесь рецепты разнятся в зависимости от того, используете вы pocketsphinx или Sphinx4. С pocketsphinx все просто: нужно просто задать трио параметров -transform, -remove_noise и -lifter. А ежели мы хотим использовать Sphinx4, то нужно включить во фронтенд компонент Denoise и немного изменить сам фронтенд. Соответствующий конвеер будет выглядеть примерно так: StreamDataSource Dither Preemphasizer RaisedCosineWindower DiscreteFourierTransform MelFrequencyFilterBank Denoise DiscreteCosineTransform2 Lifter BatchCMN DeltasFeatureExtractor FeatureTransform NB: featureTransform нужен, только если вы применяли LDA/MLLT в обучении моделей.Три компонента, выделенные жирным и обеспечивают шумоподавление.
В XML соответствующая часть конфига будет выглядеть так:
config.xml
Это работает? Вполне. Для своей задачи я получил прирост в 6.5%: с 74.65% до 81.38%. Но все равно, адаптацию к каналу стоит проводить.