主要内容

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

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

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

通过预先训练的网络识别命令

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

加载预先训练的网络。

负载(“commandNet.mat”的)

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

  • “是的”

  • “不”

  • 《飞屋环游记》

  • “向下”

  • “左”

  • “正确”

  • “上”

  • “关闭”

  • “停止”

  • “走”

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

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

听命令。

声音(x,fs)

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

使用的函数解密卫生术来计算听觉谱图。在稍后的示例中,您将了解特征提取的细节。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

根据命令的声谱图对其进行分类。

命令=分类(Trountainnet,听觉码)
命令=无条件停止

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

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

加载语音信号并收听它。

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

计算听觉频谱图。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

信号的分类。

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

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

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

X = Pinknoise(16E3);

计算听觉频谱图。

auditorySpect = helperExtractAuditoryFeatures (x, fs);

对背景噪声进行分类。

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

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

测试您的预先培训的命令检测网络,从麦克风流中的流​​式传输音频。尝试说出一个命令,例如,是的没有,或停止.然后,尝试说这样一个未知的单词,如马文希拉房子,或从零到九个的任何数字。

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

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

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

audioBuffer = dsp.AsyncBuffer (fs);. class标签= trainedNet.Layers(结束);YBuffer (1: classificationRate / 2) =分类(“背景”);probBuffer = 0([元素个数(标签),classificationRate / 2));countThreshold =装天花板(classificationRate * 0.2);probThreshold = 0.7;

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

