Разговорное распознавание цифры с рассеиванием вейвлета и глубоким обучением

Этот пример показывает, как классифицировать разговорные цифры с помощью рассеивания времени вейвлета, соединенного со случайным классификатором подпространства и глубокой сверточной сетью на основе спектрограмм mel-частоты.

Этот пример требует Wavelet Toolbox™, Audio Toolbox™, Statistics and Machine Learning Toolbox™, Deep Learning Toolbox™ и Parallel Computing Toolbox™. Audio Toolbox требуется в примере. Существуют разделы, которые требуют только частичного списка других тулбоксов. Пример отмечает, где конкретные тулбоксы требуются.

Данные

Клонируйте или загрузите Свободный разговорный набор данных цифры (FSDD), доступный в https://github.com/Jakobovski/free-spoken-digit-dataset. FSDD является открытым набором данных, что означает, что это может расти в зависимости от времени. Этот пример использует версию 1.0.8, которая состоит из 1 500 записей цифр 0 через 9 полученных от трех докладчиков. Данные выбираются на уровне 8 000 Гц.

Используйте audioDatastore, чтобы управлять доступом к данным и гарантировать случайное деление записей в наборы обучающих данных и наборы тестов. Установите свойство location на местоположение папки записей FSDD на вашем компьютере.

location = 'C:\free-spoken-digit-dataset\recordings';
ads = audioDatastore(location);

Функция помощника helpergenLabels, заданный в конце этого примера, создает категориальный массив меток из файлов FSDD. Перечислите классы и количество примеров в каждом классе.

ads.Labels = helpergenLabels(ads);
summary(ads.Labels)
     1      150 
     0      150 
     2      150 
     3      150 
     4      150 
     5      150 
     6      150 
     7      150 
     8      150 
     9      150 

Набор данных FSDD состоит из 10 сбалансированных классов с 150 записями каждый. Записи в 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)
xlabel('Signal Length (Samples)')
ylabel('Frequency')

Гистограмма показывает, что распределение записи длин положительно скашивается. Для классификации этот пример использует общую длину сигнала 8 192 выборок. Значение 8 192 было выбрано, чтобы консервативно гарантировать, что усечение более длительных записей не влияло (отключает) речевое содержимое. Если сигнал больше, чем 8 192 выборки, или 1,024 секунды, в длине, мы обрезаем запись до 8 192 выборок. Если сигнал является меньше чем 8 192 выборками в длине, мы симметрично предварительно заполняем и постзаполняем сигнал нулями к продолжительности 8 192 выборок.

Время вейвлета, рассеиваясь

Создайте время вейвлета, рассеивая среду с помощью инвариантной шкалы 0,22 секунд. Поскольку мы создадим характеристические векторы путем усреднения рассеивающегося преобразования по всем выборкам времени, установите OversamplingFactor на 2, приведя к четырехкратному увеличению количества рассеивающихся коэффициентов для каждого пути относительно критически субдискретизируемого значения. Чтобы использовать функцию waveletScattering, у вас должен быть Wavelet Toolbox.

sf = waveletScattering('SignalLength',8192,'InvarianceScale',0.22,...
    'SamplingFrequency',8000,'OversamplingFactor',2);

Разделите FSDD в наборы обучающих данных и наборы тестов. Выделите 80% данных к набору обучающих данных и сохраните 20% для набора тестов. Данные тренировки являются для обучения классификатором на основе рассеивающегося преобразования. Тестовые данные для проверки модели.

rng(100);
ads = shuffle(ads);
[adsTrain,adsTest] = splitEachLabel(ads,0.8);
countEachLabel(adsTrain)
ans=10×2 table
    Label    Count
    _____    _____

      0       120 
      1       120 
      2       120 
      3       120 
      4       120 
      5       120 
      6       120 
      7       120 
      8       120 
      9       120 

countEachLabel(adsTest)
ans=10×2 table
    Label    Count
    _____    _____

      0       30  
      1       30  
      2       30  
      3       30  
      4       30  
      5       30  
      6       30  
      7       30  
      8       30  
      9       30  

