基于kaldi和CVTE开源模型的中文识别

环境要求:ubuntu16.04

1.Kaldi安装

Kaldi是一款基于c++编写的完全开源的语音识别工具箱。
kaldi

  • 支持主流的特征提取:MFCC PLP等
  • 支持传统的GMM-HMM的声学模型构建
  • 支持WFST的解码策略
  • 支持深度神经网络的声学建模
  • 完善的社区支持

Kaldi 主要是运行在 Unix-like 操作系统下, Kaldi 项目现在托管在 github上 ,需要使用git命令将其下载到本地
在终端键入:

git clone https://github.com/kaldi-asr/kaldi.git kaldi-tian

编译kaldi源码
在终端键入 cd kaldi-tian/tools/extras
键入./check_dependencies.sh执行该脚本检查包依赖情况
根据提示安装所需要的包

在终端键入cd kaldi-tian/tools
键入make 进行编译

这里出现警告 IRSTLM库没有安装,其他一切OK
在这里插入图片描述

键入 cd kaldi-tian/tools
键入 ./extras/install_irstlm.sh 安装IRSTLM

在终端键入 cd kaldi-tian/src
键入./configure 检查外部库安装情况,根据提示进行安装
键入make depend 进行预编译
键入make 进行编译,make耗时较长请耐心等待
检测 Kaldi 是否编译成功,使用kaldi-tian/egs/yesno例子进行测试
yesno是关于yes和no两个孤立词的识别
在终端键入cd kaldi-tian/egs/yesno/s5 进入yesno样例目录
键入ls命令查看该目录下有哪些文件

ai@tiancuixia-1:~/kaldi-tian/egs/yesno/s5$ ls
conf  exp    local  path.sh  steps  waves_yesno
data  input  mfcc   run.sh   utils  waves_yesno.tar.gz

conf文件夹里是一些配置文件例如MFCC的参数 HMM的拓扑结构;
local文件夹里主要是一些准备数据的脚本,供顶层脚本run.sh调用;
steps和utils文件夹里主要是一些运行时调用的脚本;
data文件夹里主要存放语言模型、发音字典和音素信息等等。

键入./run.sh
在这里插入图片描述说明我们kaldi已经正确安装了

2.运行cvte模型

CVTE开源了kaldi的中文模型,模型下载地址:http://kaldi-asr.org/models/0002_cvte_chain_model.tar.gz
解压放到kaldi-tian/egs下即可

egs/wsj/s5中的stepsutils拷贝到egs/cvte/s5目录下

cp -r egs/wsj/s5/steps egs/cvte/s5/steps
cp -r egs/wsj/s5/utils egs/cvte/s5/utils

egs/hkust/s5/local/score.sh拷贝到egs/cvte/s5/local目录下

cp egs/hkust/s5/local/score.sh egs/cvte/s5/local
cd kaldi-tian/egs/cvte/s5

注释掉utils/lang/check_phones_compatible.shif语句中的exit 1

 36 # check if the files exist or not
 37 if [ ! -f $table_first ]; then
 38   if [ ! -f $table_second ]; then
 39     echo "$0: Error! Both of the two phones-symbol tables are absent."
 40     echo "Please check your command"
 41     #exit 1;
 42   else
 43     # The phones-symbol-table1 is absent. The model directory maybe created by old script.
 44     # For back compatibility, this script exits silently with status 0.
 45     exit 0;
 46   fi

CVTE文件结构:
CVTE文件结构

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5$ ls
cmd.sh  conf  data  exp  fbank  local  path.sh  run.sh  steps  utils

运行示例脚本:
./run.sh

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5$ ./run.sh 
steps/make_fbank.sh --nj 1 --cmd run.pl data/fbank/test exp/make_fbank/test fbank/test
steps/make_fbank.sh: moving data/fbank/test/feats.scp to data/fbank/test/.backup
utils/validate_data_dir.sh: WARNING: you have only one speaker.  This probably a bad idea.
   Search for the word 'bold' in http://kaldi-asr.org/doc/data_prep.html
   for more information.
