Этот пример показывает, как обнаружить области речи в среде низкий сигнал-шум с помощью глубокого обучения. Пример использует набор данных речевых команд, чтобы обучить двунаправленную сеть долгой краткосрочной памяти (BiLSTM) для обнаружения голосовой активности.
Обнаружение голосовой активности является важным компонентом многих аудиосистем, таких как автоматическое распознавание речи и распознавание динамика. Обнаружение голосовой активности может быть особенно сложным в ситуациях с низким сигналом в шум (ОСШ), когда речь затрудняется шумом.
Этот пример использует сети долгой краткосрочной памяти (LSTM), которые являются типом рекуррентной нейронной сети (RNN), хорошо подходящим для изучения данных последовательности и timeseries. Сеть LSTM может изучать долгосрочные зависимости между временными шагами последовательности. Слой LSTM (lstmLayer
(Deep Learning Toolbox)) может смотреть на временную последовательность в прямом направлении, в то время как двунаправленный слой LSTM (bilstmLayer
(Deep Learning Toolbox)) может посмотреть на временную последовательность как в прямом, так и в обратном направлениях. Этот пример использует двунаправленный слой 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
функция для определения местоположения областей речи. The detectSpeech
функция правильно определяет все области речи.
win = hamming(50e-3 * fs,'periodic'); detectSpeech(speech,fs,'Window',win)
Повреждение аудиосигнала с шумом стиральной машины при ОСШ -20 дБ. Прослушайте поврежденный звук.
[noise,fileFs] = audioread('WashingMachine-16-8-mono-200secs.mp3');
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
объект.
url = 'http://ssd.mathworks.com/supportfiles/audio/VoiceActivityDetection.zip'; downloadNetFolder = tempdir; netFolder = fullfile(downloadNetFolder,'VoiceActivityDetection'); if ~exist(netFolder,'dir') disp('Downloading pretrained network (1 file - 8 MB) ...') unzip(url,downloadNetFolder) end load(fullfile(netFolder,'voiceActivityDetectionExample.mat'));
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) * norm (речь )/norm (шум); signalToListenTo = "noisy";
Вызовите функцию потоковой демо-поддержки, чтобы наблюдать эффективность сети VAD при передаче потокового аудио. Параметры, которые вы устанавливаете с помощью live control, не прерывают пример потоковой передачи. После завершения потоковой демонстрации можно изменить параметры демонстрации, а затем запустить потоковую демонстрацию снова. Код для потоковой демонстрации можно найти в Вспомогательных функциях.
helperStreamingDemo(speechDetectNet,afe, ... 'Speech.wav','Noise.wav', ... testDuration,sequenceLength,sequenceHop,signalToListenTo,noiseGain);
Остальная часть примера проходит через обучение и оценку сети VAD.
Обучение:
Создайте audioDatastore
это указывает на файлы звуковой речи, используемые для обучения сети LSTM.
Создайте обучающий сигнал, состоящий из речевых сегментов, разделенных сегментами молчания различной длительности.
Повреждение сигнала речи плюс тишина с помощью шума стиральной машины (ОСШ = -10 дБ).
Извлечение функции последовательностей, состоящих из спектральных характеристик и гармонического отношения, из сигнала с шумом.
Обучите сеть LSTM с помощью функции последовательностей для идентификации областей голосовой активности.
Предсказание:
Создайте audioDatastore
речевых файлов, используемых для тестирования обученной сети, и создания тестового сигнала, состоящего из речи, разделенной сегментами молчания.
Повреждение тестового сигнала с помощью шума стиральной машины (ОСШ = -10 дБ).
Извлеките последовательности функций из шумного тестового сигнала.
Идентифицируйте области голосовой активности путем передачи тестовых функций через обученную сеть.
Сравните точность сети с базовой линией голосовой активности из тестового сигнала сигнал плюс молчание.
Вот эскиз тренировочного процесса.
Вот эскиз процесса предсказания. Вы используете обученную сеть, чтобы делать предсказания.
Загрузите и извлеките набор данных речевых команд Google [1].
url = 'https://ssd.mathworks.com/supportfiles/audio/google_speech.zip'; downloadFolder = tempdir; datasetFolder = fullfile(downloadFolder,'google_speech'); if ~exist(datasetFolder,'dir') disp('Downloading Google speech commands data set (1.9 GB)...') unzip(url,downloadFolder) end
Downloading Google speech commands data set (1.9 GB)...
Создайте audioDatastore
это указывает на обучающие данные набор.
adsTrain = audioDatastore(fullfile(datasetFolder, 'train'), "Includesubfolders",true);
Создайте audioDatastore
это указывает на набор данных валидации.
adsValidation = audioDatastore(fullfile(datasetFolder, 'validation'), "Includesubfolders",true);
Чтение содержимого аудио файла с помощью read
. Получите частоту дискретизации от adsInfo
struct.
[data,adsInfo] = read(adsTrain); Fs = adsInfo.SampleRate;
Прослушайте аудиосигнал с помощью команды sound.
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)
The 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);
The 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.mp3");
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.mp3");
noise = resample(noise,2,1);
noise = noise(1:duration);
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
объект для извлечения функции набора. Используйте 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 = 125009
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
(Deep Learning Toolbox), чтобы посмотреть на последовательность как в прямом, так и в обратном направлениях.
Задайте размер входа, чтобы быть последовательностями длины 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: 91.320312 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
Создайте временные возможности, чтобы визуализировать исходный речевой сигнал, сигнал с шумом, к которой применяется сеть, и решение, выход от сети.
scope = timescope('SampleRate',fs, ... 'TimeSpanSource','property', ... 'TimeSpan',3, ... 'BufferLength',fs*3*3, ... 'YLimits',[-1 1], ... 'TimeSpanOverrunAction','Scroll', ... 'ShowGrid',true, ... 'NumInputPorts',3, ... 'LayoutDimensions',[3,1], ... 'Title','Noisy Speech'); scope.ActiveDisplay = 2; scope.Title = 'Clean Speech (Original)'; scope.YLimits = [-1 1]; scope.ActiveDisplay = 3; scope.Title = 'Detected Speech'; scope.YLimits = [-1 1];
Создайте 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] Warden P. «Speech Commands: A public dataset for single-word speech recognition», 2017. Доступно из https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz. Копирайт Google 2017. Набор данных речевых команд лицензирован по лицензии Creative Commons Attribution 4.0