В этом примере показано, как классифицировать разговорные цифры с помощью обоих методов машинного и глубокого обучения. В примере вы выполняете классификацию с помощью времени вейвлета, рассеиваясь с машиной опорных векторов (SVM) и с сетью долгой краткосрочной памяти (LSTM). Вы также применяете Байесовую оптимизацию, чтобы определить подходящие гиперпараметры, чтобы улучшить точность сети LSTM. Кроме того, пример иллюстрирует подход с помощью глубокой сверточной нейронной сети (CNN) и спектрограмм mel-частоты.
Клонируйте или загрузите Свободный разговорный набор данных цифры (FSDD), доступный в https://github.com/Jakobovski/free-spoken-digit-dataset. FSDD является открытым набором данных, что означает, что это может расти в зависимости от времени. Этот пример использует версию, фиксировавшую 29 января 2019, который состоит из 2 000 записей на английском языке цифр 0 через 9 полученных от четырех докладчиков. В этой версии два из докладчиков являются носителями американского варианта английского языка, один докладчик является ненативным докладчиком английского языка с бельгийским французским диакритическим знаком, и один докладчик является ненативным докладчиком английского языка с немецким диакритическим знаком. Данные производятся на уровне 8 000 Гц.
Используйте audioDatastore
управлять доступом к данным и гарантировать случайное деление записей в наборы обучающих данных и наборы тестов. Установите location
свойство к местоположению папки записей FSDD на вашем компьютере, например:
pathToRecordingsFolder = fullfile(tempdir,'free-spoken-digit-dataset','recordings'); location = pathToRecordingsFolder;
Укажите audioDatastore
к тому местоположению.
ads = audioDatastore(location);
Функция помощника helpergenLabels
создает категориальный массив меток из файлов FSDD. Исходный код для helpergenLabels
перечислен в приложении. Перечислите классы и количество примеров в каждом классе.
ads.Labels = helpergenLabels(ads); summary(ads.Labels)
0 200 1 200 2 200 3 200 4 200 5 200 6 200 7 200 8 200 9 200
Набор данных FSDD состоит из 10 сбалансированных классов с 200 записями каждый. Записи в FSDD не имеют равной длительности. FSDD не является предельно большим, таким образом прочитывает файлы FSDD и создает гистограмму из длин сигнала.
LenSig = zeros(numel(ads.Files),1); nr = 1; while hasdata(ads) digit = read(ads); LenSig(nr) = numel(digit); nr = nr+1; end reset(ads) histogram(LenSig) grid on xlabel('Signal Length (Samples)') ylabel('Frequency')
Гистограмма показывает, что распределение записи длин положительно скашивается. Для классификации этот пример использует общую длину сигнала 8 192 выборок, консервативное значение, которое гарантирует, что усечение более длительных записей не отключает речевое содержимое. Если сигнал больше 8 192 выборок (1,024 секунды) в длине, запись является усеченной к 8 192 выборкам. Если сигнал меньше 8 192 выборок в длине, сигнал предварительно дополнен и постдополнен симметрично с нулями к продолжительности 8 192 выборок.
Используйте waveletScattering
(Wavelet Toolbox), чтобы создать время вейвлета, рассеивая среду с помощью инвариантной шкалы 0,22 секунд. В этом примере вы создаете характеристические векторы путем усреднения рассеивающегося преобразования по всем выборкам времени. Иметь достаточное число рассеивающихся коэффициентов на окно времени к среднему, набору OversamplingFactor
к 2, чтобы произвести четырехкратное увеличение количества рассеивающихся коэффициентов для каждого пути относительно критически прореженного значения.
sf = waveletScattering('SignalLength',8192,'InvarianceScale',0.22,... 'SamplingFrequency',8000,'OversamplingFactor',2);
Разделите FSDD в наборы обучающих данных и наборы тестов. Выделите 80% данных к набору обучающих данных и сохраните 20% для набора тестов. Обучающие данные являются для обучения классификатором на основе рассеивающегося преобразования. Тестовые данные для проверки модели.
rng default;
ads = shuffle(ads);
[adsTrain,adsTest] = splitEachLabel(ads,0.8);
countEachLabel(adsTrain)
ans=10×2 table
Label Count
_____ _____
0 160
1 160
2 160
3 160
4 160
5 160
6 160
7 160
8 160
9 160
countEachLabel(adsTest)
ans=10×2 table
Label Count
_____ _____
0 40
1 40
2 40
3 40
4 40
5 40
6 40
7 40
8 40
9 40
Функция помощника helperReadSPData
обрезает или заполняет данные к длине 8 192 и нормирует каждую запись на ее максимальное значение. Исходный код для helperReadSPData
перечислен в приложении. Создайте 8192 1600 матрица, где каждый столбец является разговорной разрядной записью.
Xtrain = []; scatds_Train = transform(adsTrain,@(x)helperReadSPData(x)); while hasdata(scatds_Train) smat = read(scatds_Train); Xtrain = cat(2,Xtrain,smat); end
Повторите процесс для набора тестов. Получившаяся матрица 8192 400.
Xtest = []; scatds_Test = transform(adsTest,@(x)helperReadSPData(x)); while hasdata(scatds_Test) smat = read(scatds_Test); Xtest = cat(2,Xtest,smat); end
Применяйте преобразование рассеивания вейвлета к наборам обучающих данных и наборам тестов.
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', 'polynomial', ... 'PolynomialOrder', 2, ... 'KernelScale', 'auto', ... 'BoxConstraint', 1, ... 'Standardize', true); classificationSVM = fitcecoc(... TrainFeatures, ... adsTrain.Labels, ... 'Learners', template, ... 'Coding', 'onevsone', ... 'ClassNames', categorical({'0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'}));
Используйте перекрестную проверку k-сгиба, чтобы предсказать точность обобщения основанного на модели на обучающих данных. Разделите набор обучающих данных в пять групп.
partitionedModel = crossval(classificationSVM, 'KFold', 5); [validationPredictions, validationScores] = kfoldPredict(partitionedModel); validationAccuracy = (1 - kfoldLoss(partitionedModel, 'LossFun', 'ClassifError'))*100
validationAccuracy = 96.8125
Предполагаемая точность обобщения составляет приблизительно 97%. Используйте обученный SVM, чтобы предсказать разговорные разрядные классы в наборе тестов.
predLabels = predict(classificationSVM,TestFeatures); testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 97.7500
Обобщите эффективность модели на наборе тестов с графиком беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Таблица в нижней части графика беспорядка показывает значения точности для каждого класса. Таблица справа от графика беспорядка показывает значения отзыва.
figure('Units','normalized','Position',[0.2 0.2 0.5 0.5]); ccscat = confusionchart(adsTest.Labels,predLabels); ccscat.Title = 'Wavelet Scattering Classification'; ccscat.ColumnSummary = 'column-normalized'; ccscat.RowSummary = 'row-normalized';
Рассеивающееся преобразование вместе с классификатором SVM классифицирует разговорные цифры на набор тестов с точностью до 98% (или коэффициент ошибок 2%).
Сеть LSTM является типом рекуррентной нейронной сети (RNN). RNNs являются нейронными сетями, которые специализированы для работы с последовательными или временными данными, такими как речевые данные. Поскольку коэффициенты рассеивания вейвлета являются последовательностями, они могут использоваться в качестве входных параметров к LSTM. При помощи рассеивания функций в противоположность необработанным данным можно уменьшать изменчивость, которую должна изучить сеть.
Измените обучение и тестирующий рассеивающиеся функции, которые будут использоваться с сетью LSTM. Исключите коэффициенты рассеивания нулевого порядка и преобразуйте функции в массивы ячеек.
TrainFeatures = Strain(2:end,:,:); TrainFeatures = squeeze(num2cell(TrainFeatures,[1 2])); TestFeatures = Stest(2:end,:,:); TestFeatures = squeeze(num2cell(TestFeatures, [1 2]));
Создайте простую сеть LSTM с 512 скрытыми слоями.
[inputSize, ~] = size(TrainFeatures{1}); YTrain = adsTrain.Labels; numHiddenUnits = 512; numClasses = numel(unique(YTrain)); layers = [ ... sequenceInputLayer(inputSize) lstmLayer(numHiddenUnits,'OutputMode','last') fullyConnectedLayer(numClasses) softmaxLayer classificationLayer];
Установите гиперпараметры. Используйте оптимизацию Адама и мини-пакетный размер 50. Определите максимальный номер эпох к 300. Используйте скорость обучения 1e-4. Можно выключить график процесса обучения, если вы не хотите отслеживать прогресс с помощью графиков. Обучение использует графический процессор по умолчанию, если вы доступны. В противном случае это использует центральный процессор. Для получения дополнительной информации смотрите trainingOptions
.
maxEpochs = 300; miniBatchSize = 50; options = trainingOptions('adam', ... 'InitialLearnRate',0.0001,... 'MaxEpochs',maxEpochs, ... 'MiniBatchSize',miniBatchSize, ... 'SequenceLength','shortest', ... 'Shuffle','every-epoch',... 'Verbose', false, ... 'Plots','training-progress');
Обучите сеть.
net = trainNetwork(TrainFeatures,YTrain,layers,options);
predLabels = classify(net,TestFeatures); testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 94
Определение подходящих установок гиперпараметров часто является одной из самых трудных частей обучения глубокой сети. Чтобы смягчить это, можно использовать Байесовую оптимизацию. В этом примере вы оптимизируете количество скрытых слоев и начальной скорости обучения при помощи Байесовых методов. Создайте новую директорию, чтобы сохранить MAT-файлы, содержащие информацию об установках гиперпараметров и сети наряду с соответствующими коэффициентами ошибок.
YTrain = adsTrain.Labels; YTest = adsTest.Labels; if ~exist("results/",'dir') mkdir results end
Инициализируйте переменные, которые будут оптимизированы и их диапазоны значений. Поскольку количество скрытых слоев должно быть целым числом, установите 'type'
к 'integer'
.
optVars = [ optimizableVariable('InitialLearnRate',[1e-5, 1e-1],'Transform','log') optimizableVariable('NumHiddenUnits',[10, 1000],'Type','integer') ];
Байесова оптимизация в вычислительном отношении интенсивна и может занять несколько часов, чтобы закончиться. В целях этого примера, набор optimizeCondition
к false
загружать предопределенные оптимизированные установки гиперпараметров. Если вы устанавливаете optimizeCondition
к true
, целевая функция helperBayesOptLSTM
минимизирован с помощью Байесовой оптимизации. Целевая функция, перечисленная в приложении, является коэффициентом ошибок сети, данной определенные установки гиперпараметров. Загруженные настройки для минимума целевой функции 0,02 (2%-й коэффициент ошибок).
ObjFcn = helperBayesOptLSTM(TrainFeatures, YTrain, TestFeatures, YTest); optimizeCondition = false; if optimizeCondition BayesObject = bayesopt(ObjFcn,optVars,... 'MaxObjectiveEvaluations',15,... 'IsObjectiveDeterministic',false,... 'UseParallel',true); else load 0.02.mat end
Если вы выполняете Байесовую оптимизацию, фигуры, похожие на следующее, сгенерированы, чтобы отследить значения целевой функции с соответствующими гиперзначениями параметров и количеством итераций. Можно увеличить число Байесовых итераций оптимизации, чтобы гарантировать, что глобальный минимум целевой функции достигнут.
Используйте оптимизированные значения для количества скрытых модулей и начальной скорости обучения и переобучите сеть.
numHiddenUnits = 768; numClasses = numel(unique(YTrain)); layers = [ ... sequenceInputLayer(inputSize) lstmLayer(numHiddenUnits,'OutputMode','last') fullyConnectedLayer(numClasses) softmaxLayer classificationLayer]; maxEpochs = 300; miniBatchSize = 50; options = trainingOptions('adam', ... 'InitialLearnRate',2.198827960269379e-04,... 'MaxEpochs',maxEpochs, ... 'MiniBatchSize',miniBatchSize, ... 'SequenceLength','shortest', ... 'Shuffle','every-epoch',... 'Verbose', false, ... 'Plots','training-progress'); net = trainNetwork(TrainFeatures,YTrain,layers,options);
predLabels = classify(net,TestFeatures); testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 97.7500
Когда график показывает, использование Байесовой оптимизации дает к LSTM с более высокой точностью.
Как другой подход к задаче разговорного распознавания цифры, используйте глубокую сверточную нейронную сеть (DCNN) на основе спектрограмм mel-частоты, чтобы классифицировать набор данных FSDD. Используйте ту же процедуру усечения/дополнения сигнала в качестве в рассеивающемся преобразовании. Точно так же нормируйте каждую запись путем деления каждой выборки сигнала на максимальное абсолютное значение. Для непротиворечивости используйте те же наборы обучающих данных и наборы тестов что касается рассеивающегося преобразования.
Установите параметры для спектрограмм mel-частоты. Используйте то же окно или систему координат, длительность как в рассеивающемся преобразовании, 0,22 секунды. Установите транзитный участок между окнами к 10 мс. Используйте 40 диапазонов частот.
segmentDuration = 8192*(1/8000); frameDuration = 0.22; hopDuration = 0.01; numBands = 40;
Сбросьте обучение и протестируйте хранилища данных.
reset(adsTrain); reset(adsTest);
Функция помощника helperspeechSpectrograms
, заданный в конце этого примера, melSpectrogram
использования получить спектрограмму mel-частоты после стандартизации продолжительности записи и нормализации амплитуды. Используйте логарифм спектрограмм mel-частоты как входные параметры к DCNN. Чтобы постараться не взять логарифм нуля, добавьте маленький эпсилон в каждый элемент.
epsil = 1e-6; XTrain = helperspeechSpectrograms(adsTrain,segmentDuration,frameDuration,hopDuration,numBands);
Computing speech spectrograms... Processed 500 files out of 1600 Processed 1000 files out of 1600 Processed 1500 files out of 1600 ...done
XTrain = log10(XTrain + epsil); XTest = helperspeechSpectrograms(adsTest,segmentDuration,frameDuration,hopDuration,numBands);
Computing speech spectrograms... ...done
XTest = log10(XTest + epsil); YTrain = adsTrain.Labels; YTest = adsTest.Labels;
Создайте маленький DCNN как массив слоев. Используйте сверточный и слои нормализации партии. и проредите карты функции с помощью макс. слоев объединения. Чтобы уменьшать возможность сети, запоминая определенные функции обучающих данных, добавьте небольшое количество уволенного к входу к последнему полносвязному слою.
sz = size(XTrain); specSize = sz(1:2); imageSize = [specSize 1]; numClasses = numel(categories(YTrain)); dropoutProb = 0.2; numF = 12; layers = [ imageInputLayer(imageSize) convolution2dLayer(5,numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,2*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(2) dropoutLayer(dropoutProb) fullyConnectedLayer(numClasses) softmaxLayer classificationLayer('Classes',categories(YTrain)); ];
Установите гиперпараметры использовать в обучении сети. Используйте мини-пакетный размер 50 и скорость обучения 1e-4. Задайте оптимизацию Адама. Поскольку объем данных в этом примере относительно мал, установите среду выполнения на 'cpu'
для воспроизводимости. Можно также обучить сеть на доступном графическом процессоре путем установки среды выполнения на любой 'gpu'
или 'auto'
. Для получения дополнительной информации смотрите trainingOptions
.
miniBatchSize = 50; options = trainingOptions('adam', ... 'InitialLearnRate',1e-4, ... 'MaxEpochs',30, ... 'MiniBatchSize',miniBatchSize, ... 'Shuffle','every-epoch', ... 'Plots','training-progress', ... 'Verbose',false, ... 'ExecutionEnvironment','cpu');
Обучите сеть.
trainedNet = trainNetwork(XTrain,YTrain,layers,options);
Используйте обучивший сеть, чтобы предсказать метки цифры для набора тестов.
[Ypredicted,probs] = classify(trainedNet,XTest,'ExecutionEnvironment','CPU'); cnnAccuracy = sum(Ypredicted==YTest)/numel(YTest)*100
cnnAccuracy = 98.2500
Обобщите эффективность обучившего сеть на наборе тестов с графиком беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Таблица в нижней части графика беспорядка показывает значения точности. Таблица справа от графика беспорядка показывает значения отзыва.
figure('Units','normalized','Position',[0.2 0.2 0.5 0.5]); ccDCNN = confusionchart(YTest,Ypredicted); ccDCNN.Title = 'Confusion Chart for DCNN'; ccDCNN.ColumnSummary = 'column-normalized'; ccDCNN.RowSummary = 'row-normalized';
DCNN использование спектрограмм mel-частоты как входные параметры классифицирует разговорные цифры на набор тестов со степенью точности приблизительно 98% также.
В этом примере показано, как использовать различные подходы машинного и глубокого обучения для классификации разговорных цифр в FSDD. Пример проиллюстрировал рассеивание вейвлета, соединенное и с SVM и с LSTM. Байесовы методы использовались, чтобы оптимизировать гиперпараметры LSTM. Наконец, пример показывает, как использовать CNN со спектрограммами mel-частоты.
Цель примера состоит в том, чтобы продемонстрировать, как использовать инструменты MathWorks®, чтобы приблизиться к проблеме существенно различными но дополнительными способами. Все рабочие процессы используют audioDatastore
управлять потоком данных из диска и гарантировать соответствующую рандомизацию.
Все подходы, используемые в этом примере, выполняемом одинаково хорошо на наборе тестов. Этот пример не предназначается как прямое сравнение между различными подходами. Например, можно также использовать Байесовую оптимизацию для выбора гиперпараметра в CNN. Дополнительная стратегия, которая полезна в глубоком обучении для небольших наборов обучающих данных как эта версия FSDD, состоит в том, чтобы использовать увеличение данных. То, как манипуляции влияют на класс, не всегда известно, таким образом, увеличение данных не всегда выполнимо. Однако для речи, установленные стратегии увеличения данных доступны через audioDataAugmenter
.
В случае времени вейвлета, рассеиваясь, существует также много модификаций, которые можно попробовать. Например, можно изменить инвариантную шкалу преобразования, варьироваться количество фильтров вейвлета на набор фильтров и попробовать различные классификаторы.
function Labels = helpergenLabels(ads) % This function is only for use in Wavelet Toolbox examples. It may be % changed or removed in a future release. tmp = cell(numel(ads.Files),1); expression = "[0-9]+_"; for nf = 1:numel(ads.Files) idx = regexp(ads.Files{nf},expression); tmp{nf} = ads.Files{nf}(idx); end Labels = categorical(tmp); end
function x = helperReadSPData(x) % This function is only for use Wavelet Toolbox examples. It may change or % be removed in a future release. N = numel(x); if N > 8192 x = x(1:8192); elseif N < 8192 pad = 8192-N; prepad = floor(pad/2); postpad = ceil(pad/2); x = [zeros(prepad,1) ; x ; zeros(postpad,1)]; end x = x./max(abs(x)); end
function x = helperBayesOptLSTM(X_train, Y_train, X_val, Y_val) % This function is only for use in the % "Spoken Digit Recognition with Wavelet Scattering and Deep Learning" % example. It may change or be removed in a future release. x = @valErrorFun; function [valError,cons, fileName] = valErrorFun(optVars) %% LSTM Architecture [inputSize,~] = size(X_train{1}); numClasses = numel(unique(Y_train)); layers = [ ... sequenceInputLayer(inputSize) bilstmLayer(optVars.NumHiddenUnits,'OutputMode','last') % Using number of hidden layers value from optimizing variable fullyConnectedLayer(numClasses) softmaxLayer classificationLayer]; % Plots not displayed during training options = trainingOptions('adam', ... 'InitialLearnRate',optVars.InitialLearnRate, ... % Using initial learning rate value from optimizing variable 'MaxEpochs',300, ... 'MiniBatchSize',30, ... 'SequenceLength','shortest', ... 'Shuffle','never', ... 'Verbose', false); %% Train the network net = trainNetwork(X_train, Y_train, layers, options); %% Training accuracy X_val_P = net.classify(X_val); accuracy_training = sum(X_val_P == Y_val)./numel(Y_val); valError = 1 - accuracy_training; %% save results of network and options in a MAT file in the results folder along with the error value fileName = fullfile('results', num2str(valError) + ".mat"); save(fileName,'net','valError','options') cons = []; end % end for inner function end % end for outer function
function X = helperspeechSpectrograms(ads,segmentDuration,frameDuration,hopDuration,numBands) % This function is only for use in the % "Spoken Digit Recognition with Wavelet Scattering and Deep Learning" % example. It may change or be removed in a future release. % % helperspeechSpectrograms(ads,segmentDuration,frameDuration,hopDuration,numBands) % computes speech spectrograms for the files in the datastore ads. % segmentDuration is the total duration of the speech clips (in seconds), % frameDuration the duration of each spectrogram frame, hopDuration the % time shift between each spectrogram frame, and numBands the number of % frequency bands. disp("Computing speech spectrograms..."); numHops = ceil((segmentDuration - frameDuration)/hopDuration); numFiles = length(ads.Files); X = zeros([numBands,numHops,1,numFiles],'single'); for i = 1:numFiles [x,info] = read(ads); x = normalizeAndResize(x); fs = info.SampleRate; frameLength = round(frameDuration*fs); hopLength = round(hopDuration*fs); spec = melSpectrogram(x,fs, ... 'Window',hamming(frameLength,'periodic'), ... 'OverlapLength',frameLength - hopLength, ... 'FFTLength',2048, ... 'NumBands',numBands, ... 'FrequencyRange',[50,4000]); % If the spectrogram is less wide than numHops, then put spectrogram in % the middle of X. w = size(spec,2); left = floor((numHops-w)/2)+1; ind = left:left+w-1; X(:,ind,1,i) = spec; if mod(i,500) == 0 disp("Processed " + i + " files out of " + numFiles) end end disp("...done"); end %-------------------------------------------------------------------------- function x = normalizeAndResize(x) % This function is only for use in the % "Spoken Digit Recognition with Wavelet Scattering and Deep Learning" % example. It may change or be removed in a future release. N = numel(x); if N > 8192 x = x(1:8192); elseif N < 8192 pad = 8192-N; prepad = floor(pad/2); postpad = ceil(pad/2); x = [zeros(prepad,1) ; x ; zeros(postpad,1)]; end x = x./max(abs(x)); end
Copyright 2018, The MathWorks, Inc.
trainingOptions
| trainNetwork