utils/validate_data_dir.sh: Successfully validated data-directory data/fbank/test
steps/make_fbank.sh: [info]: no segments file exists: assuming wav.scp indexed by utterance.
Succeeded creating filterbank features for test
steps/compute_cmvn_stats.sh data/fbank/test exp/fbank_cmvn/test fbank/test
Succeeded creating CMVN stats for test
steps/nnet3/decode.sh --acwt 1.0 --post-decode-acwt 10.0 --nj 1 --num-threads 1 --cmd run.pl --mem 64G --iter final --frames-per-chunk 50 exp/chain/tdnn/graph data/fbank/test exp/chain/tdnn/decode_test
utils/lang/check_phones_compatible.sh: Error! Both of the two phones-symbol tables are absent.
Please check your command
grep: exp/chain/tdnn/phones.txt: No such file or directory
grep: exp/chain/tdnn/graph/phones.txt: No such file or directory
steps/nnet3/decode.sh: feature type is raw
steps/diagnostic/analyze_lats.sh --cmd run.pl --mem 64G --iter final exp/chain/tdnn/graph exp/chain/tdnn/decode_test
run.pl: job failed, log is in exp/chain/tdnn/decode_test/log/analyze_alignments.log
score best paths
 - steps/score_kaldi.sh --cmd 'run.pl --mem 64G' data/fbank/test exp/chain/tdnn/graph exp/chain/tdnn/decode_test
steps/score_kaldi.sh --cmd run.pl --mem 64G data/fbank/test exp/chain/tdnn/graph exp/chain/tdnn/decode_test
steps/score_kaldi.sh: scoring with word insertion penalty=0.0,0.5,1.0
 - steps/scoring/score_kaldi_cer.sh --stage 2 --cmd 'run.pl --mem 64G' data/fbank/test exp/chain/tdnn/graph exp/chain/tdnn/decode_test
steps/scoring/score_kaldi_cer.sh --stage 2 --cmd run.pl --mem 64G data/fbank/test exp/chain/tdnn/graph exp/chain/tdnn/decode_test
steps/scoring/score_kaldi_cer.sh: scoring with word insertion penalty=0.0,0.5,1.0
 - echo 'local/score.sh: Done'
local/score.sh: Done
score confidence and timing with sclite
Decoding done.

这里会报error

utils/lang/check_phones_compatible.sh: Error! Both of the two phones-symbol tables are absent.

原因是CVTE作者没有提供phones.txt, 不影响结果,忽略就好了。
预测结果保存在exp相应的路径下,可以自行查看

3. 在CVTE上运行自己的数据

  • 准备自己的音频数据
    要求是16-bit位深,采样率16000Hz,单声道,wav格式(可以采用adobe audition软件录制)
    存放在某个路径下,比如: s5/data/wav/audio
ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/wav/audio$ ls
停止运行_standardTTS.wav          打开本地总线工具_standardTTS.wav    暂停测试_standardTTS.wav
单次触发_standardTTS.wav          打开物理层管理工具_standardTTS.wav  正常模式_standardTTS.wav
开始测试_standardTTS.wav          打开电源工具_standardTTS.wav        缩小垂直分辨率_standardTTS.wav
开始运行_standardTTS.wav          打开通用信号工具_standardTTS.wav    缩小存储深度_standardTTS.wav
打开串行外设工具_standardTTS.wav  打开闪存工具_standardTTS.wav        缩小水平时基_standardTTS.wav
打开二次方工具_standardTTS.wav    放大垂直分辨率_standardTTS.wav      缩小采样间隔_standardTTS.wav
打开数据方向工具_standardTTS.wav  放大存储深度_standardTTS.wav        退出测试_standardTTS.wav
打开时序工具_standardTTS.wav      放大水平时基_standardTTS.wav
打开时钟工具_standardTTS.wav      放大采样间隔_standardTTS.wav

这里是用阿里云的TTS工具合成了一些音频

  • 需要创建的文件

存放在data/fbank/test路径下

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/fbank/test$ ls
cmvn.scp  feats.scp  spk2utt  text  utt2spk  wav.scp

需要手动创建的文件包括 text、wav.scp、utt2spk
文件text包含每段发音的标注

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/fbank/test$ head -3 text 
audio_停止运行_standardTTS	停止运行
audio_单次触发_standardTTS	单次触发
audio_开始测试_standardTTS	开始测试

