我们基于kaldi开发的嵌入式语音识别系统升级成深度学习啦

先前的文章《三个小白是如何在三个月内搭一个基于kaldi的嵌入式在线语音识别系统的》说我们花了不到三个月的时间搭了一个基于kaldi的嵌入式语音识别系统,不过它是基于传统的GMM-HMM的,是给我们练手用的,通过搭这个系统我们累积了一定的语音识别领域的经验,接下来我们就要考虑做什么形态的产品了。语音识别可以分大词汇量连续语音识别(Large Vocabulary Continuous Speech Recognition, LVCSR)和关键词识别(Keyword Spotting, KWS)。LVCSR 要求很强的计算能力,这类方案主要在服务器上实现。KWS只要识别出关键词即可,对算力要求不是很高,可以在终端芯片上实现。由于我们公司的芯片主要用于终端产品上,算力不是很强,因此我们就准备做关键词识别。对于关键词识别又可分为几种应用场景。一是音频文献中关键词检索,用于快速找到音频文献中需要的内容。二是语音唤醒词识别,用于唤醒终端设备,让其工作(不唤醒时设备处于睡眠状态)。三是命令词识别,用于语音命令控制的场景,终端设备收到某个命令词后就执行相应的操作。比如智能家居场景中,当用户说出“打开空调”被识别到后就把空调打开了。经过讨论后我们决定做中文命令词识别,暂时把应用场景定在智能家居上,并定义了几个命令词,例如“打开空调”、“关闭空调”等。后面如果要做其他场景,只要改变命令词重新训练模型即可,代码部分是不需要改动的。

先前的系统是基于GMM-HMM的,已out,我们想用深度神经网络(Deep Neural Networks,DNN)来做。kaldi中的DNN分为nnet1、nnet2、nnet3三种。nnet1是由Karel写的,使用的是DNN-HMM架构,这里DNN说白了就是MLP(MultiLayer Perceptron,多层感知机)。nnet2和nnet3是由Daniel写的,nnet2同样使用的是DNN-HMM架构,nnet3还包含了其他网络架构,有CNN/RNN/LSTM等。nnet1没有online decoder,nnet2和nnet3则有online decoder,比较下来我们决定用nnet2。DNN-HMM是基于GMM-HMM的,是用DNN替代GMM,因而我们前面的工作还可以用得上,所以这次的工作主要分两部分,一是模型训练,二是nnet2 online decoder相关代码的移植。上次负责模型训练的同学由于忙其他工作,这次模型训练就由我来做。nnet2 online decoder代码移植由另外一个同学负责。同时我们在前处理中把VAD(Voice Activity Detection,语音活动检测)加上,只把检测到语音的部分送到后面模块处理,这样降低了功耗。

这次我来弄模型训练。由于是新手,先得学习怎么训练模型,然后根据新的需求训练出新的模型。经过半个多月的学习,大体上搞清楚了模型训练的步骤。首先是数据准备,包括准备语料、字典和语言模型等。对于语料,可以花钱买,也可以自己录,要将其分成训练集、测试集和交叉验证集。字典表示一个词是由哪些音素组成的。语言模型通过专业的工具(如srilm的ngram-count)生成。然后处理语料得到scp/spk2utt/utt2spk等文件,处理字典、语言模型等得到FST等文件。再就是做MFCC得到每一帧的特征向量,最后进行各个阶段的训练得到相应的模型文件(final.mdl)。主要的阶段有单音素训练(mono)、三音素训练(tri1)、LDA_MLLT训练(tri2b)、SAT训练(tri3b)、quick训练(tri4b),每一步训练都是基于上一步训练解码后对齐的结果。上面这几步是GMM-HMM的训练,如果要做深度神经网络(DNN)的训练,则还要把DNN训练这步加上去。我们这次做的是中文命令词的识别,先定好命令词,然后从thchs30里找到这些词的声韵母的写法,需要注意的是thchs30里声韵母的写法跟通常拼音的写法有些不一样,再根据这些命令词用工具把语言模型生成。我们的语料是自己录的,发动了周围的同学帮忙录,有男声和女声。这些都准备好后先处理语料得到scp等文件,再根据字典、语言模型等生成fst等文件,最后就开始各个阶段的训练了。先训练传统的GMM-HMM,不断的调整参数,直至WER有一个不错的值。GMM-HMM模型训练好后我把模型load进我们先前搭好的demo,实测下来效果还不错。这说明GMM-HMM的模型训练是OK的,接下来就要开始训练DNN(nnet2)的模型了。

