《简单音频识别系统设计和测试实现》14000字(论文)_第1页
《简单音频识别系统设计和测试实现》14000字(论文)_第2页
《简单音频识别系统设计和测试实现》14000字(论文)_第3页
《简单音频识别系统设计和测试实现》14000字(论文)_第4页
《简单音频识别系统设计和测试实现》14000字(论文)_第5页
已阅读5页,还剩37页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

目录简单音频识别系统设计和测试实现TOC\o"1-2"\h\z\t"英文摘要,1,中文摘要,1"摘要 摘要摘要自动语音识别技术(AutomaticSpeechRecognition)是一种将人的语音转换为文本的技术,是实现人机交互的关键技术,也是长期以来的研究热点。最近几年在AI潮流席卷全球时,围绕语音交互的产品之争正愈演愈烈,苹果siri、亚马逊echo、微软小冰这些产品风靡全球的同时,国内外科技巨头、创业团队也在暗流涌动,各种智能音箱以及语音解决方案层出不穷。这种发展方式,造就了语音交互已经成为人工智能领域最成熟也是落地最快的技术。尤其是深度学习的起势,各种硬件算力的大幅度提升,让语音识别、语音合成以及自然语言处理的发展速度提升到了一个新的高度。本毕设的目的就是为了模仿并实现其中的核心部分,也就是其中最核心的语音识别模型,语音识别模型基于thchs30中文数据集训练。由声学模型和语音模型构成,声学模型用于对普通话发声进行建模,然后输出预测的音素串,也就是拼音串,然后将拼音串交给语言模型,语言模型预测输出中文句子。虽然受限于算力,但此毕设完成可以得到一个相对准确的小型语音模型,对中文语音进行识别。关键词:语音识别;keras;python;thchs30中文数据集;声学模型;语言模型1绪论1.1研究背景语音是人类最自然的交互方式。计算机和发明之后,让计算机能够听懂人类的语言,理解语言中的内在含义,一直是人们研究的目标。让计算机能够听懂人类的语言主要涉及这样三种技术:自动语音识别(automaticspeechrecognition,ASR);自然语言处理(naturallanguageprocessing,NLP);语音合成(speechsynthesis,SS)。语音识别很明显不是只通过计算机就能实现的,他涉及到声学,心理学,生理学,计算机,语言学,文学等多个学科,是一个典型的交叉学科任务,自动语音识别技术(asr)一直是语音领域的关键一步,这个技术用来将人类的语音转换为文字,是实现人机交互的关键技术,也是长期以来的研究热点。最近几年在AI潮流席卷全球,硬件算力的大幅度提升,深度学习起势。让语音识别、语音合成以及自然语言处理的发展速度提升到了一个新的高度。本毕设也主要关注于语音识别的研究。1.2国外语音识别的发展现状分析语音识别领域的相关技术研究始于20世纪50年代初。当时,研究人员对语音的音素特征做了一些研究。1952年,贝尔实验室的研究人员利用模拟电子设备实现了针对特定说话人说英文数字的孤立词进行语音识别的功能。到了19世纪60年代,语音识别领域引入了人工神经网络。苏联的研究者Vintsyuk提出了用动态规划算法实现动态时间规整,从而实现不等长语音在时间上对齐。该技术在小规模词语环境下取得了很大的成功[1]。20世纪80年代开始,以隐马尔可夫模型方法为代表的基于统计模型方法逐渐在语音识别研究中占据了主导地位。HMM模型能够很好地描述语音信号的短时平稳特性,并且将声学、语言学、句法等知识集成到统一框架中。此后,HMM的研究和应用逐渐成为了主流。例如,第一个“非特定人连续语音识别系统”是当时还在卡耐基梅隆大学读书的李开复研发的SPHINX系统[1]。在2010年之前,基于隐含马尔可夫模型的高斯混合模型代表着当时最先进的语音识别技术,这类的模型通常采用的特征提取算法是梅尔频率倒谱系数(即MFCC)算法,常用的还有fBank等特征提取算法。到了2011年DNN在大词汇量连续语音识别上取得突破。语音识别效果得到了近十年来最大的突破。从此,基于深度神经网络的建模方式正式取代GMM-HMM,成为主流的语音识别建模方式。[1]2010年以来,随着大数据和深度学习的发展,CNN、RNN、LSTM和GRU等网络结构也应用到语音识别中,使得语音识别技术取得了又一次巨大的突破。连接时序分类(ConnectionistTemporalClassification,CTC)方法,端到端结构模型,和DFCNN、DeepSpeech、WaveNet、DFSMN等模型的出现,使得语音识别的准确率再一次得到了长足的突破。1.3国内语音识别的发展现状分析我国语音识别研究工作起步于五十年代。从1987年国家863计划执行后,国家863智能计算机专家组为语音识别技术研究专门立项。而国内如清华大学,北京大学,中科院等众多科研机构也开始了对语音识别领域的深入研究。2002年,中科院自动化所及其所属模式科技(Pattek)公司发布了“天语”中文语音系列产品——PattekASR,结束了1998年以来国外垄断中文语音识别产品的历史。而近年来,随着中国互联网的崛起,市场的需要,以及国家对人工智能的重视。语音识别研究水平也越来越高,开始逐步落地,走上实用化。从技术水平上看,我国语音识别技术的研究水平基本已经同步国外。而在汉语语音识别领域,更是世界领先水平。科大讯飞在2016年提出了一种全新的语音识别框架,称为全序列卷积神经网络(deepfullyconvolutionalneuralnetwork,DFCNN)。实验证明,DFCNN比BLSTM语音识别系统这个学术界和工业界最好的系统识别率提升了15%以上。[1]1.4工程技术1.4.1梅尔倒谱系数(Mel-scaleFrequencyCepstralCoefficients)在语音识别(SpeechRecognition)方面,最常用到的语音特征就是梅尔倒谱系数(Mel-scaleFrequencyCepstralCoefficients,简称MFCC)。它是在1980年由Davis和Mermelstein提出的。从那时起。在语音识别领域,MFCC在人工特征方面可以说是一枝独秀,直到深度学习崛起之时。而在本系统中并没有完全使用mfcc特征,或者说只是使用了mfcc的部分处理wav文件的方式,参照了它对声音建模的思路,本文使用了前期mfcc处理数据的分帧加窗,做傅里叶变换映射时间信息,得到时频图。之后本系统就如科大讯飞提出的思路一样,并没有使用人工设计的滤波器,而是使用卷积神经网路去自助训练从而提取出高级特征。[2]1.4.2深度学习深度学习作为人工智能的一个子领域,在人工智能的成长过程当中不可或缺。深度学习应用于计算机、人工网络中。原理是让计算机模仿人脑的神经元机制,模仿人脑的思考过程,来对大量的数据进行分析、统计、解释、分辨。深度学习的深度体现在,通常深度模型会使用多层,甚至几十上百层的各种各样的神经网络相组合用来学习样本数据的内在规律。因为多重神经网络的堆叠,可以从文字,图像和声音等多种数据中获得相当精准的规律和相当优秀的信息。而深度学习在语音和图像识别方面取得的效果,远远超过先前相关技术。1.4.3卷积神经网络1998年YannLecun于提出了CNN,其本质是一个多层感知机[17]。CNN的特点在于其所采用了局部连接和权值共享的方式。虽然比起DNN牺牲了部分模型精度,但是他减少了权值的数量,从而使得网络易于优化。并且他还降低了模型的复杂度,减小了过拟合的风险。[1]个人理解得的典型的CNN由3个部分构成:卷积层,池化层,全连接层。用图像处理来描述各个层的作用的话:卷积层负责提取图像中的局部特征;池化层用来降低参数量级(降维)并筛选特征;全连接层类似传统神经网络的部分,用来组合起原来分散的局部特征,输出想要的结果。[11]卷积层的运算过程如用小矩阵在大图像中取值,也就是用一个卷积核扫完整张图片。这个过程我们可以这样理解:将图像想象成沙石,我们用不同的筛网(卷积核,滤波器)筛选图像不同地方,将每个小区域中足够明显的,符合当前过滤器要求的数据过滤出来,从而得到我们要的石头(特征),也就是这些小区域的特征值。在具体应用中,往往有多个卷积核,可以认为,每个卷积核代表了一种图像特征,每个卷积核都是为了在图像中搜索过滤特定的模式,如果某个图像块与此卷积核卷积出的值大,大到足够激活激活函数,则认为此图像块十分接近于此卷积核。如果我们设计了8个卷积核,可以理解:我们认为这个图像上有8种底层纹理模式,也就是我们用8种基础模式就能描绘出一副图像。池化层(下采样)作用是数据降维,他可以大大降低数据的维度,避免过拟合。如果原始图片大小是12*12,选取采样窗口大小为3*3,对其进行下采样之后,最终将得到一个4×4大小的特征图。[21]之所以这么做的原因,是因为即使使用卷积,采用了局部连接和权值共享,减少了相当多的参数,但是图像仍然很大,参数的数量仍然很多,训练的代价和难度还是很大,所以为了平衡训练代价和模型精度,降低数据维度牺牲部分精度,下采样是必须的。全连接层作用是输出结果这个部分就是最后一步了,经过卷积层和池化层处理过的数据输入到全连接层,得到最终想要的结果。经过卷积层和池化层降维过的数据,全连接层才能”跑得动”,不然数据量太大,计算成本高,效率低下。[20]总结:卷积层的通过卷积核的过滤提取出图片中局部的特征,跟人类视觉的特征提取类似。池化层相比卷积层可以更有效的降低数据维度,这么做不但可以大大减少运算量,还可以有效的避免过拟合。最后由全连接层给出结果1.4.4Encoder-Decoder架构与注意力机制注意力(Attention)机制由Bengio团队与2014年提出并在近年广泛的应用在深度学习中的各个领域,例如在计算机视觉方向用于捕捉图像上的感受野,或者NLP中用于定位关键token或者特征。谷歌团队近期提出的用于生成词向量的BERT[3]算法在NLP的11项任务中取得了效果的大幅提升,堪称2018年深度学习领域最振奋人心的消息。而BERT算法的最重要的部分便是Transformer的概念。[3]正如论文《attentionisallyourneed》的作者所说的,Transformer中抛弃了传统的CNN和RNN,整个网络结构完全是由Attention机制组成。[3]这里我对transformer的直观理解是这样的,首先我并未使用全部的transformer的结构,那样对单独的语言模型来说结构太大,并且训练难度太大。我这里只用到了encodeer的部分,详细将会在下文解释。1.4.5ConnectionistTemporalClassification(CTC)在语音识别、图像识别等领域中,由于数据天然无法切割,且难以标注出输入和输出的序列映射关系,导致传统的RNN训练方法不能直接适用。那么,如何让RNN模型实现端到端的训练成为了关键问题。ConnectionistTemporalClassification(CTC)是AlexGraves等人在ICML2006上提出的一种端到端的RNN训练方法,它可以让RNN直接对序列数据进行学习,而无需事先标注好训练数据中输入序列和输入序列的映射关系,使得RNN模型在语音识别等序列学习任务中取得更好的效果,在语音识别和图像识别等领域CTC算法都有很比较广泛的应用。总的来说,CTC的核心思路主要分为以下几部分:它扩展了RNN的输出层,在输出序列和最终标签之间增加了多对一的空间映射,并在此基础上定义了CTCLoss函数它借鉴了HMM(HiddenMarkovModel)的Forward-Backward算法思路,利用动态规划算法有效地计算CTCLoss函数及其导数,从而解决了RNN端到端训练的问题最后,结合CTCDecoding算法RNN可以有效地对序列数据进行端到端的预测1.5课题研究内容系统主要研究的内容:次的课题研究中,主要是在通过使用tensorflow和keras用来训练thchs30数据集,训练声学模型和语言模型进行解码,并由此来预测进行音频识别。图一就是系统的总体思路以及各部件的配置方式。图1.5总体思路及结构语音识别系统的目的,是把语音转换成文字。具体来说,是输入一段语音信号,要找一个文字序列(由词或字组成),使得它与语音信号的匹配程度最高。这个匹配程度,一般是用概率表示的。用x表示语音信号,w表示文字序列,则要求解的是下面这个问题:w在经过贝叶斯变化后变成这个公式

