Речь Denoise Используя нейронные сети для глубокого обучения

В этом примере показано, как к denoise речевым сигналам с помощью нейронных сетей для глубокого обучения. Пример выдерживает сравнение, два типа сетей применились к той же задаче: полностью соединенный и сверточный.

Введение

Цель речевого шумоподавления состоит в том, чтобы удалить шум из речевых сигналов при улучшении качества и разборчивости речи. Этот пример демонстрирует удаление шума стиральной машины от речевых сигналов с помощью нейронных сетей для глубокого обучения. Пример выдерживает сравнение, два типа сетей применились к той же задаче: полностью соединенный и сверточный.

Проблемные сводные данные

Считайте следующий речевой сигнал произведенным на уровне 8 кГц.

[cleanAudio,fs] = audioread("SpeechDFT-16-8-mono-5secs.wav");
sound(cleanAudio,fs)

Добавьте шум стиральной машины в речевой сигнал. Установите шумовую степень, таким образом, что отношение сигнал-шум (SNR) является нулевым дБ.

noise        = audioread("WashingMachine-16-8-mono-1000secs.wav");

% Extract a noise segment from a random location in the noise file
ind          = randi(numel(noise) - numel(cleanAudio) + 1, 1, 1);
noiseSegment = noise(ind:ind + numel(cleanAudio) - 1);

speechPower  = sum(cleanAudio.^2);
noisePower   = sum(noiseSegment.^2);
noisyAudio   = cleanAudio + sqrt(speechPower/noisePower) * noiseSegment;

Слушайте шумный речевой сигнал.

sound(noisyAudio,fs)

Визуализируйте исходные и сигналы с шумом.

t = (1/fs) * (0:numel(cleanAudio)-1);
subplot(2,1,1)
plot(t,cleanAudio)
title("Clean Audio")
grid on
subplot(2,1,2)
plot(t,noisyAudio)
title("Noisy Audio")
xlabel("Time (s)")
grid on

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

Исследуйте набор данных

Этот пример использует набор данных Mozilla Common Voice [1], чтобы обучить и протестировать нейронные сети для глубокого обучения. Набор данных содержит записи на 48 кГц предметов говорящие короткие предложения. Загрузите набор данных и untar загруженный файл. Установите datafolder к местоположению данных.

datafolder = PathToDatabase;

Используйте audioDatastore создать datastore для всех файлов в наборе данных.

ads0 = audioDatastore(fullfile(datafolder,"clips"));

Используйте readtable считать метаданные, сопоставленные со звуковыми файлами от набора обучающих данных. Метаданные содержатся в train.tsv файл. Смотрите первые несколько строк метаданных.