我没有立刻去训练nnet2的模型,而是再去学习了下DNN的基础知识(以前简单学习过,一直没用到,理解的不深,有些已经忘记了),重点关注了梯度下降法和网络参数怎么更新,并写成了两篇博客:《机器学习中梯度下降法原理及用其解决线性回归问题的C语言实现 》& 《kaldi中CD-DNN-HMM网络参数更新公式手写推导》。接下来就去看怎么训练nnet2的模型了。先到kaldi的官方网站上看训练nnet2的相关内容,大致明白就开始基于我们自己录制的语料库调试了。nnet2的训练脚本较乱,一个脚本下有多个版本(nnet4a / nnet4b / nnet4c / nnet4d / nnet5c / nnet5d)。我刚开始不清楚孰优孰劣,把每个都调通。在网上搜索调查了一下,kaldi的作者Daniel Povey在一个论坛里说隐藏层用p-norm做激活函数的性能更好一些。于是决定用推荐的nnet4d(激活函数就是用的p-norm)来继续训练。经过多次参数tuning后得到了一个WER相对不错的模型。

在我训练DNN模型的同时,负责代码移植的同学也在把nnet2 online decoder的相关代码往我们平台上移植,套路跟我先前的一样。同时kaldi也提供了一个应用程序(代码见online2-wav-nnet2-latgen-faster.cc),对WAV文件做nnet2的online decoder。我们先要把模型在这个应用程序上调通(通常kaldi代码是没有问题的,我们在这个应用程序里调通就说明模型训练是没有问题的,后面在我们自己的平台上去调试就有基准可参考了)。当我们把模型放进应用程序里运行,报了“Feature dimension is 113 but network expects 40”的错。调查下来发现kaldi应用程序要求MFCC是13维的,且有i-vector的功能(100维的),这样加起来就是113维的。而我训练的nnet2模型是基于tri3b的(DNN-HMM要利用GMM-HMM的训练解码对齐结果,对齐的越好DNN模型的识别率就越高),13维MFCC+26维delta+1维pitch,共40维,所以模型输入是40维的。讨论后为了降低复杂度,我们决定先把应用程序中的i-vector功能给去掉,同时我基于单音素的模型(13维MFCC)重新训练nnet2模型。基于新的模型运行应用程序不报错了,但是识别率很低。我们一时没有了方向,做了几次尝试还是识别率很低。后来我们开始比较我的训练处理流程和应用程序里的处理流程,发现我训练时用了CMVN(以前做GMM-HMM训练时就有),而应用程序代码处理流程里没有。于是在代码里把CMVN的处理加上,再去运行应用程序,识别率显著提升了。我们长舒了一口气,因为我们知道这个问题被解决了,从而心里有底了。再把应用程序的机制移植到我们平台上,同时另外一个同学也帮忙把webRTC的VAD也移植进来,有语音才会把那段语音往后面模块送,这跟应用程序中读WAV文件很类似,所以处理起来机制就很类似。用了两三天就把包含VAD、前处理(ANS、AGC)和nnet2 online decoder的系统联调好了。测试了一下,被训练过的人说命令词识别率大于90%,而未被训练过的识别率大于80%。但是有个严重的问题,就是集外词(out-of-vocabulary,OOV,就是命令词以外的词)都会被识别成一个集内词(命令词),即集外词没有被拒识。