w其中P(w)表示一个文字序列本身的概率,也就是这一串词或字本身有多“像话”。P(w|x)表示给定文字后语音信号的概率,即这句话有多大的可能发成这串音。计算这两项的值,就是语言模型和声学模型各自的任务。从最简单的角度理解声学模型,声学模型是对发声的建模,也就是输入一段音频,再输出一个拼音序列,然后就是语言模型的工作了,语言模型得到拼音序列将他转化为汉字的序列输出。本毕设主要复刻DFCNN语音识别框架。DFCNN不走mfcc那一套特征提取的方式,而是直接从语谱图中寻找规律,通过较多的卷积层和池化层的组合,提取出高级的图像特征,而这些图像特征其实就是语音的特征,从而实现对整句语音的建模。DFCNN的原理不难理解,从现实意义上说,就是医生可以看懂X光照片,那厉害的语音学家当然看得懂语谱图,并且可以从中找到规律。1.6系统可行性分析对系统的可行性进行分析主要分为三个方面。技术可行性,操作可行性,经济可行性。(1)技术可行性应用修改mfcc,cnn,注意力机制等等成熟的模型和框架。使用Python语言来进行程序的编写。开发所用的技术和框架都是极其成熟稳定的,虽然涉及很多数学知识,但是大部分框架已经实现,并且数学原理最为复杂的注意力机制是仿照开源代码进行实现,所以充分可行,在技术方面,只要认真研究模型架构,掌握学习pytorch或者tensorflow和keras架构,研究本项目虽然有很大难度,但应该能够实现。(2)操作可行性本项目开发只需要电脑,但是对计算机性能要求也非常高,并且计算机对对应机器学习框架未必支持,但是如果降低数据集训练量,这样在经济方面不会造成太多负担;如果择优选择tensorflow或者pytorch框架并调整框架版本,可以解决电脑与框架的兼容问题。(3)经济可行性经济上会受制于硬件和算力,但是可以使用少量数据训练,用一个小型的系统来验证算法和模型的正确性。1.7本章小结本章节首先介绍了简单音频识别系统,主要是在通过使用thchs30,训练声学模型和语言模型进行解码,并由此来预测进行音频识别,和这个项目的研究背景及意义,然后分析了该课题的国内外研究现状。再对本系统所使用的相关技术以及软件、硬件进行介绍。最后阐述课题的研究内容,并对系统进行可行性分析。

