主要内容

基于深度学习的语音指令识别

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

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

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

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

加载预先训练好的网络。

负载(“commandNet.mat”

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

  • “是的”

  • “不”

  • 《飞屋环游记》

  • “下”

  • “左”

  • “对”

  • “在”

  • “关闭”

  • “停止”

  • “走”

在有人说“停止”的地方加载一个简短的语音信号。

[x, fs] = audioread (“停止命令。flac”);

听命令。

声音(x,fs)

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

使用的函数extractAuditoryFeature计算听觉频谱图。您将在示例之后浏览功能提取的细节。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

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

命令=分类(trainedNet、auditorySpect)
命令=无条件停止

经过训练,该网络将不属于这个集合的单词分类为“未知”。

现在,您将对未包含在要识别的命令列表中的单词(“play”)进行分类。

加载语音信号并收听它。

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

计算听觉频谱图。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

信号的分类。

命令=分类(trainedNet、auditorySpect)
命令=分类未知

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

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

x = pinknoise (16 e3);

计算听觉频谱图。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

分类背景噪音。

命令=分类(trainedNet、auditorySpect)
命令=分类背景

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

在麦克风的流式音频上测试预先训练好的命令检测网络。例如,尝试说出其中一个命令,是的没有,或停止.然后,试着说出其中一个不认识的单词,比如玛文希拉床上房子,或0到9之间的任意数字。

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

分类率=20;adr=音频设备阅读器('采样率',财政司司长,“样品性能框架”,楼(fs/分级率);

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

audiobuffer = dsp.asyncuffer(FS);标签= TRAINARNET.LAYERS(END).CLASS;YBUFFER(1:ClassificationRate / 2)=分类(“背景”);probBuffer = 0([元素个数(标签),classificationRate / 2));countThreshold =装天花板(classificationRate * 0.2);probThreshold = 0.7;

只要创建的图形存在,就创建图形并检测命令期限.要停止实时检测,只需关闭图形。

h =图(“单位”“归一化”“位置”,[0.20.1 0.6 0.8]);时限=20;tic句柄(h) && toc < timeLimit%从音频设备中提取音频样本并将样本添加到%的缓冲。x = adr ();写(audioBuffer x);fs, y =阅读(audioBuffer fs-adr.SamplesPerFrame);规范= helperExtractAuditoryFeatures (y, fs);%对当前光谱图进行分类,将标签保存到标签缓冲区,%并将预测的概率保存到概率缓冲区。[ypreedicted,probs] =分类(trountainnet,spec,“ExecutionEnvironment”“cpu”);YBuffer = [YBuffer(2:结束),YPredicted);probBuffer = [probBuffer(:, 2:结束)、聚合氯化铝(:));%绘制电流波形和谱图。次要情节(2,1,1)情节(y)轴紧的Ylim ([-1,1]) subplot(2,1,2) pcolor(spec') caxis([-4 2.6445])底纹平坦的现在通过执行一个非常简单的命令来进行实际的命令检测%的阈值操作。声明一个检测并将其显示在%数字标题如果所有以下保留:1)最常见的标签%不是背景。2)至少countThreshold的最新帧%标签一致。3)预测标签的最大概率为at%最少探针。否则,请勿声明检测。[YMode,计数]=模式(YBuffer);maxProb = max(probBuffer(labels == YMode,:)); / /输出次要情节(2,1,1)如果伊莫德==“背景”|| count < count threshold || maxprobb < probThreshold title(其他的标题(string (YMode),'字体大小', 20)结束刷新屏幕结束

加载语音命令数据集

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

URL =.'https://ssd.mathworks.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,“火车”),...“IncludeSubfolders”,真的,...“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) < includeFraction;isUnknown = isUnknown & mask;ads.Labels (isUnknown) =分类(“未知”);adsTrain =子集(广告,isCommand | isUnknown);countEachLabel (adsTrain)
ans = 11×2 table Label Count _______ _____ down 1842 go 1861 left 1839 no 1853 off 1839 on 1864 right 1852 stop 1885 unknown 6483 up 1843 yes 1860

创建验证数据存储

创建一个audioDatastore(音频工具箱)指向验证数据集。遵循与创建训练数据存储相同的步骤。

广告= audiodataStore(fullfile(datafolder,“验证”),...“IncludeSubfolders”,真的,...“FileExtensions”“.wav”...“LabelSource”“foldernames”)IsCommand = ISMember(Ads.Labels,命令);isUnknown = ~ isCommand;includeFraction = 0.2;mask = rand(numel(ads.Labels),1) < includeFraction;isUnknown = isUnknown & mask;ads.Labels (isUnknown) =分类(“未知”);ADSValidation =子集(广告,ISCommand | isunknown);CountAckeLabel(ADSValidation)
ads = audioDatastore的属性:Files:{'…\AppData\Local\Temp\google_speech\validation\bed\026290a7_nohash_0.wav';’……\ AppData \当地\ Temp \ google_speech \床验证\ \ 060 cd039_nohash_0.wav;’……\ AppData \当地\ Temp \ google_speech \床验证\ \ 060 cd039_nohash_1.wav……{'C:\Users\jibrahim\AppData\Local\Temp\google_speech\validation'}标签:[bed;床上;床上……和6795年更直言]AlternateFileSystemRoots: {} OutputDataType:“双”SupportedOutputFormats:[金宝app“wav”“flac”“ogg”“mp4”“m4a格式”]DefaultOutputFormat:“wav”ans = 11×2表标签数  _______ _____ 264 260 247 270 256 257 256停止246未知850 260对261

要使用整个数据集对网络进行训练并达到尽可能高的精度,请设置reduceDataset假的.要快速运行此示例,请设置reduceDataset真正的

reduceDataset = false;如果reduceDataset numUniqueLabels = nummel (unique(adsTrain.Labels));%将数据集缩减到原来的20倍adsTrain = splitEachLabel(adsTrain,round(numel(adsTrain. files) / numUniqueLabels / 20));adsValidation = splitEachLabel(adsValidation,round(numel(adsValidation. files) / numUniqueLabels / 20));结束

计算听觉频谱图

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

定义特征提取的参数。segmentDuration为每个演讲片段的持续时间(单位为秒)。frameDuration为进行频谱计算的每帧的持续时间。哈姆瑟为每个频谱之间的时间步长。麻木为听觉声谱图中过滤器的数量。

创建一个audioFeatureExtractor(音频工具箱)对象进行特征提取。

fs = 16e3;数据集的%已知采样率。segmentDuration=1;frameDuration=0.025;hopDuration=0.010;segmentSamples=round(segmentDuration*fs);frameSamples=round(frameDuration*fs);hopSamples=round(hopDuration*fs);OverlapseSamples=frameSamples-hopSamples;FFTLLength=512;Numberands=50;afe=audioFeatureExtractor(...'采样率',财政司司长,...“FFTLength”FFTLength,...'窗户',hann(框架样本,“周期”),...“OverlapLength”,重叠,...“巴克光谱”,真正的);setExtractorParams (afe“巴克光谱”“NumBands”,numband,“WindowNormalization”、假);

从数据集读取文件。训练卷积神经网络需要输入大小一致。数据集中的一些文件长度小于1秒。在音频信号的前面和后面应用零填充,使其具有长度segmentSamples

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

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

为了加快处理速度,可以使用将特征提取分布到多个辅助对象parfor

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

如果~z~我是空的(“平行”)) && ~reduceDataset pool = gcp;numPar = numpartitions (adsTrain、池);其他的numPar = 1;结束

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

parforII = 1:numpar subds = partition(adstrain,numpar,ii);xtrain =零(numhops,numband,1,numel(subds.files));idx=1:numel(subds.Files)x=read(subds);xPadded=[zeros(floor((segmentSamples size(x,1))/2),1);x;zeros(ceil((segmentSamples size(x,1))/2),1];XTrain(:,:,:,idx)=extract(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);

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

如果~z~我是空的(“平行”) pool = gcp;numPar = numpartitions (adsValidation、池);其他的numPar = 1;结束parforii=1:numPar subds=partition(adsvalization,numPar,ii);XValidation=zero(numHops,numBands,1,numel(subds.Files));idx = 1:numel(subds. files) x = read(subds);xPadded =[0(地板(segmentSamples-size (x, 1)) / 2), 1); x; 0(装天花板((segmentSamples-size (x, 1)) / 2), 1)];XValidation (:,:,:, idx) =提取(afe xPadded);结束XValidationC {2} = 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])着色平坦的声音(x, fs)暂停(2)结束

添加背景噪声数据

网络必须不仅能够识别不同的口语单词,还可以检测输入是否包含沉默或背景噪声。

使用音频文件_background_文件夹创建一秒背景噪音剪辑的样本。从每个背景噪声文件中创建相同数量的背景剪辑。您也可以创建自己的背景噪音录音,并将它们添加到_background_文件夹。在计算谱图之前,该函数使用从给定范围内的对数均匀分布中采样的因子对每个音频剪辑进行缩放容积法

adsbkg = audiodataStore(fullfile(datafolder,“背景”) numBkgClips = 4000;如果reduceDataset numBkgClips = numBkgClips/20;结束volumeRange = log10([1的军医,1]);numBkgFiles =元素个数(adsBkg.Files);numClipsPerFile = histcounts (1: numBkgClips, linspace (1 numBkgClips numBkgFiles + 1);Xbkg = 0(大小(XTrain, 1),大小(XTrain, 2), 1, numBkgClips,'单身的');bkgAll = readall (adsBkg);印第安纳州= 1;计数= 1:numBkgFiles bkg = bkgAll{count};idxStart =兰迪(元素个数(bkg) - f, numClipsPerFile(计数),1);idxEnd = idxStart + fs-1;^((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(“处理 ”(印第安纳州)+ +字符串“背景剪辑”+字符串(numBkgClips))结束Ind = Ind + 1;结束结束xbkg = log10(xbkg + epsil);
adsBkg = audioDatastore与属性:文件:{'…\AppData\Local\Temp\google_speech\background\doing_the_dish .wav';“…\ AppData \当地背景\ Temp \ google_speech \ \ dude_miaowing.wav;“…\ AppData \当地背景\ Temp \ google_speech \ \ exercise_bike.wav……{'C:\Users\jibrahim\AppData\Local\Temp\google_speech\background'} AlternateFileSystemRoots: {} OutputDataType: 'double'标签:{}SupportedOutputFormats: ["wav" 金宝app"flac" "ogg" "mp4" "m4a"] DefaultOutputFormat:“wav”从4000个背景剪辑中处理了1000个,从4000个背景剪辑中处理了2000个,从4000个背景剪辑中处理了3000个,从4000个背景剪辑中处理了4000个

在训练集、验证集和测试集之间分割背景噪声谱图。因为_background_noise_文件夹仅包含大约五分钟半的背景噪声,不同数据集中的背景样本高度相关。若要增加背景噪声的变化,可以创建自己的背景文件并将其添加到文件夹中。若要提高网络对噪声的鲁棒性,还可以尝试混合backgr将噪音输入语音文件。

numTrainBkg =地板(0.85 * numBkgClips);numValidationBkg =地板(0.15 * numBkgClips);XTrain(:,:,: + 1:终端+ numTrainBkg) = Xbkg (:,:,:, 1: numTrainBkg);YTrain(+ 1:结束+ numTrainBkg) =“背景”;XValidation(:,:,: + 1:终端+ numValidationBkg) = Xbkg (:,:,:, numTrainBkg + 1:结束);YValidation(+ 1:结束+ numValidationBkg) =“背景”

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

身材(“单位”“归一化”“位置”,[0.2 0.2 0.5 0.5]) subplot(2,1,1) histogram(YTrain) title(“培训标签分配”)子图(2,1,2)直方图(YValidation)标题(“验证标签分发”

定义神经网络结构

创建一个简单的网络架构,作为一组层。使用卷积和批处理归一化层,并使用最大池化层在“空间上”(即在时间和频率上)对特征映射进行向下采样。添加一个最终的最大池化层,随着时间的推移将输入特征映射全局池化。这在输入谱图中强制了(近似)时间平移不变性,允许网络执行相同的分类,而不依赖于语音在时间上的确切位置。全局池还显著减少了最终完全连接层中的参数数量。为了减少网络记忆训练数据的特定特征的可能性,在最后一个完全连接层的输入中添加少量的dropout。

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

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

classWeights = 1. / countcats (YTrain);classWeights = classWeights ' /意味着(classWeights);numClasses =元素个数(类别(YTrain));timePoolSize =装天花板(numHops / 8);dropoutProb = 0.2;numF = 12;[imageInputLayer([numHops numBands]))卷积2dlayer (3,numF,'填充'“相同”maxPooling2dLayer(3,“步”2,'填充'“相同”) convolution2dLayer (3 2 * numF'填充'“相同”maxPooling2dLayer(3,“步”2,'填充'“相同”)卷积2层(3,4*numF,'填充'“相同”maxPooling2dLayer(3,“步”2,'填充'“相同”)卷积2层(3,4*numF,'填充'“相同”)batchNormalizationLayer reluLayer卷积2Dlayer(3,4*numF,'填充'“相同”)batchNormalizationLayer reluLayer MaxPoolg2dLayer([timePoolSize,1])DropOutployer(dropoutProb)fullyConnectedLayer(numClasses)softmaxLayer weightedClassificationLayer(classWeights)];

列车网络的

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

miniBatchSize = 128;validationFrequency =地板(元素个数(YTrain) / miniBatchSize);选择= trainingOptions (“亚当”...“初始学习率”,3e-4,...“MaxEpochs”25岁的...“MiniBatchSize”,小匹马,...'洗牌'“every-epoch”...“阴谋”“训练进步”...'verbose'假的,...“ValidationData”{XValidation, YValidation},...“ValidationFrequency”,验证职权,...'shownrateschedule'“分段”...“LearnRateDropFactor”, 0.1,...“LearnRateDropPeriod”, 20);

培训网络。如果你没有GPU,那么训练网络可能会花费一些时间。

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

评估培训网络

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

如果reduceDataset负载(“commandNet.mat”“trainedNet”);结束YValPred=分类(trainedNet,XValidation);validationError=平均值(YValPred~=YValidation);YTrainPred=分类(trainedNet,XTrain);trainError=平均值(YTrainPred~=YTrain);disp(“训练错误:”+ TrainError * 100 +“%”)disp("验证错误:"+ validationError * 100 +“%”
培训误差:1.907%验证误差:5.5376%

绘制混淆矩阵。通过使用列和行摘要显示每个类的精度和召回率。对混淆矩阵的类进行排序。最大的混淆是在未知的单词和命令之间,向上下来没有,没有

身材(“单位”“归一化”“位置”,[0.2 0.2 0.5 0.5]);厘米= confusionchart (YValidation YValPred);厘米。Title ='验证数据的混淆矩阵';厘米。ColumnSummary =“column-normalized”;cm.概述='行标准化';sortClasses(厘米,[命令,“未知”“背景”])

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

信息=谁(“trainedNet”);disp (“网络大小:”+信息。字节/ 1024 +“kB”i = 1:100 x = randn([numhops,numbands]);TIC [YERPREDITED,PROPS] =分类(TRAIRATIONNET,X,“ExecutionEnvironment”“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..版权2017年谷歌。Speech Commands Dataset是在Creative Commons Attribution 4.0许可下使用的,可以在这里获得:https://creativecommons.org/licenses/by/4.0/legalcode

参考文献

[1]监狱长P。“语音指令:单字语音识别的公共数据集”,2017。可以从http://download.tensorflow.org/data/speech_commands_v0.01.tar.gz.版权2017年谷歌。Speech Commands Dataset是在Creative Commons Attribution 4.0许可下使用的,可以在这里获得:https://creativecommons.org/licenses/by/4.0/legalcode

另请参阅

||

相关的话题