yesno初体验
背景
yesno是kaldi中语音识别(ASR)的一个例子。
语音识别原理
语音识别即由语音转换为文字,具体来说由声学输出A(1, T) 到 单词序列W(1,R)的最大化概率:
argmaxP(W(1,R) | A(1,T))
根据贝叶斯定律,可以将上面的概率转换为已知单词序列W(1,R)的分布,求声学输出A(1,T)的计算问题。
argmaxP(A(1,T) | W(1,R)P(W(1,R))
这里的 P(A(1,T) | W(1,R)
称为声学模型, P(W(1,R))
称为语言模型。
yesno初体验
执行yesno例子
执行
cd kaldi/egs/yesno/s5
./run.sh
结果
…………
local/score.sh: scoring with word insertion penalty=0.0,0.5,1.0
%WER 0.00 [ 0 / 232, 0 in , 0 del, 0 ub ] exp/mono0a/decode_te t_ye no/wer_10_0.0
WER为字错误率,WER=0.00表示全部识别正确。
介绍
yesno 数据集中,包含60个音频文件。
每个音频文件,都包含8个音频片断。每个音频片断为 ken
或者 lo
,即希伯来语的 yes
/ no
。这里音频片断与音频文件名是对应的。即文件名 0_0_0_0_1_1_1_1.wav
的音频片断为 lo lo lo lo ken ken ken ken
。
yesno
例子中做的事情为:用数据集中的一半数据30个用于训练,另一个数据30个用于测试,来准确的将 ken
/ lo
的发声转换为文本。
数据准备
kaldi数据集都需要有音频文件和讲稿文件。
text
讲稿文件,每行一句话,格式为:
<utt_id> <transcript>
如
1_0_0_0_0_0_0_0 YES NO NO NO NO NO NO NO
1_0_0_0_0_0_0_1 YES NO NO NO NO NO NO YES
注:这里的utt_id中不含扩展名。
wav.scp
标识唯一Id对应的音频文件,语法格式为:
<file_id> <带路径的wav文件名 / 获取.wav文件的命令>
如
0_0_0_0_1_1_1_1 waves_yesno/0_0_0_0_1_1_1_1.wav
0_0_0_1_0_0_0_1 waves_yesno/0_0_0_1_0_0_0_1.wav
utt2spk
标识每句话的说话者,语法格式为:
<utt_id> <speaker_id>
本例中只有一句说话者,所以为:
1_0_0_0_0_0_0_0 global
1_0_0_0_0_0_0_1 global
spk2utt
反向索引,可以通过小工具 s5/utils/utt2spk_to_spk2utt.pl
来得到
最后数据目录如下:
data
├── test_yesno
│ ├── spk2utt
│ ├── text
│ ├── utt2spk
│ └── wav.scp
└── train_yesno
├── spk2utt
├── text
├── utt2spk
└── wav.scp
词典准备
构建语言知识:词典和音素词典
词典
本例子,只有 YES
和 NO
两个词,假设它们都为单音素词 Y
和 N
。
同时,考虑到还有单词之间的“沉默”,用额外的音素“SIL”表示。
因此,词典文件 input/phones.txt
为:
SIL
Y
N
完整的词典文件有5个,分别为:
文件名 | 含义 |
---|---|
lexicon.txt | 词素 - 音素对的完整列表 |
lexicon_words.txt | 单词 - 音素对列表 |
silence_phones.txt | 无声音素列表 |
nonsilence_phones.txt | 非无声音素列表 |
optional_silence.txt | 可选无声音素列表 |
在yesno例子中,文件如下:
% cat lexicon.txt
<SIL> SIL
YES Y
NO N
% cat lexicon_words.txt
YES Y
NO N
% cat silence_phones.txt
SIL
% cat nonsilence_phones.txt
Y
N
% cat optional_silence.txt
SIL
最后需要将词典文件,进行有限状态转换(FST),转换成kaldi接受的数据结构。
utils/prepare_lang.sh --position-dependent-phones false <RAW_DICT_PATH> <OOV> <TEMP_DIR> <OUTPUT_DIR>
本例中:
参数 | 值 |
---|---|
<RAW_DICT_PATH> | data/local/dict |
<OOV> | "<SIL>" |
<TEMP_DIR> | data/local/lang |
<OUTPUT_DIR> | data/lang |
最终会得到一个 arpa
格式的语言模型文件,本例中为 lm_tg.arpa
,内容如下:
% cat lm_tg.arpa
\data\
ngram 1=4
\1-grams:
-1 NO
-1 YES
-99 <s>
-1 </s>
\end\
这个 arpa
文件需要转换成FST,本例中通过 local/prepare_lm.sh
生成 G.fst
文件。
特征提取
MFCC是将音频文件的频谱信息转换为13维的特征向量。
提取 MFCC 梅尔频率倒谱系数
通过 make_mfcc.sh
工具,通过三个参数,分别指定处理器数量,训练集数据位置,和结果输出位置。
steps/make_mfcc.sh --nj <N> <INPUT_DIR> <OUTPUT_DIR>
yesno例子中,如下:
steps/make_mfcc.sh --nj 1 data/train_yesno exp/make_mfcc/train_yesno mfcc
归一化倒谱特征
通过 make_mfcc.sh
工具,通过两个参数,分别指定训练集数据位置,和结果输出位置。
steps/compute_cmvn_stats.sh <INPUT_DIR> <OUTPUT_DIR>
INPUT_DIR
和 OUTPUT_DIR
和上例中的一样。
yesno例子中,如下:
steps/compute_cmvn_stats.sh data/train_yesno exp/make_mfcc/train_yesno mfcc
训练单音素模型
在特征提取完成后,这里开始训练声学模型。本例中训练不依赖于上下文的单音素模型。
steps/train_mono.sh
--nj <N>
--cmd "$train_cmd" \
--totgauss 400 \
<训练数据路径> <语言定义路径> <输出路径>
yesno中实际如下:
steps/train_mono.sh
--nj 1
--cmd "$train_cmd" \
--totgauss 400 \
data/train_yesno data/lang exp/mono0a
到了这里,我们就有了声学模型和语言模型。
图解码
解码,即新的输入,是如何通过 AM & LM词图的。
执行:
# Graph compilation
utils/mkgraph.sh data/lang_test_tg exp/mono0a exp/mono0a/graph_tgpr
将得到一个FST图,路径为 exp/mono0a/graph_tgpr/HCLG.fst
找到最佳路径
steps/decode.sh
--nj 1 \
--cmd "utils/run.pl" \
exp/mono0a/graph_tgpr \
data/test_yesno \
exp/mono0a/decode_test_yesno
结果为 exp/mono0a/decode_test_yesno/lat.1.gz
,包含了解码器的第1个纯种处理的发言词图。
直接使用解码网络HCLG来识别
kaldi识别解码一段语音的过程是:首先提取特征,然后过声学模型AM,然后过解码网络HCLG.fst,最后输出识别结果。
准备测试集
test_yesno
├── spk2utt
├── text
├── utt2spk
└── wav.scp
提取特征
./steps/make_mfcc.sh --nj 1 data/test_yesno exp/make_mfcc/test_yesno
utils/validate_data_dir.sh: Successfully validated data-directory data/test_yesno
./steps/make_mfcc.sh: [info]: no segments file exists: assuming wav.scp indexed by utterance.
./steps/make_mfcc.sh: Succeeded creating MFCC features for test_yesno
过声学AM
过解码网络HCLG.fst
./steps/decode.sh --nj 1 --cmd "utils/run.pl" exp/mono0a/graph_tgpr data/test_yesno_good exp/mono0a/decode_test_yesno
输出识别结果
参考
- 《深度学习——语音识别技术实践,柳若边》