主要内容

使用深度学习的言语命令识别

此示例显示如何训练检测音频中的语音命令的存在的深度学习模型。该示例使用语音命令数据集[1]培训卷积神经网络以识别给定的一组命令。

要从头开始训练网络,必须首先下载数据集。如果您不想下载数据集或训练网络,那么您可以加载本示例提供的预先训练的网络,并执行示例的下两个部分:用预先训练的网络识别命令使用来自麦克风的流音频检测命令

用预先训练的网络识别命令

在详细进入培训过程之前,您将使用预先培训的语音识别网络来识别语音命令。

加载预先训练的网络。

加载(“commandNet.mat”

训练网络识别以下语音命令:

  • “是的”

  • “不”

  • “向上”

  • “下”

  • “左”

  • “对”

  • “上”

  • “离开”

  • “停止”

  • “去”

装载一个人说“停止”的短语信号。

[x,fs] = audioread('stop_command.flac');

听命令。

声音(x,fs)

预先训练的网络将基于听觉的谱图作为输入。您将首先将语音波形转换为基于听觉的频谱图。

使用功能解密卫生术计算听觉频谱图。您将在示例之后浏览功能提取的细节。

听觉探测= PerverextractAutiveFeatures(X,FS);

根据其听觉频谱图对命令进行分类。

Command =分类(Trountainnet,听觉码)
命令=分类停止

培训网络以对不属于此设置的单词进行分类为“未知”。

您现在将分类一个未包含在命令列表中的单词(“播放”)来识别。

加载语音信号并收听它。

x = audioread('play_command.flac');声音(x,fs)

计算听觉频谱图。

听觉探测= PerverextractAutiveFeatures(X,FS);

分类信号。

Command =分类(Trountainnet,听觉码)
命令=分类未知

培训网络以将背景噪声分类为“背景”。

创建由随机噪声组成的一秒信号。

X = PinkNoise(16E3);

计算听觉频谱图。

听觉探测= PerverextractAutiveFeatures(X,FS);

对背景噪声进行分类。

Command =分类(Trountainnet,听觉码)
命令=分类背景

使用来自麦克风的流音频检测命令

在从麦克风上测试您的预先培训的命令检测网络。例如,尝试说出一个命令,是的,或停止。然后,尝试说这样一个未知的单词,如玛文希拉房子,或从零到九个的任何数字。

以Hz为单位指定分类速率,并创建一个音频设备阅读器,该阅读器可以从麦克风读取音频。

分类= 20;adr = audiodevicereader(“SampleRate”,fs,'samplesperframe',地板(FS / Classificationrate));

初始化音频的缓冲区。提取网络的分类标签。为流音频的标签和分类概率初始化半秒的缓冲区。使用这些缓冲区在更长的时间内比较分类结果,并且在检测到命令时构建“协议”。指定决策逻辑的阈值。

audiobuffer = dsp.asyncuffer(FS);标签= TRAINARNET.LAYERS(END).CLASS;YBUFFER(1:ClassificationRate / 2)=分类(“背景”);probbuffer = zeros([numel(标签),分类符号/ 2]);countthreshold = ceil(分类符号* 0.2);probthreshold = 0.7;

只要创建的数字存在,创建一个数字并检测命令。无限期地运行循环,设置时限INF.。要停止直播检测,只需关闭数字即可。

h =图('单位''标准化''位置',[0.2 0.1 0.6 0.8]);timelimit = 20;Tic.尽管ishandle(h)&& toc %从音频设备提取音频样本并将样品添加到%的缓冲。x = adr ();写(audioBuffer x);fs, y =阅读(audioBuffer fs-adr.SamplesPerFrame);规范= helperExtractAuditoryFeatures (y, fs);%对当前光谱图进行分类,将标签保存到标签缓冲区,%并将预测的概率保存到概率缓冲区。[ypreedicted,probs] =分类(trountainnet,spec,'executionenvironment'“cpu”);YBUFFER = [YBUFFER(2:END),Y预期];probbuffer = [probbuffer(:,2:结束),probs(:)];%绘制当前波形和频谱图。次要情节(2,1,1)情节(y)轴紧的Ylim([ -  1,1])子图(2,1,2)PColor(SPEC')Caxis([ -  4 2.6445])阴影平坦的%现在通过执行一个非常简单的实际命令检测来进行实际命令检测%的阈值操作。声明一个检测并将其显示在%数字标题如果所有以下保留:1)最常见的标签%不是背景。2)至少countThreshold的最新帧%标签同意。3)预测标签的最大概率是在%最少探针。否则,请勿声明检测。[YMODE,COUNT] =模式(YBUFFER);maxprob = max(probbuffer(标签== ymode,:));子图(2,1,1)如果ymode ==“背景”|| count < count threshold || maxprobb < probThreshold title(别的标题(字符串(YMode),'字体大小',20)结尾drawn结尾

加载语音命令数据集

这个例子使用谷歌语音命令数据集[1]。下载数据集并解压下载的文件。将PathToDatabase设置为数据的位置。

URL =.'https://ssd.mathwands.com/金宝appsupportfiles/audio/google_speech.zip';downloadFolder = tempdir;dataFolder = fullfile (downloadFolder,'google_speech');如果〜存在(DataFolder,'dir')disp('下载数据集(1.4 GB)...'解压缩(url, downloadFolder)结尾

创建培训数据存储

创建一个audiodatastore.这指向训练数据集。

广告= audioDatastore (fullfile (dataFolder,'火车'),...'insertumbfolders',真的,...'fileextensions''.wav'...'labelsource'“foldernames”
广告= audioDataStore具有属性:文件:{'... \ appdata \ local \ temp \ google_speech \ train \ bed \ 00176480_nohash_0.wav';'... \ appdata \ local \ temp \ google_speech \ train \ bed \ 004ae714_nohash_0.wav';'... \ appdata \ local \ temp \ google_speech \ train \ bed \ 004ae714_nohash_1.wav'...和51085更多}文件夹:{'c:\ users \ jibrahim \ appdata \ local \ temp \ google_speech \ train \ train \ train \ train \ train \ train'}标签:[床;床;床......和51085更多分类] leartefilesystemroots:{} outputdatatype:'double'supportedOutputFormats:金宝app[“WAV”“FLAC”“OGG”“MP4”“M4A”] DefaultOutputFormat:“WAV”

选择要识别的单词

指定您希望模型识别为命令的单词。将所有非命令的单词标记为未知。标记不是命令的单词未知创建一组单词,该单词近似于命令以外的所有单词的分布。该网络使用此组来了解命令和所有其他单词之间的差异。

为了减少已知和未知单词之间的类不平衡,加快处理速度,在训练集中只包含一部分未知单词。

采用子集创建仅包含命令和未知单词的子集的数据存储。计算属于每个类别的示例的数量。

命令=分类([“是的”“不”“向上”“下”“左”“对”“上”“离开”“停止”“去”]);IsCommand = ISMember(Ads.Labels,命令);isunknown =〜isCommand;IncludeFraction = 0.2;mask = rand(numel(ads.labels),1)“未知”);adstrain =子集(广告,iscommand | isunknown);CountAckeLabel(adstrain)
ans = 11×2表标签计数_______ _____下调1842年1861年离开1839年1839年1839年1864年1864右1885年未知6483 UP 1843是1843

创建验证数据存储

创建一个audiodatastore.这指向验证数据集。遵循用于创建培训数据存储的相同步骤。

广告= audioDatastore (fullfile (dataFolder,'验证'),...'insertumbfolders',真的,...'fileextensions''.wav'...'labelsource'“foldernames”)IsCommand = ISMember(Ads.Labels,命令);isunknown =〜isCommand;IncludeFraction = 0.2;mask = rand(numel(ads.labels),1)“未知”);ADSValidation =子集(广告,ISCommand | isunknown);CountAckeLabel(ADSValidation)
广告=具有属性的AudioDataStore:文件:{'... \ appdata \ local \ temp \ google_speech \验证\ bick \ 026290a7_nohash_0.wav';'... \ appdata \ local \ temp \ google_speech \验证\ boad \ 060cd039_nohash_0.wav';'... \ appdata \ local \ temp \ google_speech \ viguration \ bick \ 060cd039_nohash_1.wav'...和6795更多}文件夹:{'c:\ users \ jibrahim \ appdata \ local \ temp \ google_speech \验证'}标签:[床;床;床......和6795更多分类]替代文件系统:{} outputDatatype:'double'supportedOutputFormats:[“WAV”“FLAC”“OGG金宝app”“MP4”“M4A”] DefaultOutputFormat:“WAV”ANS = 11×2表标签计数_______ _____ _____下降264转260左247 op 256 op 257 off 256右256秒钟,未知850 up 260是261

要将网络与整个数据集一起培训并达到最高的准确性,请设置逃守血统错误的。快速运行此示例,设置逃守血统真的

reduceDataset = false;如果Depentataset numuniqueLabels = numel(唯一(adstrain.labels));%将数据集减少20倍adstrain = splitheachlabel(adstrain,round(numel(adstrain.files)/ numuniqueLabels / 20));ADSValidation = SpliteachLabel(ADSValidation,ROUND(NUMER(ADSValidation.Files)/ NumUnIqueLabels / 20));结尾

计算听觉谱图

为准备卷积神经网络的有效培训数据,将语音波形转换为基于听觉的频谱图。

定义特征提取的参数。分段是每个语音剪辑的持续时间(以秒为单位)。框架是频谱计算的每个帧的持续时间。哈姆瑟为每个频谱之间的时间步长。numbands.是听觉频谱图中的过滤器数量。

创建一个audiofeatureextractor.对象执行特征提取。

fs = 16e3;数据集的%已知采样率。semmentduration = 1;Framedrion = 0.025;Hopduration = 0.010;segmensamples = round(semmentduration * fs);FramesAmples =圆形(框架* FS);HOPSAMPLES =圆形(HOPDURATION * FS);重叠amples = framesamples  -  hoppamples;FFTLength = 512;numbands = 50;AFE = audiofeatureextractor(...“SampleRate”,fs,...'fftlength',fftlength,...'窗户',汉恩(框架,'定期'),...“OverlapLength”,重叠,...'Barkspectrum',真的);setExtractorParams(AFE,'Barkspectrum''numbands',numband,“WindowNormalization”,错误的);

从数据集中读取文件。训练卷积神经网络需要输入尺寸是一致的大小。数据集中的某些文件少于1秒。将零填充应用于音频信号的正面和背面,使其长度为长度SEGMANSEMPLES.

x =阅读(adsTrain);numSamples =大小(x, 1);numToPadFront = floor((segmentSamples - numSamples)/2);numToPadBack = cell ((segmentSamples - numSamples)/2);xPadded = 0 (numToPadFront 1'像', x); x; 0 (numToPadBack 1'像', x));

要提取音频特征,调用提取。输出是横跨行的树皮谱。

特点=提取(afe xPadded);[numHops, numFeatures] =大小(特性)
numhops = 98 numfeatures = 50

在此示例中,您通过应用对数来处理听觉频谱图。拍摄少数的日志可能会导致循环错误。

要加快处理,可以使用多个工人分发功能提取议案

首先,确定数据集的分区数。如果您没有并行计算工具箱™,请使用单个分区。

如果〜isempty(ver('平行线')) && ~reduceDataset pool = gcp;numPar = numpartitions (adsTrain、池);别的numpar = 1;结尾

对于每个分区,从数据存储读取,零打击信号,然后提取该功能。

议案II = 1:numpar subds = partition(adstrain,numpar,ii);xtrain =零(numhops,numband,1,numel(subds.files));为了idx = 1:numel(subds.files)x =读取(subds);Xpadded = [零(楼层((Segmensampless尺寸(x,1))/ 2),1); x; zeros(ceil((segmentsampless-size(x,1))/ 2),1)];XTrain(:,::,idx)=提取物(afe,xpadded);结尾XTrainC {2} = XTrain;结尾

使用沿着第四维度的听觉频谱图转换为4维数组。

XTrain =猫(4,XTrainC {:});[numHops, numBands numChannels numSpec] =大小(XTrain)
numHops = 98 numBands = 50 numChannels = 1 numSpec = 25021

通过窗口电源缩放功能,然后拍摄日志。要获得具有更平滑的分布的数据,请使用小偏移采用频谱图的对数。

epsil = 1e-6;XTrain = log10(Xtrain + Epsil);

执行上面描述的特征提取步骤到验证集。

如果〜isempty(ver('平行线'))pool = gcp;numpar = numpartitions(adsvalidation,pool);别的numpar = 1;结尾议案II = 1:numpar subds = partition(ADSValidation,NumPar,II);xvalidation = zeros(numhops,numband,1,numel(subds.files));为了idx = 1:numel(subds.files)x =读取(subds);Xpadded = [零(楼层((Segmensampless尺寸(x,1))/ 2),1); x; zeros(ceil((segmentsampless-size(x,1))/ 2),1)];xValidation(:,::,idx)=提取物(afe,xpadded);结尾xvalidationc {ii} = xvalidation;结尾XValidation =猫(4,XValidationC {:});XValidation = log10(XValidation + epsil);

隔离列车和验证标签。删除空型。

ytrain = removecats(adstrain.labels);yvalidation = removecats(adsvalidation.labels);

可视化数据

绘制一些训练样本的波形和听觉谱图。播放相应的音频剪辑。

specmin = min(xtrain,[],'全部');specmax = max(xtrain,[],'全部');idx = randperm(numel(adstrain.files),3);数字('单位''标准化''位置',[0.2 0.2 0.6 0.6]);为了i = 1:3 [x,fs] = audioread(adstrain.files {idx(i)});子图(2,3,i)绘图(x)轴紧的标题(字符串(adstrain.labels(idx(i))))子图(2,3,i + 3)spect =(xtrain(:,:,1,idx(i))');PColor(SPECT)Caxis([Specmin Specmax])阴影平坦的sound(x,fs)暂停(2)结尾

添加背景噪声数据

该网络不仅要能够识别不同的语音,还必须能够检测输入是否包含沉默或背景噪声。

使用音频文件_背景_文件夹创建一秒背景噪音剪辑的样本。从每个背景噪声文件中创建相同数量的背景剪辑。您也可以创建自己的背景噪音录音,并将它们添加到_背景_ 文件夹。在计算频谱图之前,该函数重新分配每个音频剪辑,其中因子从所提供的范围中的日志均匀分布中采样volumerange.

adsbkg = audiodataStore(fullfile(datafolder,'背景'))numbkgclips = 4000;如果SDENTATASET NUMBKGCLIPS = NUM​​BKGCLIPS / 20;结尾volumerange = log10([1e-4,1]);numbkgfiles = numel(adsbkg.files);numclipsperfile = histcounts(1:numbkgclips,linspace(1,numbkgclips,numbkgfiles + 1));xbkg = zeros(尺寸(xtrain,1),尺寸(xtrain,2),1,numbkgclips,'单身的');bkgall = readall(adsbkg);IND = 1;为了count = 1:numbkgfiles bkg = bkgall {count};idxstart = randi(numel(bkg)-fs,numclipsperfile(count),1);idxend = idxstart + fs-1;增益= 10. ^((volumerange(2)-volumerange(1))* rand(numclipsperfile(count),1)+ volumerange(1));为了j = 1:numclipsperfile(count)x = bkg(idxstart(j):idxend(j))* gain(j);x = max(min(x,1), -  1);xbkg(::,:,ind)=提取物(afe,x);如果mod(ind,1000)== 0 disp(“处理 ”+字符串(IND)+“背景剪辑”+字符串(numbkgclips))结尾Ind = Ind + 1;结尾结尾xbkg = log10(xbkg + epsil);
adsbkg = audiodataStore具有属性:文件:{'... \ appdata \ local \ temp \ google_speech \ background \ dope_the_dishes.wav';'... \ appdata \ local \ temp \ google_speech \ background \ dude_miaowing.wav';'... \ appdata \ local \ temp \ google_speech \ background \ strotting_bike.wav'...和3更多}文件夹:{'c:\ users \ jibrahim \ appdata \ local \ temp \ google_speech \ background'} learstefilesystemroots:{} OutputDataType:'Double'标签:{} Support金宝appedOutputFormats:[“WAV”“FLAC”“ogg”“MP4”“M4a”] DefaultOutputFormat:“WAV”处理1000个背景剪辑中的4000个加工2000个背景剪辑中的4000个已加工40003000个背景剪辑中的4000个4000个背景夹子为4000

在训练,验证和测试集之间分离背景噪声的谱图。因为这_背景噪音_文件夹仅包含大约五个半分钟的背景噪声,不同数据集中的背景样本高度相关。为了增加背景噪声的变化,您可以创建自己的背景文件并将其添加到文件夹中。为了增加网络的稳健性,您还可以尝试将背景噪声与语音文件混合到语音文件中。

numtrainbkg =楼层(0.85 * numbkgclips);numvalidationbkg =楼层(0.15 * numbkgclips);XTrain(:,::,结束+ 1:end + numtrainbkg)= xbkg(:,:,:,1:numtrainbkg);YTRAIN(结束+ 1:end + NUMTRAINBKG)=“背景”;xValidation(::::,结束+ 1:end + NumValidationBkg)= XBKG(:,::,numtrainbkg + 1:结束);YValidation(终点+ 1:end + NumValidationBKG)=“背景”

绘制培训和验证集中不同类标签的分布。

数字('单位''标准化''位置',[0.2 0.2 0.5 0.5]) subplot(2,1,1) histogram(YTrain) title(“培训标签分布”)子图(2,1,2)直方图(YValidation)标题(“验证标签分发”

定义神经网络架构

创建一个简单的网络架构作为一系列图层。使用卷积和批量归一化层,并将特征映射“空间上”(即,在时间和频率上)使用MAX池层。添加最终最大池层,可以随时间全局汇集输入功能映射。这在输入频谱图中强制(近似)时间转换不变性,允许网络在时间上独立于语音的确切位置执行相同的分类。全球池也显着降低了最终完全连接层中的参数数量。为了减少网络记忆训练数据的特定功能的可能性,将少量丢失添加到最后一个完全连接的图层。

网络很小,因为它只有五个卷积层,滤波器很少。numf.控制卷积层中的过滤器数量。为了提高网络的准确性,尝试通过添加相同的卷积块,批量归一化和relu层来增加网络深度。您还可以尝试通过增加提高卷积滤波器的数量numf.

使用加权交叉熵分类损失。权重ClassificationLayer(类别级)创建自定义分类层,用于计算跨熵丢失,并通过观察加权类别级别。按照类出现的顺序指定类权重分类(Ytrain)。为了使每个类同等于丢失的总重量,使用与每个类中的训练示例的数量成反比的类重量。使用ADAM优化器培训网络时,培训算法与类重量的总体正常化无关。

Classweights = 1./countcats(itrain);classweights = classweights'/均值(类别级);numclasses = numel(类别(YTrain));timepoolsize = ceil(numhops / 8);dropoutprob = 0.2;numf = 12;图层= [imageInputLayer([numhops numbands])卷积2dlayer(3,numf,'填充''相同的')BatchnormalizationLayer Ruilulayer MaxPooling2dlayer(3,'走吧'2,'填充''相同的')卷积2dlayer(3,2 * numf,'填充''相同的')BatchnormalizationLayer Ruilulayer MaxPooling2dlayer(3,'走吧'2,'填充''相同的')卷积2dlayer(3,4 * numf,'填充''相同的')BatchnormalizationLayer Ruilulayer MaxPooling2dlayer(3,'走吧'2,'填充''相同的')卷积2dlayer(3,4 * numf,'填充''相同的')BatchnormalizationLayer Rufulayer卷积2dlayer(3,4 * numf,'填充''相同的')BatchnormalizationLayer Ruilulayer MaxPooling2dlayer([timepoolsize,1])oploutlayer(dropoutprob)全连接列(numclasses)softmaxlayer权重classificationlayer(classweights)];

火车网络

指定培训选项。使用Adam优化器,迷你批量大小为128。训练25个周期,20个周期后学习率降低10倍。

minibatchsize = 128;验证频率=地板(Numel(YTrain)/小型匹配);选项=培训选项(“亚当”...'italllearnrate',3e-4,...'maxepochs'25岁的...“MiniBatchSize”,小匹马,...'洗牌''每个时代'...'plots'“训练进步”...'verbose',错误的,...'vightationdata',{xvalidation,yvalidation},...'验证职业',验证职权,...'shownrateschedule'“分段”...'学习ropfactor',0.1,...'学习ropperiod'20);

训练网络。如果您没有GPU,则培训网络可能需要时间。

trainedNet = trainNetwork (XTrain、YTrain层,选择);

评估培训的网络

计算网络在训练集(无数据增强)和验证集上的最终精度。这个网络对这个数据集非常精确。然而,培训、验证和测试数据都有类似的分布,不一定反映真实环境。这个限制特别适用于未知类别,其中仅包含少数单词的话语。

如果SDENTATASET LOAD(“commandNet.mat”“trainedNet”);结尾YVALPRED =分类(TRAIRATIONNET,XValidation);validationError =均值(yvalpred〜= yvalidation);YTrainPred =分类(TRAIRATIONNET,XTRAIN);TrainError =平均值(Ytrainpred〜= Ytrain);DISP(“训练误差:+ TrainError * 100 +“%”)disp(“验证错误:”+ validationError * 100 +“%”
训练错误:1.907%验证错误:5.5376%

绘制混乱矩阵。使用列和行摘要显示每个类的精度并回忆。对混淆矩阵的类进行排序。最大的混乱是在未知的单词和命令之间,向上离开, 和

数字('单位''标准化''位置',[0.2 0.2 0.5 0.5]);CM = ConfusionChart(YValidation,YVALPRED);cm.title ='验证数据的混淆矩阵';cm.columnsummary ='列 - 归一化';cm.rowsummary ='行标准化';SortClasses(cm,[命令,“未知”“背景”])

当处理具有受限硬件资源的应用程序(如移动应用程序)时,请考虑可用内存和计算资源的限制。以千字节为单位计算网络的总大小,并在使用CPU时测试其预测速度。预测时间是对单个输入图像进行分类的时间。如果你向网络输入多个图像,这些图像可以同时分类,从而缩短每个图像的预测时间。然而,在对流音频进行分类时,单图像的预测时间是最相关的。

信息=谁(“trainedNet”);DISP(“网络大小:”+信息。字节/ 1024 +“kb”为了i = 1:100 x = randn([numhops,numbands]);TIC [YERPREDITED,PROPS] =分类(TRAIRATIONNET,X,“刽子果环境”“cpu”);时间(i)= toc;结尾DISP(CPU上单图像预测时间:+均值(时间(11:结束))* 1000 +“ 多发性硬化症”
网络尺寸:286.7402 kB单图像预测时CPU:2.5119毫秒

参考

[1]监狱长P。“语音指令:单字语音识别的公共数据集”,2017。可以从https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz。版权所有Google 2017.语音命令DataSet在Creative Commons归因4.0许可下许可,可在此处提供:https://creativeCommons.org/licenses/by/4.0/Legalcode.