针对这个问题,我查了些资料并静下心来想了想,在当前架构下说出一个词,只会以WFST中路径最短的一个作为识别结果输出,所以才会有集外词被识别成了集内词。我们的系统目前只能识别那些指定的关键词,但是还不具备关键词识别系统的任何特点。我在前面的文章《语音识别中唤醒技术调研》中曾总结过实现关键词识别的三种方法,一是基于LVCSR来做,在终端芯片上不太可行。二是keyword/filler方法,说白了就是把一些垃圾词也放进模型里去训练(大意如下图),识别时说集外词很大可能是垃圾词作为识别结果而不输出从而实现集外词拒识,在终端芯片上可行。三是纯深度学习方法(相对于3,1和2是传统方法)。我们的架构是DNN-HMM,虽然也用了深度神经网络,但DNN是用来替代GMM的,本质上还是一种传统方法,所以我决定把keyword/filler方法用到我们的系统上。先从thchs30里找到几百个集外词(垃圾词),然后根据这些词录制语料并放进模型里训练。用新生成的模型去测试,集外词拒识率大幅提高,但是一些情况下集外词还是被识别为集内词。例如关键词是“深度科技”,如说成“深度科学”就有可能被识别成“深度科技”。针对这种情况,我把相关的词(常用的)都放进垃圾词里,如“深度科学”、“深度科普”、“深度科研”等,再去测试这些词就不会被识别成集内词了。再例如一些词发音跟集内词发音很相似,比如说“深度科器”会被识别成“深度科技”,我试了试百度的小度音箱,把唤醒词“小度小度”说成“角度角度”或者“巧度巧度”,小度音箱依旧会被唤醒。市面上已大规模商用的产品都有这个现象,我也就没管它。与此同时,我还在看一些集外词拒识的相关论文,发现好多都结合用置信度(conference measure)来解决这个问题,其中中科院自动化所的一篇博士论文《语音识别中的置信度研究与应用》讲的比较好。看后我明白了要想在传统架构下把集外词拒识问题解决好,一是要用上keyword/filler方法,二是要用上置信度。目前我是没有能力根据论文去实现置信度的,也没有找到开源的关于置信度的实现,于是在kaldi WFST lattice代码里想办法。通过大量的集内词和集外词的测试我发现可以用一些变量去做判断,但是有可能集外词拒识率提高了,集内词识别率也下降了(用置信度也会有同样的问题,这个度很难掌控。这块内容也是挺难的,尤其对我一个做工程的且做语音识别没多久的来说) 。经过一段时间的努力后集内词的识别率和集外词的拒识率都有了一个相当的水准,但离商用还有一段距离,后面还有很多事情要做,比如加大语料(我们目前只有一个几十人的语料库,没有好几百人并且覆盖男女以及不同年龄段的语料库是不能商用的),后面会越来越难!

马上就2019年过去了。回首这一年,三分之二的时间都用来做语音识别了,全是摸索着向前走,有痛苦,也有欢乐,从最初的什么都不懂到现在的懂一点。希望2020年自己在这个领域进步再大一点。

原文地址:https://www.cnblogs.com/talkaudiodev/p/12085248.html

时间: 2024-08-01 00:56:48

我们基于kaldi开发的嵌入式语音识别系统升级成深度学习啦的相关文章

基于软件开发对嵌入式开发的思考

由于本人专业方向是计算机体系结构方向的,平时做嵌入式方面的实验以及项目较多,这个学期又学习了软件工程的课程,因此想借此机会,总结下在软件工程上面学习到的知识,并看看是否有什么能够借鉴到嵌入式方向的开发上面去. 首先我想总结下,软件开发与嵌入式开发的不同之处.作为软件开发,首先应当从用户或者用户的需求入手,明白用户想让你去实现什么功能,而到了具体的实现,有时却限制的不是那么的死.而至于嵌入式的开发,从需求入手是相同的,但是对于实现的方式,却明显不同于传统的软件开发.对于编程语言,不同的嵌入式开发平

对比学习资料《深度学习入门:基于Python的理论与实现》+《深度学习原理与实践》+《深度学习理论与实战基础篇》电子资料

我认为<深度学习入门:基于Python的理论与实现>电子书代码基本原理,简洁清楚,所用数学基本就是大一水平,适合做第一本入门书. <深度学习入门:基于Python的理论与实现>书代码实现的神经网络全是用numpy,超级适合刚刚入门想了解神经网络,数学不好看公式看不懂的人,看完这本基本就懂深度学习是弄啥子了. 如果连这本都看不懂的话,可以去看<Python神经网络编程>.我个人认为这两本书是最简单直白的方式让人理解神经网络的了. <深度学习原理与实践>电子书代

