В этом примере показано, как обнаружить области речи в низкой среде сигнала к шуму с помощью глубокого обучения. Пример использует Речевой Набор данных Команд, чтобы обучить сеть Bidirectional Long Short-Term Memory (BiLSTM) обнаруживать речевое действие.
Речевое обнаружение действия является важной составляющей многих аудиосистем, таких как распознавание динамика и автоматическое распознавание речи. Речевое обнаружение действия может быть особенно сложным в низком сигнале к шуму (ОСШ) ситуации, где речь затруднена шумом.
Этот пример использует сети долгой краткосрочной памяти (LSTM), которые являются типом рекуррентной нейронной сети (RNN), подходящей, чтобы изучить данные timeseries и последовательность. Сеть LSTM может изучить долгосрочные зависимости между временными шагами последовательности. Слой LSTM (lstmLayer
) может посмотреть в то время последовательность в прямом направлении, в то время как двунаправленный слой LSTM (bilstmLayer
) может посмотреть в то время последовательность и во вперед и в обратные направления. Этот пример использует двунаправленный слой LSTM.
Этот пример обучает речевое обнаружение действия двунаправленная сеть LSTM с последовательностями функции спектральных характеристик и гармонической метрики отношения.
В высоких сценариях ОСШ традиционные речевые алгоритмы обнаружения выполняют соответственно. Читайте в звуковом файле, который состоит из слов, произнесенных с паузами между. Передискретизируйте аудио к 16 кГц. Слушайте аудио.
fs = 16e3;
[speech,fileFs] = audioread('Counting-16-44p1-mono-15secs.wav');
speech = resample(speech,fs,fileFs);
speech = speech/max(abs(speech));
sound(speech,fs)
Используйте detectSpeech
функция, чтобы определить местоположение областей речи. detectSpeech
функция правильно идентифицирует все области речи.
win = hamming(50e-3 * fs,'periodic'); detectSpeech(speech,fs,'Window',win)
Повредите звуковой сигнал с шумом стиральной машины в ОСШ на-20 дБ. Слушайте поврежденное аудио.
[noise,fileFs] = audioread('WashingMachine-16-8-mono-200secs.wav');
noise = resample(noise,fs,fileFs);
SNR = -20;
noiseGain = 10^(-SNR/20) * norm(speech) / norm(noise);
noisySpeech = speech + noiseGain*noise(1:numel(speech));
noisySpeech = noisySpeech./max(abs(noisySpeech));
sound(noisySpeech,fs)
Вызовите detectSpeech
на шумном звуковом сигнале. Функции не удается обнаружить речевые области, учитывая очень низкий ОСШ.
detectSpeech(noisySpeech,fs,'Window',win)
Загрузите предварительно обученную сеть и сконфигурированный audioFeatureExtractor
объект. Сеть была обучена, чтобы обнаружить речь в низком ОСШ среды, данные функции выход от audioFeatureExtractor
объект.
load('Audio_VoiceActivityDetectionExample.mat','speechDetectNet','afe')
speechDetectNet
speechDetectNet = SeriesNetwork with properties: Layers: [6×1 nnet.cnn.layer.Layer] InputNames: {'sequenceinput'} OutputNames: {'classoutput'}
afe
afe = audioFeatureExtractor with properties: Properties Window: [256×1 double] OverlapLength: 128 SampleRate: 16000 FFTLength: [] SpectralDescriptorInput: 'linearSpectrum' Enabled Features spectralCentroid, spectralCrest, spectralEntropy, spectralFlux, spectralKurtosis, spectralRolloffPoint spectralSkewness, spectralSlope, harmonicRatio Disabled Features linearSpectrum, melSpectrum, barkSpectrum, erbSpectrum, mfcc, mfccDelta mfccDeltaDelta, gtcc, gtccDelta, gtccDeltaDelta, spectralDecrease, spectralFlatness spectralSpread, pitch To extract a feature, set the corresponding property to true. For example, obj.mfcc = true, adds mfcc to the list of enabled features.
Извлеките функции из речевых данных и затем нормируйте их. Восток функции так, чтобы время было через столбцы.
features = extract(afe,noisySpeech); features = (features - mean(features,1)) ./ std(features,[],1); features = features';
Передайте функции через речевую сеть обнаружения, чтобы классифицировать каждый характеристический вектор как принадлежащий системе координат речи или нет.
decisionsCategorical = classify(speechDetectNet,features);
Каждое решение соответствует аналитическому окну, анализируемому audioFeatureExtractor
. Реплицируйте решения так, чтобы они были во взаимно-однозначном соответствии с аудиосэмплами. Постройте речь, шумную речь и решения VAD.
decisionsWindow = 1.2*(double(decisionsCategorical)-1); decisionsSample = [repelem(decisionsWindow(1),numel(afe.Window)), ... repelem(decisionsWindow(2:end),numel(afe.Window)-afe.OverlapLength)]; t = (0:numel(decisionsSample)-1)/afe.SampleRate; plot(t,noisySpeech(1:numel(t)), ... t,speech(1:numel(t)), ... t,decisionsSample); xlabel('Time (s)') ylabel('Amplitude') legend('Noisy Speech','Speech','VAD','Location','southwest')
Можно также использовать обученную сеть VAD в потоковом контексте. Чтобы симулировать среду потоковой передачи, сначала сохраните речь и шумовые сигналы как файлы WAV. Чтобы симулировать вход потоковой передачи, вы считаете системы координат из файлов и смешаете их в желаемом ОСШ.
audiowrite('Speech.wav',speech,fs) audiowrite('Noise.wav',noise,fs)
Чтобы применить сеть VAD к передаче потокового аудио, необходимо обменять между задержкой и точностью. Задайте параметры для речевого обнаружения действия потоковой передачи на шумовой демонстрации. Можно установить длительность теста, длина последовательности, поданная в сеть, длину транзитного участка последовательности и ОСШ тестировать. Обычно увеличение длины последовательности увеличивает точность, но также и увеличивает задержку. Можно также выбрать выход сигнала к устройству как исходный сигнал или сигнал с шумом.
testDuration = 20; sequenceLength = 400; sequenceHop = 20; ОСШ = -20; noiseGain = 10^ (-SNR/20) * норма (речь) / норма (шум); signalToListenTo = "noisy";
Вызовите демонстрационную функцию помощника потоковой передачи, чтобы наблюдать производительность сети VAD на передаче потокового аудио. Параметры вы устанавливаете использование живых средств управления, не прерывают пример потоковой передачи. После того, как демонстрация потоковой передачи завершена, можно изменить параметры демонстрации, затем запустить демонстрацию потоковой передачи снова. Можно найти код для демонстрации потоковой передачи в Функциях Поддержки.
helperStreamingDemo(speechDetectNet,afe, ... 'Speech.wav','Noise.wav', ... testDuration,sequenceLength,sequenceHop,signalToListenTo,noiseGain);
Остаток от примера идет посредством обучения и оценки сети VAD.
Обучение:
Создайте audioDatastore
это указывает на аудио речевые файлы, используемые, чтобы обучить сеть LSTM.
Создайте учебный сигнал, состоящий из речевых сегментов, разделенных сегментами тишины различной длительности.
Повредите сигнал речи плюс тишина с шумом стиральной машины (ОСШ =-10 дБ).
Извлеките последовательности функции, состоящие из спектральных характеристик и гармонического отношения от сигнала с шумом.
Обучите сеть LSTM с помощью последовательностей функции, чтобы идентифицировать области речевого действия.
Предсказание:
Создайте audioDatastore
из речевых файлов, используемых, чтобы протестировать обучивший сеть, и создать тестовый сигнал, состоящий из речи, разделенной сегментами тишины.
Повредите тестовый сигнал с шумом стиральной машины (ОСШ =-10 дБ).
Извлеките последовательности функции из шумного тестового сигнала.
Идентифицируйте области речевого действия путем передачи тестовых функций через обучивший сеть.
Сравните точность сети с речевой базовой линией действия от тестового сигнала сигнала плюс тишина.
Вот эскиз учебного процесса.
Вот эскиз процесса предсказания. Вы используете обучивший сеть, чтобы сделать предсказания.
Загрузите и извлеките Google Speech Commands Dataset [1].
url = 'https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz'; downloadFolder = tempdir; datasetFolder = fullfile(downloadFolder,'google_speech'); if ~exist(datasetFolder,'dir') disp('Downloading Google speech commands data set (1.9 GB)...') untar(url,datasetFolder) end
Создайте audioDatastore
это указывает на набор данных.
ads = audioDatastore(datasetFolder,"Includesubfolders",true);
Набор данных содержит файлы фонового шума, которые не используются в этом примере. Используйте subset
создать новый datastore, который не имеет файлов фонового шума.
indices = cellfun(@(c)~contains(c,"_background_noise_"),ads.Files);
ads = subset(ads,indices);
Папка набора данных содержит текстовые файлы, которые перечисляют, которым звуковые файлы должны быть в наборе валидации и которым звуковые файлы должны быть в наборе тестов. Они предопределили валидацию, и наборы тестов не содержат произнесение того же слова тем же человеком, таким образом, лучше использовать эти предопределенные наборы, чем выбрать случайное подмножество целого набора данных.
Поскольку этот пример обучает одну сеть, он только использует набор валидации а не набор тестов, чтобы оценить обученную модель. Если вы обучаете много сетей и выбираете сеть с самой высокой точностью валидации как ваша итоговая сеть, то можно использовать набор тестов, чтобы оценить итоговую сеть.
Считайте список файлов валидации.
c = importdata(fullfile(datasetFolder,'validation_list.txt'));
filesValidation = string(c);
Считайте список тестовых файлов.
c = importdata(fullfile(datasetFolder,'testing_list.txt'));
filesTest = string(c);
Определите, какие файлы в datastore должны перейти к набору валидации и который должен перейти к набору обучающих данных. Используйте subset
создать два новых хранилища данных.
files = ads.Files; sf = split(files,filesep); isValidation = ismember(sf(:,end-1) + "/" + sf(:,end),filesValidation); isTest = ismember(sf(:,end-1) + "/" + sf(:,end),filesTest); adsValidation = subset(ads,isValidation); adsTrain = subset(ads,~isValidation & ~isTest);
Считайте содержимое звукового файла с помощью read
. Получите частоту дискретизации от adsInfo
struct ().
[data,adsInfo] = read(adsTrain); Fs = adsInfo.SampleRate;
Слушайте звуковой сигнал с помощью звуковой команды.
sound(data,Fs)
Постройте звуковой сигнал.
timeVector = (1/Fs) * (0:numel(data)-1); plot(timeVector,data) ylabel("Amplitude") xlabel("Time (s)") title("Sample Audio") grid on
Сигнал имеет неречевые фрагменты (тишина, фоновый шум, и т.д.), которые не содержат полезную информацию о речи. Этот пример удаляет тишину с помощью detectSpeech
функция.
Извлеките полезный фрагмент данных. Задайте периодическое Окно Хэмминга на 50 мс для анализа. Вызовите detectSpeech
без выходных аргументов, чтобы построить обнаруженные речевые области. Вызовите detectSpeech
снова возвратить индексы обнаруженной речи. Изолируйте обнаруженные речевые области и затем используйте sound
команда, чтобы слушать аудио.
win = hamming(50e-3 * Fs,'periodic'); detectSpeech(data,Fs,'Window',win);
speechIndices = detectSpeech(data,Fs,'Window',win);
sound(data(speechIndices(1,1):speechIndices(1,2)),Fs)
detectSpeech
функция возвращает индексы, которые плотно окружают обнаруженную речевую область. Было определено опытным путем, что, для этого примера, расширяя индексы обнаруженной речи пятью системами координат с обеих сторон увеличил производительность итоговой модели. Расширьте речевые индексы пятью системами координат и затем слушайте речь.
speechIndices(1,1) = max(speechIndices(1,1) - 5*numel(win),1); speechIndices(1,2) = min(speechIndices(1,2) + 5*numel(win),numel(data)); sound(data(speechIndices(1,1):speechIndices(1,2)),Fs)
Сбросьте учебный datastore и переставьте порядок файлов в хранилищах данных.
reset(adsTrain) adsTrain = shuffle(adsTrain); adsValidation = shuffle(adsValidation);
detectSpeech
функция вычисляет основанные на статистике пороги, чтобы определить речевые области. Можно пропустить пороговое вычисление и ускорить detectSpeech
функция путем определения порогов непосредственно. Чтобы определить пороги для набора данных, вызовите detectSpeech
на выборке файлов и получают пороги, которые она вычисляет. Возьмите среднее значение порогов.
TM = []; for index1 = 1:500 data = read(adsTrain); [~,T] = detectSpeech(data,Fs,'Window',win); TM = [TM;T]; end T = mean(TM); reset(adsTrain)
Создайте 1000-секундный учебный сигнал путем объединения нескольких речевых файлов от обучающего набора данных. Используйте detectSpeech
удалить нежелательные фрагменты каждого файла. Вставьте случайный период тишины между речевыми сегментами.
Предварительно выделите учебный сигнал.
duration = 2000*Fs; audioTraining = zeros(duration,1);
Предварительно выделите речевую маску обучения действию. Значения 1 в маске соответствуют выборкам, расположенным в областях с речевым действием. Значения 0 соответствуют областям без речевого действия.
maskTraining = zeros(duration,1);
Задайте максимальную длительность сегмента тишины 2 секунд.
maxSilenceSegment = 2;
Создайте учебный сигнал путем вызова read
на datastore в цикле.
numSamples = 1; while numSamples < duration data = read(adsTrain); data = data ./ max(abs(data)); % Normalize amplitude % Determine regions of speech idx = detectSpeech(data,Fs,'Window',win,'Thresholds',T); % If a region of speech is detected if ~isempty(idx) % Extend the indices by five frames idx(1,1) = max(1,idx(1,1) - 5*numel(win)); idx(1,2) = min(length(data),idx(1,2) + 5*numel(win)); % Isolate the speech data = data(idx(1,1):idx(1,2)); % 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 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 Region") 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)
Обратите внимание на то, что вы получили базовую речевую маску действия с помощью бесшумного сигнала речи плюс тишина. Проверьте то использование detectSpeech
на поврежденном шумом сигнале не приводит к хорошим результатам.
speechIndices = detectSpeech(audioTrainingNoisy,Fs,'Window',win); speechIndices(:,1) = max(1,speechIndices(:,1) - 5*numel(win)); speechIndices(:,2) = min(numel(audioTrainingNoisy),speechIndices(:,2) + 5*numel(win)); noisyMask = zeros(size(audioTrainingNoisy)); for ii = 1:size(speechIndices) noisyMask(speechIndices(ii,1):speechIndices(ii,2)) = 1; end
Визуализируйте 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); data = data ./ max(abs(data)); % Normalize amplitude % Determine regions of speech idx = detectSpeech(data,Fs,'Window',win,'Thresholds',T); % If a region of speech is detected if ~isempty(idx) % Extend the indices by five frames idx(1,1) = max(1,idx(1,1) - 5*numel(win)); idx(1,2) = min(length(data),idx(1,2) + 5*numel(win)); % Isolate the speech data = data(idx(1,1):idx(1,2)); % Write speech segment to training signal audioValidation(numSamples:numSamples+numel(data)-1) = data; % Set VAD Baseline maskValidation(numSamples:numSamples+numel(data)-1) = true; % Random silence period numSilenceSamples = randi(maxSilenceSegment*Fs,1,1); numSamples = numSamples + numel(data) + numSilenceSamples; end 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, использующую следующие функции:
Этот пример использует audioFeatureExtractor
создать оптимальный конвейер извлечения признаков для набора функций. Создайте audioFeatureExtractor
возразите, чтобы извлечь набор функций. Используйте окно Hann с 256 точками с 50%-м перекрытием.
afe = audioFeatureExtractor('SampleRate',Fs, ... 'Window',hann(256,"Periodic"), ... 'OverlapLength',128, ... ... 'spectralCentroid',true, ... 'spectralCrest',true, ... 'spectralEntropy',true, ... 'spectralFlux',true, ... 'spectralKurtosis',true, ... 'spectralRolloffPoint',true, ... 'spectralSkewness',true, ... 'spectralSlope',true, ... 'harmonicRatio',true); featuresTraining = extract(afe,audioTrainingNoisy);
Отобразите размерности матрицы функций. Первая размерность соответствует количеству окон, в сигнал ворвались (это зависит от длины окна и длины перекрытия). Второе измерение является количеством функций, использованных в этом примере.
[numWindows,numFeatures] = size(featuresTraining)
numWindows = 124999
numFeatures = 9
В приложениях классификации это - хорошая практика, чтобы нормировать все функции, чтобы иметь нулевое среднее значение и стандартное отклонение единицы.
Вычислите среднее и стандартное отклонение для каждого коэффициента и используйте их, чтобы нормировать данные.
M = mean(featuresTraining,1); S = std(featuresTraining,[],1); featuresTraining = (featuresTraining - M) ./ S;
Извлеките функции из сигнала валидации использование того же процесса.
featuresValidation = extract(afe,audioValidationNoisy); featuresValidation = (featuresValidation - mean(featuresValidation,1)) ./ std(featuresValidation,[],1);
Каждая функция соответствует 128 выборкам данных (длина транзитного участка). Для каждого транзитного участка, установленного ожидаемое речевое/нет речевое значение к режиму базовых значений маски, соответствующих тем 128 выборкам. Преобразуйте речевую/нет речевую маску в категориальный.
windowLength = numel(afe.Window); hopLength = windowLength - afe.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; sequenceOverlap = round(0.75*sequenceLength); trainFeatureCell = helperFeatureVector2Sequence(featuresTraining',sequenceLength,sequenceOverlap); trainLabelCell = helperFeatureVector2Sequence(maskTrainingCat',sequenceLength,sequenceOverlap);
Сети 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",5);
Обучите сеть LSTM с заданными опциями обучения и архитектурой слоя с помощью trainNetwork
. Поскольку набор обучающих данных является большим, учебный процесс может занять несколько минут.
doTraining = true; if doTraining [speechDetectNet,netInfo] = trainNetwork(trainFeatureCell,trainLabelCell,layers,options); fprintf("Validation accuracy: %f percent.\n", netInfo.FinalValidationAccuracy); else load speechDetectNet end
Validation accuracy: 90.195313 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";
Если вы изменили параметры своей сети или конвейера извлечения признаков, рассмотрите пересохранение файла MAT с новой сетью и audioFeatureExtractor
объект.
resaveNetwork = false; if resaveNetwork сохранение'Audio_VoiceActivityDetectionExample.mat','speechDetectNet','afe'); end
function [sequences,sequencePerFile] = helperFeatureVector2Sequence(features,featureVectorsPerSequence,featureVectorOverlap) if featureVectorsPerSequence <= featureVectorOverlap error('The number of overlapping feature vectors must be less than the number of feature vectors per sequence.') end if ~iscell(features) features = {features}; end hopLength = featureVectorsPerSequence - featureVectorOverlap; idx1 = 1; sequences = {}; sequencePerFile = cell(numel(features),1); for ii = 1:numel(features) sequencePerFile{ii} = floor((size(features{ii},2) - featureVectorsPerSequence)/hopLength) + 1; idx2 = 1; for j = 1:sequencePerFile{ii} sequences{idx1,1} = features{ii}(:,idx2:idx2 + featureVectorsPerSequence - 1); %#ok<AGROW> idx1 = idx1 + 1; idx2 = idx2 + hopLength; end end end
function helperStreamingDemo(speechDetectNet,afe,cleanSpeech,noise,testDuration,sequenceLength,sequenceHop,signalToListenTo,noiseGain)
Создайте dsp.AudioFileReader
объекты читать из речи и шумовых файлов структурируют системой координат.
speechReader = dsp.AudioFileReader(cleanSpeech,'PlayCount',inf); noiseReader = dsp.AudioFileReader(noise,'PlayCount',inf); fs = speechReader.SampleRate;
Создайте dsp.MovingStandardDeviation
возразите и dsp.MovingAverage
объект. Вы будете использовать их, чтобы определить стандартное отклонение и среднее значение функций аудио для нормализации. Статистика должна улучшаться в зависимости от времени.
movSTD = dsp.MovingStandardDeviation('Method','Exponential weighting','ForgettingFactor',1); movMean = dsp.MovingAverage('Method','Exponential weighting','ForgettingFactor',1);
Создайте три dsp.AsyncBuffer
объекты. Один, чтобы буферизовать входное аудио, один, чтобы буферизовать извлеченные функции, и один, чтобы буферизовать буфер вывода. Буфер вывода только необходим для визуализации решений в режиме реального времени.
audioInBuffer = dsp.AsyncBuffer; featureBuffer = dsp.AsyncBuffer; audioOutBuffer = dsp.AsyncBuffer;
Для аудио буферов вы буферизуете и исходный чистый речевой сигнал и сигнал с шумом. Вы воспроизведете только заданный signalToListenTo
. Преобразуйте signalToListenTo
переменная к каналу вы хотите слушать.
channelToListenTo = 1; if strcmp(signalToListenTo,"clean") channelToListenTo = 2; end
Создайте dsp.TimeScope
визуализировать исходный речевой сигнал, сигнал с шумом, что к сети применяются, и решение выход от сети.
scope = dsp.TimeScope('SampleRate',fs, ... 'TimeSpan',3, ... 'BufferLength',fs*3*3, ... 'YLimits',[-1.2 1.2], ... 'TimeSpanOverrunAction','Scroll', ... 'ShowGrid',true, ... 'NumInputPorts',3, ... 'LayoutDimensions',[3,1], ... 'Title','Noisy Speech'); scope.ActiveDisplay = 2; scope.Title = 'Clean Speech (Original)'; scope.ActiveDisplay = 3; scope.Title = 'Detected Speech';
Создайте audioDeviceWriter
возразите, чтобы проигрывать или исходное или шумное аудио от ваших докладчиков.
deviceWriter = audioDeviceWriter('SampleRate',fs);
Инициализируйте переменные, используемые в цикле.
windowLength = numel(afe.Window); hopLength = windowLength - afe.OverlapLength; myMax = 0; audioBufferInitialized = false; featureBufferInitialized = false;
Запустите демонстрацию потоковой передачи.
tic while toc < testDuration % Read a frame of the speech signal and a frame of the noise signal speechIn = speechReader(); noiseIn = noiseReader(); % Mix the speech and noise at the specified SNR noisyAudio = speechIn + noiseGain*noiseIn; % Update a running max for normalization myMax = max(myMax,max(abs(noisyAudio))); % Write the noisy audio and speech to buffers write(audioInBuffer,[noisyAudio,speechIn]); % If enough samples are buffered, % mark the audio buffer as initialized and push the read pointer % for the audio buffer up a window length. if audioInBuffer.NumUnreadSamples >= windowLength && ~audioBufferInitialized audioBufferInitialized = true; read(audioInBuffer,windowLength); end % If enough samples are in the audio buffer to calculate a feature % vector, read the samples, normalize them, extract the feature vectors, and write % the latest feature vector to the features buffer. while (audioInBuffer.NumUnreadSamples >= hopLength) && audioBufferInitialized x = read(audioInBuffer,windowLength + hopLength,windowLength); write(audioOutBuffer,x(end-hopLength+1:end,:)); noisyAudio = x(:,1); noisyAudio = noisyAudio/myMax; features = extract(afe,noisyAudio); write(featureBuffer,features(2,:)); end % If enough feature vectors are buffered, mark the feature buffer % as initialized and push the read pointer for the feature buffer % and the audio output buffer (so that they are in sync). if featureBuffer.NumUnreadSamples >= (sequenceLength + sequenceHop) && ~featureBufferInitialized featureBufferInitialized = true; read(featureBuffer,sequenceLength - sequenceHop); read(audioOutBuffer,(sequenceLength - sequenceHop)*windowLength); end while featureBuffer.NumUnreadSamples >= sequenceHop && featureBufferInitialized features = read(featureBuffer,sequenceLength,sequenceLength - sequenceHop); features(isnan(features)) = 0; % Use only the new features to update the % standard deviation and mean. Normalize the features. localSTD = movSTD(features(end-sequenceHop+1:end,:)); localMean = movMean(features(end-sequenceHop+1:end,:)); features = (features - localMean(end,:)) ./ localSTD(end,:); decision = classify(speechDetectNet,features'); decision = decision(end-sequenceHop+1:end); decision = double(decision)' - 1; decision = repelem(decision,hopLength); audioHop = read(audioOutBuffer,sequenceHop*hopLength); % Listen to the speech or speech+noise deviceWriter(audioHop(:,channelToListenTo)); % Visualize the speech+noise, the original speech, and the % voice activity detection. scope(audioHop(:,1),audioHop(:,2),audioHop(:,1).*decision) end end release(deviceWriter) release(audioInBuffer) release(audioOutBuffer) release(featureBuffer) release(movSTD) release(movMean) release(scope) end
[1] Начальник П. "Речевые Команды: общедоступный набор данных для распознавания речи однословного", 2017. Доступный от https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz. Авторское право Google 2017. Речевой Набор данных Команд лицензируется при Приписывании Creative Commons 4,0 лицензии