基于深度学习的语音指令识别
这个例子展示了如何训练一个深度学习模型来检测音频中语音命令的存在。这个例子使用了语音命令数据集[1]训练卷积神经网络来识别一组给定的命令。
要从头开始训练网络,必须首先下载数据集。如果你不想下载数据集或训练网络,那么你可以加载这个示例提供的预训练网络,并执行示例的下面两部分:使用预训练网络识别命令而且使用来自麦克风的流音频检测命令.
使用预训练网络识别命令
在详细介绍训练过程之前,请使用预先训练好的语音识别网络来识别语音命令。
加载预训练的网络。
负载(“commandNet.mat”)
该网络经过训练,可以识别以下语音命令:是的,没有,向上,下来,左,正确的,在,从,停止,去.
加载一个简短的语音信号,一个人说停止.
[x,fs] = audioread(“stop_command.flac”);
听命令。
声音(x, fs)
预训练网络以基于听觉的频谱图作为输入。首先将语音波形转换为基于听觉的频谱图。
使用函数helperExtractAuditoryFeature
计算听觉谱图。您将在本例后面详细介绍特征提取的细节。
auditorySpect = helperExtractAuditoryFeatures(x,fs);
根据听觉谱图对命令进行分类。
command = classification (trainedNet,auditorySpect)
命令=分类停止
该网络经过训练,将不属于这个集合的单词分类为未知的.
现在您将对一个单词进行分类(玩),该命令未包含在要识别的命令列表中。
首先,加载语音信号并收听它。
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=floor(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;
创建一个timescope
对象以可视化来自麦克风的音频输入。创建一个dsp。MatrixViewer
对象可视化用于进行预测的听觉频谱图。
波绘图仪=时规(...SampleRate = fs,...Title =“…”,...TimeSpanSource =“财产”,...时间间隔= 1,...YLimits = [1],...位置= [600640800340],...TimeAxisLabels =“没有”,...AxesScaling =“手动”);show(wavePlotter) specPlotter = dsp。MatrixViewer (...XDataMode =“自定义”,...AxisOrigin =“左下角”,...位置= [600220800380],...ShowGrid = false,...Title =“…”,...包含=“时间(s)”,...YLabel =“汪汪”(本));显示(specPlotter)
使用麦克风的音频输入执行实时语音命令识别。要无限地运行循环,请设置期限
来正
.若要停止实时检测,请关闭时规和dsp。MatrixViewer
数字。
初始化用于绘图的变量currentTime = 0;colorLimits = [-1,1];timeLimit = 10;抽搐而toc从音频设备中提取音频样本,并将样本添加到%缓冲。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(:)];绘制当前波形和频谱图。wavePlotter (y (end-adr.SamplesPerFrame + 1:结束)specPlotter(规范)现在通过执行阈值操作来进行实际的命令检测。如果满足以下条件,则声明检测并显示在图中:% 1)最常见的标签不是背景。% 2)最新帧标签一致的阈值。% 3)预测标签的最大概率至少是probThreshold。%否则,不声明检测。[YMode,count] = mode(YBuffer);maxProb = max(probBuffer(labels == YMode,:));如果YMode = =“背景”|| count < countThreshold || maxProb < probThreshold wavePlotter. .Title =“…”;specPlotter。Title =“…”;其他的wavePlotter。Title =string(YMode); specPlotter.Title = string(YMode);结束更新用于绘图的变量currentTime = currentTime + adm . samplesperframe /fs;colorLimits = [min([colorLimits(1),min(spec,[],“所有”)))、马克斯([colorLimits (2), max(规范、[]“所有”))));specPlotter。CustomXData = [currentTime-1,currentTime];specPlotter。ColorLimits = ColorLimits;结束发行版(wavePlotter)
发行版(specPlotter)
加载语音命令数据集
本示例使用谷歌语音命令数据集[1].下载数据集并解压缩下载的文件。
downloadFolder = matlab.internal.examples.download万博1manbetxSupportFile(“音频”,“google_speech.zip”);dataFolder = tempdir;unzip(下载文件夹,数据文件夹)dataset = fullfile(数据文件夹,数据文件夹)“google_speech”);
创建培训数据存储
创建一个audioDatastore
它指向训练数据集。
ads = audioDatastore(fullfile(dataset,“训练”),...IncludeSubfolders = true,...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\bhemmat\AppData\Local\Temp\google_speech\train'}标签:[床;床上;床上……和51085更分类]AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFor万博1manbetxmats: ["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下1839上1864右1852停1885未知6490上1843是1860
创建验证数据存储
创建一个audioDatastore
它指向验证数据集。遵循与创建训练数据存储相同的步骤。
ads = audioDatastore(fullfile(dataset,“确认”),...IncludeSubfolders = true,...FileExtensions =“wav”,...LabelSource =“foldernames”)
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\bhemmat\AppData\Local\Temp\google_speech\validation'}标签:[床;床上;床上……和6795更分类]AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFor万博1manbetxmats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav"
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)
ans =11×2表标签计数_______ _____下264走260左247不270 off 256上257右256停止246未知828上260是261
为了用整个数据集训练网络并达到尽可能高的精度,设置speedupExample
来假
.要快速运行此示例,请设置speedupExample
来真正的
.
speedupExample =假;如果speedupExample 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 = true);setExtractorParameters (afe“barkSpectrum”NumBands = NumBands WindowNormalization = false);
从数据集中读取文件。训练卷积神经网络需要输入一致的大小。数据集中的一些文件长度小于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(版本(“平行”)) && ~speedupExample 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 = 25028
根据窗口功率缩放特征,然后记录日志。为了获得分布更平滑的数据,使用小偏移量对谱图取对数。
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);
分离训练标签和验证标签。删除空类别。
TTrain = removecats(adsTrain.Labels);TValidation = removecats(adsValidation.Labels);
可视化数据
绘制一些训练样本的波形和听觉谱图。播放相应的音频片段。
specMin = min(XTrain,[],“所有”);specMax = max(XTrain,[],“所有”);idx = randperm(numel(adsTrain.Files),3);图(单位=“归一化”,位置=[0.2 0.2 0.6 0.6]);为ii = 1:3 [x,fs] = audioread(adsTrain.Files{idx(ii)});Subplot (2,3,ii) plot(x)轴紧title(string(adsTrain.Labels(idx(ii)))) subplot(2,3,ii+3) spect = XTrain(:,:,1,idx(ii)))';pcolor(spect) caxis([specMin specMax])着色平声音(x, fs)暂停(2)结束
添加背景噪声数据
该网络不仅必须能够识别不同的口语,还必须能够检测输入是否包含静音或背景噪声。
使用背景文件夹中的音频文件创建一秒钟背景噪音剪辑的样本。从每个背景噪声文件中创建相同数量的背景剪辑。您还可以创建自己的背景噪音录音,并将其添加到背景文件夹。在计算声谱图之前,该函数用对数均匀分布在给定范围内采样的因子重新缩放每个音频剪辑volumeRange
.
adsBkg = audioDatastore(fullfile(dataset,“背景”))
adsBkg = audioDatastore属性:Files:{'…\AppData\Local\Temp\google_speech\background\doing_the_dish .wav';’……当地\ Temp \ bhemmat \ AppData \ \ google_speech \背景\ dude_miaowing.wav”;\bhemmat\AppData\Local\Temp\google_speech\background\exercise_bike.wav…{'C:\Users\bhemmat\AppData\Local\Temp\google_speech\background'} AlternateFileSystemRoots: {} OutputDataType: 'double' Labels: {} SupportedOutputFormats: 万博1manbetx["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav"
numkgclips = 4000;如果speedupExample 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);如果Mod (ind,1000)==0 progress =“加工”+ string(ind) +“背景剪辑取出”+字符串(numBkgClips)结束Ind = Ind + 1;结束结束
progress = "处理了4000个背景片段中的1000个"
progress = "处理了4000个背景剪辑中的2000个"
progress = "处理了4000个背景片段中的3000个"
progress = "处理4000个背景剪辑中的4000个"
Xbkg = log10(Xbkg + epsil);
在训练集、验证集和测试集之间分割背景噪声的频谱图。因为背景噪声文件夹只包含大约5分半钟的背景噪声,所以不同数据集中的背景样本是高度相关的。要增加背景噪声的变化,您可以创建自己的背景文件并将其添加到文件夹中。为了提高网络对噪声的鲁棒性,您还可以尝试将背景噪声混合到语音文件中。
numTrainBkg =地板(0.85* numkgclips);numValidationBkg =地板(0.15* numkgclips);XTrain(:,:,: + 1:终端+ numTrainBkg) = Xbkg (:,:,:, 1: numTrainBkg);TTrain(+ 1:结束+ numTrainBkg) =“背景”;XValidation(:,:,:,end+1:end+numValidationBkg) = Xbkg(:,:,:,numTrainBkg+1:end);TValidation(+ 1:结束+ numValidationBkg) =“背景”;
绘制训练集和验证集中不同类别标签的分布。
图(单位=“归一化”,Position=[0.2 0.2 0.5 0.5]) tiledlayout(2,1) nexttile直方图(TTrain) title(“培训标签发放”nexttile直方图(TValidation)“验证标签分发”)
定义神经网络架构
创建一个简单的网络体系结构作为层的数组。使用卷积和批处理归一化层,并使用最大池化层在“空间上”(即在时间和频率上)对特征映射进行下采样。添加最后的最大池化层,随着时间的推移将输入特征映射全局池化。这加强了输入频谱图中的(近似)时间平移不变性,允许网络执行相同的分类,而不依赖于语音在时间上的确切位置。全局池化还显著减少了最终全连接层中的参数数量。为了降低网络记忆训练数据特定特征的可能性,在最后一个全连接层的输入中加入少量的dropout。
该网络很小,因为它只有5个卷积层和很少的滤波器。numF
控制卷积层中的过滤器数量。为了提高网络的准确性,可以尝试通过添加卷积、批处理归一化和ReLU层的相同块来增加网络深度。你也可以尝试增加卷积滤波器的数量numF
.
为了让每个类在损失中拥有相同的总权重,使用与每个类中的训练示例数量成反比的类权重。当使用Adam优化器训练网络时,训练算法独立于类权值的整体归一化。
类=类别(TTrain);classWeights = 1./countcats(TTrain);classWeights = classWeights'/mean(classWeights);numClasses = numel(类别(TTrain));timePoolSize = ceil(numHops/8);dropoutProb = 0.2;numF = 12;layers = [imageInputLayer([numHops numBands]) convolution2dLayer(3,numF,Padding=“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,Stride=2,Padding=“相同”) convolution2dLayer (3 2 * numF填充=“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,Stride=2,Padding=“相同”) convolution2dLayer(3、4 * numF填充=“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,Stride=2,Padding=“相同”) convolution2dLayer(3、4 * numF填充=“相同”) batchNormalizationLayer reluLayer convolution2dLayer(3,4*numF,Padding=“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer([timePoolSize,1]) dropoutLayer(dropoutProb) fullyConnectedLayer(numClasses) softmaxLayer classificationLayer(Classes= Classes,ClassWeights= ClassWeights)];
列车网络的
指定培训选项。使用Adam优化器,迷你批量大小为128。训练25个周期,并在20个周期后将学习率降低10倍。
miniBatchSize = 128;validationFrequency = floor(数字(TTrain)/miniBatchSize);选项= trainingOptions(“亚当”,...InitialLearnRate = 3的军医,...MaxEpochs = 25,...MiniBatchSize = MiniBatchSize,...洗牌=“every-epoch”,...情节=“训练进步”,...Verbose = false,...ValidationData = {XValidation, TValidation},...ValidationFrequency = ValidationFrequency,...LearnRateSchedule =“分段”,...LearnRateDropFactor = 0.1,...LearnRateDropPeriod = 20);
培训网络。如果你没有GPU,那么训练网络可能需要时间。
trainedNet = trainNetwork(XTrain,TTrain,图层,选项);
评估训练网络
计算网络在训练集(没有数据增强)和验证集上的最终精度。网络在这个数据集上非常准确。然而,训练、验证和测试数据都有类似的分布,不一定反映现实环境。这一限制尤其适用于未知的
类别,它只包含少量单词的发音。
如果speedupExample负载(“commandNet.mat”,“trainedNet”);结束YValPred =分类(trainedNet,XValidation);validationError = mean(YValPred ~= TValidation);YTrainPred =分类(trainedNet,XTrain);trainError = mean(YTrainPred ~= TTrain);disp (“训练错误:”+ trainError*100 +“%”)
训练误差:1.5794%
disp ("验证错误:"+ validationError*100 +“%”)
验证错误:4.6692%
绘制混淆矩阵。通过使用列和行摘要显示每个类的精度和召回率。对混淆矩阵的类进行排序。最大的混淆是不认识的单词和命令之间,向上而且从,下来而且没有,去而且没有.
图(单位=“归一化”,Position=[0.2 0.2 0.5 0.5]);cm = confusichart (TValidation,YValPred,...Title =验证数据混淆矩阵,...ColumnSummary =“column-normalized”RowSummary =“row-normalized”);sortClasses(厘米,[命令,“未知”,“背景”])
当处理具有受限硬件资源的应用程序(如移动应用程序)时,请考虑可用内存和计算资源的限制。以千字节为单位计算网络的总大小,并在使用CPU时测试其预测速度。预测时间是对单个输入图像进行分类的时间。如果向网络输入多个图像,则可以同时对这些图像进行分类,从而缩短每张图像的预测时间。然而,在对流媒体音频进行分类时,单幅图像预测时间是最相关的。
信息= whoos (“trainedNet”);disp (“网络规模:”+信息。字节/ 1024 +“知识库”)
网络大小:292.2139 kB
时间= 0 (100,1);为ii = 1:100 x = randn([numHops,numBands]);tic [ypredict,probs] = category (trainedNet,x,ExecutionEnvironment=“cpu”);时间(ii) = toc;结束disp (CPU单图像预测时间:+ mean(time(11:end))*1000 +“女士”)
CPU单图像预测时间:2.4838 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.