metadata = readtable(fullfile(datafolder,"train.tsv"),"FileType","text");
head(metadata)
ans=8×8 table
                                                                 client_id                                                                                                                                  path                                                                                                               sentence                                               up_votes    down_votes        age           gender        accent  
    ____________________________________________________________________________________________________________________________________    ____________________________________________________________________________________________________________________________________    ______________________________________________________________________________________________    ________    __________    ____________    __________    __________

    {'55451a804635a88160a09b9b8122e3dddba46c2e6df2d6d9ec9d3445c38180fd18516d76acc9035978f27ee1f798f480dcb55dcbd31a142374c3af566c9be3c4'}    {'f480b8a93bf84b7f74c141284a71c39ff47d264a75dc905dc918286fb67f0333595206ff953a27b8049c7ec09ea895aa66d1cd4f7547535167d3d7901d12feab'}    {'Unfortunately, nobody can warrant the sanctions that will have an effect on the community.'}       3            0         {'twenties'}    {'female'}    {'canada'}
    {'55451a804635a88160a09b9b8122e3dddba46c2e6df2d6d9ec9d3445c38180fd18516d76acc9035978f27ee1f798f480dcb55dcbd31a142374c3af566c9be3c4'}    {'7647873ce81cd81c90b9e0fe3cb6c85cc03df7c0c4fdf2a04c356d75063af4b9de296a24e3bef0ba7ef0b0105d166abf35597e9c9a4b3857fd09c57b79f65a99'}    {'Came down and picked it out himself.'                                                      }       2            0         {'twenties'}    {'female'}    {'canada'}
    {'55451a804635a88160a09b9b8122e3dddba46c2e6df2d6d9ec9d3445c38180fd18516d76acc9035978f27ee1f798f480dcb55dcbd31a142374c3af566c9be3c4'}    {'81a3dd920de6251cc878a940aff258e859ef13efb9a6446610ab907e08832fafdc463eda334ee74a24cc02e3652a09f5573c133e6f46886cb0ba463efc7a6b43'}    {'She crossed the finish line just in time.'                                                 }       2            0         {'twenties'}    {'female'}    {'canada'}
    {'5b8c0f566c1201a94e684a334cf8a2cbced8a009a5a346fc24f1d51446c6b8610fc7bd78f69e559b29d138ab92652a45408ef87c3ec0e426d2fc5f1b2b44935b'}    {'5e6fc96a7bc91ec2261a51e7713bb0ed8a9f4fa9e20a38060dc4544fb0c2600c192d6e849915acaf8ea0766a9e1d481557d674363e780dbb064586352e560f2c'}    {'Please find me the Home at Last trailer.'                                                  }       2            0         {0×0 char  }    {0×0 char}    {0×0 char}
    {'5b8c0f566c1201a94e684a334cf8a2cbced8a009a5a346fc24f1d51446c6b8610fc7bd78f69e559b29d138ab92652a45408ef87c3ec0e426d2fc5f1b2b44935b'}    {'3a0929094a9aac80b961d479a3ee54311cc0d60d51fe8f97071edc2999e7747444b261d2c0c2345f86fb8161f3b73a14dc19da911d19ca8d9db39574c6199a34'}    {'Play something by Louisiana Blues'                                                         }       2            0         {0×0 char  }    {0×0 char}    {0×0 char}
    {'5b8c0f566c1201a94e684a334cf8a2cbced8a009a5a346fc24f1d51446c6b8610fc7bd78f69e559b29d138ab92652a45408ef87c3ec0e426d2fc5f1b2b44935b'}    {'82b8edf3f1420295069b5bb4543b8f349faaca28f45a3279b0cd64c39d16afb590a4cc70ed805020161f8c1f94bc63d3b69756fbc5a0462ce12d2e17c4ebaeeb'}    {'When is The Devil with Hitler playing in Bow Tie Cinemas'                                  }       2            0         {0×0 char  }    {0×0 char}    {0×0 char}
    {'60013a707ac8cdd2b44427418064915f7810b2d58d52d8f81ad3c6406b8922d61c134259747f3c73d2e64c885fc6141761d29f7e6ada7d6007c48577123e4af0'}    {'8606bac841a08bcbf5ddb83c768103c467ffd1bf38b16052414210dc3ce3267561cb0368d227b6eb420dc147387cc1807032102b6248a13a40f83e5ac06d7122'}    {'Give me the list of animated movies playing at the closest movie house'                    }       3            0         {0×0 char  }    {0×0 char}    {0×0 char}
    {'60013a707ac8cdd2b44427418064915f7810b2d58d52d8f81ad3c6406b8922d61c134259747f3c73d2e64c885fc6141761d29f7e6ada7d6007c48577123e4af0'}    {'0f7e63d320cfbf6ea5d1cda674007131d804a06f5866d38f81da7def33a4ce8ee4f2cb7e47b45eee97903cad3160b3a10f715862227e8ecdc3fb3bafc6b4279d'}    {'The better part of valor is discretion'                                                    }       3            0         {0×0 char  }    {0×0 char}    {0×0 char}

Найдите файлы в datastore, соответствующем набору обучающих данных.

csvFiles = metadata.path;
adsFiles =  ads0.Files;
adsFiles = cellfun(@HelperGetFilePart,adsFiles,'UniformOutput',false);
[~,indA] = intersect(adsFiles,csvFiles);

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

ads = subset(ads0,indA);

Вы обучите нейронные сети для глубокого обучения на подмножестве файлов. Создайте подмножество datastore, содержащее первые 1 000 файлов datastore.

ads = subset(ads,1:1000);