h =图(“单位”'标准化'“位置”,[0.2 0.1 0.6 0.8]);timelimit = 20;t句柄(h) && toc < timeLimit%从音频设备提取音频样本并将样品添加到%的缓冲。x = adr ();写(audioBuffer x);fs, y =阅读(audioBuffer fs-adr.SamplesPerFrame);规范= helperExtractAuditoryFeatures (y, fs);%对当前光谱图进行分类,将标签保存到标签缓冲区,%并将预测的概率保存到概率缓冲区。[YPredicted,聚合氯化铝]=分类(trainedNet,规范,'executionenvironment'“cpu”);YBUFFER = [YBUFFER(2:结束),Y预期];probbuffer = [probbuffer(:,2:结束),probs(:)];%绘制电流波形和谱图。次要情节(2,1,1)情节(y)轴ylim([ -  1,1])子图(2,1,2)PColor(SPEM')Caxis([ -  4 2.6445])阴影%现在通过执行非常简单的实际命令检测%的阈值操作。声明一个检测并将其显示在%数字标题,如果所有以下持有:1)最常见的标签%不是背景。2)至少countThreshold的最新帧%标签同意。3)预测标签的最大概率在于% probThreshold最少。否则,不要声明检测。[YMode,计数]=模式(YBuffer);maxProb = max(probBuffer(labels == YMode,:)); / /输出次要情节(2,1,1)如果YMode ==.“背景”|| count < count threshold || maxprobb < probThreshold title(”“的)别的标题(string (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,“火车”),...“IncludeSubfolders”,真的,...“FileExtensions”'.wav'...“LabelSource”“foldernames”的)
ads = audioDatastore与属性:文件:{'…\AppData\Local\Temp\google_speech\train\bed\00176480_nohash_0.wav';’……\ AppData \当地\ Temp \床google_speech \培训\ \ 004 ae714_nohash_0.wav;’……\ AppData \当地\ Temp \床google_speech \培训\ \ 004 ae714_nohash_1.wav……{'C:\Users\jibrahim\AppData\Local\Temp\google_speech\train'}标签:[bed;床上;床上……alteratefilesystemroots: {} OutputDataType: 'double' SupportedOutputFormats: ["wav金宝app" "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,commands);isUnknown = ~ isCommand;includeFraction = 0.2;mask = rand(numel(ads.Labels),1) < includeFraction;isUnknown = isUnknown & mask;ads.Labels (isUnknown) =分类(“未知”);adsValidation =子集(广告,isCommand | isUnknown);countEachLabel (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;如果Depentataset numuniqueLabels = numel(唯一(adstrain.labels));%将数据集缩减到原来的20倍adstrain = splitheachlabel(adstrain,round(numel(adstrain.files)/ numuniqueLabels / 20)));ADSValidation = SpliteachLabel(ADSValidation,Round(Numel(AdsValidation.Files)/ NumUniqueLabels / 20));结尾

计算听觉谱图

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

定义特征提取的参数。分段为每个演讲片段的持续时间(单位为秒)。frameDuration为进行频谱计算的每帧的持续时间。hopDuration为每个频谱之间的时间步长。numbands.是听觉频谱图中的过滤器数量。

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

fs = 16 e3;%数据集的已知抽样率。semmentDuration = 1;Framedrion = 0.025;HopDuration = 0.010;segmensamples = round(semmentduration * fs);FramesAmples =圆形(框架* FS);HOPSAMALSERS =圆形(HOPDOUNT * FS);重叠amples = framesamples  -  hoppamples;FFTLength = 512;numbands = 50;AFE = audiofeatureextractor(...“SampleRate”,fs,...'fftlength'FFTLength,...“窗口”,汉恩(弗林斯,'定期'),...“OverlapLength”overlapSamples,...'Barkspectrum',真正的);setExtractorParams (afe'Barkspectrum'“NumBands”numBands,“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

在本例中,通过应用对数对听觉声谱图进行后处理。取小数字的对数会导致四舍五入错误。

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

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

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

对于每个分区,从数据存储中读取,填充信号零,然后提取特征。

parii = 1:numPar subds = partition(adsTrain,numPar,ii);XTrain = 0 (numHops numBands 1,元素个数(subds.Files));为了idx = 1:numel(subds.files)x =读取(subds);Xpadded = [零(楼层((Segmensampless-size(x,1))/ 2),1); x; zeros(ceil(segmentsampless-size(x,1))/ 2),1)];XTrain(:,:,:,IDX)= EXTRIZE(AFE,XPADDED);结尾XTrainC {2} = XTrain;结尾

将输出转换为沿第四维度的听觉光谱图的四维阵列。

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、池);别的numpar = 1;结尾parII = 1:numpar subds = partition(adsvalidation,numpar,ii);xvalidation = zeros(numhops,numband,1,numel(subds.files));为了idx = 1:numel(subds.files)x =读取(subds);Xpadded = [零(楼层((Segmensampless-size(x,1))/ 2),1); x; zeros(ceil(segmentsampless-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])阴影Sound(x,fs)暂停(2)结尾

添加背景噪声数据

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

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

adsBkg = audioDatastore (fullfile (dataFolder'背景') numBkgClips = 4000;如果reduceDataset numBkgClips = numBkgClips/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;为了计数= 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(:,:,:,印第安纳州)=提取(afe x);如果国防部(印第安纳州,1000)= = 0 disp (“加工”+字符串(IND)+“背景剪辑退出”+字符串(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_文件夹仅包含大约五个半分钟的背景噪声,不同数据集中的背景样本高度相关。为了增加背景噪声的变化,您可以创建自己的背景文件并将其添加到文件夹中。为了增加网络的稳健性噪音,您还可以尝试将背景噪声混合到语音文件中。

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)标题(“验证标签分配”的)

定义神经网络架构

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

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

使用加权交叉熵分类损失。权重ClassificationLayer(类别)创建一个自定义分类层,计算交叉熵损失与观测加权classWeights.以与类出现的相同顺序指定类权重分类(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,“填充”“相同”)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 Convolution2Dlayer(3,4 * Numf,“填充”“相同”)BatchnormalizationLayer rululayer MaxPooling2dlayer([timepoolsize,1])oppoutlayer(dropoutprob)全连接列(numclasses)softmaxlayer权重classificationlayer(classweights)];

火车网络

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

miniBatchSize = 128;validationFrequency =地板(元素个数(YTrain) / miniBatchSize);选择= trainingOptions (“亚当”...'italllearnrate',3e-4,...“MaxEpochs”25岁的...“MiniBatchSize”miniBatchSize,...“洗牌”“every-epoch”...'plots'“训练进步”...“详细”假的,...“ValidationData”{XValidation, YValidation},...“ValidationFrequency”validationFrequency,...“LearnRateSchedule”“分段”...'学习ropfactor',0.1,...“LearnRateDropPeriod”,20);

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

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

评估培训网络

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

如果SDENTATASET LOAD(“commandNet.mat”“trainedNet”);结尾yvalpred =分类(Trountainnet,xvalidation);validationError =均值(yvalpred〜= yvalidation);YtrainPred =对(TrountaInnet,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 =“验证数据的混淆矩阵”;厘米。ColumnSummary ='列标准化';cm.rowsummary =“row-normalized”;SortClasses(cm,[命令,“未知”“背景”])

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

信息=谁(“trainedNet”);DISP(“网络大小:”+信息。字节/ 1024 +“kb”的)为了i = 1:100 x = randn([numHops,numBands]);tic [YPredicted,probs] = classification(训练网,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.版权2017年谷歌。Speech Commands Dataset是在Creative Commons Attribution 4.0许可下使用的,可以在这里获得:https://creativeCommons.org/licenses/by/4.0/Legalcode.