Аудио datastore adsTrain, который соответствует набору обучающих данных, содержит 1200 или 80% записей. Аудио datastore adsTest, который соответствует набору тестов, содержит остающиеся 20%. Оба хранилища данных сохраняют равновесие класса исходного набора данных. Хранилища данных используются в обоих рабочих процессах классификации в этом примере.

Чтобы вычислить рассеивающиеся преобразования для данных параллельно, создайте версии tall обоих обучение и протестируйте хранилища данных. Чтобы использовать функцию tall, у вас должен быть Parallel Computing Toolbox.

Ttrain = tall(adsTrain);
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 6).
Ttest = tall(adsTest);

Функция помощника helperdigitscat, заданный в конце этого примера, обрезает или заполняет записи так, чтобы каждая запись была 8 192 выборками. Функция также нормирует данные путем деления каждой выборки сигнала на максимальное абсолютное значение. Для каждого сигнала функция получает журнал рассеивающегося, преобразовывают использование предопределенной среды, коэффициенты рассеивания нулевого порядка удалены, и среднее значение взято по всем моментам времени для каждого пути.

scatteringTrain = cellfun(@(x)helperdigitscat(x,sf),Ttrain,'UniformOutput',false);
scatteringTest = cellfun(@(x)helperdigitscat(x,sf),Ttest,'UniformOutput',false);

Получите рассеивающиеся функции набора обучающих данных.