《解析深度学习 语音识别实践》高清中文版PDF下载

<解析深度学习 语音识别实践>高清中文版PDF下载高清中文版PDF,全书321页带目录 下载链接:https://pan.baidu.com/s/1Ly4sdpNpcU_AwnwEVdBKLA备用链接:https://u1593575.ctfile.com/fs/1593575-330744495 本书首次专门讲述了如何将深度学习方法,特别是深度神经网络(DNN)技术应用于语音识别(ASR)领域.在过去的几年中,深度神经网络技术在语音识别领域的应用取得了前所未有的成功.这使得本书成为在深度神经

学习参考+《深度学习基于Keras的Python实践》PDF+ 源代码+魏贞原

深度学习学习框架有tensorflow.pytorch.keras.学习keras时,推荐<深度学习:基于Keras的Python实践>,适合深度学习入门和实践. 尤其是第三部分,利用卷积神经网络解决情感分析问题比较好. <深度学习:基于Keras的Python实践>系统讲解了深度学习的基本知识,以及使用深度学习解决实际问题,详细介绍了如何构建及优化模型,并针对不同的问题给出不同的解决方案,通过不同的例子展示了在具体项目中的应用和实践经验. 推荐参考:<深度学习:基于Kera

基于80251的嵌入式语音识别

一.文档介绍 嵌入式语音识别技术在251内核的实现. 缩写.术语 解 释 Specific Person Isolated Word Speech Recognition 特定人孤立词语音识别 Endpoint detection 端点检测 Feature parameter extraction 特征参数提取 DTW (Dynamic Time Warping) 动态时间规整 LPCC 线性预测倒谱参数 二.语音识别技术介绍 1.应用分类 (1)特定人与非特定人识别,特定人识别相对简单,训练者

基于 Quartz 开发企业级任务调度应用--转

Quartz 基本概念及原理 Quartz Scheduler 开源框架 Quartz 是 OpenSymphony 开源组织在任务调度领域的一个开源项目,完全基于 Java 实现.该项目于 2009 年被 Terracotta 收购,目前是 Terracotta 旗下的一个项目.读者可以到 http://www.quartz-scheduler.org/站点下载 Quartz 的发布版本及其源代码.笔者在产品开发中使用的是版本 1.8.4,因此本文内容基于该版本.本文不仅介绍如何应用 Quar

基于SpringBoot开发一个Restful服务,实现增删改查功能

在去年的时候,在各种渠道中略微的了解了SpringBoot,在开发web项目的时候是如何的方便.快捷.但是当时并没有认真的去学习下,毕竟感觉自己在Struts和SpringMVC都用得不太熟练.不过在看了很多关于SpringBoot的介绍之后,并没有想象中的那么难,于是开始准备学习SpringBoot. 在闲暇之余的时候,看了下SpringBoot实战以及一些大神关于SpringBoot的博客之后,开始写起了我的第一个SpringBoot的项目.在能够对SpringBoot进行一些简单的开发Re

用Kotlin开发android平台语音识别,语义理解应用(olamisdk)

用Kotlin开发android平台语音识别,语义理解应用(olamisdk) http://blog.csdn.net/ls0609/article/details/75084994

基于DevExpress开发的GridView如何实现某一列的一行让用户可以从下列列表选择选项

在很多DevExpress的使用例子里面,我们可以看到,基于GridView实现的不同控件展示的时候,每一列的控件类型都是一样的,如果我要某一列的一行让用户可以从下列列表选择选项,而其他行不可选择,那我们可以实现这种效果吗,应该如何实现? 1.GridView实现的显示效果 例如下面的效果就是我希望达到的,在第一行的流程处理人列允许用户选择,其他行禁止用户选择. 单用户单击第一行的“流程处理人”列的时候,弹出一个列表供用户选择,选择后显示具体的人员的姓名即可. 2.功能实现具体步骤 实现上面所说