这个例子展示了如何训练一个深度学习模型来检测音频中语音命令的存在。该示例使用语音命令数据集[1]来训练卷积神经网络来识别给定的命令集。
要从头开始训练网络,必须首先下载数据集。如果你不想下载数据集或训练网络,那么你可以加载这个示例提供的预训练网络,并执行示例的下面两部分:用预先训练好的网络识别命令而且使用来自麦克风的流音频检测命令.
在详细介绍训练过程之前,您将使用预先训练好的语音识别网络来识别语音命令。
加载预训练的网络。
负载(“commandNet.mat”)
该网络经过训练,可以识别以下语音命令:
“是的”
“不”
《飞屋环游记》
“向下”
“左”
“正确”
“上”
“关闭”
“停止”
“走”
加载一个简短的语音信号,其中一个人说“停止”。
[x,fs] = audioread(“stop_command.flac”);
听命令。
声音(x, fs)
预训练网络以基于听觉的频谱图作为输入。首先将语音波形转换为基于听觉的频谱图。
使用函数extractAuditoryFeature
计算听觉谱图。您将在本例后面详细介绍特征提取的细节。
auditorySpect = helperExtractAuditoryFeatures(x,fs);
根据听觉谱图对命令进行分类。
command = classification (trainedNet,auditorySpect)
命令=绝对停止
该网络经过训练,将不属于这个集合的单词分类为“未知”。
现在,您将对未包含在要识别的命令列表中的单词(“play”)进行分类。
加载语音信号并收听它。
X = audieread (“play_command.flac”);声音(x, fs)
计算听觉谱图。
auditorySpect = helperExtractAuditoryFeatures(x,fs);
将信号分类。
command = classification (trainedNet,auditorySpect)
命令=绝对未知
该网络经过训练,可以将背景噪声分类为“背景”。
创建一个由随机噪声组成的一秒信号。
X = pinknoise(16e3);
计算听觉谱图。
auditorySpect = helperExtractAuditoryFeatures(x,fs);
分类背景噪声。
command = classification (trainedNet,auditorySpect)
命令=分类背景
在麦克风的流音频上测试预先训练好的命令检测网络。例如,试着说出其中的一个命令,是的,没有,或停止.然后,试着说出其中一个不认识的单词,比如马文,希拉,床上,房子,猫,鸟,或从0到9的任何数字。
指定以Hz为单位的分类速率,并创建一个可以从麦克风读取音频的音频设备阅读器。
classificationRate = 20;adr = audioDeviceReader(“SampleRate”fs,“SamplesPerFrame”、地板(fs / classificationRate));
初始化音频缓冲区。提取网络的分类标签。为流音频的标签和分类概率初始化半秒的缓冲区。使用这些缓冲区比较较长时间内的分类结果,并在检测到命令时构建“一致”。为决策逻辑指定阈值。
audioBuffer = dsp.AsyncBuffer(fs);labels = trainedNet.Layers(end).Classes;YBuffer(1:classificationRate/2) = categorical(“背景”);probBuffer = 0 ([number (labels),classificationRate/2]);countThreshold = ceil(classificationRate*0.2);probThreshold = 0.7;
创建一个图形并检测命令,只要创建的图形存在。要无限地运行循环,请设置期限
来正
.要停止活体检测,只需关闭图形。
H =数字(“单位”,“归一化”,“位置”,[0.2 0.1 0.6 0.8]);timeLimit = 20;抽搐而ishandle(h) && toc < timeLimit从音频设备中提取音频样本,并将样本添加到%缓冲。X = adr();写(audioBuffer x);y = read(audioBuffer,fs,fs- adm . samplesperframe);spec = helperExtractAuditoryFeatures(y,fs);对当前谱图进行分类,将标签保存到标签缓冲区,%,并将预测的概率保存到概率缓冲区中。[ypredict,probs] = category (trainedNet,spec,“ExecutionEnvironment”,“cpu”);YBuffer = [YBuffer(2:end), yexpected];probBuffer = [probBuffer(:,2:end),probs(:)];绘制当前波形和频谱图。Subplot (2,1,1) plot(y)轴紧Ylim ([-1,1]) subplot(2,1,2) pcolor(spec') caxis([-4 2.6445])底纹平现在通过执行一个非常简单的命令来做实际的检测%阈值操作。方法中声明检测并将其显示如果以下所有情况都符合,则%图形标题:1)最常见的标签%不是背景。2)至少count最新帧的阈值%标签同意。3)预测标签的最大概率为% least probThreshold。否则,不声明检测。[YMode,count] = mode(YBuffer);maxProb = max(probBuffer(labels == YMode,:));次要情节(2,1,1)如果YMode = =“背景”|| count < countThreshold || maxProb < probThreshold title(”“)其他的标题(string (YMode),“字形大小”, 20)结束drawnow结束
本示例使用谷歌语音命令数据集[1]。下载数据集并解压缩下载的文件。将PathToDatabase设置为数据的位置。
url =“https://ssd.mathworks.com/金宝appsupportfiles/audio/google_speech.zip”;downloadFolder = tempdir;dataFolder = fullfile(下载文件夹,“google_speech”);如果~存在(dataFolder“dir”) disp (“正在下载数据集(1.4 GB)…”解压缩(url, downloadFolder)结束
创建一个audioDatastore
(音频工具箱)它指向训练数据集。
ads = audioDatastore(fullfile(数据文件夹,“火车”),...“IncludeSubfolders”,真的,...“FileExtensions”,“wav”,...“LabelSource”,“foldernames”)
ads = audioDatastore属性:Files:{'…\AppData\Local\Temp\google_speech\train\bed\00176480_nohash_0.wav';’……\ AppData \当地\ Temp \床google_speech \培训\ \ 004 ae714_nohash_0.wav;\AppData\Local\Temp\google_speech\train\bed\004ae714_nohash_1.wav'…文件夹:{'C:\Users\jibrahim\AppData\Local\Temp\google_speech\train'}标签:[床;床上;床上……和51085更分类]AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFor金宝appmats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav"
指定希望模型识别为命令的单词。将所有不是命令的单词标记为未知的
.把不是命令的词标记为未知的
创建一组单词,它近似于除命令之外的所有单词的分布。网络使用这个组来学习命令和所有其他单词之间的区别。
为了减少已知和未知单词之间的类不平衡,加快处理速度,在训练集中只包含一部分未知单词。
使用子集
(音频工具箱)创建一个仅包含命令和未知单词子集的数据存储。计算属于每个类别的例子的数量。
命令= category ([“是的”,“不”,《飞屋环游记》,“向下”,“左”,“正确”,“上”,“关闭”,“停止”,“走”]);isCommand = ismember(ads.Labels,commands);isUnknown = ~isCommand;includeffraction = 0.2;mask = rand(number (ads.Labels),1) < includeFraction;isUnknown = isUnknown &掩码;ads.Labels(isUnknown) =分类的(“未知”);adsTrain =子集(ads,isCommand|isUnknown);countEachLabel (adsTrain)
ans = 11×2表标签计数_______ _____向下1842走1861左1839没有1853 off 1839上1864右1852停止1885未知6483向上1843是1860
创建一个audioDatastore
(音频工具箱)它指向验证数据集。遵循与创建训练数据存储相同的步骤。
ads = audioDatastore(fullfile(数据文件夹,“验证”),...“IncludeSubfolders”,真的,...“FileExtensions”,“wav”,...“LabelSource”,“foldernames”isCommand = ismember(ads.Labels,commands);isUnknown = ~isCommand;includeffraction = 0.2;mask = rand(number (ads.Labels),1) < includeFraction;isUnknown = isUnknown &掩码;ads.Labels(isUnknown) =分类的(“未知”);adsValidation =子集(ads,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\Local\Temp\google_speech\validation\bed\060cd039_nohash_1.wav…{'C:\Users\jibrahim\AppData\Local\Temp\google_speech\validation'}标签:[床;床上;床上……和6795更多分类]AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFo金宝apprmats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav" ans = 11×2表标签计数_______ _____下264走260左247无270 off 256上257右256停止246未知850上260 yes 261
为了用整个数据集训练网络并达到尽可能高的精度,设置reduceDataset
来假
.要快速运行此示例,请设置reduceDataset
来真正的
.
reduceDataset = false;如果reduceDataset numUniqueLabels = numel(unique(adsTrain.Labels));将数据集减少20倍adsTrain = splitEachLabel(adsTrain,round(numel(adsTrain. files) / numUniqueLabels / 20));adsValidation = splitEachLabel(adsValidation,round(numel(adsValidation. files) / numUniqueLabels / 20));结束
为了为卷积神经网络的有效训练准备数据,将语音波形转换为基于听觉的频谱图。
定义特征提取的参数。segmentDuration
是每个演讲剪辑的持续时间(以秒为单位)。frameDuration
是用于频谱计算的每帧的持续时间。hopDuration
是每个频谱之间的时间步长。numBands
是听觉谱图中滤波器的数量。
创建一个audioFeatureExtractor
(音频工具箱)对象执行特征提取。
Fs = 16e3;%数据集的已知抽样率。segmentDuration = 1;frameDuration = 0.025;hopDuration = 0.010;segmentSamples = round(segmentDuration*fs);frameSamples = round(frameDuration*fs);hopSamples = round(hopDuration*fs);overlapSamples = framessamples - hopSamples;FFTLength = 512;numBands = 50;afe = audioFeatureExtractor(...“SampleRate”fs,...“FFTLength”FFTLength,...“窗口”损害(frameSamples“周期”),...“OverlapLength”overlapSamples,...“barkSpectrum”,真正的);setExtractorParams (afe“barkSpectrum”,“NumBands”numBands,“WindowNormalization”、假);
从数据集中读取文件。训练卷积神经网络需要输入一致的大小。数据集中的一些文件长度小于1秒。对音频信号的前面和后面应用零填充,使其具有长度segmentSamples
.
x = read(adsTrain);numSamples = size(x,1);numToPadFront = floor((segmentSamples - numSamples)/2);numToPadBack = cell ((segmentSamples - numSamples)/2);xpadding = [0 (numToPadFront,1,“喜欢”, x); x; 0 (numToPadBack 1“喜欢”, x));
要提取音频特征,请呼叫提取
.输出是跨行时间的Bark频谱。
features = extract(afe, xpadding);[numHops,numFeatures] = size(features)
numHops = 98 numFeatures = 50
在本例中,通过应用对数对听觉频谱图进行后处理。取小数的对数可能导致舍入错误。
为了加快处理速度,您可以使用parfor
.
首先,确定数据集的分区数量。如果没有并行计算工具箱™,请使用单个分区。
如果~ isempty(版本(“平行”)) && ~reduceDataset pool = gcp;numPar = numpartitions(adsTrain,pool);其他的numPar = 1;结束
对于每个分区,从数据存储中读取,对信号进行零填充,然后提取特征。
parforii = 1:numPar subds =分区(adsTrain,numPar,ii);XTrain = 0 (numHops,numBands,1,numel(subds.Files));为idx = 1: number (subds. files) x = read(subds);xpadding = [zeros(ground (segmentSamples-size(x,1))/2),1);x;zeros(ceil(segmentSamples-size(x,1))/2),1)];XTrain(:,:,:,idx) = extract(afe, xpadding);结束XTrainC{ii} = XTrain;结束
将输出转换为带有沿第四维听觉频谱图的四维阵列。
XTrain = cat(4,XTrainC{:});[numHops,numBands,numChannels,numSpec] = size(XTrain)
numHops = 98 numBands = 50 numChannels = 1 numSpec = 25021
根据窗口功率缩放特征,然后记录日志。为了获得分布更平滑的数据,使用小偏移量对谱图取对数。
Epsil = 1e-6;XTrain = log10(XTrain + epsil);
对验证集执行上述特征提取步骤。
如果~ isempty(版本(“平行”)) pool = gcp;numPar = numpartitions(adsValidation,pool);其他的numPar = 1;结束parforii = 1:numPar subds =分区(adsValidation,numPar,ii);XValidation = 0 (numHops,numBands,1, nummel (subds.Files));为idx = 1: number (subds. files) x = read(subds);xpadding = [zeros(ground (segmentSamples-size(x,1))/2),1);x;zeros(ceil(segmentSamples-size(x,1))/2),1)];XValidation(:,:,:,idx) = extract(afe, xpadding);结束XValidationC{ii} = XValidation;结束XValidation = cat(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)});Subplot (2,3,i) plot(x)轴紧标题(string (adsTrain.Labels (idx (i))))次要情节(2 3 i + 3) spect = (XTrain (:,: 1, idx(我)');pcolor(spect) caxis([specMin specMax])着色平声音(x, fs)暂停(2)结束
该网络不仅必须能够识别不同的口语,还必须能够检测输入是否包含静音或背景噪声。
中的音频文件_background
_文件夹创建一秒背景噪音剪辑的样本。从每个背景噪声文件中创建相同数量的背景剪辑。您还可以创建自己的背景噪音记录,并将其添加到_background
_文件夹。在计算声谱图之前,该函数用对数均匀分布在给定范围内采样的因子重新缩放每个音频剪辑volumeRange
.
adsBkg = audioDatastore(fullfile(dataFolder,“背景”)) numkgclips = 4000;如果reducedatset numkgclips = numkgclips /20;结束volumeRange = log10([1e-4,1]);numkgfiles = nummel (adsBkg.Files);numClipsPerFile = histcounts(1: numkgclips,linspace(1, numkgclips, numkgfiles +1));Xbkg = 0 (size(XTrain,1),size(XTrain,2),1, numkgclips,“单一”);bkgAll = readall(adsBkg);Ind = 1;为count = 1: numbergfiles 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) = extract(afe,x);如果国防部(印第安纳州,1000)= = 0 disp (“加工”+ string(ind) +“背景剪辑取出”+字符串(numBkgClips))结束Ind = Ind + 1;结束结束Xbkg = log10(Xbkg + epsil);
adsBkg = audioDatastore属性:Files:{'…\AppData\Local\Temp\google_speech\background\doing_the_dish .wav';“…\ AppData \当地背景\ Temp \ google_speech \ \ dude_miaowing.wav;\AppData\Local\Temp\google_speech\background\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
_文件夹只包含大约5分半钟的背景噪声,不同数据集中的背景样本是高度相关的。要增加背景噪声的变化,您可以创建自己的背景文件并将其添加到文件夹中。为了提高网络对噪声的鲁棒性,您还可以尝试将背景噪声混合到语音文件中。
numTrainBkg =地板(0.85* numkgclips);numValidationBkg =地板(0.15* numkgclips);XTrain(:,:,: + 1:终端+ numTrainBkg) = Xbkg (:,:,:, 1: numTrainBkg);YTrain(+ 1:结束+ numTrainBkg) =“背景”;XValidation(:,:,:,end+1:end+numValidationBkg) = Xbkg(:,:,:,numTrainBkg+1:end);YValidation(+ 1:结束+ numValidationBkg) =“背景”;
绘制训练集和验证集中不同类别标签的分布。
图(“单位”,“归一化”,“位置”,[0.2 0.2 0.5 0.5])子图(2,1,1)直方图(YTrain)标题(“培训标签发放”) subplot(2,1,2)直方图(YValidation)“验证标签分发”)
创建一个简单的网络体系结构作为层的数组。使用卷积和批处理归一化层,并使用最大池化层在“空间上”(即在时间和频率上)对特征映射进行下采样。添加最后的最大池化层,随着时间的推移将输入特征映射全局池化。这加强了输入频谱图中的(近似)时间平移不变性,允许网络执行相同的分类,而不依赖于语音在时间上的确切位置。全局池化还显著减少了最终全连接层中的参数数量。为了降低网络记忆训练数据特定特征的可能性,在最后一个全连接层的输入中加入少量的dropout。
该网络很小,因为它只有5个卷积层和很少的滤波器。numF
控制卷积层中的过滤器数量。为了提高网络的准确性,可以尝试通过添加卷积、批处理归一化和ReLU层的相同块来增加网络深度。你也可以尝试增加卷积滤波器的数量numF
.
使用加权交叉熵分类损失。weightedClassificationLayer (classWeights)
创建一个自定义分类层,计算交叉熵损失与观测加权classWeights
.按照类出现的顺序指定类的权重类别(YTrain)
.为了让每个类在损失中拥有相同的总权重,使用与每个类中的训练示例数量成反比的类权重。当使用Adam优化器训练网络时,训练算法独立于类权值的整体归一化。
classWeights = 1./countcats(YTrain);classWeights = classWeights'/mean(classWeights);numClasses = nummel(类别(YTrain));timePoolSize = ceil(numHops/8);dropoutProb = 0.2;numF = 12;layers = [imageInputLayer([numHops numBands]) convolution2dLayer(3,numF, numF)“填充”,“相同”maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer (3 2 * numF“填充”,“相同”maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer(3、4 * numF,“填充”,“相同”maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer(3、4 * numF,“填充”,“相同”(3,4*numF,“填充”,“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer([timePoolSize,1]) dropoutLayer(dropoutProb) fullyConnectedLayer(numClasses) softmaxLayer weightedClassificationLayer(classWeights)];
指定培训选项。使用Adam优化器,迷你批量大小为128。训练25个周期,并在20个周期后将学习率降低10倍。
miniBatchSize = 128;validationFrequency = floor(编号(YTrain)/miniBatchSize);选项= trainingOptions(“亚当”,...“InitialLearnRate”3的军医,...“MaxEpochs”25岁的...“MiniBatchSize”miniBatchSize,...“洗牌”,“every-epoch”,...“阴谋”,“训练进步”,...“详细”假的,...“ValidationData”{XValidation, YValidation},...“ValidationFrequency”validationFrequency,...“LearnRateSchedule”,“分段”,...“LearnRateDropFactor”, 0.1,...“LearnRateDropPeriod”, 20);
培训网络。如果你没有GPU,那么训练网络可能需要时间。
trainedNet = trainNetwork(XTrain,YTrain,图层,选项);
计算网络在训练集(没有数据增强)和验证集上的最终精度。网络在这个数据集上非常准确。然而,训练、验证和测试数据都有类似的分布,不一定反映现实环境。这一限制尤其适用于未知的
类别,它只包含少量单词的发音。
如果reduceDataset负载(“commandNet.mat”,“trainedNet”);结束YValPred =分类(trainedNet,XValidation);validationError = mean(YValPred ~= YValidation);YTrainPred =分类(trainedNet,XTrain);trainError = mean(YTrainPred ~= YTrain);disp (“训练错误:”+ trainError*100 +“%”) disp ("验证错误:"+ validationError*100 +“%”)
训练误差:1.907%验证误差:5.5376%
绘制混淆矩阵。通过使用列和行摘要显示每个类的精度和召回率。对混淆矩阵的类进行排序。最大的混淆是不认识的单词和命令之间,向上而且从,下来而且没有,去而且没有.
图(“单位”,“归一化”,“位置”,[0.2 0.2 0.5 0.5]);cm = confusionchart(YValidation,YValPred);厘米。Title =验证数据混淆矩阵;厘米。ColumnSummary =“column-normalized”;厘米。RowSummary =“row-normalized”;sortClasses(厘米,[命令,“未知”,“背景”])
当处理具有受限硬件资源的应用程序(如移动应用程序)时,请考虑可用内存和计算资源的限制。以千字节为单位计算网络的总大小,并在使用CPU时测试其预测速度。预测时间是对单个输入图像进行分类的时间。如果向网络输入多个图像,则可以同时对这些图像进行分类,从而缩短每张图像的预测时间。然而,在对流媒体音频进行分类时,单幅图像预测时间是最相关的。
信息= whoos (“trainedNet”);disp (“网络规模:”+信息。字节/ 1024 +“知识库”)为i = 1:100 x = randn([numHops,numBands]);* * * * * * * * * * * * * * * * * * * *“ExecutionEnvironment”,“cpu”);时间(i) = toc;结束disp (CPU单图像预测时间:+ mean(time(11:end))*1000 +“女士”)
网络大小:286.7402 kB CPU上的单幅图像预测时间:2.5119 ms
狱长P。“语音命令:用于单词语音识别的公共数据集”,2017年。可以从https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz.版权所有谷歌2017。语音命令数据集是在创作共用属性4.0许可下授权的,可在这里获得:https://creativecommons.org/licenses/by/4.0/legalcode.
狱长P。“语音命令:用于单词语音识别的公共数据集”,2017年。可以从http://download.tensorflow.org/data/speech_commands_v0.01.tar.gz.版权所有谷歌2017。语音命令数据集是在创作共用属性4.0许可下授权的,可在这里获得:https://creativecommons.org/licenses/by/4.0/legalcode.
analyzeNetwork
|分类
|trainNetwork