Речевое обнаружение действия в шуме Используя глубокое обучение

В этом примере показано, как обнаружить области речи в низкой среде сигнала к шуму с помощью глубокого обучения. Пример использует Речевой Набор данных Команд, чтобы обучить сеть 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.

Обучите и оцените сеть VAD

Обучение:

  1. Создайте audioDatastore это указывает на аудио речевые файлы, используемые, чтобы обучить сеть LSTM.

  2. Создайте учебный сигнал, состоящий из речевых сегментов, разделенных сегментами тишины различной длительности.

  3. Повредите сигнал речи плюс тишина с шумом стиральной машины (ОСШ =-10 дБ).

  4. Извлеките последовательности функции, состоящие из спектральных характеристик и гармонического отношения от сигнала с шумом.

  5. Обучите сеть LSTM с помощью последовательностей функции, чтобы идентифицировать области речевого действия.

Предсказание:

  1. Создайте audioDatastore из речевых файлов, используемых, чтобы протестировать обучивший сеть, и создать тестовый сигнал, состоящий из речи, разделенной сегментами тишины.

  2. Повредите тестовый сигнал с шумом стиральной машины (ОСШ =-10 дБ).

  3. Извлеките последовательности функции из шумного тестового сигнала.

  4. Идентифицируйте области речевого действия путем передачи тестовых функций через обучивший сеть.

  5. Сравните точность сети с речевой базовой линией действия от тестового сигнала сигнала плюс тишина.

Вот эскиз учебного процесса.

Вот эскиз процесса предсказания. Вы используете обучивший сеть, чтобы сделать предсказания.

Загрузите речевой набор данных команд

Загрузите и извлеките 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 могут изучить долгосрочные зависимости между временными шагами данных о последовательности. Этот пример использует двунаправленный слой 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

Обучите сеть 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 лицензии

Для просмотра документации необходимо авторизоваться на сайте