Используйте read получить содержимое первого файла в datastore.

[audio,info] = read(ads);

Слушайте речевой сигнал.

sound(audio,info.SampleRate)

Постройте речевой сигнал.

figure
t = (1/info.SampleRate) * (0:numel(audio)-1);
plot(t,audio)
title("Example Speech Signal")
xlabel("Time (s)")
grid on

Системный обзор глубокого обучения

Основной план подготовки глубокого обучения показывают ниже. Обратите внимание на то, что, поскольку речь обычно падает ниже 4 кГц, вы сначала прореживаете чистые и шумные звуковые сигналы к 8 кГц, чтобы уменьшать вычислительную загрузку сети. Предиктор и целевые сетевые сигналы являются спектрами величины шумных и чистых звуковых сигналов, соответственно. Выход сети является спектром величины сигнала denoised. Сеть регрессии использует вход предиктора, чтобы минимизировать среднеквадратичную погрешность между ее выходом и входной целью. denoised аудио преобразовано назад в область времени использование выходного спектра величины и фазы сигнала с шумом [2].

Вы преобразовываете аудио к частотному диапазону с помощью Кратковременного преобразования Фурье (STFT), с продолжительностью окна 256 выборок, перекрытием 75% и Окном Хэмминга. Вы уменьшаете размер спектрального вектора к 129 путем отбрасывания выборок частоты, соответствующих отрицательным частотам (потому что речевой сигнал временного интервала действителен, это не приводит ни к какой информационной потере). Вход предиктора состоит из 8 последовательных шумных векторов STFT, так, чтобы каждый STFT оценка выхода был вычислен на основе текущего шумного STFT и 7 предыдущих шумных векторов STFT.

Цели STFT и предикторы

Этот раздел иллюстрирует, как сгенерировать цель и сигналы предиктора из одного учебного файла.

Во-первых, задайте системные параметры:

windowLength = 256;
win          = hamming(windowLength,"periodic");
overlap      = round(0.75 * windowLength);
ffTLength    = windowLength;
inputFs      = 48e3;
fs           = 8e3;
numFeatures  = ffTLength/2 + 1;
numSegments  = 8;

Задайте конвертер частоты дискретизации, используемый, чтобы преобразовать аудио на 48 кГц в 8 кГц.

src = dsp.SampleRateConverter("InputSampleRate",inputFs, ...
                              "OutputSampleRate",fs, ...
                              "Bandwidth",7920);

Используйте read получить содержимое звукового файла от datastore.

audio = read(ads);

Убедитесь, что аудио длина является кратной фактору децимации конвертера частоты дискретизации.

decimationFactor = inputFs/fs;
L = floor(numel(audio)/decimationFactor);
audio = audio(1:decimationFactor*L);

Преобразуйте звуковой сигнал в 8 кГц.

audio = src(audio);
reset(src)

Создайте случайный шумовой сегмент из вектора шума стиральной машины.

randind      = randi(numel(noise) - numel(audio),[1 1]);
noiseSegment = noise(randind : randind + numel(audio) - 1);

Добавьте, что шум к речи сигнализирует таким образом, что ОСШ составляет 0 дБ.

noisePower   = sum(noiseSegment.^2);
cleanPower   = sum(audio.^2);
noiseSegment = noiseSegment .* sqrt(cleanPower/noisePower);
noisyAudio   = audio + noiseSegment;

Используйте stft сгенерировать величину векторы STFT от исходных и шумных звуковых сигналов.

cleanSTFT = stft(audio,'Window',win,'OverlapLength',overlap,'FFTLength',ffTLength);
cleanSTFT = abs(cleanSTFT(numFeatures-1:end,:));
noisySTFT = stft(noisyAudio,'Window',win,'OverlapLength',overlap,'FFTLength',ffTLength);
noisySTFT = abs(noisySTFT(numFeatures-1:end,:));

Сгенерируйте учебные сигналы предиктора с 8 сегментами от шумного STFT. Перекрытие между последовательными предикторами является 7 сегментами.

