[转]kaldi ASR: DNN训练
作者:zqh_zy
链接:http://www.jianshu.com/p/c5fb943afaba
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
本文通过简单kaldi源码,分析DNN训练声学模型时神经网络的输入与输出。在进行DNN训练之前需要用到之前GMM-HMM训练的模型,以训练好的mono模型为例,对模型进行维特比alignement(对齐),该部分主要完成了每个语音文件的帧到transition-id的映射。
不妨查看对齐后的结果:
$ copy-int-vector "ark:gunzip -c ali.1.gz|" ark,t:- | head -n 1
speaker001_00003 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 16 15 15 15 18 890 889 889 889 889 889 889 892 894 893 893 893 86 88 87 90 89 89 89 89 89 89 89 89 89 89 89 89 89 89 194 193 196 195 195 198 197 386 385 385 385 385 385 385 385 385 388 387 387 390 902 901 901 904 903 906 905 905 905 905 905 905 905 905 905 905 905 914 913 913 916 918 917 917 917 917 917 917 752 751 751 751 751 751 754 753 753 753 753 753 753 753 753 756 755 755 926 925 928 927 927 927 927 927 927 927 930 929 929 929 929 929 929 929 929 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 16 18
对于一个训练语音文件speaker001_00003,后面的每一个数字标示一个transition-id,同时每个数字对应一个特征向量(对应的向量可以copy-matrix查看,参考特征提取)。同样查看transition-id:
$ show-transitions phones.txt final.mdl
Transition-state 1: phone = sil hmm-state = 0 pdf = 0
Transition-id = 1 p = 0.966816 [self-loop]
Transition-id = 2 p = 0.01 [0 -> 1]
Transition-id = 3 p = 0.01 [0 -> 2]
Transition-id = 4 p = 0.013189 [0 -> 3]
Transition-state 2: phone = sil hmm-state = 1 pdf = 1
Transition-id = 5 p = 0.970016 [self-loop]
Transition-id = 6 p = 0.01 [1 -> 2]
Transition-id = 7 p = 0.01 [1 -> 3]
Transition-id = 8 p = 0.01 [1 -> 4]
Transition-state 3: phone = sil hmm-state = 2 pdf = 2
Transition-id = 9 p = 0.01 [2 -> 1]
Transition-id = 10 p = 0.968144 [self-loop]
Transition-id = 11 p = 0.01 [2 -> 3]
Transition-id = 12 p = 0.0118632 [2 -> 4]
Transition-state 4: phone = sil hmm-state = 3 pdf = 3
Transition-id = 13 p = 0.01 [3 -> 1]
Transition-id = 14 p = 0.01 [3 -> 2]
Transition-id = 15 p = 0.932347 [self-loop]
Transition-id = 16 p = 0.0476583 [3 -> 4]
Transition-state 5: phone = sil hmm-state = 4 pdf = 4
Transition-id = 17 p = 0.923332 [self-loop]
Transition-id = 18 p = 0.0766682 [4 -> 5]
Transition-state 6: phone = a1 hmm-state = 0 pdf = 5
Transition-id = 19 p = 0.889764 [self-loop]
Transition-id = 20 p = 0.110236 [0 -> 1]
...
唯一的Transition-state对应唯一的pdf,其下又包括多个 Transition-id,
接下来看神经网络的输入与输出到底是什么。这里以steps/nnet为例。追溯脚本到steps/nnet/train.sh,找到相关的命令:
...
labels_tr="ark:ali-to-pdf $alidir/final.mdl \"ark:gunzip -c $alidir/ali.*.gz |\" ark:- | ali-to-post ark:- ark:- |"
...
feats_tr="ark:copy-feats scp:$dir/train.scp ark:- |"
...
# input-dim,
get_dim_from=$feature_transform
num_fea=$(feat-to-dim "$feats_tr nnet-forward \"$get_dim_from\" ark:- ark:- |" -)
# output-dim,
num_tgt=$(hmm-info --print-args=false $alidir/final.mdl | grep pdfs | awk '{ print $NF }')
...
dnn)
utils/nnet/make_nnet_proto.py $proto_opts \
${bn_dim:+ --bottleneck-dim=$bn_dim} \
$num_fea $num_tgt $hid_layers $hid_dim >$nnet_proto
;;
从上面关键的几个神经网络的训练的准备阶段可以看出,神经网络的输入很清楚是变换后的特征向量(feats_tr),输出是labels_tr,下面单独运行上面的命令,来查看神经网络的输出(target)是什么。labels_tr的生成分两步:
- ali-to-pdf: 将上面对齐文件中的transition-id转化为对应的pdf-id.
- ali-to-post: 根据得到的pdf-id,生成[pdf, post]对,即pdf与其对应的后验概率。
$ ali-to-pdf final.mdl "ark:gunzip -c ali.1.gz|" ark,t:- | head -n 1
speaker001_00003 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 4 440 440 440 440 440 440 440 441 442 442 442 442 38 39 39 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 92 92 93 93 93 94 94 188 188 188 188 188 188 188 188 188 189 189 189 190 446 446 446 447 447 448 448 448 448 448 448 448 448 448 448 448 448 452 452 452 453 454 454 454 454 454 454 454 371 371 371 371 371 371 372 372 372 372 372 372 372 372 372 373 373 373 458 458 459 459 459 459 459 459 459 459 460 460 460 460 460 460 460 460 460 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4
观察前两帧,结合文章一开始,transition-id 分别为4和1,而对应的pdf均为0。对该结果再进行ali-to-post:
$ ali-to-pdf final.mdl "ark:gunzip -c ali.1.gz|" ark,t:- | head -n 1 | ali-to-post ark,t:- ark,t:-
speaker001_00003 [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] ...... [ 3 1 ] [ 3 1 ] [ 3 1 ] [ 3 1 ] [ 4 1 ] [ 440 1 ] [ 440 1 ] [ 440 1 ] [ 440 1 ] [ 440 1 ] [ 440 1 ] [ 440 1 ] [ 441 1 ] [ 442 1 ] [ 442 1 ] [ 442 1 ] [ 442 1 ] [ 38 1 ] [ 39 1 ] [ 39 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 40 1 ] [ 92 1 ] [ 92 1 ]...... [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 0 1 ] [ 3 1 ] [ 4 1 ]
得到pdf-id以及相应的后验概率,这里均为1。
由此得到了训练数据以及对应的target label。进一步来看神经网络的输入与输出的维度,网络结构被utils/nnet/make_nnet_proto.py写到nnet_proto文件中,该Python脚本的两个重要参数 num_fea和num_tgt分别为神经网络的输入与输出的维度。其中num_fea是由feat-to-dim获得:
$ feat-to-dim scp:../tri4b_dnn/train.scp ark,t:- | grep speaker001_00003
speaker001_00003 40
这里为fbank特征,维度为40,而在真正作为神经网络输入时,进一步对特征向量进行的变换,从源码steps/nnet/train.sh也可以看到splice参数(默认值为5),指定了对特征向量的变换:取对应帧前后5帧,拼成一个11帧组成的大向量(维度为440)。该部分特征变换的拓扑也被保存到final.feature_transform:
$ more final.feature_transform
<Nnet>
<Splice> 440 40
[ -5 -4 -3 -2 -1 0 1 2 3 4 5 ]
<!EndOfComponent>
...
...
后面在进行神经网络的训练时会使用该拓扑对特征向量进行变换,最终的神经网络输入维度为440。
而num_tgt的维度则是通过hmm-info获得:
$ hmm-info final.mdl
number of phones 218
number of pdfs 1026
number of transition-ids 2834
number of transition-states 1413
$ hmm-info final.mdl | grep pdfs | awk '{ print $NF }'
1026
因此,看到神经网络的输出维度为1026,这时查看nnet_proto:
<AffineTransform> <InputDim> 440 <OutputDim> 1024 <BiasMean> -2.000000 <BiasRange> 4.000000 <ParamStddev> 0.037344 <MaxNorm> 0.000000
<Sigmoid> <InputDim> 1024 <OutputDim> 1024
<AffineTransform> <InputDim> 1024 <OutputDim> 1024 <BiasMean> -2.000000 <BiasRange> 4.000000 <ParamStddev> 0.109375 <MaxNorm> 0.000000
<Sigmoid> <InputDim> 1024 <OutputDim> 1024
<AffineTransform> <InputDim> 1024 <OutputDim> 1024 <BiasMean> -2.000000 <BiasRange> 4.000000 <ParamStddev> 0.109375 <MaxNorm> 0.000000
<Sigmoid> <InputDim> 1024 <OutputDim> 1024
<AffineTransform> <InputDim> 1024 <OutputDim> 1024 <BiasMean> -2.000000 <BiasRange> 4.000000 <ParamStddev> 0.109375 <MaxNorm> 0.000000
<Sigmoid> <InputDim> 1024 <OutputDim> 1024
<AffineTransform> <InputDim> 1024 <OutputDim> 1026 <BiasMean> 0.000000 <BiasRange> 0.000000 <ParamStddev> 0.109322 <LearnRateCoef> 1.000000 <BiasLearnRateCoef> 0.100000
<Softmax> <InputDim> 1026 <OutputDim> 1026
这里可以看到神经网络的输入维度有40变为440,输出为pdf的个数(对应HMM状态的个数)。
如果继续追查代码,最后可以找到单次神经网络的训练实现,kaldi/src/nnetbin/nnet-train-frmshuff.cc:
Perform one iteration (epoch) of Neural Network training with mini-batch Stochastic Gradient Descent. The training targets are usually pdf-posteriors, prepared by ali-to-post.
继续分析代码,可以看到几个关键步骤:
- 解析训练参数,配置网络
- 读取特征向量和target label,输入为Matrix< BaseFloat >类型,输出为Posterior类型,即<pdf-id, posterior>对。
// get feature / target pair,
Matrix<BaseFloat> mat = feature_reader.Value();
Posterior targets = targets_reader.Value(utt); - 随机打乱训练数据,作为神经网络输入与期望输出:
const CuMatrixBase<BaseFloat>& nnet_in = feature_randomizer.Value();
const Posterior& nnet_tgt = targets_randomizer.Value();
const Vector<BaseFloat>& frm_weights = weights_randomizer.Value(); - 前向传播,计算估计值nnet_out
// forward pass,
nnet.Propagate(nnet_in, &nnet_out); - 计算cost,这里支持交叉熵和平方差和multitask。结果为obj_diff
// evaluate objective function we've chosen,
if (objective_function == "xent") {
// gradients re-scaled by weights in Eval,
xent.Eval(frm_weights, nnet_out, nnet_tgt, &obj_diff);
} else if (objective_function == "mse") {
// gradients re-scaled by weights in Eval,
mse.Eval(frm_weights, nnet_out, nnet_tgt, &obj_diff);
}
... - 根据误差反向传播,更新参数
if (!crossvalidate) {
// back-propagate, and do the update,
nnet.Backpropagate(obj_diff, NULL);
} - 完成一次参数更新,继续迭代。
total_frames += nnet_in.NumRows(),
最终由调用该部分代码的/steps/nnet/train_scheduler.sh指定最大迭代次数max_iters或accept训练的模型,
accepting: the loss was better, or we had fixed learn-rate, or we had fixed epoch-number
小结
在进行DNN训练前,
- 训练GMM-HMM模型,聚类,并得到音素(或状态)的后验。
- 对语音数据进行对齐,这里得到语音文件按时间顺序transition-id到帧特征向量的对应。
- 生成< pdf-id, posterior > 对作为训练目标target
- 语音文件特征向量进行变换,这里取前后5帧,拼成一个11帧维度更高的特征向量,作为神经网络输入。
- 神经网络输入变换后的特征向量,通过前向传播,经Softmax层,得到该帧特征对应每个pdf的概率预测值。
- 对每个pdf根据< pdf-id, posterior >查到目标后验概率,与预测值求误差
- 反向传播更新参数。
- 不断迭代,直到达到最大训练次数,或模型经过cross validation得到较低的误差(loss)停止训练。
解码时,用训练好的DNN-HMM模型,输入帧的特征向量,得到该帧为每个状态(对应pdf)的概率。
其中 x_t 对应t时刻的观测值(输入),q_t=s_i 即表示t时刻的状态为 s_i。p(x_t) 为该观测值出现概率,对结果影响不大。p(s_i) 为 s_i 出现的先验概率,可以从语料库中统计得到。最终得到了与GMM相同的目的:HMM状态到观测帧特征向量的输出概率。就有了下面的示意图:
[转]kaldi ASR: DNN训练的更多相关文章
- [转]异常声音检测之kaldi DNN 训练
转自:http://blog.csdn.net/huchad/article/details/52092796 使用kaldi的DNN做音频分类,异常声音检测. HMM/GMM -> HMM/D ...
- [专题论文阅读]【分布式DNN训练系统】 FireCaffe
FireCaffe Forrest N. Iandola FireCaffe: near-linear acceleration of deep neural network training on ...
- 【机器学习】DNN训练中的问题与方法
感谢中国人民大学的胡鹤老师,人工智能课程讲的很有深度,与时俱进 由于深度神经网络(DNN)层数很多,每次训练都是逐层由后至前传递.传递项<1,梯度可能变得非常小趋于0,以此来训练网络几乎不会有什 ...
- DNN训练技巧(Tips for Training DNN)
本博客是针对李宏毅教授在Youtube上上传的课程视频<ML Lecture 9-1:Tips for Training DNN>的学习笔记. 课程链接 Recipe of Deep Le ...
- Kaldi的BaseLine训练过程
steps/train_mono.sh --nj "$train_nj" --cmd "$train_cmd" data/train data/lang exp ...
- kaldi HMM-GMM全部训练脚本分解
目录 train_mono.sh train_deltas.sh train_lda_mllt.sh train_sat.sh train_mono.sh 单音素训练脚本: //初始化,[topo f ...
- DNN模型训练词向量原理
转自:https://blog.csdn.net/fendouaini/article/details/79821852 1 词向量 在NLP里,最细的粒度是词语,由词语再组成句子,段落,文章.所以处 ...
- [转]kaldi上的深度神经网络
转:http://blog.csdn.net/wbgxx333/article/details/41019453 深度神经网络已经是语音识别领域最热的话题了.从2010年开始,许多关于深度神经网络的文 ...
- [转]语音识别中区分性训练(Discriminative Training)和最大似然估计(ML)的区别
转:http://blog.sina.com.cn/s/blog_66f725ba0101bw8i.html 关于语音识别的声学模型训练方法已经是比较成熟的方法,一般企业或者研究机构会采用HTK工具包 ...
随机推荐
- 监控服务器配置(二)-----Grafana安装配置
1.下载grafana安装包(linux版)到 /opt/minitor/grafana . 下载地址:https://download.csdn.net/download/a15565772151/ ...
- 2. 2A03简介
2A03简介 1.CPU 1.1 内部寄存器 1.累加寄存器A(Accumulator):8位寄存器,用于同算术逻辑单元(ALU)共同完成各种算术逻辑运算,它既为ALU提供原始操作数又担任存放ALU运 ...
- leetcode7:反转整数
给定一个 32 位有符号整数,将整数中的数字进行反转. 示例 1: 输入: 123 输出: 321 示例 2: 输入: -123 输出: -321 示例 3: 输入: 120 输出: 21 注意: 假 ...
- openstack系列文章(2)dashboard
玩转dashboard之前,考虑一些事情:(1)安全问题:网络访问策略(2)镜像的密码管理:windows或者linux,root或者administrator密码怎么管理(3)怎样创建自己的镜像:w ...
- propertychange事件导致的IE浏览器堆栈溢出
前段事件做项目,在IE下测试时,发现会报堆栈溢出的错误,其他浏览器正常,于是开始了苦逼的IE查错路程... 由于是在操作了某个输入框之后才出现的错误,所以把重点放到了input的相关事件,最终发现是这 ...
- Kudu基本操作及概念
Kudu: 针对 Apache Hadoop 平台而开发的列式存储管理器. 使用场景: 适用于那些既有随机访问,也有批量数据扫描的复合场景. 高计算量的场景. 使用了高性能的存 ...
- txt 修改
[61TECH_HEIBAILIUYI]#gaIcldGcyd7ducFc3deaRaOdAd4dPdMdaenc1chaeedeGcfcfeyd1cedhbidIcXcIdBdbdvdfcvbjdr ...
- 解决sqlserver还原数据库失败命令
1.使用Master数据库 2.选择数据库 select * from master.sys.sysprocesses where dbid=db_id('database') 3.查出进程使用kil ...
- Cordova配置与WebApp混合开发环境配置
好久都没来更新随笔了,这阵子比较忙,不过还是在不断的学习当中,今天给大家分享一下Cordova的配置与搭建WebApp混合开发环境的配置. 准备好了吗?让我们一步步来咯!!! 1.配置JDK环境 用的 ...
- C++二分图匹配基础:zoj1002 FireNet 火力网
直接给出题目吧... 问题 D(1988): [高级算法]火力网 时间限制: 1 Sec 内存限制: 128 MB 题目描述 给出一个N*N的网格,用'.'表示空地,用'X'表示墙.在网格上放碉堡,可 ...