В этом примере показано, как идентифицировать ключевое слово в шумной речи с помощью нейронной сети для глубокого обучения. В частности, пример использует сеть Bidirectional Long Short-Term Memory (BiLSTM) и mel-частоту cepstral коэффициенты (MFCC).
Ключевое слово, определяющее (KWS), является важной составляющей речи - помогают технологиям, где пользователь говорит предопределенное ключевое слово с пробуждением система прежде, чем говорить полную команду или запрос к устройству.
Этот пример обучает KWS глубокая сеть с последовательностями функции mel-частоты cepstral коэффициентов (MFCC). Пример также демонстрирует, как сетевая точность в шумной среде может быть улучшена с помощью увеличения данных.
Этот пример использует сети долгой краткосрочной памяти (LSTM), которые являются типом рекуррентной нейронной сети (RNN), подходящей, чтобы изучить данные timeseries и последовательность. Сеть LSTM может изучить долгосрочные зависимости между временными шагами последовательности. Слой LSTM (lstmLayer
) может посмотреть в то время последовательность в прямом направлении, в то время как двунаправленный слой LSTM (bilstmLayer
) может посмотреть в то время последовательность и во вперед и в обратные направления. Этот пример использует двунаправленный слой LSTM.
Пример использует google Speech Commands Dataset, чтобы обучить модель глубокого обучения. Чтобы запустить пример, необходимо сначала загрузить набор данных. Если вы не хотите загружать набор данных или обучать сеть, то можно загрузить предварительно обученную сеть путем открытия этого примера в MATLAB® и ввода load("KWSNet.mat")
в командной строке.
Пример проходит следующие шаги:
Смотрите базовую линию определения ключевого слова "золотого стандарта" на сигнале валидации.
Создайте учебное произнесение из бесшумного набора данных.
Обучите ключевое слово, определяющее сеть LSTM с помощью последовательностей MFCC, извлеченных из того произнесения.
Проверяйте сетевую точность путем сравнения базовой линии валидации с выходом сети, когда применено сигнал валидации.
Проверяйте сетевую точность на сигнал валидации, поврежденный шумом.
Увеличьте обучающий набор данных путем введения шума к речевым данным с помощью audioDataAugmenter
.
Переобучите сеть с увеличенным набором данных.
Проверьте, что переобученная сеть теперь дает к более высокой точности, когда применено шумный сигнал валидации.
В этом примере ключевым словом, чтобы определить является YES.
Вы используете демонстрационный речевой сигнал подтвердить сеть KWS. Сигнал валидации состоит 34 секунды речи с ключевым словом YES, появляющийся периодически.
Загрузите сигнал валидации.
[audioIn,fs] = audioread('KeywordSpeech-16-16-mono-34secs.flac');
Слушайте сигнал.
sound(audioIn,fs)
Визуализируйте сигнал.
t = (1/fs) * (0:length(audioIn)-1); plot(t,audioIn); grid on; xlabel('Time (s)') title('Validation Speech Signal')
Загрузите базовую линию KWS. Эта базовая линия была получена с помощью speech2text
: Создайте ключевое слово, определяющее маску Используя Audio Labeler.
load('KWSBaseline.mat','KWSBaseline')
Базовая линия является логическим вектором той же длины как звуковой сигнал валидации. Сегменты в audioIn
где ключевое слово произнесено, установлены в одного в KWSBaseline
.
Визуализируйте речевой сигнал наряду с базовой линией KWS.
h = figure; plot(t,audioIn) grid on xlabel('Time (s)') hold on plot(t,KWSBaseline) legend('Speech','KWS Baseline','Location','southeast') l = findall(h,'type','line'); l(1).LineWidth = 2; title("Validation Signal")
Слушайте речевые сегменты, идентифицированные как ключевые слова.
sound(audioIn(KWSBaseline),fs)
Цель сети, которую вы обучаете, состоит в том, чтобы вывести маску KWS нулей и единиц как эта базовая линия.
Загрузите обучающий набор данных с Речевого Набора данных Команд и извлеките загруженные файлы. Установите datafolder
к местоположению данных. Используйте audioDatastore
создать datastore, который содержит имена файлов. Используйте имена папок в качестве источника метки.
datafolder = PathToDatabase; ads = audioDatastore(datafolder,'LabelSource','foldername','Includesubfolders',true);
Набор данных содержит файлы фонового шума, которые не используются в этом примере. Используйте subset
создать новый datastore, который не имеет файлов фонового шума.
isBackNoise = ismember(ads.Labels,"_background_noise_");
ads = subset(ads,~isBackNoise);
Набор данных имеет приблизительно 65 000 одного второго длинного произнесения 30 коротких слов (включая ключевое слово YES). Получите отказ распределения слова в datastore.
countEachLabel(ads)
ans = 30×2 table Label Count ______ _____ bed 1713 bird 1731 cat 1733 dog 1746 down 2359 eight 2352 five 2357 four 2372 go 2372 happy 1742 house 1750 left 2353 marvin 1746 nine 2364 no 2375 off 2357 on 2367 one 2370 right 2367 seven 2377 sheila 1734 six 2369 stop 2380 three 2356 tree 1733 two 2373 up 2375 wow 1745 yes 2377 zero 2376
Разделите ads
в два хранилища данных: первый datastore содержит файлы, соответствующие ключевому слову. Второй datastore содержит все другие слова.
keyword = 'yes';
isKeyword = ismember(ads.Labels,keyword);
ads_keyword = subset(ads,isKeyword);
ads_other = subset(ads,~isKeyword);
Получите отказ распределения слова в каждом datastore.
countEachLabel(ads_keyword) countEachLabel(ads_other)
ans = 1×2 table Label Count _____ _____ yes 2377 ans = 29×2 table Label Count ______ _____ bed 1713 bird 1731 cat 1733 dog 1746 down 2359 eight 2352 five 2357 four 2372 go 2372 happy 1742 house 1750 left 2353 marvin 1746 nine 2364 no 2375 off 2357 on 2367 one 2370 right 2367 seven 2377 sheila 1734 six 2369 stop 2380 three 2356 tree 1733 two 2373 up 2375 wow 1745 zero 2376
Учебные хранилища данных содержат вторые речевые сигналы, где одно слово произнесено. Вы создадите более комплексное учебное речевое произнесение, которое содержит смесь ключевого слова наряду с другими словами.
Вот пример созданного произнесения. Считайте одно ключевое слово из datastore ключевого слова и нормируйте его, чтобы иметь максимальное значение одного.
yes = read(ads_keyword); yes = yes / max(abs(yes));
Сигнал имеет неречевые фрагменты (тишина, фоновый шум, и т.д.), которые не содержат полезную информацию о речи. Этот пример удаляет тишину с помощью простого подхода пороговой обработки, идентичного тому, используемому в, Классифицируют Пол Используя Сети LSTM.
Получите запуск и индексы конца полезного фрагмента сигнала.
[~,~,startIndex,endIndex] = HelperGetSpeechSegments(yes,fs);
Случайным образом выберите количество слов, чтобы использовать в синтезируемом учебном предложении. Используйте максимум 10 слов.
numWords = randi([0 10]);
Случайным образом выберите местоположение, в котором происходит ключевое слово.
keywordLocation = randi([1 numWords+1]);
Считайте желаемое количество произнесения неключевого слова и создайте учебное предложение и маску.
sentence = []; mask = []; for index = 1:numWords+1 if index == keywordLocation sentence = [sentence;yes]; %#ok newMask = zeros(size(yes)); newMask(startIndex:endIndex) = 1; mask = [mask;newMask]; %#ok else other = read(ads_other); other = other ./ max(abs(other)); sentence = [sentence;other]; %#ok mask = [mask;zeros(size(other))]; %#ok end end
Постройте учебное предложение наряду с маской.
figure t = (1/fs) * (0:length(sentence)-1); h = figure; plot(t,sentence) grid on hold on plot(t,mask) xlabel('Time (s)') legend('Training Signal' , 'Mask','Location','southeast') l = findall(h,'type','line'); l(1).LineWidth = 2; title("Example Utterance")
Слушайте учебное предложение.
sound(sentence,fs)
Этот пример обучает нейронную сеть для глубокого обучения с помощью 42 коэффициентов MFCC (14 MFCC, 14 дельт и 14 коэффициентов дельты дельты).
Задайте параметры, требуемые для экстракции MFCC.
WindowLength = 512; OverlapLength = 384;
Извлеките функции MFCC.
[coeffs,delta,deltaDelta] = mfcc(sentence,fs,'WindowLength',WindowLength,'OverlapLength',OverlapLength);
Конкатенация коэффициентов в одну матрицу функции.
featureMatrix = [coeffs delta deltaDelta]; size(featureMatrix)
ans = 1113 42
Обратите внимание на то, что вы вычисляете MFCC путем скольжения окна через вход, таким образом, матрица функции короче, чем входной речевой сигнал. Каждая строка в featureMatrix соответствует 128 выборкам от речевого сигнала (WindowLength-OverlapLength).
Вычислите маску той же длины как featureMatrix.
HopLength = WindowLength - OverlapLength; range = (HopLength) * (1:size(coeffs,1)) + HopLength; featureMask = zeros(size(range)); for index = 1:numel(range) featureMask(index) = mode(mask( (index-1)*HopLength+1:(index-1)*HopLength+WindowLength )); end
Синтез предложения и извлечение признаков для целого обучающего набора данных могут быть довольно длительными. Чтобы ускорить обработку, если у вас есть Parallel Computing Toolbox™, делят учебный datastore и процесс каждый раздел на отдельном рабочем.
Выберите много разделов datastore.
numPartitions = 6;
Инициализируйте массивы ячеек для матриц функции и масок.
TrainingFeatures = {}; TrainingMasks = {};
Выполните синтез предложения, извлечение признаков и создание маски с помощью parfor.
tic parfor ii = 1:numPartitions subads_keyword = partition(ads_keyword,numPartitions,ii); subads_other = partition(ads_other,numPartitions,ii); count = 1; localFeatures = cell(length(subads_keyword.Files),1); localMasks = cell(length(subads_keyword.Files),1); while hasdata(subads_keyword) % Create a training sentence [sentence,mask] = synthesizeSentence(subads_keyword,subads_other,fs); % Compute mfcc features [coeffs,delta,deltaDelta] = mfcc(sentence,fs,'WindowLength',WindowLength,'OverlapLength',OverlapLength); featureMatrix = [coeffs delta deltaDelta]; featureMatrix(~isfinite(featureMatrix)) = 0; % Create mask hopLength = WindowLength - OverlapLength; range = (hopLength) * (1:size(coeffs,1)) + hopLength; featureMask = zeros(size(range)); for index = 1:numel(range) featureMask(index) = mode(mask( (index-1)*hopLength+1:(index-1)*hopLength+WindowLength )); end localFeatures{count} = featureMatrix; catVect = categorical(featureMask); catVect = addcats(catVect,{'1'}); localMasks{count} = catVect; count = count + 1; end TrainingFeatures = [TrainingFeatures;localFeatures]; TrainingMasks = [TrainingMasks;localMasks]; end fprintf('Training feature extraction took %f seconds.\n',toc)
Starting parallel pool (parpool) using the 'local' profile ... Connected to the parallel pool (number of workers: 6). Training feature extraction took 114.697514 seconds.
Это - хорошая практика, чтобы нормировать все функции, чтобы иметь нулевое среднее значение и стандартное отклонение единицы. Вычислите среднее и стандартное отклонение для каждого коэффициента и используйте их, чтобы нормировать данные.
sampleFeature = TrainingFeatures{1}; numFeatures = size(sampleFeature,2); featuresMatrix = cat(1,TrainingFeatures{:}); M = mean(featuresMatrix); S = std(featuresMatrix); for index = 1:length(TrainingFeatures) f = TrainingFeatures{index}; f = (f - M) ./ S; TrainingFeatures{index} = f.'; %#ok end
Извлеките функции MFCC из сигнала валидации.
[coeffs,delta,deltaDelta] = mfcc(audioIn,fs,'WindowLength',WindowLength,'OverlapLength',OverlapLength); featureMatrix = [coeffs,delta,deltaDelta]; featureMatrix(~isfinite(featureMatrix)) = 0;
Нормируйте функции валидации.
FeaturesValidationClean = (featureMatrix - M)./S; range = (HopLength) * (1:size(FeaturesValidationClean,1)) + HopLength;
Создайте валидацию маска KWS.
featureMask = zeros(size(range)); for index = 1:numel(range) featureMask(index) = mode(KWSBaseline( (index-1)*HopLength+1:(index-1)*HopLength+WindowLength )); end BaselineV = categorical(featureMask);
Сети LSTM могут изучить долгосрочные зависимости между временными шагами данных о последовательности. Этот пример использует двунаправленный слой LSTM bilstmLayer
смотреть на последовательность и во вперед и в обратные направления.
Задайте входной размер, чтобы быть последовательностями размера numFeatures
. Задайте два скрытых двунаправленных слоя LSTM с выходным размером 150 и выведите последовательность. Эта команда дает двунаправленному слою LSTM команду сопоставлять входные временные ряды в 150 функций, которые передаются следующему слою. Задайте два класса включением полносвязного слоя размера 2, сопровождаемый softmax слоем и слоем классификации.
layers = [ ... sequenceInputLayer(numFeatures) bilstmLayer(150,"OutputMode","sequence") bilstmLayer(150,"OutputMode","sequence") fullyConnectedLayer(2) softmaxLayer classificationLayer ];
Задайте опции обучения для классификатора. Установите MaxEpochs
к 10 так, чтобы сеть сделала 10, проходит через обучающие данные. Установите MiniBatchSize
к 64
так, чтобы сеть посмотрела на 64 учебных сигнала за один раз. Установите Plots
к "training-progress"
сгенерировать графики, которые показывают процесс обучения количеством увеличений итераций. Установите Verbose
к false
отключить печать таблицы выход, который соответствует данным, показанным в графике. Установите Shuffle
к "every-epoch"
переставить обучающую последовательность в начале каждой эпохи. Установите LearnRateSchedule
к "piecewise"
чтобы уменьшить темп обучения заданным фактором (0.1) каждый раз, определенное число эпох (4) передало. Установите ValidationData
к предикторам валидации и целям.
Этот пример использует адаптивную оценку момента (ADAM) решатель. ADAM выполняет лучше с рекуррентными нейронными сетями (RNNs) как LSTMs, чем стохастический градиентный спуск по умолчанию с импульсом (SGDM) решатель.
maxEpochs = 10; miniBatchSize = 64; options = trainingOptions("adam", ... "InitialLearnRate",1e-4,... "MaxEpochs",maxEpochs, ... "MiniBatchSize",miniBatchSize, ... "Shuffle","every-epoch",... "Verbose",false, ... "ValidationFrequency",floor(numel(TrainingFeatures)/miniBatchSize),... "ValidationData",{FeaturesValidationClean.',BaselineV},... "Plots","training-progress",... "LearnRateSchedule","piecewise",... "LearnRateDropFactor",0.1, ... "LearnRateDropPeriod",5);
Обучите сеть LSTM с заданными опциями обучения и архитектурой слоя с помощью trainNetwork
. Поскольку набор обучающих данных является большим, учебный процесс может занять несколько минут.
doTraining = true; if doTraining [keywordNetNoAugmentation,info] = trainNetwork(TrainingFeatures,TrainingMasks,layers,options); fprintf("Validation accuracy: %f percent.\n" , info.ValidationAccuracy(end)); else load('keywordNetNoAugmentation.mat','keywordNetNoAugmentation','M','S');%#ok end
Validation accuracy: 89.470030 percent.
Оцените маску KWS для сигнала валидации использование обучившего сеть.
v = classify(keywordNetNoAugmentation , FeaturesValidationClean.');
Вычислите и постройте матрицу беспорядка валидации от векторов фактических и предполагаемых меток.
figure cm = confusionchart(BaselineV,v,"title","Validation Accuracy"); cm.ColumnSummary = "column-normalized"; cm.RowSummary = "row-normalized";
Преобразуйте сетевой выход от категориального, чтобы удвоиться.
v = double(v) - 1; v = repmat(v,HopLength,1); v = v(:);
Слушайте области ключевого слова, идентифицированные сетью.
sound(audioIn(logical(v)),fs)
Визуализируйте предполагаемые и ожидаемые маски KWS.
baseline = double(BaselineV) - 1; baseline = repmat(baseline,HopLength,1); baseline = baseline(:); t = (1/fs) * (0:length(v)-1); h = figure; plot(t,audioIn(1:length(v))) grid on hold on plot(t,v) plot(t,0.8 * baseline) xlabel('Time (s)') legend('Training Signal','Network Mask','Baseline Mask','Location','southeast') l = findall(h,'type','line'); l(1).LineWidth = 2; l(2).LineWidth = 2; title('Results for Noise-Free Speech')
Вы будете теперь проверять сетевую точность на шумный речевой сигнал. Сигнал с шумом был получен путем повреждения чистого сигнала валидации аддитивным белым Гауссовым шумом.
Загрузите сигнал с шумом.
[audioInNoisy,fs] = audioread('NoisyKeywordSpeech-16-16-mono-34secs.flac');
sound(audioInNoisy,fs)
Визуализируйте сигнал.
figure t = (1/fs) * (0:length(audioInNoisy)-1); plot(t,audioInNoisy); grid on; xlabel('Time (s)') title('Noisy Validation Speech Signal')
Извлеките матрицу функции из сигнала с шумом.
[coeffs,delta,deltaDelta] = mfcc(audioInNoisy,fs,'WindowLength',WindowLength,'OverlapLength',OverlapLength); featureMatrixV = [coeffs,delta,deltaDelta]; featureMatrixV(~isfinite(featureMatrixV)) = 0; FeaturesValidationNoisy = (featureMatrixV - M)./S;
Передайте матрицу функции сети.
v = classify(keywordNetNoAugmentation,FeaturesValidationNoisy.');
Сравните сетевой выход с базовой линией. Обратите внимание на то, что точность ниже, чем та, которую вы получили для чистого сигнала.
figure cm = confusionchart(BaselineV,v,"title","Validation Accuracy - Noisy Speech"); cm.ColumnSummary = "column-normalized"; cm.RowSummary = "row-normalized";
Преобразуйте сетевой выход от категориального, чтобы удвоиться.
v = double(v) - 1; v = repmat(v,HopLength,1); v = v(:);
Слушайте области ключевого слова, идентифицированные сетью.
sound(audioIn(logical(v)),fs)
Визуализируйте предполагаемые и базовые маски.
t = (1/fs) * (0:length(v)-1); h = figure; plot(t,audioInNoisy(1:length(v))) grid on hold on plot(t,v) plot(t,0.8 * baseline) xlabel('Time (s)') legend('Training Signal','Network Mask','Baseline Mask','Location','southeast') l = findall(h,'type','line'); l(1).LineWidth = 2; l(2).LineWidth = 2; title('Results for Noisy Speech - No Data Augmentation')
Обучивший сеть не выполнял хорошо на сигнале с шумом, потому что обученный набор данных содержал только бесшумные предложения. Вы исправите это путем увеличения набора данных, чтобы включать шумные предложения.
Используйте audioDataAugmenter
увеличивать ваш набор данных.
ada = audioDataAugmenter('TimeStretchProbability',0,... 'PitchShiftProbability',0,... 'VolumeControlProbability',0,... 'TimeShiftProbability',0,... 'SNRRange',[-1 1],... 'AddNoiseProbability',.85);
С этими настройками, audioDataAugmenter
возразите повреждает входной звуковой сигнал с белым гауссовым шумом с вероятностью 85%. ОСШ случайным образом выбран из области значений [-1 1] (в дБ). Существует 15%-я вероятность, что увеличение не изменяет ваш входной сигнал.
Как пример, передайте звуковой сигнал увеличению.
reset(ads_keyword) x = read(ads_keyword); data = augment(ada,x,fs)
data = 1×2 table Audio AugmentationInfo ________________ ________________ {16000×1 double} [1×1 struct]
Смотрите AugmentationInfo
переменная в data
проверять, как сигнал был изменен.
data.AugmentationInfo
ans = struct with fields: SNR: 0.5247
Сбросьте хранилища данных.
reset(ads_keyword) reset(ads_other)
Инициализируйте ячейки маски и функция.
TrainingFeatures = {}; TrainingMasks = {};
Выполните извлечение признаков снова. Каждый сигнал повреждается шумом с вероятностью 85%, таким образом, ваш увеличенный набор данных имеет приблизительно 85%-е зашумленные данные и 15%-е бесшумные данные.
tic parfor ii = 1:numPartitions subads_keyword = partition(ads_keyword,numPartitions,ii); subads_other = partition(ads_other,numPartitions,ii); count = 1; localFeatures = cell(length(subads_keyword.Files),1); localMasks = cell(length(subads_keyword.Files),1); while hasdata(subads_keyword) [sentence,mask] = synthesizeSentence(subads_keyword,subads_other,fs); % Corrupt with noise augmentedData = augment(ada,sentence,fs); sentence = augmentedData.Audio{1}; % Compute mfcc features [coeffs,delta,deltaDelta] = mfcc(sentence,fs,'WindowLength',WindowLength,'OverlapLength',OverlapLength); featureMatrix = [coeffs delta deltaDelta]; featureMatrix(~isfinite(featureMatrix)) = 0; hopLength = WindowLength - OverlapLength; range = (hopLength) * (1:size(coeffs,1)) + hopLength; featureMask = zeros(size(range)); for index = 1:numel(range) featureMask(index) = mode(mask( (index-1)*hopLength+1:(index-1)*hopLength+WindowLength )); end localFeatures{count} = featureMatrix; catVect = categorical(featureMask); catVect = addcats(catVect,{'1'}); localMasks{count} = catVect; count = count + 1; end TrainingFeatures = [TrainingFeatures;localFeatures]; TrainingMasks = [TrainingMasks;localMasks]; end fprintf('Training feature extraction took %f seconds.\n',toc)
Training feature extraction took 61.492070 seconds.
Вычислите среднее и стандартное отклонение для каждого коэффициента; используйте их, чтобы нормировать данные.
sampleFeature = TrainingFeatures{1}; numFeatures = size(sampleFeature,2); featuresMatrix = cat(1,TrainingFeatures{:}); M = mean(featuresMatrix); S = std(featuresMatrix); for index = 1:length(TrainingFeatures) f = TrainingFeatures{index}; f = (f - M) ./ S; TrainingFeatures{index} = f.'; %#ok end
Нормируйте функции валидации с новыми значениями среднего и стандартного отклонения.
FeaturesValidationNoisy = (featureMatrixV - M)./S;
Воссоздайте опции обучения. Используйте шумные базовые функции и маску для валидации.
options = trainingOptions("adam", ... "InitialLearnRate",1e-4,... "MaxEpochs",maxEpochs, ... "MiniBatchSize",miniBatchSize, ... "Shuffle","every-epoch",... "Verbose",false, ... "ValidationFrequency",floor(numel(TrainingFeatures)/miniBatchSize),... "ValidationData",{FeaturesValidationNoisy.',BaselineV},... "Plots","training-progress",... "LearnRateSchedule","piecewise",... "LearnRateDropFactor",0.1, ... "LearnRateDropPeriod",5);
Обучите сеть.
if doTraining [KWSNet,info] = trainNetwork(TrainingFeatures,TrainingMasks,layers,options); else load('KWSNet.mat','KWSNet');%#ok end
Проверьте сетевую точность на сигнале валидации.
v = classify(KWSNet,FeaturesValidationNoisy.');
Сравните предполагаемые и ожидаемые маски KWS.
figure cm = confusionchart(BaselineV,v,"title","Validation Accuracy with Data Augmentation"); cm.ColumnSummary = "column-normalized"; cm.RowSummary = "row-normalized";
Слушайте идентифицированные области ключевого слова.
v = double(v) - 1; v = repmat(v,HopLength,1); v = v(:); sound(audioIn(logical(v)),fs)
Визуализируйте предполагаемые и ожидаемые маски.
h = figure; plot(t,audioInNoisy(1:length(v))) grid on hold on plot(t,v) plot(t,.8 * baseline) xlabel('Time (s)') legend('Training Signal' , 'Network Mask','Baseline Mask','Location','southeast') l = findall(h,'type','line'); l(1).LineWidth = 2; l(2).LineWidth = 2; title('Results for Noisy Speech - With Data Augmentation')