2“简单音频识别”系统需求分析2.1需求概述本次课题主要功能是通过使用thchs30数据集合,训练声学模型和语言模型进行解码,并由此来预测进行音频识别。图2.1系统需求分析图2.2功能需求分析1、数据处理模块:数据处理,时频图的合成,数据的padding,语音字典的合成。数据处理模块的功能需求主要如下,首先要将一般的wav音频数据仿照mfcc处理方式进行时频图的处理。其次要合成数据字典,便于将语音label转化成id,便于处理和计算。最后数据处理模块还需要修改数据规格以符合tensorflow的需求。数据处理同时需要得到数据的一些参数用于配置模型的卷积核,神经元等等参数。2、解码器模块设计:解码器由声学模型以及语言模型组成。声学模型的需求是将语音wav文件解码成唯一的拼音序列。声学模型需要利用Keras仿照DFCNN的实现思路,使用多层卷积层加池化层,来从时频图中提取高级特征,然后经过全连接输出一个后验概率矩阵后,连接上ctc模块进行预测输出。语言模型的需求是用来将拼音序列解码成汉字序列。语言模型使用transformer中的代码进行实现。3、模型调用,并组建预测模块预测模块用于组合之前各个部件,集合部件的功能,调用训练完成的声学模型和语言模型,得到汉字文本的测试输出,并可以计算词错率,查看预测输出与label的对比,对系统的功能实现有直观的认识。。图2.2系统用例图2.3非功能需求分析受制于算力和硬件,目标是使用少量的thchs30数据,生成一个小型的语音识别系统,对一些特定的句子有着较高的识别率,可以验证算法和模型的正确性就可以。2.4本章小结总结来说本系统的需求如下:建立声学模型,使用声学模型对发声建模,用来将音频输入转化为音素串的输出,这里我简单理解成拼音串的输出。然后再建立语言模型,用来将得到的拼音转化为对应的汉字输出,可以理解成拼音输入法的感觉。3系统软件设计3.1数据处理模块设计数据使用的是thchs30中文数据集,考虑神经网络训练过程中接收的输入输出。首先需要batch_size内数据需要统一数据的shape。格式为:[batch_size,weight,height,feature_dim]然而读取的每一个sample的时间轴长都不一样,所以需要对时间轴进行处理,选择batch内最长的那个时间为基准,进行padding。这样一个batch内的数据都相同,就能进行并行训练啦。padding就是需要构成一个tensorflow块,这就要求每个样本数据形式是一样的。除此之外,ctc需要获得的信息还有输入序列的长度。输入序列经过卷积网络后,长度缩短了8倍,因此我们训练实际输入的数据为wav_len//8。接下来定义函数read_label读取音频文件对应的拼音label,为label建立拼音到id的映射,即词典。有了词典就能将读取到的label映射到对应的id。接下来合成时频图。input为输入音频数据,需要转化为频谱图数据,然后通过cnn处理图片的能力进行识别。这里转化为频谱图的方法是学习的提取mfcc的前期做法,首先使用scipy.io.wavfile库来读取wav文件,其次构建汉明窗,并为数据分帧,其次加上汉明窗,做快速傅里叶变化。图3.1(1)wavfile读取的数据图3.1(2)汉明窗图3.1(3)时频图接下来搭建generator,先确定batch_size和batch_num,再创建shuffle用来打乱数据的顺序,我们通过查询乱序的索引值,来确定训练数据的顺序,最后将batch_size的信号时频图和标签数据,存放到两个list中去。总结:我们提取出了每个音频文件对应的拼音标签label_data,通过索引就可以获得该索引的标签。也生成了对应的拼音词典.由此词典,我们可以映射拼音标签为id序列,我们同时保证了数据的格式,可以构成tensorflow块并且可以将数据正常喂给ctc,最后我们创建了生成符合规格的训练数据的generator。3.2解码器模块设计解码器由声学模型以及语言模型组成。主要指模型构造设计3.2.1声学模型构造训练输入为时频图,标签为对应的拼音标签,如下所示:搭建语音识别模型,采用了CNN+CTC的结构。图3.2.1声学模型结构具体结构设计是这样的。首先是CnnBasicCell层,一层卷积加一层Bn,再加一层卷积加一层Bn,然后衔接上一层maxpool层。由此构成一个CnnBasicCell。总体系统首先由三层CnnBasicCell相连接后接上两层关闭maxpool的CnnBasicCell,然后再经过flatten层将数据拉平输入给两层全连接层,由最后一层全连接层的softmax激活函数输出预测值。由此,时频图转化为后验概率矩阵。此时即可连接ctc模块。3.2.2语言模型构造语言模型用于将上层声学模型输出得到的音素串,也即拼音串,进行对应中文语句的输出。语言模型参照论文《AttentionIsAllYouNeed》以及下面的开源代码实现,/Kyubyong/transformer/blob/master/modules.py文章的完整模型结构这里并不表示。我们都了解EncoderDecoder框架,如果我们把中间语义编码C换成根据当前输出来调整成加入注意力模型的变化的Ci,那么增加了注意力模型的Encoder-Decoder框架如下:图3.2.2(1)Encoder-Decoder框架但是语言模型我并没有使用完整的en-de框架,因为拼音到汉字更像是利用上下文确定具体映射的过程,翻译是句子对句子,两个句子不必等长。我只需要经过encoder之后的中间语义完成拼音对应的汉字映射即可,所以只需要实现文章原本模型中的左半部分即可。图3.2.2(2)transformer部分模型其中Feedforward层是两层全连接,用卷积模拟加速运算,也可以使用dense层。multihead层实现了下面功能:论文中表明,将模型分为多个头,形成多个子空间,可以让模型去关注不同方面的信息。Multi-HeadAttention就是将ScaledDot-ProductAttention过程做H次,再把输出合并起来。语言模型在系统中的作用也可以这样直观地理解,首先我们得到有上层声学模型输出得到的音素串,也即拼音串,然后输入给当前transformer中的encoder结构,首先经过Embedding结构将对应的词转换为一个高纬度的词向量,然后再经过PositionalEncoding进行位置编码,因为transformer使用mutiattention机构,所以会同时工作,导致顺序非常的重要,每个词都是不同的向量,具有位置信息。通过PositionalEncoding就可以区分顺序。然后交给具有2层fc的Feedforward层进行预测输出。并重复这样的结构,进行多层encoder结构(后面的encoder不需要前两步位置编码和位置嵌入)连接,由于层数过深,很有可能导致梯度爆炸和梯度消失,所以我们同时要引入残差的结构3.3预测模块设计模型调用,并组建预测模型使用声学模型的ctc_decode预测输出唯一的拼音序列,再将拼音序列交给语言模型进行预测,最终输出预测的汉语序列。3.5本章小结本章主要是介绍了系统分为三大部分,数据处理部分和解码器部分,预测输出部分,之间的交互也进行了介绍,数据处理模块输出时频图交由解码器解码,并由预测模块输出识别结果。