noisySTFT    = [noisySTFT(:,1:numSegments - 1), noisySTFT];
stftSegments = zeros(numFeatures, numSegments , size(noisySTFT,2) - numSegments + 1);
for index = 1:size(noisySTFT,2) - numSegments + 1
    stftSegments(:,:,index) = (noisySTFT(:,index:index + numSegments - 1)); 
end

Поставьте цели и предикторы. Последняя размерность обеих переменных соответствует количеству отличных пар предиктора/цели, сгенерированных звуковым файлом. Каждый предиктор 129 8, и каждая цель 129 1.

targets = cleanSTFT;
size(targets)
ans = 1×2

   129   457

predictors = stftSegments;
size(predictors)
ans = 1×3

   129     8   457

Извлеките функции Используя длинные массивы

Чтобы ускорить обработку, извлеките последовательности функции из речевых сегментов всех звуковых файлов в datastore с помощью длинных массивов. В отличие от массивов в оперативной памяти, длинные массивы обычно остаются неоцененными, пока вы не вызываете gather функция. Эта отсроченная оценка позволяет вам работать быстро с большими наборами данных. Когда вы в конечном счете запрашиваете выход с помощью gather, MATLAB комбинирует вычисления в очереди, где возможный и берет минимальное количество проходов через данные. Если у вас есть Parallel Computing Toolbox™, можно использовать длинные массивы в локальном сеансе работы с MATLAB, или на локальном параллельном пуле. Можно также выполнить вычисления длинного массива на кластере, если вам установили MATLAB® Parallel Server™.

Во-первых, преобразуйте datastore в длинный массив.

reset(ads)
T = tall(ads)
Starting parallel pool (parpool) using the 'local' profile ...
Connected to the parallel pool (number of workers: 8).

T =

  M×1 tall cell array

    {156144×1 double}
    {176880×1 double}
    {193008×1 double}
    {144624×1 double}
    {212592×1 double}
    {219504×1 double}
    {198768×1 double}
    {247152×1 double}
        :        :
        :        :

Отображение указывает, что количество строк (соответствующий количеству файлов в datastore), M, еще не известно. M является заполнителем, пока вычисление не завершается.

Извлеките цель и величину предиктора STFT от длинной таблицы. Это действие создает новые переменные длинного массива, чтобы использовать в последующих вычислениях. Функциональный HelperGenerateSpeechDenoisingFeatures выполняет шаги, уже подсвеченные в разделе STFT Targets и Predictors. cellfun команда применяет HelperGenerateSpeechDenoisingFeatures к содержимому каждого звукового файла в datastore.

[targets,predictors] = cellfun(@(x)HelperGenerateSpeechDenoisingFeatures(x,noise,src),T,"UniformOutput",false);

Используйте gather оценивать цели и предикторы.

[targets,predictors] = gather(targets,predictors);
Evaluating tall expression using the Parallel Pool 'local':
- Pass 1 of 1: Completed in 46 sec
Evaluation completed in 1 min 41 sec

Это - хорошая практика, чтобы нормировать все функции, чтобы обнулить стандартное отклонение единицы и среднее значение.

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

predictors    = cat(3,predictors{:});
targets       = cat(2,targets{:});
noisyMean     = mean(predictors(:));
noisyStd      = std(predictors(:));
predictors(:) = (predictors(:) - noisyMean)/noisyStd;
cleanMean     = mean(targets(:));
cleanStd      = std(targets(:));
targets(:)    = (targets(:) - cleanMean)/cleanStd;

Измените предикторы и цели к размерностям, ожидаемым нейронными сетями для глубокого обучения.

predictors = reshape(predictors,size(predictors,1),size(predictors,2),1,size(predictors,3));
targets    = reshape(targets,1,1,size(targets,1),size(targets,2));

Вы будете использовать 1% данных для валидации во время обучения. Валидация полезна, чтобы обнаружить сценарии, где сеть сверхсоответствует обучающим данным.

Случайным образом разделите данные в наборы обучения и валидации.

inds               = randperm(size(predictors,4));
L                  = round(0.99 * size(predictors,4));
trainPredictors    = predictors(:,:,:,inds(1:L));
trainTargets       = targets(:,:,:,inds(1:L));
validatePredictors = predictors(:,:,:,inds(L+1:end));
validateTargets    = targets(:,:,:,inds(L+1:end));