这个文件的格式是:
<speaker_id>-<utterance-id> transcription
每行的第一项是发音编号(utterance-id), 可以是任意的文本字符串(如果设置中还包含说话人信息, 则应该把说话人编号(speaker-id)作为发音编号的前缀,和utterance-id以破折号"-"隔开)。这对于音频文件的排序非常重要。发音编号后面跟着的是每段发音的标注。你不用保证这里出现的每一个字都出现在你的词汇表中。词汇表之外的词会被映射到oov.txt 中。

另外一个很重要的文件是wav.scp

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/fbank/test$ head -3 wav.scp 
audio_停止运行_standardTTS   /home/ai/kaldi-tian/egs/cvte/s5/data/wav/audio/停止运行_standardTTS.wav
audio_单次触发_standardTTS   /home/ai/kaldi-tian/egs/cvte/s5/data/wav/audio/单次触发_standardTTS.wav
audio_开始测试_standardTTS   /home/ai/kaldi-tian/egs/cvte/s5/data/wav/audio/开始测试_standardTTS.wav

这个文件的格式是:
<recording-id> <extended-filename>
其中, extended-filename 是一个实际的文件名, 或者一段提取wav格式文件的命令。
在这里 wav.scp 每一行的第一项就是发音编号,即recording-id等于utterance-id。

最后一个需要手动创建的文件是utt2spk。
该文件指明某一段发音是哪一个说话人发出的

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/fbank/test$ head -3 utt2spk 
audio_停止运行_standardTTS   audio_停止运行_standardTTS
audio_单次触发_standardTTS   audio_单次触发_standardTTS
audio_开始测试_standardTTS   audio_开始测试_standardTTS

格式是:
<utterance-id> <speaker-id>
注意一点,说话人编号并不需要与说话人实际的名字完全一致——只需要大概能够猜出来就行。在这种情况下,我们假定每一个说话方(电话对话的每一方)对应一个说话人。这可能不完全正确—— 有时一个说话人会把电话交给另外一个说话人,或者同一个说话人会在不同的对话中出现——但是上述假定对我们来说也足够用了。如果完全没有关于说话人的信息, 可以把发音编号当做说话人编号。那么对应的文件格式就变为<utterance-id> <utterance-id>

上述所有文件都应该按照utterance-id排序。如果没有排序, 运行脚本的时候就会出现错误。这与Kaldi的I/O框架有关,归根到底是因为排序后的文件可以在一些不支持 fseek()的流中,例如,含有管道的命令,提供类似于随机存取查找的功能。许多Kaldi程序都会从其他Kaldi命令中读取多个管道流,读入各种不同类型的对象,然后对不同输入做一些类似于―合并然后排序‖的处理。既然要合并排序, 当然需要输入是经过排序的。

不需要手动创建的文件
以用如下的一条命令创建spk2utt文件

#通过 utt2spk 创建 spk2utt 文件
utils/utt2spk_to_spk2utt.pl data/fbank/test/utt2spk > data/fbank/test/spk2utt

最后需要移除旧测试数据产生的cmvn.scp, 否则后续执行steps/make_mfcc.sh的时候代码会自动去检查cmvn.scp与utt2spk的一致性, 导致冲突error

以上创建相应文件的过程比较麻烦,因此我写了一个python自动化处理脚本( establish_fbank_files.py ),需要copy到s5目录下执行。

准备好以上数据,就可以运行run.sh等待结果了,结果保存在exp相应的路径下。

我们的任务是将wav文件识别为特定的指令词(指令词汇有限),但是有时候文本不一定一致,比如:识别结果“但 次 触发” 对应的指令是 “单次触发”。我们需要对识别的文本进行纠错。
考虑的方案是先将识别的文字转换为拼音,然后与指令词汇表里每一条指令对应的拼音进行比较,比较方式是计算编辑距离。此外,针对一些容易识别错的发音组合,再通过增加一些规则及正则表达式组合来处理。相应的脚本名为asr_modify.py, 调用之前需要安装汉字转拼音及计算编辑距离的python包,pypinyin 及Levenshtein。

这里写了一些数据处理及修正相关的脚本(包含establish_fbank_files.pyasr_modify.py在内),详见PythonProjects/utils: 代码

4. CVTE 中调用的一些脚本解析