4“简单音频识别”系统的详细设计与实现4.1系统整体结构图4系统整体设计4.2数据处理模块设计数据处理模块的流程图如下图4.1数据处理流程图4.2.1读取音频文件并转化为时频图deffbankCpu(file):

x=numpy.linspace(0,400-1,400,dtype=64)#数据格式为int64

w=0.54-0.46*numpy.cos(2*numpy.pi*(x)/(400-1))#构造汉明窗,也就是正太分布曲线。

fs,wavsignal=wav.read(file)

#wav波形加时间窗以及时移10ms

twindow=25 #设置窗口为25ms

wav_arrary=numpy.array(wavsignal)

rangetheend=int(len(wavsignal)/fs*1000-twindow)//10+1#计算循环终止的位置,也就是最终生成的窗数

dinput=numpy.zeros((rangetheend,200),dtype=numpy.float)#数据格式为float#存放最终的频率数据

dl=numpy.zeros((1,400),dtype=numpy.float)#数据格式为float

foriinrange(0,rangetheend):

dl=wav_arrary[i*160:i*160+400]

dl=dl*w#相乘,也就是俗称加窗

dl=numpy.abs(fft(dl))#快速傅里叶变换

dinput[i]=dl[0:200]#取一半数据200,因为是对称的

dinput=numpy.log(dinput+1)

returndinput4.2.2数据初试化初试化重要的参数后导入不同的字典文件并以list形式存储,并非核心内容,代码不在此展示。核心代码如下:

defget_am_batch(self):#准备声学模型训练数据

sflist=[iforiinrange(len(self.wavlist))]#打乱训练数据的顺序