Речевое шумоподавление с полносвязными слоями

Вы сначала считаете сеть шумоподавления состоявшей из полносвязных слоев. Каждый нейрон в полносвязном слое соединяется со всеми активациями от предыдущего слоя. Полносвязный слой умножает вход на матрицу веса и затем добавляет вектор смещения. Размерности матрицы веса и вектора смещения определяются количеством нейронов в слое и количеством активаций от предыдущего слоя.

Задайте слои сети. Задайте входной размер, чтобы быть изображениями размера NumFeatures-by-NumSegments (129 8 в этом примере). Задайте два скрытых полносвязных слоя, каждого с 1 024 нейронами. Начиная с чисто линейных систем следуйте за каждым скрытым полносвязным слоем со слоем Rectified Linear Unit (ReLU). Пакетные слои нормализации нормируют средние значения и стандартные отклонения выходных параметров. Добавьте полносвязный слой с 129 нейронами, сопровождаемыми слоем регрессии.

layers = [
    imageInputLayer([numFeatures,numSegments])
    fullyConnectedLayer(1024)
    batchNormalizationLayer
    reluLayer
    fullyConnectedLayer(1024)
    batchNormalizationLayer
    reluLayer
    fullyConnectedLayer(numFeatures)
    regressionLayer
    ];

Затем задайте опции обучения для сети. Установите MaxEpochs к 3 так, чтобы сеть сделала 3, проходит через обучающие данные. Установите MiniBatchSize из 128 так, чтобы сеть посмотрела на 128 учебных сигналов за один раз. Задайте Plots как "training-progress" сгенерировать графики, которые показывают процесс обучения количеством увеличений итераций. Установите Verbose к false отключить печать таблицы выход, который соответствует данным, показанным в графике в окно командной строки. Задайте Shuffle как "every-epoch" переставить обучающие последовательности в начале каждой эпохи. Задайте LearnRateSchedule к "piecewise" чтобы уменьшить темп обучения заданным фактором (0.9) каждый раз, определенное число эпох (1) передало. Установите ValidationData к предикторам валидации и целям. Установите ValidationFrequency таким образом, что среднеквадратичная погрешность валидации вычисляется однажды в эпоху. Этот пример использует адаптивную оценку момента (Адам) решатель.

miniBatchSize = 128;
options = trainingOptions("adam", ...
    "MaxEpochs",3, ...
    "InitialLearnRate",1e-5,...
    "MiniBatchSize",miniBatchSize, ...
    "Shuffle","every-epoch", ...
    "Plots","training-progress", ...
    "Verbose",false, ...
    "ValidationFrequency",floor(size(trainPredictors,4)/miniBatchSize), ...
    "LearnRateSchedule","piecewise", ...
    "LearnRateDropFactor",0.9, ...
    "LearnRateDropPeriod",1, ...
    "ValidationData",{validatePredictors,validateTargets});

Обучите сеть с заданными опциями обучения и архитектурой слоя с помощью trainNetwork. Поскольку набор обучающих данных является большим, учебный процесс может занять несколько минут. Чтобы загрузить предварительно обученную сеть вместо того, чтобы обучить сеть с нуля, установите doTraining к false.

doTraining = true;
if doTraining
    denoiseNetFullyConnected = trainNetwork(trainPredictors,trainTargets,layers,options);
else
    s = load("denoisenet.mat");
    denoiseNetFullyConnected = s.denoiseNetFullyConnected;
    cleanMean = s.cleanMean;
    cleanStd  = s.cleanStd;
    noisyMean = s.noisyMean;
    noisyStd  = s.noisyStd;
end

Считайте количество весов в полносвязных слоях сети.

numWeights = 0;
for index = 1:numel(denoiseNetFullyConnected.Layers)
    if isa(denoiseNetFullyConnected.Layers(index),"nnet.cnn.layer.FullyConnectedLayer")
        numWeights = numWeights + numel(denoiseNetFullyConnected.Layers(index).Weights);
    end
end
fprintf("The number of weights is %d.\n",numWeights);
The number of weights is 2237440.