TrainFeatures = gather(scatteringTrain);
Evaluating tall expression using the Parallel Pool 'local':
- Pass 1 of 1: Completed in 39 sec
Evaluation completed in 39 sec
TrainFeatures = cell2mat(TrainFeatures')';

TrainFeatures 1200 321 матрица. Каждая строка является рассеивающимся характеристическим вектором для соответствующей записи в данных тренировки. Обратите внимание на то, что записи с 8192 выборками уменьшались до 321 характеристического вектора элемента.

Повторите процесс для набора тестов.

TestFeatures = gather(scatteringTest);
Evaluating tall expression using the Parallel Pool 'local':
- Pass 1 of 1: Completed in 8.6 sec
Evaluation completed in 8.6 sec
TestFeatures = cell2mat(TestFeatures')';

Этот пример использует случайный классификатор ансамбля подпространства с рассеивающимися функциями. Случайный классификатор подпространства, как все методы ансамбля, обучает несколько учеников, которые затем "усреднены", чтобы получить итоговую модель. В случайном классификаторе подпространства функции случайным образом выбираются с заменой для каждого ученика. В этом случае определите номер функций к случайным образом демонстрационному приблизительно к половине общего количества функций. Чтобы использовать случайное классифицированное подпространство, у вас должен быть Statistics and Machine Learning Toolbox.

subspaceDimension = ceil(size(TrainFeatures,2)/2);
classificationEnsemble = fitcensemble(...
    TrainFeatures, ...
    adsTrain.Labels, ...
    'Method', 'Subspace', ...
    'NumLearningCycles', 30, ...
    'Learners', 'discriminant', ...
    'NPredToSample', subspaceDimension, ...
    'ClassNames', categorical(0:9));

Используйте перекрестную проверку k-сгиба, чтобы предсказать точность обобщения модели. Разделите набор обучающих данных в пять групп.

partitionedModel = crossval(classificationEnsemble, 'KFold', 5);
[validationPredictions, validationScores] = kfoldPredict(partitionedModel);
validationAccuracy = (1 - kfoldLoss(partitionedModel, 'LossFun', 'ClassifError'))*100
validationAccuracy = 97.5000

Предполагаемая точность обобщения составляет 97,5%. Используйте модель дискриминанта подпространства, чтобы предсказать разговорные разрядные классы в наборе тестов.

predLabels = predict(classificationEnsemble,TestFeatures);
testAccuracy = sum(predLabels==adsTest.Labels)/numel(predLabels)*100
testAccuracy = 99.3333

Обобщите производительность модели на наборе тестов с графиком беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Таблица в нижней части графика беспорядка показывает значения точности для каждого класса. Таблица справа от графика беспорядка показывает значения отзыва.

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';

Рассеивающееся преобразование вместе с классификатором дискриминанта подпространства классифицирует разговорные цифры на набор тестов с процентом точности 99,3 или коэффициентом ошибок 0,7%.

Глубоко сверточная сеть Используя спектрограммы Mel-частоты

Как другой подход к задаче разговорного распознавания цифры, используйте глубоко сверточную нейронную сеть (DCNN) на основе спектрограмм mel-частоты, чтобы классифицировать набор данных FSDD. Используйте ту же процедуру усечения/дополнения сигнала в качестве в рассеивающемся преобразовании. Точно так же нормируйте каждую запись путем деления каждой выборки сигнала на максимальное абсолютное значение. Для непротиворечивости используйте те же наборы обучающих данных и наборы тестов что касается рассеивающегося преобразования. Чтобы завершить этот пример, у вас должен быть Deep Learning Toolbox.

Установите параметры для спектрограмм 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 1200
Processed 1000 files out of 1200
...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

Создайте маленький DCNN как массив слоев. Используйте сверточные и пакетные слои нормализации и субдискретизируйте карты функции с помощью макс. слоев объединения. Чтобы уменьшать возможность сети, запоминая определенные функции данных тренировки, добавьте небольшое количество уволенного к входу к последнему полносвязному слою.

sz = size(XTrain);
specSize = sz(1:2);
imageSize = [specSize 1];

numClasses = numel(categories(YTrain));

dropoutProb = 0.2;
numF = 12;
layers = [
    imageInputLayer(imageSize)

    convolution2dLayer(3,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 = 97.3333

Обобщите производительность обучившего сеть на наборе тестов с графиком беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Таблица в нижней части графика беспорядка показывает значения точности. Таблица справа от графика беспорядка показывает значения отзыва.

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-частоты как входные параметры классифицирует разговорные цифры на набор тестов со степенью точности приблизительно 97%.

Сводные данные

Этот пример показывает, как использовать два разных подхода для классификации разговорных цифр в FSDD. Цель примера состоит в том, чтобы просто продемонстрировать, как использовать инструменты MathWorks™, чтобы приблизиться к проблеме двумя существенно различными, но дополнительными способами. Оба рабочих процесса используют audioDatastore, чтобы управлять потоком данных из диска и гарантировать соответствующую рандомизацию.

Один подход изучения использует рассеивание времени вейвлета, соединенное со случайным классификатором подпространства. Другое изучение приближается к спектрограммам mel-частоты использования как к входным параметрам к DCNN. Оба подхода выполняют хорошо на наборе тестов. Этот пример не предназначается как прямое сравнение между двумя подходами. И с методами, можно попробовать различные гиперпараметры и с архитектуру, которая может значительно влиять на результаты. В случае подхода спектрограммы mel-частоты можно экспериментировать с различной параметризацией спектрограммы mel-частоты, а также изменений в слоях DCNN, включая добавляющие слои. Дополнительная стратегия, которая полезна в глубоком обучении для небольших наборов обучающих данных как эта версия FSDD, состоит в том, чтобы использовать увеличение данных. То, как манипуляции влияют на класс, не всегда известно, таким образом, увеличение данных не всегда выполнимо. Однако для речевого анализа, установленные стратегии увеличения данных доступны.

В случае времени вейвлета, рассеиваясь, существует также много модификаций, которые можно попробовать. Например, можно изменить инвариантную шкалу преобразования, отличаться количество фильтров вейвлета на набор фильтров и попробовать различные классификаторы.

Приложение: Функции помощника

function Labels = helpergenLabels(ads)
% 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.

Labels = categorical(numel(ads.Files),1);
expression = "[0-9]+_";
for nf = 1:numel(ads.Files)
    idx = regexp(ads.Files{nf},expression);
    Labels(nf) = categorical(str2double(ads.Files{nf}(idx)));
end

end

function features = helperdigitscat(x,sf)
% 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));
features = sf.featureMatrix(x,'transform','log');
% Remove 0-th order scattering coefficients
features(1,:) = [];
features = mean(features,2);
end

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, ...
        'WindowLength',frameLength, ...
        'OverlapLength',frameLength - hopLength, ...
        'FFTLength',2048, ...
        'NumBands',numBands, ...
        'FrequencyRange',[50,7000]);
    
    % 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.

Смотрите также

Похожие темы