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

В этом примере показано, как классифицировать разговорные цифры с помощью обоих методов машинного и глубокого обучения. В примере вы выполняете классификацию с помощью времени вейвлета, рассеиваясь с машиной опорных векторов (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-master','recordings');
location = pathToRecordingsFolder;

Укажите audioDatastore к тому местоположению.

ads = audioDatastore(location);

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

ads.Labels = helpergenLabels(ads);
summary(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 = 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       240 
      1       240 
      2       240 
      3       240 
      4       240 
      5       240 
      6       240 
      7       240 
      8       240 
      9       240 

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

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

Функция помощника 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 с квадратичным полиномиальным ядром. Соответствуйте 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 = 97.4167

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

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

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

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)

Сеть 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 (Deep Learning Toolbox).

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 = 96.3333

Байесова оптимизация

Определение подходящих установок гиперпараметров часто является одной из самых трудных частей обучения глубокой сети. Чтобы смягчить это, можно использовать Байесовую оптимизацию. В этом примере вы оптимизируете количество скрытых слоев и начальной скорости обучения при помощи Байесовых методов. Создайте новую директорию, чтобы сохранить 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
    url = 'http://ssd.mathworks.com/supportfiles/audio/SpokenDigitRecognition.zip';
    downloadNetFolder = tempdir;
    netFolder = fullfile(downloadNetFolder,'SpokenDigitRecognition');
    if ~exist(netFolder,'dir')
        disp('Downloading pretrained network (1 file - 12 MB) ...')
        unzip(url,downloadNetFolder)
    end
    load(fullfile(netFolder,'0.02.mat'));
end
Downloading pretrained network (1 file - 12 MB) ...

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

Используйте оптимизированные значения для количества скрытых модулей и начальной скорости обучения и переобучите сеть.

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.5000

Когда график показывает, использование Байесовой оптимизации дает к LSTM с более высокой точностью.

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

Как другой подход к задаче разговорного распознавания цифры, используйте глубокую сверточную нейронную сеть (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 2400
Processed 1000 files out of 2400
Processed 1500 files out of 2400
Processed 2000 files out of 2400
...done
XTrain = log10(XTrain + epsil);

XTest = helperspeechSpectrograms(adsTest,segmentDuration,frameDuration,hopDuration,numBands);
Computing speech spectrograms...
Processed 500 files out of 600
...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(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 (Deep Learning Toolbox).

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.1667

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

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.