Этот пример показывает, как обнаружить области речи в низкой среде сигнала к шуму с помощью глубокого обучения. Пример использует Речевой Набор данных Команд, чтобы обучить сеть Bidirectional Long Short-Term Memory (BiLSTM) обнаруживать речевое действие.
Речевое обнаружение действия является важной составляющей многих аудиосистем, таких как распознавание динамика и автоматическое распознавание речи. Речевое обнаружение действия может быть особенно сложным в низком сигнале к шуму (ОСШ) ситуации, где речь затруднена шумом.
Этот пример использует сети долгой краткосрочной памяти (LSTM), которые являются типом рекуррентной нейронной сети (RNN), подходящей, чтобы изучить данные timeseries и последовательность. Сеть LSTM может изучить долгосрочные зависимости между временными шагами последовательности. Слой LSTM (lstmLayer
) может посмотреть в то время последовательность в прямом направлении, в то время как двунаправленный слой LSTM (bilstmLayer
) может посмотреть в то время последовательность и во вперед и в обратные направления. Этот пример использует двунаправленный слой LSTM.
Этот пример обучает речевое обнаружение действия двунаправленная сеть LSTM с последовательностями функции спектральных характеристик и гармонической метрики отношения.
Чтобы запустить пример, необходимо сначала загрузить набор данных. Если вы не хотите загружать набор данных или обучать сеть, то можно загрузить предварительно обученную сеть путем открытия этого примера в MATLAB® и ввода load("speechDetectNet.mat")
в командной строке.
Пример проходит следующие шаги:
Обучение:
Создайте audioDatastore
, который указывает на аудио речевые файлы, используемые, чтобы обучить сеть LSTM.
Создайте учебный сигнал, состоящий из речевых сегментов, разделенных сегментами тишины переменной длительности.
Повредите сигнал речи плюс тишина с шумом стиральной машины (ОСШ =-10 дБ).
Извлеките последовательности функции, состоящие из спектральных характеристик и гармонического отношения от сигнала с шумом.
Обучите сеть LSTM с помощью последовательностей функции, чтобы идентифицировать области речевого действия.
Предсказание:
Создайте audioDatastore
речевых файлов, используемых, чтобы протестировать обучивший сеть, и создать тестовый сигнал, состоящий из речи, разделенной сегментами тишины.
Повредите тестовый сигнал с шумом стиральной машины (ОСШ =-10 дБ).
Извлеките последовательности функции от шумного тестового сигнала.
Идентифицируйте области речевого действия путем передачи тестовых функций через обучивший сеть.
Сравните точность сети с речевой базовой линией действия от тестового сигнала сигнала плюс тишина.
Вот эскиз учебного процесса.
Вот эскиз процесса прогноза. Вы используете обучивший сеть, чтобы сделать прогнозы.
Загрузите набор данных с https://download.tensorflow.org/data/speech_commands_v0.01.tar.gz и извлеките загруженный файл. Установите datafolder
на местоположение данных. Используйте audioDatastore
, чтобы создать datastore, который содержит имена файлов.
datafolder = PathToDatabase;
ads = audioDatastore(datafolder,"Includesubfolders",true);
Набор данных содержит файлы фонового шума, которые не используются в этом примере. Используйте subset
, чтобы создать новый datastore, который не имеет файлов фонового шума.
indices = cellfun(@(c)~contains(c,"_background_noise_"),ads.Files);
ads = subset(ads,indices);
Папка набора данных содержит текстовые файлы, который перечисляет, которым звуковые файлы должны быть в наборе валидации и которым звуковые файлы должны быть в наборе тестов. Они предопределили валидацию, и наборы тестов не содержат произнесение того же слова тем же человеком, таким образом, лучше использовать эти предопределенные наборы, чем выбрать случайное подмножество целого набора данных. Используйте функцию поддержки splitData
, чтобы разделить datastore в наборы обучения и валидации на основе списка валидации и тестовых файлов.
[adsTrain,adsValidation] = splitData(ads,datafolder);
Переставьте порядок файлов в хранилищах данных.
adsTrain = shuffle(adsTrain); adsValidation = shuffle(adsValidation);
Считайте содержимое звукового файла с помощью read
. Получите частоту дискретизации от struct info
.
[data,info] = read(adsTrain); Fs = info.SampleRate;
Слушайте звуковой сигнал с помощью звуковой команды.
sound(data,Fs)
Постройте звуковой сигнал.
timeVector = (1/Fs) * (0:numel(data)-1); plot(timeVector,data) ylabel("Amplitude") xlabel("Time (s)") title("Sample Audio") grid on
Сигнал имеет неречевые фрагменты (тишина, фоновый шум, и т.д.), которые не содержат полезную информацию о речи. Этот пример удаляет тишину с помощью простого подхода пороговой обработки, идентичного тому, используемому в, Классифицируют Пол Используя Длинные Краткосрочные Сети Памяти.
Извлеките полезный фрагмент данных. Постройте новый звуковой сигнал и слушайте его с помощью команды sound
.
segments = HelperGetSpeechSegments(data,Fs); speechData = segments{1}; figure timeVector = (1/Fs) * (0:numel(speechData)-1); plot(timeVector,speechData) ylabel("Amplitude") xlabel("Time (s)") title("Sample Audio") grid on
Создайте 1000-секундный учебный сигнал путем объединения нескольких речевых файлов от обучающего набора данных. Используйте HelperGetSpeechSegments
, чтобы удалить нежелательные фрагменты каждого файла. Вставьте случайный период тишины между речевыми сегментами.
Предварительно выделите учебный сигнал.
duration = 1000*Fs; audioTraining = zeros(duration,1);
Предварительно выделите речевую маску обучения действию. Значения 1 в маске соответствуют выборкам, расположенным в областях с речевым действием. Значения 0 соответствуют областям без речевого действия.
maskTraining = zeros(duration,1);
Задайте максимальную длительность сегмента тишины 2 секунд.
maxSilenceSegment = 2;
Создайте учебный сигнал путем вызова read
на datastore в цикле.
numSamples = 1; while numSamples < duration data = read(adsTrain); % Remove non-voice areas from the segment data = HelperGetSpeechSegments(data,Fs); data = data{1}; % Write speech segment to training signal audioTraining(numSamples:numSamples+numel(data)-1) = data; % Set VAD baseline maskTraining(numSamples:numSamples+numel(data)-1) = true; % Random silence period numSilenceSamples = randi(maxSilenceSegment * Fs,1,1); numSamples = numSamples + numel(data) + numSilenceSamples; end
Визуализируйте 10 вторых фрагментов учебного сигнала. Постройте базовую речевую маску действия.
figure range = 1:10*Fs; plot((1/Fs)*(range-1),audioTraining(range)); hold on plot((1/Fs)*(range-1),maskTraining(range)); grid on lines = findall(gcf,"Type","Line"); lines(1).LineWidth = 2; xlabel("Time (s)") legend("Signal","Speech Area") title("Training Signal (first 10 seconds)");
Слушайте первые 10 секунд учебного сигнала.
sound(audioTraining(range),Fs);
Повредите учебный сигнал с шумом стиральной машины путем добавления, что шум стиральной машины к речи сигнализирует таким образом, что отношение сигнал-шум составляет-10 дБ.
Считайте шум на 8 кГц и преобразуйте его в 16 кГц.
noise = audioread("WashingMachine-16-8-mono-1000secs.wav");
noise = resample(noise,2,1);
Повредите учебный сигнал с шумом.
audioTraining = audioTraining(1:numel(noise)); SNR = -10; noise = 10^(-SNR/20) * noise * norm(audioTraining) / norm(noise); audioTrainingNoisy = audioTraining + noise; audioTrainingNoisy = audioTrainingNoisy / max(abs(audioTrainingNoisy));
Визуализируйте 10 вторых фрагментов шумного учебного сигнала. Постройте базовую речевую маску действия.
figure plot((1/Fs)*(range-1),audioTrainingNoisy(range)); hold on plot((1/Fs)*(range-1),maskTraining(range)); grid on lines = findall(gcf,"Type","Line"); lines(1).LineWidth = 2; xlabel("Time (s)") legend("Noisy Signal","Speech Area") title("Training Signal (first 10 seconds)");
Слушайте первые 10 секунд шумного учебного сигнала.
sound(audioTrainingNoisy(range),Fs)
Обратите внимание на то, что вы получили базовую речевую маску действия с помощью бесшумного сигнала речи плюс тишина. Проверьте, что использование HelperGetSpeechSegments
на поврежденном шумом сигнале не приводит к хорошим результатам.
[~,noisyMask] = HelperGetSpeechSegments(audioTrainingNoisy,Fs);
Визуализируйте 10 вторых фрагментов шумного учебного сигнала. Постройте речевую маску действия, полученную путем анализа сигнала с шумом.
figure plot((1/Fs)*(range-1),audioTrainingNoisy(range)); hold on plot((1/Fs)*(range-1),noisyMask(range)); grid on lines = findall(gcf,"Type","Line"); lines(1).LineWidth = 2; xlabel("Time (s)") legend("Noisy Signal","Mask from Noisy Signal") title("Training Signal (first 10 seconds)");
Создайте 200-секундный шумный речевой сигнал подтвердить обучивший сеть. Используйте datastore валидации. Обратите внимание на то, что валидация и учебные хранилища данных имеют различные динамики.
Предварительно выделите сигнал валидации и маску валидации. Вы будете использовать эту маску, чтобы оценить точность обучившего сеть.
duration = 200*Fs; audioValidation = zeros(duration,1); maskValidation = zeros(duration,1);
Создайте сигнал валидации путем вызова read
на datastore в цикле.
numSamples = 1; while numSamples < duration data = read(adsValidation); % Remove non-voice areas from the segment data = HelperGetSpeechSegments(data,Fs); data = data{1}; % Write speech segment to training signal audioValidation(numSamples:numSamples+numel(data)-1) = data; maskValidation(numSamples:numSamples+numel(data)-1) = true; % Random silence period numSilenceSamples = randi(maxSilenceSegment*Fs,1,1); numSamples = numSamples + numel(data) + numSilenceSamples; end
Повредите сигнал валидации с шумом стиральной машины путем добавления, что шум стиральной машины к речи сигнализирует таким образом, что отношение сигнал-шум составляет-10 дБ. Используйте различный шумовой файл для сигнала валидации, чем вы сделали для учебного сигнала.
noise = audioread("WashingMachine-16-8-mono-200secs.wav");
noise = resample(noise,2,1);
audioValidation = audioValidation(1:numel(noise));
noise = 10^(-SNR/20) * noise * norm(audioValidation) / norm(noise);
audioValidationNoisy = audioValidation + noise;
audioValidationNoisy = audioValidationNoisy / max(abs(audioValidationNoisy));
Этот пример обучает сеть LSTM, использующую следующие функции:
Задайте системные параметры.
WindowLength = 256; params.WindowLength = WindowLength; params.Window = hann(WindowLength,"Periodic"); params.OverlapLength = 128; params.FFTLength = 256; params.Range = [0, Fs/2]; params.SpectrumType = "Power";
Вычислите спектр мощности учебного сигнала.
[~,frequencyVector,~,S] = spectrogram(audioTrainingNoisy,hann(WindowLength,"Periodic"),128,WindowLength,Fs,"power" ,"onesided");
Извлеките функции и затем конкатенируйте их.
SCentroid = spectralCentroid(S,frequencyVector); SCrest = spectralCrest(S,frequencyVector); Sentropy = spectralEntropy(S,frequencyVector); SFlux = spectralFlux(S,frequencyVector); SKurtosis = spectralKurtosis(S,frequencyVector); SRolloffPoint = spectralRolloffPoint(S,frequencyVector); SSkewness = spectralSkewness(S,frequencyVector); SSlope = spectralSlope(S,frequencyVector); hr = harmonicRatio(audioTrainingNoisy,Fs,"Window",params.Window,"OverlapLength",params.OverlapLength); featuresTraining = [SCentroid SCrest Sentropy SFlux SKurtosis SRolloffPoint SSkewness SSlope hr];
Отобразите размерности матрицы функций. Первая размерность соответствует количеству окон, в сигнал ворвались (это зависит от длины окна и длины перекрытия). Второе измерение является количеством функций, использованных в этом примере.
[numWindows,numFeatures] = size(featuresTraining)
numWindows = 124999 numFeatures = 9
В приложениях классификации это - хорошая практика, чтобы нормировать все функции, чтобы иметь нулевое среднее значение и стандартное отклонение единицы.
Вычислите среднее и стандартное отклонение для каждого коэффициента и используйте их, чтобы нормировать данные.
M = mean(featuresTraining,1); S = std(featuresTraining,[],1); featuresTraining = (featuresTraining - M) ./ S;
Извлеките функции от сигнала валидации использование того же процесса. Функция помощника getSpeechActivityFeatures
инкапсулирует шаги нормализации и выделение признаков.
featuresValidation = getSpeechActivityFeatures(audioValidationNoisy,Fs,params);
Каждая функция соответствует 128 выборкам данных (длина транзитного участка). Для каждого транзитного участка, установленного ожидаемое речевое/нет речевое значение к режиму базовых значений маски, соответствующих тем 128 выборкам. Преобразуйте речевую/нет речевую маску в категориальный.
hopLength = (WindowLength - params.OverlapLength); range = (hopLength) * (1:size(featuresTraining,1)) + hopLength; maskMode = zeros(size(range)); for index = 1:numel(range) maskMode(index) = mode(maskTraining( (index-1)*hopLength+1:(index-1)*hopLength+WindowLength )); end maskTraining = maskMode.'; maskTrainingCat = categorical(maskTraining);
Сделайте то же самое для маски валидации.
range = (hopLength) * (1:size(featuresValidation,1)) + hopLength; maskMode = zeros(size(range)); for index = 1:numel(range) maskMode(index) = mode(maskValidation( (index-1)*hopLength+1:(index-1)*hopLength+WindowLength )); end maskValidation = maskMode.'; maskValidationCat = categorical(maskValidation);
Разделите учебные функции и маску в последовательности длины 800 с 75%-м перекрытием между последовательными последовательностями.
sequenceLength = 800; trainFeatureCell = {}; trainLabelCell = {}; index = 1; while index < size(featuresTraining,1) - sequenceLength trainFeatureCell{end+1} = featuresTraining(index:index+sequenceLength-1,:).'; %#ok trainLabelCell{end+1} = maskTrainingCat(index:index+sequenceLength-1).'; %#ok index = index + round(sequenceLength/4); end
Сети LSTM могут изучить долгосрочные зависимости между временными шагами данных о последовательности. Этот пример использует двунаправленный слой LSTM bilstmLayer
, чтобы посмотреть на последовательность и во вперед и в обратные направления.
Задайте входной размер, чтобы быть последовательностями длины 9
(количество функций). Задайте скрытый двунаправленный слой LSTM с выходным размером 200 и выведите последовательность. Эта команда дает двунаправленному слою LSTM команду сопоставлять входные временные ряды в 200 функций, которые передаются следующему слою. Затем задайте двунаправленный слой LSTM с выходным размером 200 и выведите последний элемент последовательности. Эта команда дает двунаправленному слою LSTM команду сопоставлять свой вход в 200 функций и затем готовит вывод к полносвязному слою. Наконец, задайте два класса включением полносвязного слоя размера 2
, сопровождаемый softmax слоем и слоем классификации.
layers = [ ... sequenceInputLayer( size(featuresValidation,2) ) bilstmLayer(200,"OutputMode","sequence") bilstmLayer(200,"OutputMode","sequence") fullyConnectedLayer(2) softmaxLayer classificationLayer ];
Затем, задайте опции обучения для классификатора. Установите MaxEpochs
на 20
так, чтобы сеть сделала 20, проходит через данные тренировки. Установите MiniBatchSize
на 64
так, чтобы сеть посмотрела на 64 учебных сигнала за один раз. Установите Plots
на "training-progress"
генерировать графики, которые показывают учебный прогресс количеством увеличений итераций. Установите Verbose
на false
отключать печать таблицы вывод, который соответствует данным, показанным в графике. Установите Shuffle
на "every-epoch"
переставлять обучающую последовательность в начале каждой эпохи. Установите LearnRateSchedule
на "piecewise"
уменьшать темп обучения заданным фактором (0.1) каждый раз, когда определенное число эпох (10) передало. Установите ValidationData
на предикторы валидации и цели.
Этот пример использует адаптивную оценку момента (ADAM) решатель. ADAM выполняет лучше с рекуррентными нейронными сетями (RNNs) как LSTMs, чем стохастический спуск градиента по умолчанию с импульсом (SGDM) решатель.
maxEpochs = 20; miniBatchSize = 64; options = trainingOptions("adam", ... "MaxEpochs",maxEpochs, ... "MiniBatchSize",miniBatchSize, ... "Shuffle","every-epoch",... "Verbose",0, ... "SequenceLength",sequenceLength,... "ValidationFrequency",floor(numel(trainFeatureCell)/miniBatchSize),... "ValidationData",{featuresValidation.',maskValidationCat.'},... "Plots","training-progress",... "LearnRateSchedule","piecewise",... "LearnRateDropFactor",0.1, ... "LearnRateDropPeriod",10);
Обучите сеть LSTM с заданными опциями обучения и архитектурой слоя с помощью trainNetwork
. Поскольку набор обучающих данных является большим, учебный процесс может занять несколько минут.
doTraining = true; if doTraining [speechDetectNet,info] = trainNetwork(trainFeatureCell,trainLabelCell,layers,options); fprintf("Validation accuracy: %f percent.\n" , info.ValidationAccuracy(end)); else load speechDetectNet end
Validation accuracy: 90.734375 percent.
Оцените речевое действие в сигнале валидации использование обучившего сеть. Преобразуйте предполагаемую маску VAD от категориального, чтобы удвоиться.
EstimatedVADMask = classify(speechDetectNet,featuresValidation.'); EstimatedVADMask = double(EstimatedVADMask); EstimatedVADMask = EstimatedVADMask.' - 1;
Вычислите и постройте матрицу беспорядка валидации от векторов фактических и предполагаемых меток.
figure cm = confusionchart(maskValidation,EstimatedVADMask,"title","Validation Accuracy"); cm.ColumnSummary = "column-normalized"; cm.RowSummary = "row-normalized";
Каждое значение маски соответствует 128 выборкам (длина транзитного участка). Повторите значения маски, чтобы сделать маску из той же длины как звуковой сигнал валидации. Сделайте то же самое для базовой маски.
EstimatedVADMask = repmat(EstimatedVADMask,1,hopLength); EstimatedVADMask = reshape(EstimatedVADMask.',numel(EstimatedVADMask),1); ActualVADMask = repmat(maskValidation,1,hopLength); ActualVADMask = reshape(ActualVADMask.',numel(ActualVADMask),1);
Изолируйте области предполагаемого речевого действия путем установки областей без речи на NaN
.
audioEst = audioValidationNoisy; audioEst(EstimatedVADMask == 0) = NaN; audioAct = audioValidation; audioAct(ActualVADMask == 0) = NaN;
Визуализируйте предполагаемые и фактические области речевого действия для 50 вторых фрагментов сигнала валидации.
figure range = 50*Fs:100*Fs; subplot(2,1,1) plot(range/Fs,[audioValidationNoisy(range),audioEst(range)]) title("Estimated Speech Region") legend("Signal","Speech Detected") grid on subplot(2,1,2) plot(range/Fs,[audioValidation(range),audioAct(range)]) title("Baseline") legend("Signal","Speech Detected") grid on xlabel("Time (s)")