cvte 提供了offline识别(即数据的特征提取、解码都是批量进行的,主要用于评估预测效果),而cvte的模型加载起来要17个G左右,预测起来会很慢。
Kaldi 中新版的 online 识别,其特征处理过程跟CVTE的不一致,另外它也只是模拟实时,Kaldi 官网给的建议是自己根据实际需求写实时服务器。
http://kaldi-asr.org/doc/online_decoding.html#online_decoding_scope)

我们的想法是基于CVTE提供的offline识别代码进行修改。
想要实现实时语音识别,就要把模型预加载起来,offline中特征、模型加载、特征解码都是批量的,需要把代码拆分融合。
首先把整个预测过程中相关的部分脚本梳理一下:

s5/run.sh着手, 分步执行

特征处理部分:
  • 生成feats.scp
steps/make_fbank.sh --nj 1 --cmd run.pl data/fbank/test exp/make_fbank/test fbank/test

调用了

./compute-fbank-feats --verbose=2 --config=../../egs/cvte/s5/conf/fbank.conf scp,p:../../egs/cvte/s5/data/fbank/test/wav1.scp ark:out.ark

输出文件out.ark

./copy-feats --compress=true ark:out.ark ark,scp:raw_fbank_test.1.ark,raw_fbank_test.1.scp

接受out.ark作为输入,输出文件raw_fbank_test.1.ark, raw_fbank_test.1.scp
shell脚本后续将raw_fbank_test.1.scp合并成feats.scp

注:
.ark (archieve)文件为二进制格式,一般很大,里面是真正的数据,
.scp(script)文件记录对应ark的路径

ai@tiancuixia-1:~/kaldi-tian/egs/cvte/s5/data/fbank/test$ head -1 feats.scp 
audio_停止运行_standardTTS /home/ai/kaldi-tian/egs/cvte/s5/fbank/test/raw_fbank_test.1.ark:31

意 思 是 , 打 开 存 档(archive)文件/home/ai/kaldi-tian/egs/cvte/s5/fbank/test/raw_fbank_test.1.ark, fseek()定位到 31(字节), 然后开始读数据。

  • cmvn 计算,生成cmvn.scp
steps/compute_cmvn_stats.sh data/fbank/test exp/fbank_cmvn/test fbank/test
./compute-cmvn-stats --spk2utt=ark:spk2utt scp:raw_fbank_test.1.scp ark,scp:cmvn_test.ark,cmvn_test.scp

输出文件cmvn_test.ark,cmvn_test.scp
shell脚本后续复制cmvn_test.scp并保存为cmvn.scp

decode部分

steps/nnet3/decode.sh --acwt 1.0 --post-decode-acwt 10.0 --nj 1 --num-threads 1 --cmd run.pl --mem 64G --iter final --frames-per-chunk 50 exp/chain/tdnn/graph data/fbank/test exp/chain/tdnn/decode_test

调用了src/nnet3bin/下编译好的nnet3-latgen-faster

nnet3-latgen-faster --frame-subsampling-factor=3 --frames-per-chunk=50 --extra-left-context=0 --extra-right-context=0 --extra-left-context-initial=-1 --extra-right-context-final=-1 --minimize=false --max-active=7000 --min-active=200 --beam=15.0 --lattice-beam=8.0 --acoustic-scale=1.0 --allow-partial=true --word-symbol-table=/home/ai/kaldi-tian/egs/cvte/s5/exp/chain/tdnn/graph/words.txt /home/ai/kaldi-tian/egs/cvte/s5/exp/chain/tdnn/final.mdl /home/ai/kaldi-tian/egs/cvte/s5/exp/chain/tdnn/graph/HCLG.fst 'ark,s,cs:apply-cmvn --norm-means=true --norm-vars=false --utt2spk=ark:/home/ai/kaldi-tian/src/featbin/utt2spk scp:/home/ai/kaldi-tian/src/featbin/cmvn_test.scp scp:/home/ai/kaldi-tian/src/featbin/raw_fbank_test.1.scp ark:- |' 'ark:|lattice-scale --acoustic-scale=10.0 ark:- ark:- | gzip -c >lat.1.gz'

代码中很多地方出现rspecifier和wspecifier标记,这里解读一下:
rspecifier: 用于说明如何读表的字符串;
wspecifier: 用于说明如何写入表的字符串。

(因为改了方案,所以CVTE的实时识别暂时就不做了,这里只是梳理一下代码,记录一下,后续有时间可以尝试继续做下去)

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值