bSize=self.batch_sizeforiinrange(len(self.wavlist)//bSize):

wavDataList=[]

labelDataList=[]

subList=sflist[i*bSize:(i+1)*bSize]

forjinsubList:#使用fbank生成对应的时频图并建立映射

fbank=fbankCpu(self.data_path+self.wavlist[j])

paddingfbank=numpy.zeros((fbank.shape[0]//8*8+8,fbank.shape[1]))#对时频图进行padding补0

paddingfbank[:fbank.shape[0],:]=fbank

label=self.pny2id(self.pny_lst[j],self.am_vocab)#得到label的id序列

lbCtcLength=self.ctc_len(label)#得到ctc需要的label长度

ifpaddingfbank.shape[0]//8>=lbCtcLength:

wavDataList.append(paddingfbank)

labelDataList.append(label)

paddingWavData,inputLength=wav_padding(wavDataList)

paddingLabelData,labelLength=label_padding(labelDataList)

inputs={'input':paddingWavData,

'label':paddingLabelData,

'inputLength':inputLength,

'labelLength':labelLength,

}#整合总体输入数据,输入为填充后的时频图,填充后的label数据#输入长度和label长度

outputs={'ctc':numpy.zeros(paddingWavData.shape[0],)}

yieldinputs,outputs

#语言模型训练数据准备

defget_lm_batch(self): bSize=self.batch_size#首先获取batchsize和batch_num

batch_num=len(self.pny_lst)//bSize#batch数量为拼音列表长度/batchsize

forkinrange(batch_num):

begin=k*bSize#根据batchsize决定步长,每次改变起点

end=begin+bSize#batchsize为步长,每次取这么多

IBatch=self.pny_lst[begin:end]#取得拼音列表

LBatch=self.han_lst[begin:end]#取得汉字列表

maxLength=max([len(line)forlineinIBatch])

IBatch=numpy.array(

[self.pny2id(line,self.pny_vocab)+[0]*(maxLength-len(line))forlineinIBatch])#使用拼音转id模块方法,得到Input的拼音的id序列

LBatch=numpy.array(

[self.han2id(line,self.han_vocab)+[0]*(maxLength-len(line))forlineinLBatch])#使用汉字转id模块方法,得到Input的汉字的id序列

yieldIBatch,LBatch

#上方各个模型所使用的功能模块

defpny2id(self,line,vocab):#拼音转id

return[vocab.index(pny)forpnyinline]

#取出每一个拼音在line里的index

defhan2id(self,line,vocab):#汉字转id

return[vocab.index(han)forhaninline]

#取出每一个汉字在line里的index

defwav_padding(self,wDataLst):

wLenth=[len(data)fordatainwDataLst]#得到所有wav长度

maxWavLength=max(wLenth)#取出最大的wav长度

wLenth=numpy.array([len//8forleninwLenth])'''ctc模块需要获得输入序列的长度。这里输入序列经过的卷积网络中有3层maxpool,所以长度要/8倍,因此我们训练实际输入的数据为wav_len//8。'''

newWDataLst=numpy.zeros((len(wDataLst),maxWavLength,200,1))#200,1为视频图的shape要求

foriinrange(len(wDataLst)):

newWDataLst[i,:wDataLst[i].shape[0],:,0]=wDataLst[i]

returnnewWDataLst,wLenth

#对label进行padding和长度获取,不同的是数据维度不同,且label的长度就是输入给ctc的长度,不需要额外处理

deflabel_padding(self,lbDatalst):

llength=numpy.array([len(lb)forlbinlbDatalst])#得到所有label长度

maxLabelLength=max(llength)#得到最大label长度

newLbDatalst=numpy.zeros((len(lbDatalst),maxLabelLength))#进行0填充来满足数据格式要求

foriinrange(len(lbDatalst)):

newLbDatalst[i][:len(lbDatalst[i])]=lbDatalst[i]

returnnewLbDatalst,llength

defmk_am_vocab(self,data):##生成声学模型的字典

vocab=[]

forlineintqdm(data):

line=line

forpnyinline:

ifpnynotinvocab:

vocab.append(pny)#如果拼音不在拼音字典中就添加

vocab.append('_')

returnvocab

defmk_lm_pny_vocab(self,data):#生成语言模型的拼音字典

vocab=['<PAD>']

forlineintqdm(data):

forpnyinline:

ifpnynotinvocab:#如果拼音不在拼音字典中就添加

vocab.append(pny)

returnvocab

defmk_lm_han_vocab(self,data):#生成语言模型的汉字字典

vocab=['<PAD>']

forlineintqdm(data):

line=''.join(line.split(''))#将数据切割

forhaninline:

ifhannotinvocab:

vocab.append(han)#如果汉字不在拼音字典中就添加

returnvocab#根据论文描述,label_smoothing对于训练有好处,将0变为接近零的小数,1变为接近1的数。deflabel_smoothing(inputs,epsilon=0.1):K=inputs.get_shape().as_list()[-1]#numberofchannelsreturn((1-epsilon)*inputs)+(epsilon/K)defctc_len(self,label):#ctc需要获得的信息还有输入序列的长度。

adLength=0

lblength=len(label)

foriinrange(lblength-1):

iflabel[i]==label[i+1]:

adLength+=1

returnlblength+adLength4.3模型构造设计4.3.1声学模型图4.3(1)声学模型构造示意图基本参数设定并导入vocab_size=50,lr=0.0008,gpu_nums=1,is_training=True下面贴上核心代码。

def_model_init(self):#定义模型初试化方法

self.inputs=Input(name='the_inputs',shape=(None,200,1))#设定输入时频图的shape

self.h1=CnnBasicCell(32,self.inputs)#连接上上面的inpus#一个之前设定的CnnBasicCell机构,32为输出维度,也是滤波器数量

self.h2=CnnBasicCell(64,self.h1)#连接上上面的h1层

self.h3=CnnBasicCell(128,self.h2)#连接上上面的h2层

self.h4=CnnBasicCell(128,self.h3,pool=False)#连接上上面的h3层#一个之前设定的CnnBasicCell机构,关闭了池化层

self.h5=CnnBasicCell(128,self.h4,pool=False)#连接上上面的h4层

#五层CnnBasicCell结构,就是双卷积加maxpool#最后两层关闭池化,用于组合高级特征

#200/8*128=3200

#这里其实是flatten了,200是频率高度

self.h6=Reshape((-1,3200))(self.h5)#连接上上面的h5层

self.h6=Dropout(0.2)(self.h6) #droput比例,即随机废弃20%的神经节点,防止过拟合

self.h7=dense(256)(self.h6)#连接上上面的h6层

#神经元256的dense层

self.h7=Dropout(0.2)(self.h7)

self.outputs=dense(self.vocab_size,activation='softmax')(self.h7) #连接上上面的h7层 #最后一层使用softmax的激活函数,其他层都是relu #使用relu可以是曲线正数部分为y=x的曲线,导数为1 #用来防止梯度爆炸

#输出神经元数目和拼音数量一致

self.model=Model(inputs=self.inputs,outputs=self.outputs)#创建model

self.model.summary()#使用summary输出模型各层的参数状况

def_ctc_init(self):#ctc模块#首先传递必须的参数

self.labels=Input(name='label',shape=[None],dtype='float32')#从输入中取出标签参数

self.input_length=Input(name='inputLength',shape=[1],dtype='int64')#从输入中取出输入长度参数

self.label_length=Input(name='labelLength',shape=[1],dtype='int64')#从输入中取出label长度参数#设置ctc_loss

self.lossOut=Lambda(ctc_lambda,output_shape=(1,),name='ctc')\

([self.labels,self.outputs,self.input_length,self.label_length])#使用Lambda()操作把ctc损失包装为一个层传给self.lossOut

self.ctc_model=Model(inputs=[self.labels,self.inputs,

self.input_length,self.label_length],outputs=self.lossOut)

defopt_init(self):#优化器初试化

#初试化adam优化算法

opt=Adam(lr=self.lr,beta_1=0.9,beta_2=0.999,decay=0.01,epsilon=10e-8)

ifself.gpu_nums>1:#判断是否由多核gpuself.ctc_model=multi_gpu_model(self.ctc_model,gpus=self.gpu_nums)#如果有多个gpu,则开启多核训练的模式self.ctc_pile(loss={'ctc':lambday_true,output:output},optimizer=opt)#optimizer=opt也即是配置优化器'''Adam是一种可以替代传统随机梯度下降过程的一阶优化算法,它能基于训练数据迭代地更新神经网络权重。'''

defconv2d(size):#调用keras的Conv2D方法构建二位卷积单元

returnConv2D(size,(3,3),use_bias=True,activation='relu',

padding='same',kernel_initializer='he_normal')

#(3,3)是卷积核大小size是输出维度(即卷积中过滤器输出的数量)也是过滤器的个数使用relu激活函数

#kernel_initializer是建网络层时内核或者偏差权重的初始化方案。此参数是名称或可调用对象。

#he_normal是符合正太分布的初始化方案

defnorm(x):#使用BatchNormalization规范化

returnBatchNormalization(axis=-1)(x)'''BN实现的效果是:对于某一层x来说,它的每个元素xi的数值,在一个batch上的分布是一个任意的未知分布,BN首先把它标准化为了一个标准正态分布。直接这么做太暴力了,如果所有输入样本被层层改分布,相当于输入信息都损失掉了,网络是没法训练的。所以需要第二步对标准正态分布再进行一定程度的还原操作,即缩放平移。最终使得这个数值分布,兼顾保留有效信息、加速梯度训练。图4.3(2)BN原理

defmaxpool(x):#调用keras的MaxPooling2D方法构建二位池化单元

returnMaxPooling2D(pool_size=(2,2),strides=None,padding="valid")(x)

#pool_size:整数或长度为2的整数元组,代表在两个方向【竖直、水平】上的下采样因子,

#如取(2,2)将使图片在两个维度上均变为原长的一半【4个数据合成为1个】。

#为整数表示各个维度值相同且为该数字。

defdense(units,activation="relu"): #调用keras的Dense方法构建全连接层

returnDense(units,activation=activation,use_bias=True,

kernel_initializer='he_normal')

#units:输出的维度大小,改变inputs的最后一维

#使用relu激活函数和he_normal初试化方案

#2层加了bn的conv和一层maxpool构成的一个cell单元

defCnnBasicCell(size,output,pool=True):

output=norm(conv2d(size)(output))

#经过卷积层之后再进行bn归一化

output=norm(conv2d(size)(output))

ifpool:#可以通过pool参数选择关闭maxpool层

output=maxpool(output)

returnoutput

#定义ctc方法

defctc_lambda(args):

label,yPredict,inputLen,labelLen=args#从args中导入标签,y预测值,输入长度和标签长度

yPredict=yPredict[:,:,:]

returnK.ctc_batch_cost(label,yPredict,inputLen,labelLen)

#k是backend计算引擎的东西4.3.2语言模型(使用的是transformer中的部分)图4.3(3)语言模型结构示意图代码主要使用了《attentionisallyourneed》提出的transformer中的部分代码,所以此处并不重复说明代码,将使用结构图的方式说明语言模型的构造和原理。InputEmbedding用于将输入的词配置成词向量的形式,便于使用数学形式表达,以及后续使用矩阵计算加速。PositionalEncoding使用cos和sin的和差公式的数学原理,用来对句子中的词进行位置编码,用以区分位置序列,因为输入语句中的词的顺序包含着重要的上下文信息,在句子输入Muti-headattention机制中时,他会并行计算,此时要想在输出时还原,不丢失信息,就需要使用位置编码。 接下来是多头注意力模块,可以理解为通过注意力机制提出高级特征,而多头的意思是多个独立的attention计算,让不同的attention函数想卷积核一样去关注不同的特征信息,最后集成起来,使用一致的Q,K,V参数,通过线性转换,每个attention只负责最终输出序列中一个子空间,以此来防止过拟合。多头注意力机制引用原文代码,且过于复杂,所以此处并不额外表述。多头注意力机制在得到高级特征后便交给feedward模块,是一个由2层fc构成的前馈神经网络,用于整合特征,并输出预测概率。4.4模型训练过程设计训练流程图如下图4.4模型训练流程图4.4.1准备训练所需数据并导入参数其实就是分割出训练集,验证集,测试集,然后设置文件路径,调用前面的数据处理模块,并设置shuffle=True,打乱数据的顺序,我们通过查询乱序的索引值,来确定训练数据的顺序。最后设置batchsize为4,就是一次读入4张视频图同时训练。因为无核心代码,所以不展示代码。4.4.2声学模型训练首先设置超参,比如gpu数量为1,设置优化器的学习率为0.0008,epochs设置为10。核心代码如下:

#设置训练检查点,用来检测训练情况

cp="model_{the_epoch:02d}{val_accuracy:.2f}.hdf5"

checkpoint=ModelCheckpoint(os.path.join('./checkpoint',cp),monitor='val_loss',save_weights_only=False,verbose=1,save_best_only=True)

#save_best_only=True当设置为True时,监测值有改进时才会保存当前的模型。 #save_weights_only=False若设置为True,则只保存模型权重,否则将保存整个模型(包括模型结构,配置信息等)#检测器为val_loss,数据集与训练集分隔,单独留出的样本集,也称之为验证集合,它可以调整模型的超参数,并且初步评估模型的能力。

#调用ctcmodel,使用ctc_loss进行计算 epoch=10 #因为thchs30训练数据集非常大,不能同时将数据载入内存中。所以利用fit_generator进行训练。原理是使用Python的生成器,逐个生成数据的batch并进行训练。生成器与模型将并行执行以提高效率。

am.ctc_model.fit_generator(batch,steps_per_epoch=len(train_data.wav_lst)//train_data.batch_size,epochs=epoch,callbacks=[checkpoint],workers=1,use_multiprocessing=False,validation_data=dev_batch,validation_steps=200) #设置batchsize,workers等参数,关闭多头处理,设置验证集数据和步长

am.ctc_model.save_weights('logs_am/model.h5')#设置ctc模型

#Keras用动态数据生成器(DataGenerator)和fitgenerator动态训练模型

4.4.3语言模型训练dropout的作用:普通神经网络加上Dropout,防止过拟合原理Dropout是在训练的过程中,随机选择去除一些神经元,在测试的时候用全部的神经元,这样可以使得模型的泛化能力更强,因为它不会依赖某些局部的特征.我们设置舍弃神经元比例,即dropout=0.2。并设置学习率为0.0003,epochs为10。核心代码如下

withlanguageModel.graph.as_default():#将languageModel作为整个tensorflow运行环境的默认图

TrainSav=tf.train.Saver()#通过tf.train.Saver类实现神经网络模型的保存和提取

withtf.Session(graph=languageModel.graph)assession:

merged=tf.summary.merge_all()#.merge_all()将图形、训练过程等数据合并在一起session.run(tf.global_variables_initializer())#先运行调用tensorflow中的global_variables_initializer()初试化后的数据

adNum=0epochs=10

ifos.path.exists('logsLanguageModel/checkpoint'):

print('正在载入语言模型')

latest=tf.train.latest_checkpoint('logsLanguageModel')#自动寻找最新的checkpoint

adNum=int(latest.split('_')[-1])

TrainSav.restore(session,latest)

FileW=tf.summary.FileWriter('logsLanguageModel/tensorboard',tf.get_default_graph())#将训练日志写入到logsLanguageModel/tensorboard文件夹下#tf.get_default_graph()用于使用同一个全局性的默认graph

forkinrange(epochs):#每个epoch的训练过程

loss=0

batch=train_data.get_lm_batch()#取得一个batch的训练数据

foriinrange(bcNum):#batch数量为拼音列表长度/batchsize

inBatch,lBatch=next(batch)#取出当前batch中的input和label

feed={languageModel.x:inBatch,languageModel.y:lBatch}

cost,_=session.run([languageModel.mean_loss,languageModel.train_op],feed_dict=feed)#mean_loss为均方误差,,train_op,一个计算loss并应用梯度的操作

loss+=cost

if0==(k*bcNum+i)%10

resultSet=session.run(merged,feed_dict=feed)#为了取回(Fetch)操作的输出内容,使用Session对象的run()调用执行图时,传入一些tensor,这些tensor会取回结果。

FileW.add_summary(resultSet,k*bcNum+i)#调用FileW的add_summary方法将训练过程以及训练步数保存#add_summary用来FileWriter对象的缓存中存放eventdata。#而向disk上写数据是由FileW对象控制的。

TrainSav.save(session,'logsLanguageModel/model_%d'%(epochs+adNum))#使用TrainSav.save()函数保存模型

FileW.close()4.5预测模块设计图4.5预测模块流程图4.5.1词错率计算defGetLev(str1,str2):

cost=0#定义总体代价#使用difflib.SequenceMatcher比较文本内容,返回差异部分

result=difflib.SequenceMatcher(None,str1,str2)#使用最小编辑距离或莱文斯坦距离(Levenshtein)计算,指由字符串A转化为字#符串B的最小编辑次数。#许可的编辑操作包括将一个字符替换成另一个字符,插入字符,删除字符。一般来说,编辑距离越小,两个串的相似度越大

forflag,ab1,ab2,ba1,ba2inresult.get_opcodes():

ifflag=='replace':#替换操作

cost+=max(ab2-ab1,ba2-ba1)

elseifflag=='insert':#插入操作

cost+=(ba2-ba1)

elseifflag=='delete':#删除操作

cost+=(ab2-ab1)

returncost4.5.2定义解码器一般在分类问题中,训练好模型之后,模型的预测过程非常简单,只需要加载模型文件从前到后执行即可得到分类结果。但在序列学习问题中,模型的预测过程本质是一个空间搜索过程,也称为解码。对CTC网络进行Decoding解码本质过程是选取条件概率最大的输出序列。按照时间序列展开得到栅格网络,解码的过程相当于空间搜索。有这样两种解码策略:第一,暴力解码,即穷举搜索,虽然可以找到最优解,但时间复杂度是指数级的,代价太大,明显不可行。第二,在每一步选择概率最大的输出值,以此得到最终解码的输出序列。然而,CTC的输出序列只对应了搜索空间的一条路径,一个最终标签可对应搜索空间多条路径,所以概率最大的路径并不代表最终label的概率最大,也就是说不一定找得到最优解。两种常见的CTC解码算法:CTCPrefixSearchDecoding和CTCBeamSearchDecoding。简而言之,PrefixSearchDecoding是基于前缀概率的搜索算法,它能确保找到最优解,但最坏情况下耗时可能会随着序列长度呈指数增长;CTCBeamSearchDecoding是一种BeamSearch算法,它能在限定时间下找到近似解,但不保证一定能找到最优解。CTCPrefixSearchDecoding本质是贪心算法,每一次搜索都会选取“前缀概率”最大的节点扩展,直到找到最大概率的目标label,它的核心是利用动态规划算法计算“前缀概率”。CTCBeamSearchDecoding算法虽然简单,但在实际中应用广泛,我们有必要深入了解它的具体实现细节。BeamSearch的过程非常简单,每一步搜索选取概率最大的W个节点进行扩展,W也称为BeamWidth,其核心还是计算每一步扩展节点的概率。#我使用的是CTCBeamSearchDecoding算法

defdecode_ctc(numResult,numToWord):

result=numResult[:,:,:]

in_len=np.zeros((1),dtype=32)

in_len[0]=result.shape[1]#这里我们配置beam_width为10,即只选择最大的10个进行扩展

result=K.ctc_decode(result,in_len,greedy=True,beam_width=10,top_paths=1)

result1=K.get_value(result[0][0])

result1=result1[0]

words=[]

foriinresult1:

text.append(numToWord[i])

returnresult1,words4.5.3预测模块首先导入参数,并准备前面得到的解码字典,参数和训练一致,因为不是核心代码,所以此处不显示。准备测试所需数据,原则上和训练数据不一致,但考虑到系统很小,所以可以使用traindata中的数据,以防止看不到模型的效果,无法验证模型的正确性。核心代码如下:

data_args.data_type='train'

data_args.shuffle=False#这里是选取特定文件测试,所以不用shuffle打乱

data_args.batch_size=1#这里是测试,所以batch_size设置为1,相当于单独将每一个文件放入模型

#预测test_data=get_data(data_args)

进行测试

foriinrange(5):

print('\nthe',i,'thexample.')

#载入训练好的模型,并进行识别

inputs,_=next(am_batch)#取得声学模型数据

x=inputs['the_inputs']

y=test_data.pny_lst[i]

result=am.model.predict(x,steps=1)#得到声学模型的预测结果

_,words=decode_ctc(result,train_data.am_vocab)#使用ctcdecode模块预测拼音序列

words=''.join(words)

print('拼音串预测结果:',words)

print('拼音串真实label结果:',''.join(y))

withsession.as_default():

words=words.strip('\n').split('')#移除换行符并返回列表后使用空格进行切片存取

x=np.array([train_data.pny_vocab.index(pinYin)forpinYininwords])

x=x.reshape(1,-1)#指定数据行为1,列数不定

predicts=session.run(languageModel.preds,{languageModel.x:x})#返回语言模型的预测

label=test_data.han_lst[i]#取出测试数据集合中的汉字label

preds=''.join(train_data.han_vocab[j]forjinpredicts[0])#使用字典取汉字数据并组合

print('原文汉字label:',label)

print('识别结果:',preds)

ifpreds.__contains__("香港"):#这里可以设置关键词用于模拟唤醒功能

print("机器已唤醒")

wordErrorNums+=min(len(label),GetLev(label,preds))#调用之前定义的GetEditDistance计算词错率

wordNums+=len(label)

print('词错误率:',wordErrorNums/wordNums)

session.close()

5系统软件测试5.1测试方法调用wav文件,输入系统,并通过系统输出以及对应的label判断系统的有效性。测试代码使用上文中的预测输出模块。5.2测试用例wav文件以及对应的label,具体来说就是thchs30中的文件。Thchs-30中test文件夹中5个随机测试文件图片5.2测试文件一个测试文件由一个wav文件,以及一个trn文件构成,trn存放label。5.3测试结果下文为测试的运行结果第一个测试结果拼音串预测结果:er4yue4si4ri4zhu4jin4xin1xi1men2wai4luo2jia1nian3wang2jia1gang1zhu1zi4qing1wen2xun4te4di4cong2dong1men2wai4gan3lai2qing4he4拼音串真实label结果:er4yue4si4ri4zhu4jin4xin1xi1men2wai4luo2jia1nian3wang2jia1gang1zhu1zi4qing1wen2xun4te4di4cong2dong1men2wai4gan3lai2qing4he4原文汉字label:二月四日住进新西门外罗家碾王家冈朱自清闻讯特地从东门外赶来庆贺识别结果:二月四日住进新西门外罗家碾王家冈朱自清闻讯特地从东门外赶来庆贺第二个测试结果拼音串预测结果:dan1wei4bu2shi4wo3lao3die1kai1deping2shen2meyao4yi1ci4er4ci4zhao4gu4wo3wo3bu4neng2ba3zi4ji3debao1fu2wang3xue2xiao4qu4拼音串真实label结果:dan1wei4bu2shi4wo3lao3die1kai1deping2shen2meyao4yi1ci4er4ci4zhao4gu4wo3wo3bu4neng2ba3zi4ji3debao1fu2wang3xue2xiao4shuai3原文汉字label:单位不是我老爹开的凭什么要一次二次照顾我我不能把自己的包袱往学校甩识别结果:单位不是我老爹开的凭什么要一次二次照顾我我不能把自己的包袱往学校去第三个测试结果拼音串预测结果:dou1yong4cao3mao4huo4ge1bozhou3hu4zhewan3lie4lie4qieju1chuan1guo4lan4ni2tang2ban1deyuan4ba4pao3hui2zi

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论