Речевое шумоподавление со сверточными слоями

Рассмотрите сеть, которая использует сверточные слои вместо полносвязных слоев [3]. 2D сверточный слой применяет скользящие фильтры к входу. Слой применяет операцию свертки к входу путем перемещения фильтров вдоль входа вертикально и горизонтально и вычисления скалярного произведения весов и входа, и затем добавления срока смещения. Сверточные слои обычно состоят из меньшего количества параметров, чем полносвязные слоя.

Задайте слои полностью сверточной сети, описанной в [3], включив 16 сверточных слоев. Первые 15 сверточных слоев являются группами из 3 слоев, повторенных 5 раз, с ширинами фильтра 9, 5, и 9, и количество фильтров 18, 30 и 8, соответственно. Последний сверточный слой имеет ширину фильтра 129 и 1 фильтра. В этой сети свертки выполняются только в одном направлении (по измерению частоты), и ширина фильтра вдоль измерения времени установлена в 1 для всех слоев кроме первого. Подобно полностью связанной сети сверточные слои сопровождаются ReLu и обрабатывают слои нормализации в пакетном режиме.

layers = [imageInputLayer([numFeatures,numSegments])
          convolution2dLayer([9 8],18,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer
          
          repmat( ...
          [convolution2dLayer([5 1],30,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer
          convolution2dLayer([9 1],8,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer
          convolution2dLayer([9 1],18,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer],4,1)
          
          convolution2dLayer([5 1],30,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer
          convolution2dLayer([9 1],8,"Stride",[1 100],"Padding","same")
          batchNormalizationLayer
          reluLayer
          
          convolution2dLayer([129 1],1,"Stride",[1 100],"Padding","same")
          
          regressionLayer
          ];

Опции обучения идентичны опциям для полностью связанной сети, за исключением того, что размерности целевых сигналов валидации переставлены, чтобы быть сопоставимыми с размерностями, ожидаемыми слоем регрессии.

options = trainingOptions("adam", ...
    "MaxEpochs",3, ...
    "InitialLearnRate",1e-5,...
    "MiniBatchSize",miniBatchSize, ...
    "Shuffle","every-epoch", ...
    "Plots","training-progress", ...
    "Verbose",false, ...
    "ValidationFrequency",floor(size(trainPredictors,4)/miniBatchSize),...
    "LearnRateSchedule","piecewise",...
    "LearnRateDropFactor",0.9,...
    "LearnRateDropPeriod",1,...
    "ValidationData",{validatePredictors,permute(validateTargets,[3 1 2 4])});

Обучите сеть с заданными опциями обучения и архитектурой слоя с помощью trainNetwork. Поскольку набор обучающих данных является большим, учебный процесс может занять несколько минут. Чтобы загрузить предварительно обученную сеть вместо того, чтобы обучить сеть с нуля, установите doTraining к false.

doTraining = true;
if doTraining
    denoiseNetFullyConvolutional = trainNetwork (trainPredictors, переставьте (trainTargets, [3 1 2 4]), слои, опции);
else
    s = загрузка ("denoisenet.mat");
    denoiseNetFullyConvolutional = s.denoiseNetFullyConvolutional;
    cleanMean = s.cleanMean;
    cleanStd = s.cleanStd;
    noisyMean = s.noisyMean;
    noisyStd = s.noisyStd;
end

Считайте количество весов в полносвязных слоях сети.

numWeights = 0;
for index = 1:numel(denoiseNetFullyConvolutional.Layers)
    if isa(denoiseNetFullyConvolutional.Layers(index),"nnet.cnn.layer.Convolution2DLayer")
        numWeights = numWeights + numel(denoiseNetFullyConvolutional.Layers(index).Weights);
    end
end
fprintf("The number of weights in convolutional layers is %d\n",numWeights);
The number of weights in convolutional layers is 31812

Протестируйте сети шумоподавления

Вы создадите набор данных валидации с помощью того же подхода, который вы использовали в обучающем наборе данных. Используйте readtable функционируйте, чтобы считать метаданные, сопоставленные с файлами валидации.

metadata = readtable(fullfile(datafolder,"test.tsv"),"FileType","text");

Найдите файлы валидации в datastore.

csvFiles   = metadata.path;
adsFiles =  ads0.Files;
adsFiles = cellfun(@HelperGetFilePart,adsFiles,'UniformOutput',false);
[~,indA] = intersect(adsFiles,csvFiles);

Создайте datastore валидации из большого datastore.

ads = subset(ads0,indA);

% Create a datastore subset containing the first 100 files of the
% datastore.
ads = subset(ads,1:100);

Переставьте файлы в datastore.

ads = shuffle(ads);

Считайте содержимое файла от datastore.

[cleanAudio,info] = read(ads);

Убедитесь, что аудио длина является кратной фактору децимации конвертера частоты дискретизации.

L          = floor( numel(cleanAudio)/decimationFactor);
cleanAudio = cleanAudio(1:decimationFactor*L);

Преобразуйте звуковой сигнал в 8 кГц.

cleanAudio = src(cleanAudio);
reset(src)

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

noise = audioread("WashingMachine-16-8-mono-200secs.wav");

Создайте случайный шумовой сегмент из вектора шума стиральной машины.

randind      = randi(numel(noise) - numel(cleanAudio), [1 1]);
noiseSegment = noise(randind : randind + numel(cleanAudio) - 1);

Добавьте, что шум к речи сигнализирует таким образом, что ОСШ составляет 0 дБ.

noisePower   = sum(noiseSegment.^2);
cleanPower   = sum(cleanAudio.^2);
noiseSegment = noiseSegment .* sqrt(cleanPower/noisePower);
noisyAudio   = cleanAudio + noiseSegment;

Используйте stft сгенерировать величину векторы STFT от шумных звуковых сигналов.

noisySTFT  = stft(noisyAudio,'Window',win,'OverlapLength',overlap,'FFTLength',ffTLength);
noisyPhase = angle(noisySTFT(numFeatures-1:end,:));
noisySTFT  = abs(noisySTFT(numFeatures-1:end,:));

Сгенерируйте учебные сигналы предиктора с 8 сегментами от шумного STFT. Перекрытие между последовательными предикторами является 7 сегментами.

noisySTFT  = [noisySTFT(:,1:numSegments-1) noisySTFT];
predictors = zeros( numFeatures, numSegments , size(noisySTFT,2) - numSegments + 1);
for index = 1 : size(noisySTFT,2) - numSegments + 1
    predictors(:,:,index) = noisySTFT(:,index:index + numSegments - 1); 
end

Нормируйте предикторы средним и стандартным отклонением, вычисленным на учебном этапе.

predictors(:) = (predictors(:) - noisyMean) / noisyStd;

Вычислите denoised величину STFT при помощи predict с этими двумя обучил нейронные сети.

predictors = reshape(predictors, [numFeatures,numSegments,1,size(predictors,3)]);
STFTFullyConnected = predict(denoiseNetFullyConnected, predictors);
STFTFullyConvolutional = predict(denoiseNetFullyConvolutional, predictors);

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

STFTFullyConnected(:)     = cleanStd * STFTFullyConnected(:)     +  cleanMean;
STFTFullyConvolutional(:) = cleanStd * STFTFullyConvolutional(:) +  cleanMean;

Преобразуйте односторонний STFT в STFT в центре.

STFTFullyConnected     = STFTFullyConnected.' .* exp(1j*noisyPhase);
STFTFullyConnected     = [conj(STFTFullyConnected(end-1:-1:2,:)) ; STFTFullyConnected];
STFTFullyConvolutional = squeeze(STFTFullyConvolutional) .* exp(1j*noisyPhase);
STFTFullyConvolutional = [conj(STFTFullyConvolutional(end-1:-1:2,:)) ; STFTFullyConvolutional];

Вычислите denoised речевые сигналы. istft выполняет обратный STFT. Используйте фазу шумных векторов STFT, чтобы восстановить сигнал временной области.

denoisedAudioFullyConnected     = istft(STFTFullyConnected,  ...
                                        'Window',win,'OverlapLength',overlap, ...
                                        'FFTLength',ffTLength,'ConjugateSymmetric',true);
denoisedAudioFullyConvolutional = istft(STFTFullyConvolutional,  ...
                                        'Window',win,'OverlapLength',overlap, ...
                                        'FFTLength',ffTLength,'ConjugateSymmetric',true);

Постройте чистые, шумные и denoised звуковые сигналы.

figure
subplot(4,1,1)
t = (1/fs) * (0:numel(denoisedAudioFullyConnected)-1);
plot(t,cleanAudio(1:numel(denoisedAudioFullyConnected)))
title("Clean Speech")
grid on
subplot(4,1,2)
plot(t,noisyAudio(1:numel(denoisedAudioFullyConnected)))
title("Noisy Speech")
grid on
subplot(4,1,3)
plot(t,denoisedAudioFullyConnected)
title("Denoised Speech (Fully Connected Layers)")
grid on
subplot(4,1,4)
plot(t,denoisedAudioFullyConvolutional)
title("Denoised Speech (Convolutional Layers)")
grid on
xlabel("Time (s)")

Постройте чистые, шумные, и denoised спектрограммы.

h = figure;
subplot(4,1,1)
spectrogram(cleanAudio,win,overlap,ffTLength,fs);
title("Clean Speech")
grid on
subplot(4,1,2)
spectrogram(noisyAudio,win,overlap,ffTLength,fs);
title("Noisy Speech")
grid on
subplot(4,1,3)
spectrogram(denoisedAudioFullyConnected,win,overlap,ffTLength,fs);
title("Denoised Speech (Fully Connected Layers)")
grid on
subplot(4,1,4)
spectrogram(denoisedAudioFullyConvolutional,win,overlap,ffTLength,fs);
title("Denoised Speech (Convolutional Layers)")
grid on
p = get(h,'Position');
set(h,'Position',[p(1) 65 p(3) 800]);

Слушайте шумную речь.

sound(noisyAudio,fs)

Слушайте denoised речь от сети с полносвязными слоями.

sound(denoisedAudioFullyConnected,fs)

Слушайте denoised речь от сети со сверточными слоями.

sound(denoisedAudioFullyConvolutional,fs)

Послушайте чистую речь.

sound(cleanAudio,fs)

Можно протестировать больше файлов от datastore путем вызова testDenoisingNets. Функция производит временной интервал и графики частотного диапазона, подсвеченные выше, и также возвращает чистые, шумные, и denoised звуковые сигналы.

[cleanAudio,noisyAudio,denoisedAudioFullyConnected,denoisedAudioFullyConvolutional] = testDenoisingNets(ads,denoiseNetFullyConnected,denoiseNetFullyConvolutional,noisyMean,noisyStd,cleanMean,cleanStd);

Приложение реального времени

Процедура в предыдущем разделе передает целый спектр сигнала с шумом к predict. Это не подходит для приложений реального времени, где низкая задержка является требованием.

Запустите speechDenoisingRealtimeApp для примера того, как симулировать потоковую передачу, версию в реальном времени сети шумоподавления. Приложение использует сеть с полносвязными слоями. Аудио длина системы координат равна размеру транзитного участка STFT, который является 0.25 * 256 = 64 выборки.

speechDenoisingRealtimeApp запускает Пользовательский интерфейс (UI), спроектированный, чтобы взаимодействовать с симуляцией. Пользовательский интерфейс позволяет настройкам параметров, и результаты отражаются в симуляции немедленно. Можно также позволить/запретить подавитель шума, который управляет на denoised выходом, чтобы далее уменьшать шум, а также настроить время атаки, время релиза и порог подавителя шума. Можно слушать шумное, чистое или denoised аудио от пользовательского интерфейса.

Осциллограф строит чистое, шумное и сигналы denoised, а также усиление подавителя шума.

Ссылки

[1] https://voice.mozilla.org/en

[2] "Эксперименты на глубоком обучении для речевого шумоподавления", Дин Лю, Париж Smaragdis, Миндж Ким, INTERSPEECH, 2014.

[3] "Полностью сверточная нейронная сеть для речевого улучшения", парк Se Rim, Чжин выигранный Ли, INTERSPEECH, 2017.