В этом примере показано, как обнаружить области речи в низкой среде сигнала к шуму с помощью глубокого обучения. Пример использует Речевой Набор данных Команд, чтобы обучить сеть 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://storage.googleapis.com/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
. Получите частоту дискретизации от info
struct ().
[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
Сигнал имеет неречевые фрагменты (тишина, фоновый шум, и т.д.), которые не содержат полезную информацию о речи. Этот пример удаляет тишину с помощью простого подхода пороговой обработки, идентичного тому, используемому в, Классифицируют Пол Используя Сети LSTM (Audio Toolbox).
Извлеките полезный фрагмент данных. Постройте новый звуковой сигнал и слушайте его с помощью 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.820313 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)")
trainNetwork
| trainingOptions