基于小波散射和深度学习的语音数字识别
这个例子展示了如何使用机器和深度学习技术对语音数字进行分类。在本例中,您使用支持向量机(SVM)和长短期记忆(LSTM)网络的小波时间散射来执行分类。金宝app您还可以应用贝叶斯优化来确定合适的超参数,以提高LSTM网络的准确性。此外,该示例说明了使用深度卷积神经网络(CNN)和mel频率谱图的方法。
数据
克隆或下载免费语音数字数据集(FSDD),可在https://github.com/Jakobovski/free-spoken-digit-dataset获得。FSDD是一个开放的数据集,这意味着它可以随着时间的推移而增长。本例使用的是2019年1月29日提交的版本,该版本由2000个从4个说话者处获得的数字0到9的英语录音组成。在这个版本中,两个说话的人是母语为美国英语的人,一个说话的人是非母语为英语的人,带有比利时法语口音,还有一个说话的人是非母语为英语的人,带有德国口音。数据采样频率为8000hz。
使用audioDatastore
管理数据访问,确保将记录随机划分为训练集和测试集。设置位置
属性设置为计算机上FSDD录音文件夹的位置,例如:
pathToRecordingsFolder = fullfile(tempdir,“free-spoken-digit-dataset-master”,“录音”);location = pathToRecordingsFolder;
点audioDatastore
去那个地方。
ads = audioDatastore(location);
辅助函数helpergenLabels
从FSDD文件创建标签分类数组。的源代码helpergenLabels
列在附录中。列出类别和每个类别的示例数量。
ads. labels = helpergenLabels(ads);总结(ads.Labels)
0 300 1 300 2 300 3 300 4 300 5 300 6 300 7 300 8 300 9 300
FSDD数据集由10个平衡的类组成,每个类有200条记录。FSDD中的录音时间不等。FSDD不是特别大,所以读取FSDD文件并构建信号长度的直方图。
LenSig = 0 (numel(ads.Files),1);Nr = 1;而Hasdata (ads) = read(ads);LenSig(nr) = nummel (digit);Nr = Nr +1;结束重置(ads)直方图(LenSig)网格在包含(“信号长度(样本)”) ylabel (“频率”)
直方图显示,记录长度的分布正偏斜。对于分类,本例使用8192个样本的公共信号长度,这是一个保守值,可以确保截断较长的录音不会切断语音内容。如果信号的长度大于8192个样本(1.024秒),则记录被截断为8192个样本。如果信号的长度小于8192个样本,则对信号进行预加和后加,并以零为零对称地延长到8192个样本的长度。
小波时间散射
使用waveletScattering
利用0.22秒不变尺度建立小波时间散射框架。在本例中,通过对所有时间样本的散射变换取平均值来创建特征向量。要使每个时间窗有足够数量的散射系数平均,设置OversamplingFactor
到2,使每条路径的散射系数数量相对于临界下采样值增加四倍。
sf =小波散射“SignalLength”, 8192,“InvarianceScale”, 0.22,…“SamplingFrequency”, 8000,“OversamplingFactor”2);
将FSDD拆分为训练集和测试集。将80%的数据分配给训练集,保留20%的数据用于测试集。训练数据用于训练基于散射变换的分类器。测试数据用于验证模型。
rng默认的;Ads =洗牌(Ads);[adsTrain,adsTest] = splitachlabel (ads,0.8);countEachLabel (adsTrain)
ans =10×2表标签计数_____ _____ 0 240 1 240 2 240 3 240 4 240 5 240 6 240 7 240 8 240 9 240
countEachLabel (adsTest)
ans =10×2表标签计数_____ _____ 0 60 1 60 2 60 3 60 4 60 5 60 6 60 7 60 8 60 9 60
辅助函数helperReadSPData
截断或填充数据到8192的长度,并按其最大值规范化每个记录。的源代码helperReadSPData
列在附录中。创建一个8192 × 1600的矩阵,其中每一列都是一个语音数字记录。
Xtrain = [];scatds_Train = transform(adsTrain,@(x)helperReadSPData(x));而hasdata(scatds_Train) = read(scatds_Train);Xtrain = cat(2,Xtrain, smart);结束
对测试集重复此过程。得到的矩阵是8192 × 400。
Xtest = [];scatds_Test = transform(adsTest,@(x)helperReadSPData(x));而hasdata(scatds_Test) = read(scatds_Test);Xtest = cat(2,Xtest,smat);结束
将小波散射变换应用于训练集和测试集。
Strain = sf.featureMatrix(Xtrain);Stest = sf.featureMatrix(Xtest);
获得训练集和测试集的平均散射特征。排除零阶散射系数。
TrainFeatures = Strain(2:end,:,:);TrainFeatures = squeeze(mean(TrainFeatures,2))';TestFeatures = Stest(2:end,:,:);TestFeatures = squeeze(mean(TestFeatures,2))';
支持向量机分类器
现在数据已经被简化为每个记录的特征向量,下一步是使用这些特征对记录进行分类。创建一个具有二次多项式核的SVM学习模板。将SVM与训练数据拟合。
template = templateSVM(…“KernelFunction”,多项式的,…“PolynomialOrder”2,…“KernelScale”,“汽车”,…“BoxConstraint”, 1…“标准化”,真正的);classificationSVM = fitcecoc(…TrainFeatures,…adsTrain。标签,…“学习者”模板,…“编码”,“onevsone”,…“类名”分类({' 0 ';' 1 ';' 2 ';“3”;“4”;“5”;“6”;“7”;“8”;“9”}));
基于训练数据,使用k-fold交叉验证来预测模型的泛化精度。将训练集分成五组。
partitionedModel = crossval(分类svm;“KFold”5);[validationPredictions, validationScores] = kfoldPredict(partitionedModel);validationAccuracy = (1 - kfoldLoss(partitionedModel,“LossFun”,“ClassifError”)) * 100
validationAccuracy = 97.4167
估计的泛化精度约为97%。使用训练好的支持向量机来预测测试集中的辐条数字类。
predLabels = predict(classiationsvm,TestFeatures);testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 97.1667
用混淆图总结模型在测试集上的表现。通过使用列和行摘要显示每个类的精度和召回率。混淆图底部的表格显示了每个类的精度值。混淆图右侧的表格显示了召回值。
图(“单位”,“归一化”,“位置”,[0.2 0.2 0.5 0.5]);ccscat = confusionchart(adest . labels,predLabels);ccscat。Title =“小波散射分类”;ccscat。ColumnSummary =“column-normalized”;ccscat。RowSummary =“row-normalized”;
散射变换结合SVM分类器对测试集中的语音数字进行分类,准确率为98%(错误率为2%)。
长短期记忆(LSTM)网络
LSTM网络是一种递归神经网络(RNN)。rnn是专门用于处理顺序或时间数据(如语音数据)的神经网络。由于小波散射系数是序列,它们可以作为LSTM的输入。通过使用散射特征而不是原始数据,您可以减少网络需要学习的可变性。
修改训练和测试散射特征,以便与LSTM网络一起使用。排除零阶散射系数并将特征转换为单元阵列。
TrainFeatures = Strain(2:end,:,:);TrainFeatures = squeeze(num2cell(TrainFeatures,[1 2]));TestFeatures = Stest(2:end,:,:);TestFeatures = squeeze(num2cell(TestFeatures, [1 2]));
构造一个具有512个隐藏层的简单LSTM网络。
[inputSize, ~] = size(TrainFeatures{1});YTrain = adsTrain.Labels;numHiddenUnits = 512;numClasses = nummel (unique(YTrain));图层= […sequenceInputLayer inputSize lstmLayer (numHiddenUnits,“OutputMode”,“最后一次”) fulllyconnectedlayer (numClasses) softmaxLayer classificationLayer];
设置超参数。使用Adam优化和50个小批量大小。设置最大epoch数为300。使用1e-4的学习率。如果不想使用图跟踪进度,可以关闭训练进度图。如果有GPU,训练默认使用GPU。否则,使用CPU。有关更多信息,请参见trainingOptions
(深度学习工具箱)。
maxEpochs = 300;miniBatchSize = 50;options = trainingOptions(“亚当”,…“InitialLearnRate”, 0.0001,…“MaxEpochs”maxEpochs,…“MiniBatchSize”miniBatchSize,…“SequenceLength”,“最短”,…“洗牌”,“every-epoch”,…“详细”假的,…“阴谋”,“训练进步”);
训练网络。
net = trainNetwork(TrainFeatures,YTrain,layers,options);
predLabels = classification (net,TestFeatures);testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 96.3333
贝叶斯优化
确定合适的超参数设置通常是训练深度网络中最困难的部分之一。为了减轻这种情况,您可以使用贝叶斯优化。在本例中,您将使用贝叶斯技术优化隐藏层的数量和初始学习率。创建一个新目录来存储mat文件,其中包含有关超参数设置和网络的信息以及相应的错误率。
YTrain = adsTrain.Labels;YTest = adest . labels;如果~ (”结果/”,“dir”mkdir)结果结束
初始化待优化的变量及其取值范围。因为隐藏层的数量必须是一个整数“类型”
来“整数”
。
optVars = [optimizableVariable]“InitialLearnRate”(1 e-5, 1 e 1),“转换”,“日志”) optimizableVariable (“NumHiddenUnits”(1000),“类型”,“整数”));
贝叶斯优化是计算密集型的,可能需要几个小时才能完成。对于本例,设置optimizeCondition
来假
下载并使用预先优化的超参数设置。如果你设置optimizeCondition
来真正的
,目标函数helperBayesOptLSTM
使用贝叶斯优化最小化。在附录中列出的目标函数是给定特定超参数设置的网络的错误率。加载设置的目标函数最小值为0.02(2%错误率)。
ObjFcn = helperBayesOptLSTM(TrainFeatures,YTrain,TestFeatures,YTest);optimizeconcondition = false;如果优化条件BayesObject = bayesopt(ObjFcn,optVars,…“MaxObjectiveEvaluations”15岁的…“IsObjectiveDeterministic”假的,…“UseParallel”,真正的);其他的url =“http://ssd.mathworks.com/金宝appsupportfiles/audio/SpokenDigitRecognition.zip”;downloadNetFolder = tempdir;netFolder = fullfile(下载netFolder,“SpokenDigitRecognition”);如果~存在(netFolder“dir”) disp (“正在下载预训练网络(1个文件- 12mb)…”解压缩(url, downloadNetFolder)结束负载(fullfile (netFolder“0.02.mat”));结束
下载预训练网络(1个文件- 12 MB)…
如果进行贝叶斯优化,将生成如下图所示的图形,以跟踪目标函数值及其对应的超参数值和迭代次数。您可以增加贝叶斯优化迭代的次数,以确保达到目标函数的全局最小值。
使用隐单元数和初始学习率的优化值重新训练网络。
numHiddenUnits = 768;numClasses = nummel (unique(YTrain));图层= […sequenceInputLayer inputSize lstmLayer (numHiddenUnits,“OutputMode”,“最后一次”) fulllyconnectedlayer (numClasses) softmaxLayer classificationLayer];maxEpochs = 300;miniBatchSize = 50;options = trainingOptions(“亚当”,…“InitialLearnRate”2.198827960269379 e-04…“MaxEpochs”maxEpochs,…“MiniBatchSize”miniBatchSize,…“SequenceLength”,“最短”,…“洗牌”,“every-epoch”,…“详细”假的,…“阴谋”,“训练进步”);net = trainNetwork(TrainFeatures,YTrain,layers,options);predLabels = classification (net,TestFeatures);testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 97.5000
如图所示,使用贝叶斯优化产生具有更高精度的LSTM。
基于mel频谱图的深度卷积网络
作为语音数字识别任务的另一种方法,使用基于mel频率谱图的深度卷积神经网络(DCNN)对FSDD数据集进行分类。使用与散射变换相同的信号截断/填充过程。同样,通过将每个信号样本除以最大绝对值对每个记录进行归一化。为了保持一致性,使用与散射变换相同的训练集和测试集。
设置mel-frequency谱图参数。使用与散射变换相同的窗口或框架持续时间,0.22秒。将窗口之间的跳数设置为10毫秒。使用40个频段。
segmentDuration = 8192*(1/8000);frameDuration = 0.22;hopDuration = 0.01;numBands = 40;
重置训练和测试数据存储。
重置(adsTrain);重置(adsTest);
辅助函数helperspeechSpectrograms
,在本例末尾定义,使用melSpectrogram
对记录长度进行标准化,对振幅进行归一化,得到梅尔频谱图。使用mel频率谱图的对数作为DCNN的输入。为了避免取0的对数,在每个元素上加上一个小的。
Epsil = 1e-6;XTrain = helperspeechSpectrograms(adsTrain,segmentDuration, framedduration,hopDuration,numBands);
计算语音谱图…2400个文件中处理了500个文件处理了1000个文件处理了1500个文件处理了2400个文件中的2000个……完成了
XTrain = log10(XTrain + epsil);XTest = helperspeechSpectrograms(adsTest,segmentDuration, framedduration,hopDuration,numBands);
计算语音谱图…处理了600个文件中的500个,完成了
XTest = log10(XTest + epsil);YTrain = adsTrain.Labels;YTest = adest . labels;
定义DCNN架构
构造一个小的DCNN作为层数组。使用卷积和批处理归一化层,并使用最大池化层对特征映射进行下采样。为了减少网络记忆训练数据特定特征的可能性,在最后一个完全连接层的输入中添加少量dropout。
sz = size(XTrain);specSize = sz(1:2);imageSize = [specSize 1];numClasses = nummel (categories(YTrain));dropoutProb = 0.2;numF = 12;layers = [imageInputLayer(imageSize)]卷积2dlayer (5,numF,“填充”,“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer (3 2 * numF“填充”,“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer(3、4 * numF,“填充”,“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(3,“步”2,“填充”,“相同”) convolution2dLayer(3、4 * numF,“填充”,“相同”) batchNormalizationLayer reluLayer convolution2dLayer(3,4*numF,“填充”,“相同”) batchNormalizationLayer reluLayer maxPooling2dLayer(2) dropoutLayer(dropoutProb) fulllyconnectedlayer (numClasses) softmaxLayer classificationLayer(“类”类别(YTrain));];
设置用于训练网络的超参数。使用50个小批大小和1e-4的学习率。指定Adam优化。由于本例中的数据量相对较小,因此将执行环境设置为“cpu”
再现性。您还可以通过将执行环境设置为任意一种来在可用的GPU上训练网络“图形”
或“汽车”
。有关更多信息,请参见trainingOptions
(深度学习工具箱)。
miniBatchSize = 50;options = trainingOptions(“亚当”,…“InitialLearnRate”1的军医,…“MaxEpochs”30岁的…“MiniBatchSize”miniBatchSize,…“洗牌”,“every-epoch”,…“阴谋”,“训练进步”,…“详细”假的,…“ExecutionEnvironment”,“cpu”);
训练网络。
trainedNet = trainNetwork(XTrain,YTrain,layers,options);
使用训练好的网络来预测测试集的数字标签。
[y预测,probs] =分类(trainedNet,XTest)“ExecutionEnvironment”,“CPU”);cnnAccuracy = sum(ypredict ==YTest)/numel(YTest)*100
cnnAccuracy = 98.1667
用混淆图总结训练后的网络在测试集上的性能。通过使用列和行摘要显示每个类的精度和召回率。混淆图底部的表格显示了精度值。混淆图右侧的表格显示了召回值。
图(“单位”,“归一化”,“位置”,[0.2 0.2 0.5 0.5]);ccDCNN = confusionchart(YTest, ypredict);ccDCNN。Title =“DCNN混淆图”;ccDCNN。ColumnSummary =“column-normalized”;ccDCNN。RowSummary =“row-normalized”;
DCNN使用mel频率谱图作为输入,对测试集中的语音数字进行分类,准确率约为98%。
总结
这个例子展示了如何使用不同的机器和深度学习方法来对FSDD中的语音数字进行分类。示例说明了小波散射与支持向量机和LSTM的配对。采用贝叶斯技术对LSTM超参数进行优化。最后,该示例展示了如何使用带有mel频率谱图的CNN。
示例的目的是演示如何使用MathWorks®工具以根本不同但互补的方式解决问题。所有工作流都使用audioDatastore
管理来自磁盘的数据流,确保适当的随机化。
本例中使用的所有方法在测试集上的表现都一样好。本示例不打算直接比较各种方法。例如,你也可以在CNN中使用贝叶斯优化进行超参数选择。在使用小型训练集(如本版本的FSDD)进行深度学习时,另一个有用的策略是使用数据增强。操作如何影响类并不总是已知的,因此数据增强并不总是可行的。然而,对于语音,建立数据增强策略可通过audioDataAugmenter
。
在小波时间散射的情况下,也有一些修改可以尝试。例如,您可以更改变换的不变尺度,更改每个滤波器组的小波滤波器的数量,并尝试不同的分类器。
附录:辅助函数
函数标签= helpergenLabels(ads)此函数仅用于小波工具箱示例。可能是%在将来的版本中已更改或已删除。tmp = cell(numel(ads.Files),1);表达=“[0 - 9]+ _”;为nf = 1: null (ads.Files); idx = regexp(ads.Files{nf},expression);tmp{nf} = ads.Files{nf}(idx);结束标签=分类(tmp);结束
函数x = helperReadSPData(x)此函数仅供使用小波工具箱的示例。它可能会改变%将在以后的版本中删除。N = nummel (x);如果N > 8192 x = x(1:8192);elseifN < 8192 pad = 8192-N;Prepad = floor(pad/2);Postpad = ceil(pad/2);X = [0 (pread,1);x;0 (postpad 1)];结束X = X /max(abs(X));结束
函数x = helperBayesOptLSTM(X_train, Y_train, X_val, Y_val)%此函数仅用于基于小波散射和深度学习的语音数字识别%的例子。它可能会在将来的版本中更改或删除。x = @valErrorFun;函数[valError,cons, fileName] = valErrorFun(optVars)%% LSTM体系结构[inputSize,~] = size(X_train{1});numClasses = nummel (unique(Y_train));图层= […sequenceInputLayer inputSize bilstmLayer (optVars。NumHiddenUnits,“OutputMode”,“最后一次”)%使用隐藏层数从优化变量的值fulllyconnectedlayer (numClasses) softmaxLayer分类层];%训练时不显示的图options = trainingOptions(“亚当”,…“InitialLearnRate”, optVars。InitialLearnRate,…使用优化变量的初始学习率值“MaxEpochs”, 300,…“MiniBatchSize”30岁的…“SequenceLength”,“最短”,…“洗牌”,“永远”,…“详细”、假);%%训练网络net = trainNetwork(X_train, Y_train, layers, options);%%训练准确度X_val_P = net. classified (X_val);accuracy_training = sum(X_val_P == Y_val)./numel(Y_val);valError = 1 - accuracy_training;%%将网络和选项的结果与错误值一起保存在结果文件夹中的MAT文件中fileName = fullfile(“结果”, num2str(valError) +“.mat”);保存(文件名,“净”,“valError”,“选项”) cons = [];结束%结束内部函数结束% end用于外部函数
函数X = helperspeechSpectrograms(ads,segmentDuration, framedduration,hopDuration,numBands)%此函数仅用于基于小波散射和深度学习的语音数字识别%的例子。它可能会在将来的版本中更改或删除。%% helperspeechSpectrograms(广告、segmentDuration frameDuration、hopDuration numBands)%计算数据存储广告中文件的语音谱图。% segmentDuration是演讲片段的总时长(以秒为单位);% frameDuration表示每个谱图帧的持续时间,hopDuration表示每个谱图帧之间的时移%,numBands的个数%频带。disp (“计算语音谱图……”);numHops = ceil((segmentDuration - frameDuration)/hopDuration);numFiles = length(ads.Files);X = 0 ([numBands,numHops,1,numFiles],“单一”);为i = 1:numFiles [x,info] = read(ads);= normalizeAndResize(x);fs = info.SampleRate;frameLength = round(frameLength *fs);hopLength = round(hopDuration*fs);spec = melSpectrogram(x,fs,…“窗口”汉明(frameLength“周期”),…“OverlapLength”,frameLength - hopLength;…“FFTLength”, 2048,…“NumBands”numBands,…“FrequencyRange”[4000]);如果谱图的宽度小于numHops,则将谱图放入% X的中间。W = size(spec,2);left = floor((numHops-w)/2)+1;Ind = left:左+w-1;X(:,ind,1,i) = spec;如果Mod (i,500) == 0 disp(“加工”+ I +"文件退出"+ numFiles)结束结束disp (“…”);结束%--------------------------------------------------------------------------函数x = normalizeAndResize(x)%此函数仅用于基于小波散射和深度学习的语音数字识别%的例子。它可能会在将来的版本中更改或删除。N = nummel (x);如果N > 8192 x = x(1:8192);elseifN < 8192 pad = 8192-N;Prepad = floor(pad/2);Postpad = ceil(pad/2);X = [0 (pread,1);x;0 (postpad 1)];结束X = X /max(abs(X));结束
版权所有:The MathWorks, Inc。