语音识别之DTW算法的应用(Python)
简介本文目的是探究语音识别中DTW算法的应用,本人在相关算法中加入了自己的理解并对实时识别进行了扩展。
代码链接:基于Python的智能家居语言识别模拟系统
注意:在运行之前,需要修改代码os.chdir()中的绝对路径。
1 设计任务及要求设计高准确率的连续语音识别系统。本实验模拟智能家居声控系统,对使用者的语音进行录制与识别,得到对应的命令词。10个命令词为:开灯、关灯、播放音乐、关闭音乐、打开空调、关闭空调、打开电视、关闭电视、开始扫地、停止扫地。
2 引言语音识别以语音为研究对象,是语音信号处理的一个重要研究方向,也是语音信号处理的一个基本问题。是模式识别的一个分支,涉及到生理学,心理学,语言学,计算机科学,以及信号处理等诸多领域,其最终实现的是人机交互,同时也在其他领域发挥着重要的作用,如导航,辅助机器人技术,移动设备服务等多个领域。
语音识别系统的分类方式及依据是根据说话人说话方式的要求,可以分为孤立字(词)语音识别系统,连接字语音识别系统以及连续语音识别系统,本文建立的系统为连续语音识别系统。不同的语音识别系统,尽管设计和实现的细节不同,但所采用的技术都是相似的,一个典型的语音识别系统如图1所示,主要包括预处理,特征提取和训练识别网络。
图1语音识别系统组成部分本系统用于模拟智能家居的声控部分,对10个命令词进行识别。系统首先对原始语音进行预加重、分帧、加窗等预处理操作,提取语音对应的特征参数。在得到了特征参数的基础上,采用不同的方法进行识别:(1).采用模板匹配技术DTW算法进行进行时间的对准和相似度度量.(2).利用GMM-HMM建立声学模型,最终完成识别。
3 实验原理及应用3.1预处理在信号处理系统中,对原始信号进行预处理是必要的这样可以获得一个比较理想的处理对象。在语音识别系统中,语音信号的预处理主要包括分帧,预加重及端点检测。
3.1.1 分帧语音信号是一种典型的非平稳信号,它的均值函数u(x)和自相关函数R(xl,x2)都随时间而发生较大的变化。但研究发现,语音信号在短时间内频谱特性保持平稳,即具有短时平稳特性。因此,在实际处理时可以将语音信号分成很小的时间段(约10~30ms),称之为“帧”,作为语音信号处理的最小单位,帧与帧的非重叠部分称为帧移,而将语音信号分成若干帧的过程称为分帧。分帧小能清楚地描绘语音信号的时变特征但计算量大;分帧大能减少计算量但相邻帧间变化不大,容易丢失信号特征。一般取帧长20ms,帧移为帧长的1/3~1/2。
在本系统中帧长25ms,帧移10ms。
3.1.2 预加重对于语音信号的频谱,通常是频率越高幅值越小,在语音信号的频率增加两倍时,其功率谱的幅度下降6dB。因此必须对高频进行加重处理,一般是将语音信号通过一个一阶高通滤波器1-0.9375z-1,即为预加重滤波器。其目的是滤除低频干扰,特别是50Hz到60Hz的工频干扰,将对语音识别更为有用的高频部分进行频谱提升。在计算短时能量之前将语音信号通过预加重滤波器还可起到消除直流漂移、抑制随机噪声和提升清音部分能量的效果。
3.1.3 端点检测语音信号起止点的判别是任何一个语音识别系统中必不可少的组成部分。因为只有准确的找出语音段的起始点和终止点,才能得到真正要分析的语言信号。为达到提取人声部分的语音段,本实验采用双门限法进行端点检测。该算法使用了三个阈值(包括两个短时能量阈值和一个过零率阈值)。
短时能量计算方法:
这里使用矩形窗w(m),对于每一个要计算的帧而言,它是1,因此这里可以忽略掉,因此,每一帧采样点平方和对应的就是该帧的短时能量。
短时过零率计算方法:
其中,sgn函数:
w(m)函数为:
本实验采用的端点检测算法具体分为三步:
图2双门限法示意图第一步:取一个较高的短时能量作为阈值MH,首先分出语音中的浊音部分(如图2,A1-A2区间)。本实验的MH,取值为所有帧的短时能量的平均数的一半(平均数偏大)。
第二步:取一个较低的能量阈值ML,从A1-A2区间,向两端进行搜索,将较低能量段的语音部分也加入到语音段,进一步扩大语音段范围(如图所示,B1-B2区间)。本实验中,首先计算语音前一段的静音部分的能量均值(前5帧),本文将静音部分的能量均值和MH相加的一半作为ML。
第三步:使用短时过零率阈值为Zs。由于语音的两端部分是辅音(也就是清音部分),也是语音中的一部分,但是辅音的能量与静音部分的能量一样低,但是过零率比静音部分高出很多。为了区分开二者,将利用短时能量区分完的语音段继续向两端进行搜索,短时过零率大于3倍Zs的部分,则认为是语音的清音部分。将该部分加入语言段,就是求得的语音段(如图2,C1-C2区间)。
3.2特征提取人的听觉系统是一个特殊的非线性系统,它响应不同频率信号的灵敏度是不同的,基本上是一个对数的关系。近年来,一种能够比较充分利用人耳的这种特殊感知特性的系数得到了广泛应用,这就是Mel尺度倒谱系数(Mel-scaledCepstrumCoefficients,简称MFCC)。
MFCC系数的计算流程(图3)如下:
先对语音进行预加重、分帧和加窗;对每一个短时分析窗,通过FFT得到对应的频谱;将上面的频谱通过Mel滤波器组得到Mel频谱;在Mel频谱上面进行倒谱分析(取对数,做逆变换,实际逆变换一般是通过DCT离散余弦变换来实现),获得Mel频率倒谱系数MFCC,这个MFCC就是这帧语音的特征;图3MFCC系数计算流程图本实验采用HTK工具包中的Hcopy命令进行FFCC特征提取,存储在.mfc文件中。提取的特征39维MFCC特征。包括12维dct系数,能量特征,一阶差分和二阶差分等。提取算法如下:
首先进行预加重,然后进行分帧,加窗,然后进行快速傅里叶变换,将它转换为频域上的能量分布;将能量谱通过一组Mel尺度的三角形滤波器组,对频谱进行平滑化,并消除谐波的作用,突显原先语音的共振峰;计算每个滤波器输出的对数能量,经离散余弦变换(DCT)得到MFCC系数;然后计算对数能量;最后提取动态差分参数(包括一阶差分和二阶差分等)
图4为Hcopy命令参数设置,图5为一段语音信号的第一帧MFCC特征:
图4Hcopy命令参数设置图5语音信号单帧MFCC特征值3.3DTW语音匹配由于即使同一个人不同时间发出同一个声音,也不可能具有相同的长度,因此就需要用到动态时间归正(DTW)算法。把时间归正和距离测度计算结合起来的一种非线性归正技术。DTW本质上是一个简单的动态规划算法,是用来计算两个维数不同的向量之间的相似度的问题,即计算向量M1和M2的最短距离。是一种非常常用的语音匹配算法。
3.3.1 算法基本思想对两个不同维数的语音向量m1和m2进行匹配(m1和m2的每一维也是一个向量,是语音每一帧的特征值,这里利用的是MFCC特征)。设两个向量的长度为M1和M2,则距离可以表示为:
那么,就可以这样进行匹配:
每一条从(1,1)到(M1,M2)路径都有一个累计距离称为路径的代价;
每一条路径都代表一种对齐情况;
代价最小的路径就是所求的对准路径。
这样就可以将对准问题,或者说将求两个语音段的相似度问题,转化成了搜索代价最小的最优路径问题,图6为其中一条路径。
图6(1,1)到(M1,M2)路径其中一条路径在搜索过程中,往往要进行路径的限制,如图7,(i,j)点只能由相邻的三点到达:
图7路径限制在此限制条件下,可以将全局最优化问题转化为许多局部最优化问题一步一步地来求解,这就动态规划(DynamicProgramming,简称DP)的思想。
3.3.2 算法基本步骤1.定义一个代价函数,表示从起始点(1,1)出发,到达(i,j)点最小代价路径的累计距离。有:
2.要计算两个向量之间的最短距离,可以表示为:
依次类推,可由更低一层的代价函数计算得到:
3.因此,可以从 开始计算,递推出(1,1)到(i,j)的路径。递推过程中定义加权系数wn,加权系数的取值与局部路径有关:
4.为了减少计算量,可以定义平行四边形区域约束,如图8:
图8平行四边形路径限制3 实验结果(DTW)3.1设计实验任务及过程本实验旨在模拟智能家居声控系统,一共录制了6组语音,每组10个命令词,一共60段语音。通过实验,取出2组作为模板。剩余的40个语音命令作为测试样例,测试语音识别系统的精确度。
实验过程如下:
首先录制60个语音,采样频率为16kHz,存储在RecordedVoice文件夹下;利用实验一的代码对录制的语音进行端点检测,存储在RecordedVoice-EndPointed文件夹下;对端点检测后的语音命令进行操作,利用HTK中的HCopy工具提取出MFCC特征,存放在xxx.mfc文件中,放在MFCC-EndPointedVoice文件夹下;在代码中取出所有语音命令的MFCC特征,区分开模板和测试样例。根据DTW算法,对40个测试样例进行静态语音匹配,检测正确率;在代码中录音,然后保存录音的文件,存放在RecordedVoice-RealTime中。利用端点检测的代码对其进行端点检测,保存在RecordedVoice-RealTime中;在代码中动态调用HCopy程序,对实时录的语音进行MFCC特征提取,然后进行语音匹配,检测是否与对应的命令匹配上;3.2实验结果本次测试的4组数据,40段语音,识别率达到95%,结果如图9。该结果表明,DTW算法的语音识别率十分理想,最后两端语音识别失败的主要原因是,录音者的声调、响度和速度与模板差别巨大所造成的。因此,DTW的识别率受到模板数量的限制,因此在实际的智能家居声控系统应用中,需要采集每个家庭成员尽量多的语音作为模板,以提高系统识别率。
图9 前两组段语音识别结果另外,本实验还设计了简洁的GUI界面,用于实时对使用者的语音信号进行采集和识别,更好的模拟智能家居声控系统。GUI界面如图10:
图10语音识别GUI界面4 总结在使用DWT匹配算法过程中,模板的好坏直接影响了整个识别系统的准确率,因此我们通过多组数据对比,选取了最优的模板,便能够达到高识别率。
参考:https://blog.csdn.net/rocketeerLi/article/details/84638701
语音识别之发音字典
1.概念发音字典(lexicon)包含了从单词(words)到音素(phones)之间的映射,作用是用来连接声学模型和语言模型的。
发音字典在语音识别过程中的位置如图所示:
发音字典包含系统所能处理的单词的集合,并标明了其发音。通过发音字典得到声学模型的建模单元和语言模型建模单元间的映射关系,从而把声学模型和语言模型连接起来,组成一个搜索的状态空间用于解码器进行解码工作。
2.字典生成以下过程仅适用于中文语音识别的发音字典。
2.1拼音-音素的映射首先需要确定的是拼音到音素的转换规则/映射关系。可以有不同的映射关系,如汉字一的拼音“yi1”可以对应“iii1”,也可以对应“yi1”(前者是清华语音识别使用的规则)。不同的映射关系会产生不同的识别效果。
2.2中文词-拼音的映射需要列出尽可能多的中文词及其对应的拼音,有多音字的可列出其不同组合。
2.3g2p工具实现通过以上两个步骤即可实现中文词-音素的转换,也就是G2P(Grapheme-to-PhonemeConversion)。通过脚本可实现输入中文词,输出对应音素的功能。
2.4收集中文词发音字典需要覆盖尽可能多的词-音素。可以通过之前的文章构建语言模型(二):模型训练与评估中的方式,将分词后的文本语料统计各单词及其词频,去掉低频词与过长的词,得到中文词表。
2.5生成词典将中文词表作为输入,通过g2p工具即可得到词-音素的映射,也就是发音词典。
3.参考文章WhatisG2P?http://www.voxforge.org/home/docs/faq/faq/what-is-g2p语音识别技术简介http://blog.csdn.net/rfc2008/article/details/9151755?utm_source=tuicool&utm_medium=referral语音识别技术的应用及发展
语音识别是以语音为研究对象,通过语音信号处理和模式识别让机器自动识别和理解人类口述的语言。语音识别技术就是让机器通过识别和理解过程把语音信号转变为相应的文本或命令的高技术。语音识别是一门涉及面很广的交叉学科,它与声学、语音学、语言学、信息理论、模式识别理论以及神经生物学等学科都有非常密切的关系。语音识别技术正逐步成为计算机信息处理技术中的关键技术,语音技术的应用已经成为一个具有竞争性的新兴高技术产业。本文引用地址:http://www.eepw.com.cn/article/201710/368484.htm
1、语音识别的基本原理
语音识别系统本质上是一种模式识别系统,包括特征提取、模式匹配、参考模式库等三个基本单元,它的基本结构如下图所示:
未知语音经过话筒变换成电信号后加在识别系统的输入端,首先经过预处理,再根据人的语音特点建立语音模型,对输入的语音信号进行分析,并抽取所需的特征,在此基础上建立语音识别所需的模板。而计算机在识别过程中要根据语音识别的模型,将计算机中存放的语音模板与输入的语音信号的特征进行比较,根据一定的搜索和匹配策略,找出一系列最优的与输入语音匹配的模板。然后根据此模板的定义,通过查表就可以给出计算机的识别结果。显然,这种最优的结果与特征的选择、语音模型的好坏、模板是否准确都有直接的关系。
2、语音识别技术的发展历史及现状
1952年,ATTBell实验室的Davis等人研制了第一个可十个英文数字的特定人语音增强系统一Audry系统1956年,美国普林斯顿大学RCA实验室的Olson和Belar等人研制出能10个单音节词的系统,该系统采用带通滤波器组获得的频谱参数作为语音增强特征。195
LD3320语音识别模块:LDV7模块使用详解
喜欢请关注微信公众号:嵌入式从0到1
公众号内容面向在校大学生、电子爱好者、嵌入式工程师;
涉及电子制作、模块使用、单片机技术、物联网相关知识分享;
软硬件全栈工程师,玩模块,学硬件,带你从0走到1
LD3320是非特定人(不用针对指定人)语音识别芯片,即语音声控芯片。最多可以识别50条预先内置的指令。 工作模式:LD3320(LDV7)语音模块可以工作在以下三种模式:普通模式:直接说话,模块直接识别;按键模式:按键触发开始ASR进程;口令模式:需要一级唤醒词(口令);推荐使用口令模式,这样避免嘈杂环境下误动作。 应用场景:1、家居智能控制2、智能小车DIY设计3、毕业设计创新功能4、个人DIY设计5、语音控制嵌入式产品设计 我购买的LDV7语音识别模块,内置单片机,LD3320语音识别芯片的驱动是靠模块上自带的STC11L08单片机驱动的,我们可以修改模块自带工程的Demo程序,修改里面的关键词,然后将识别的结果通过串口打印出来,我们使用另外一个MCU与该模块进行串口通信,解析收到的串口指令,进而做响应的指令动作。 例如我们说口令:当前时间(dangqianshijian)》》模块识别之后,串口输出:PrintCom("{"VoiceCommandCode":2}");》》MCU接收串口信息之后,对该串口指令进行解析(可以使用cJSON解析此字符串);》》然后通过TTS文字转语音模块播放当前时间,即完成本次语音识别控制过程。 识别原理:就是你说一句话,然后模块用拼音去和你的发音做比较,在关键词中找出哪些预设值的词语与发音的接近程度大于多少的(假定50%),然后再从一堆大于(50%)里面输出一个最接近的。 这里就会造成一个问题,容易造成误识别太多。如果你只有一个关键词:时间然后你说了一句:‘时刻’,他识别到‘时’这个发音,所以有50%以上的相似,但是候选词中没有‘时刻’这个比他更接近的关键词,所以他就会输出‘时间’的结果。这样子他就是错误了。如果你设置一些拟声词,有可能随随便便就触发了。 解决方案:在设定好要识别的关键词语后,为了进一步降低误识别率,可以再添加一些其他的任意词汇进识别列表,用来吸收错误识别,从而达到降低误识别率的目的。我们把这样一些关键词语称之为“垃圾关键词语”。 比如,某个应用场景中,需要识别的关键词语是4条,“前进”,“后退”,“开门”,“关门”。在把这4个关键词语设置进LD3320后,可以再另外设置10~30个词语进LD3320,比如“前门”,“后门”,“阿阿阿”,“呜呜”等等。所以最好把一些拟声词或者容易混淆的词语设置为垃圾关键词,即,识别后不进行输出。 只有识别结果是4个关键词语之内的,才认为识别有效。如果识别结果是“垃圾关键词语”,则说明是其他的声音导致的误识别,产品应该重新开始一次识别过程。这样,可以非常有效地降低误识别率。极大地提高终端用户的主观使用体验。 移植过程: 1、向LD模块中添加关键词 /************************************************************************功能描述:向LD模块添加关键词入口参数:none返回值:flag:1->添加成功其他说明:用户修改. 1、根据如下格式添加拼音关键词,同时注意修改sRecog和pCode数组的长度 和对应变了k的循环次数设置。拼音串和识别码是一一对应的。 2、开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中 关于垃圾词语吸收错误的用法,来提供识别效果。 3、”duogongnengbaoshi“为口令,故在每次识别时,必须先发一级口令“多功能报时”**************************************************************************/uint8LD_AsrAddFixed(){ uint8k,flag; uint8nAsrAddLength; #defineDATE_A8 /*数组二维数值*/ #defineDATE_B21 /*数组一维数值*/ uint8codesRecog[DATE_A][DATE_B]={ "duogongnengbaoshi", "xianzaijidianle", "dangqianshijian", "xianzaishike", "baoshi", "dangqianwendu", "dangqianshidu", "wenshiduduoshao" }; /*添加关键词,用户修改*/ uint8codepCode[DATE_A]={ CODE_CMD, CODE_XZJDL, CODE_DQSJ, CODE_XZSK, CODE_BS, CODE_DQWD, CODE_DQSD, CODE_WSDDS }; /*添加识别码,用户修改*/ flag=1; for(k=0;kvalueint==0) { printf("收到一级口令智能报时... "); } elseif(item_obj->valueint==1) { printf("“现在几点了”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } else { sprintf((char*)dtbuf,"[v5][s5][m51]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } elseif(item_obj->valueint==2) { printf("“当前时间”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } else { sprintf((char*)dtbuf,"[v5][s5][m51]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } elseif(item_obj->valueint==3) { printf("“现在时刻”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } else { sprintf((char*)dtbuf,"[v5][s5][m51]当前时间为%02d时%02d分%02d秒 ",NowHour,NowMinute,NowSecond); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } elseif(item_obj->valueint==4) { printf("“关灯”命令识别成功 "); } elseif(item_obj->valueint==5) { printf("“当前温度”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]当前温度为%.1f摄氏度 ",DHT11Temp); } else { sprintf((char*)dtbuf,"[v5][s5][m51]当前温度为%.1f摄氏度 ",DHT11Temp); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } elseif(item_obj->valueint==6) { printf("“当前湿度”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]当前湿度为百分之%.1f ",DHT11Hum); } else { sprintf((char*)dtbuf,"[v5][s5][m51]当前湿度为百分之%.1f ",DHT11Hum); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } elseif(item_obj->valueint==7) { printf("“温湿度多少”命令识别成功 "); m_nCurrentVoicePlayTimes++; while(U5152_BSY){} if(m_nCurrentVoicePlayTimes%2==0) { sprintf((char*)dtbuf,"[v5][s5][m3]温度为%.1f度,湿度为%.1f ",DHT11Temp,DHT11Hum); } else { sprintf((char*)dtbuf,"[v5][s5][m51]温度为%.1f度,湿度为%.1f ",DHT11Temp,DHT11Hum); } printf((char*)dtbuf); XFS_FrameInfo((char*)dtbuf); delay_ms(10);while(U5152_BSY){delay_ms(500);printf("while(U5152_BSY) ");} } } item_obj=item_obj->next; printf("while(item_obj) "); } } cJSON_Delete(receive_json); USART5_RX_STA=0; memset(USART5_RX_BUF,0,sizeof(USART5_RX_BUF)); //清空数组 }LDV7模块固件下载: 1、打开编译后的.hex文件 2、选对串口号,芯片型号后,选择“下载/编程”按钮,然后给STC单片机上电或者复位。 3、发出语音,测试语音识别结果 {"VoiceCommandCode":0}收到{"VoiceCommandCode":1}“现在几点了”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":2}“当前时间”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":3}“现在时刻”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":4}“报时”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":5}“当前温度”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":6}“当前湿度”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":7}“温湿度多少”命令识别成功{"VoiceCommandCode":0}收到{"VoiceCommandCode":7}“温湿度多少”命令识别成功
然后其他与LDV7模块相连的MCU只要解析接收到的串口数据即可。
从六月份开始,每个月会制作一个毕业设计难度的DIY作品,
前期作品以模块组合的形式搭建,降低门槛,方便大家一起跟着做;
DIY过程只在微信公众号中分享,大家没关注的,赶紧关注哈。
每个月时间大致安排:
上个月25号,公布DIY作品名称;
每月1日公布作品功能点及所需要的功能模块链接;
每月10日前绘制完模块配合的线路板;
每月15日之前硬件搭建完毕,之后按模块撰写代码,调试,随时公众号更新进展;
每月月底开源整个作品的源码和PCB工程文件。
题目选取原则:
公众号每个月20日发起投票,25号截止,票数最多的作为下个月的DIY内容;
投票的备选项大家可以后台留言给我,我会选出五种留言最多的作为选项;
每个月的DIY内容尽量与上个月分享的文章有一定的相关度,起到温故知新的作用。
有什么想法或者建议,留言给我哈。