这个例子展示了如何使用深度学习网络对语音信号进行降噪。这个例子比较了应用于同一任务的两种类型的网络:完全连接的和卷积的。
语音去噪的目的是去除语音信号中的噪声,同时提高语音的质量和可理解性。这个例子展示了使用深度学习网络从语音信号中去除洗衣机噪音。这个例子比较了应用于同一任务的两种类型的网络:完全连接的和卷积的。
考虑以下以8 kHz采样的语音信号。
[cleanAudio,fs] = audioread(“SpeechDFT-16-8-mono-5secs.wav”);声音(cleanAudio fs)
在语音信号中加入洗衣机噪音。设置噪声功率使信噪比(SNR)为零dB。
噪音= audioread(“洗衣机- 16 - 8 mono - 1000 - secs.mp3”);从噪声文件中的随机位置提取噪声段ind = randi(numel(noise) - numel(cleanAudio) + 1,1,1);noiseSegment = noise(ind:ind + numel(cleanAudio) - 1);speechPower = sum(cleanAudio.^2);noisePower = sum(noisessegment .^2);noisyAudio = cleanAudio + sqrt(speechPower/noisePower) * noisessegment;
听嘈杂的语音信号。
声音(noisyAudio fs)
可视化原始信号和噪声信号。
t = (1/fs) * (0:numel(cleanAudio)-1);subplot(2,1,1) plot(t,cleanAudio) title(“干净的声音”网格)在subplot(2,1,2) plot(t,noisyAudio) title(“嘈杂的声音”)包含(“时间(s)”网格)在
语音去噪的目的是从语音信号中去除洗衣机噪声,同时尽量减少输出语音中不需要的伪影。
这个例子使用了Mozilla公共语音数据集[1来训练和测试深度学习网络。该数据集包含48千赫的受试者说短句的录音。下载数据集并解压缩下载的文件。
url =“http://ssd.mathworks.com/金宝appsupportfiles/audio/commonvoice.zip”;downloadFolder = tempdir;dataFolder = fullfile(下载文件夹,“无法推进”);如果~存在(dataFolder“dir”) disp (“正在下载数据集(956 MB)…”解压缩(url, downloadFolder)结束
使用audioDatastore
为训练集创建一个数据存储。若要以牺牲性能为代价加速示例的运行时,请设置reduceDataset
来真正的
。
adsTrain = audioDatastore(fullfile(dataFolder),“火车”),“IncludeSubfolders”,真正的);reduceDataset =真正的;如果reducedatasset adsTrain = shuffle(adsTrain);adsTrain =子集(adsTrain,1:1000);结束
使用读
获取数据存储中第一个文件的内容。
[audio,adsTrainInfo] = read(adsTrain);
听语音信号。
声音(音频、adsTrainInfo.SampleRate)
绘制语音信号。
figure t = (1/adsTrainInfo.SampleRate) * (0: nummel (audio)-1);情节(t,音频)标题(“语音信号示例”)包含(“时间(s)”网格)在
基本的深度学习训练方案如下所示。请注意,由于语音通常低于4 kHz,因此首先将干净和有噪声的音频信号降至8 kHz,以减少网络的计算负载。预测网络信号和目标网络信号分别是含噪音频信号和干净音频信号的幅度谱。网络的输出是去噪信号的幅度谱。回归网络使用预测器输入来最小化其输出和输入目标之间的均方误差。利用带噪信号的输出幅度谱和相位,将降噪后的音频转换回时域[2].
使用短时傅里叶变换(STFT)将音频转换到频域,窗口长度为256个样本,重叠度为75%,并使用汉明窗口。通过减少与负频率对应的频率样本,可以将频谱矢量的大小减小到129(因为时域语音信号是实的,所以不会导致任何信息丢失)。预测器输入由8个连续的有噪声STFT向量组成,因此每个STFT输出估计是基于当前的有噪声STFT和之前的7个有噪声STFT向量计算的。
本节说明如何从一个训练文件生成目标和预测器信号。
首先,定义系统参数:
windowLength = 256;win = hamming(windowLength,“周期”);overlap = round(0.75 * windowLength);ffTLength = windowLength;inputFs = 48e3;Fs = 8e3;numFeatures = ffTLength/2 + 1;numSegments = 8;
创建一个dsp。SampleRateConverter
(DSP系统工具箱)对象将48 kHz音频转换为8 kHz。
SRC = dsp。SampleRateConverter (“InputSampleRate”inputFs,…“OutputSampleRate”fs,…“带宽”, 7920);
使用读
从数据存储中获取音频文件的内容。
audio = read(adsTrain);
确保音频长度是采样率转换器抽取因子的倍数。
decimationFactor = inputFs/fs;L = floor(numel(audio)/decimationFactor);audio = audio(1:decimationFactor*L);
将音频信号转换为8 kHz。
音频= src(音频);重置(src)
从洗衣机噪声向量中创建一个随机噪声段。
Randind = randi(numel(noise) - numel(audio),[1 1]);noiseSegment = noise(randind: randind + numel(audio) - 1);
在语音信号中加入噪声,使信噪比为0 dB。
noisePower = sum(noisessegment .^2);cleanPower = sum(audio.^2);noiseSegment = noiseSegment .* sqrt(cleanPower/noisePower);noisyAudio = audio + noisessegment;
使用stft
从原始和噪声音频信号生成幅度STFT矢量。
cleanSTFT = stft(音频,“窗口”,赢了,“OverlapLength”重叠,“FFTLength”, ffTLength);cleanSTFT = abs(cleanSTFT(numFeatures-1:end,:));noisySTFT = stft(noisyAudio;“窗口”,赢了,“OverlapLength”重叠,“FFTLength”, ffTLength);noisySTFT = abs(noisySTFT(numFeatures-1:end,:));
从噪声STFT中生成8段训练预测信号。连续预测因子之间的重叠部分为7段。
noisySTFT = [noisySTFT(:,1:numSegments - 1), noisySTFT];stftSegments = 0 (numFeatures, numSegments, size(noisySTFT,2) - numSegments + 1);为index = 1:size(noisySTFT,2) - numSegments + 1 stftSegments(:,:,index) = (noisySTFT(:,index:index + numSegments - 1));结束
设定目标和预测指标。这两个变量的最后一个维度对应于音频文件生成的不同预测器/目标对的数量。每个预测器是129乘8,每个目标是129乘1。
targets = cleanSTFT;大小(目标)
ans =1×2129 544
predictors = stftSegments;大小(预测)
ans =1×3129 8 544
为了加快处理速度,使用tall数组从数据存储中所有音频文件的语音片段中提取特征序列。与内存数组不同,tall数组通常保持不求值状态,直到调用收集
函数。这种延迟求值使您能够快速处理大型数据集。请求输出时使用收集
, MATLAB在可能的情况下结合排队计算,并采用最小的数据传递次数。如果您有Parallel Computing Toolbox™,您可以在本地MATLAB会话或本地并行池中使用tall数组。如果安装了MATLAB®Parallel Server™,您还可以在集群上运行高数组计算。
首先,将数据存储转换为tall数组。
reset(adsTrain) T = tall(adsTrain)
使用“本地”配置文件启动并行池(parpool)…连接到并行池(工人数量:6)。
T = M×1高细胞数组{234480×1双}{210288×1双}{282864×1双}{292080×1双}{410736×1双}{303600×1双}{326640×1双}{233328×1双}::::
该显示表明行数(对应于数据存储中的文件数)M还不知道。M是一个占位符,直到计算完成。
从高表中提取目标和预测量STFT。此操作将创建新的tall数组变量,以便在随后的计算中使用。这个函数HelperGenerateSpeechDenoisingFeatures
控件中已突出显示的步骤STFT目标和预测因子部分。的cellfun
命令适用于HelperGenerateSpeechDenoisingFeatures
到数据存储中每个音频文件的内容。
[目标,预测器]= cellfun(@(x))HelperGenerateSpeechDenoisingFeatures(x,noise,src),T,“UniformOutput”、假);
使用收集
评估目标和预测因素。
[目标,预测因子]= gather(目标,预测因子);
使用并行池'local'评估tall表达式:-通过1 / 1:在42秒内完成评估在1分36秒内完成
将所有特征归一化为零均值和单位标准差是一种很好的做法。
分别计算预测因子和目标的均值和标准差,并使用它们对数据进行规范化。
Predictors = cat(3, Predictors {:});noisyMean =均值(predictors(:));noisyStd = std(predictors(:));predictors(:) = (predictors(:) - noisyMean)/noisyStd;目标= cat(2,目标{:});cleanMean = mean(targets(:));cleanStd = std(targets(:));targets(:) = (targets(:) - cleanMean)/cleanStd;
将预测器和目标重塑为深度学习网络所期望的维度。
Predictors =重塑(Predictors,size(Predictors,1),size(Predictors,2),1,size(Predictors,3));Targets =重塑(Targets,1,1,size(Targets,1),size(Targets,2));
在训练期间,您将使用1%的数据进行验证。验证对于检测网络过度拟合训练数据的场景很有用。
将数据随机分成训练集和验证集。
Inds = randperm(size(predictors,4));L = round(0.99 * size(predictors,4));trainPredictors = predictors(:,:,:, indeds (1:L));trainTargets = targets(:,:,:, indeds (1:L));validatePredictors = predictors(:,:,:, indeds (L+1:end));validateTargets = targets(:,:,:, indeds (L+1:end));
首先考虑一个由完全连接层组成的去噪网络。完全连接层中的每个神经元都连接到前一层的所有激活。一个完全连接的层将输入乘以一个权重矩阵,然后加上一个偏置向量。权重矩阵和偏置向量的维度由层中神经元的数量和前一层激活的数量决定。
定义网络的层。指定输入大小为图像的大小NumFeatures
——- - - - - -NumSegments
(在这个例子中是129 × 8)。定义两个隐藏的完全连接层,每个层有1024个神经元。由于是纯线性系统,每个隐藏的全连接层后面都有一个整流线性单元(ReLU)层。批归一化层对输出的均值和标准差进行归一化。添加一个有129个神经元的全连接层,然后是一个回归层。
layers = [imageInputLayer([numFeatures,numSegments]) fulllyconnectedlayer (1024) batchNormalizationLayer reluLayer fulllyconnectedlayer (1024) batchNormalizationLayer reluLayer fulllyconnectedlayer (numFeatures) regressionLayer];
接下来,为网络指定训练选项。集MaxEpochs
来3.
使得网络通过3次训练数据。集MiniBatchSize
的128
这样网络一次能看128个训练信号。指定情节
作为“训练进步”
生成随迭代次数增加而显示训练进度的图。集详细的
来假
禁用将图表中显示的数据对应的表输出打印到命令行窗口中。指定洗牌
作为“every-epoch”
在每个历元的开始对训练序列进行洗牌。指定LearnRateSchedule
来“分段”
每经过一定次数的epoch(1),学习率就降低一个指定的因子(0.9)。集ValidationData
到验证预测器和目标。集ValidationFrequency
这样验证均方误差每历元计算一次。这个例子使用了自适应矩估计(Adam)求解器。
miniBatchSize = 128;options = trainingOptions(“亚当”,…“MaxEpochs”3,…“InitialLearnRate”1 e-5…“MiniBatchSize”miniBatchSize,…“洗牌”,“every-epoch”,…“阴谋”,“训练进步”,…“详细”假的,…“ValidationFrequency”、地板(大小(trainPredictors, 4) / miniBatchSize),…“LearnRateSchedule”,“分段”,…“LearnRateDropFactor”, 0.9,…“LearnRateDropPeriod”, 1…“ValidationData”, {validatePredictors, validateTargets});
使用指定的训练选项和层架构训练网络trainNetwork
。由于训练集很大,训练过程可能需要几分钟。要下载和加载预训练的网络,而不是从头开始训练网络,设置doTraining
来假
。
doTraining =真正的;如果doTraining denoiseNetFullyConnected = trainNetwork(trainPredictors,trainTargets,layers,options);其他的url =“http://ssd.mathworks.com/金宝appsupportfiles/audio/SpeechDenoising.zip”;downloadNetFolder = tempdir;netFolder = fullfile(下载netFolder,“SpeechDenoising”);如果~存在(netFolder“dir”) disp (“正在下载预训练网络(1个文件- 8mb)…”解压缩(url, downloadNetFolder)结束s = load(fullfile(netFolder),“denoisenet.mat”));denoiseNetFullyConnected = s.d noisenetfullyconnected;cleanMean = s.cleanMean;cleanStd = s.cleanStd;noisyMean = s.noisyMean;noisyStd = s.noisyStd;结束
计算网络中完全连接层的权重数。
numWeights = 0;为index = 1: nummel (denoiseNetFullyConnected.Layers)如果isa (denoiseNetFullyConnected.Layers(指数),“nnet.cnn.layer.FullyConnectedLayer”) numWeights = numWeights + nummel (denoiseNetFullyConnected.Layers(index).Weights);结束结束流("权重的个数是%d.\n", numWeights);
权重个数为2237440。
考虑一个使用卷积层而不是完全连接层的网络[3.].二维卷积层对输入应用滑动滤波器。该层通过沿输入垂直和水平移动滤波器来卷积输入,并计算权重和输入的点积,然后添加一个偏置项。卷积层通常比全连接层包含更少的参数。
定义[中描述的全卷积网络的层数3.],包含16个卷积层。前15个卷积层为3层一组,重复5次,滤波器宽度分别为9、5、9,滤波器个数分别为18、30、8。最后一个卷积层的滤波器宽度为129,有1个滤波器。在这个网络中,只在一个方向上(沿着频率维度)进行卷积,除了第一层之外,所有层沿着时间维度的滤波器宽度都设置为1。与全连接网络类似,卷积层之后是ReLu和批处理归一化层。
layers = [imageInputLayer([numFeatures,numSegments]) convolution2dLayer([9 8],18,“步”100年[1],“填充”,“相同”) batchNormalizationLayer relullayerrepmat (…30岁的[convolution2dLayer (1 [5]“步”100年[1],“填充”,“相同”) batchNormalizationLayer reluLayer convolution2dLayer([91],8,“步”100年[1],“填充”,“相同”) batchNormalizationLayer reluLayer convolution2dLayer([91],18,“步”100年[1],“填充”,“相同”) batchNormalizationLayer reluLayer],4,1) convolution2dLayer([51],30,“步”100年[1],“填充”,“相同”) batchNormalizationLayer reluLayer convolution2dLayer([91],8,“步”100年[1],“填充”,“相同”) batchNormalizationLayer reluLayer convolution2dLayer([129 1],1,“步”100年[1],“填充”,“相同”) regressionLayer];
训练选项与完全连接网络的选项相同,除了验证目标信号的维度被排列为与回归层期望的维度一致。
options = trainingOptions(“亚当”,…“MaxEpochs”3,…“InitialLearnRate”1 e-5…“MiniBatchSize”miniBatchSize,…“洗牌”,“every-epoch”,…“阴谋”,“训练进步”,…“详细”假的,…“ValidationFrequency”、地板(大小(trainPredictors, 4) / miniBatchSize),…“LearnRateSchedule”,“分段”,…“LearnRateDropFactor”, 0.9,…“LearnRateDropPeriod”, 1…“ValidationData”,{validatePredictors,permute(validateTargets,[3 1 2 4])});
使用指定的训练选项和层架构训练网络trainNetwork
。由于训练集很大,训练过程可能需要几分钟。要下载和加载预训练的网络,而不是从头开始训练网络,设置doTraining
来假
。
doTraining =真正的;如果doTraining denoiseNetFullyConvolutional = trainNetwork(trainPredictors,permute(trainTargets,[3 1 2 4]),layers,options);其他的url =“http://ssd.mathworks.com/金宝appsupportfiles/audio/SpeechDenoising.zip”;downloadNetFolder = tempdir;netFolder = fullfile(下载netFolder,“SpeechDenoising”);如果~存在(netFolder“dir”) disp (“正在下载预训练网络(1个文件- 8mb)…”解压缩(url, downloadNetFolder)结束s = load(fullfile(netFolder),“denoisenet.mat”));denoiseNetFullyConvolutional = s.d inoisenetfullyconvolutional;cleanMean = s.cleanMean;cleanStd = s.cleanStd;noisyMean = s.noisyMean;noisyStd = s.noisyStd;结束
计算网络中完全连接层的权重数。
numWeights = 0;为index = 1: nummel (denoiseNetFullyConvolutional.Layers)如果isa (denoiseNetFullyConvolutional.Layers(指数),“nnet.cnn.layer.Convolution2DLayer”) numWeights = numWeights + nummel (denoiseNetFullyConvolutional.Layers(index).Weights);结束结束流(“卷积层中的权重数是%d\n”, numWeights);
卷积层的权值是31812
读入测试数据集。
adsTest = audioDatastore(fullfile(dataFolder),“测试”),“IncludeSubfolders”,真正的);
从数据存储中读取文件的内容。
[cleanAudio,adsTestInfo] = read(adsTest);
确保音频长度是采样率转换器抽取因子的倍数。
L = floor(numel(cleanAudio)/decimationFactor);cleanAudio = cleanAudio(1:decimationFactor*L);
将音频信号转换为8 kHz。
cleanAudio = src(cleanAudio);重置(src)
在这个测试阶段,你用训练阶段没有使用过的洗衣机噪音来破坏语音。
噪音= audioread(“洗衣机- 16 - 8 mono - 200 - secs.mp3”);
从洗衣机噪声向量中创建一个随机噪声段。
randind = randi(numel(noise) - numel(cleanAudio), [1 1]);noiseSegment = noise(randind: randind + numel(cleanAudio) - 1);
在语音信号中加入噪声,使信噪比为0 dB。
noisePower = sum(noisessegment .^2);cleanPower = sum(cleanAudio.^2);noiseSegment = noiseSegment .* sqrt(cleanPower/noisePower);noisyAudio = cleanAudio + noisessegment;
使用stft
从有噪声的音频信号中生成幅度的STFT矢量。
noisySTFT = stft(noisyAudio;“窗口”,赢了,“OverlapLength”重叠,“FFTLength”, ffTLength);noisyPhase = angle(noisySTFT(numFeatures-1:end,:));noisySTFT = abs(noisySTFT(numFeatures-1:end,:));
从噪声STFT中生成8段训练预测信号。连续预测因子之间的重叠部分为7段。
noisySTFT = [noisySTFT(:,1:numSegments-1) noisySTFT];predictors = 0 (numFeatures, numSegments, size(noisySTFT,2) - numSegments + 1);为index = 1:(size(noisySTFT,2) - numSegments + 1) predictors(:,:,index) = noisySTFT(:,index:index + numSegments - 1);结束
通过在训练阶段计算的均值和标准差对预测因子进行归一化。
predictors(:) = (predictors(:) - noisyMean) / noisyStd;
计算去噪后STFT的幅度预测
用两个训练过的网络。
predictors =重塑(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];
计算去噪后的语音信号。istft
执行逆STFT。利用噪声STFT矢量的相位重构时域信号。
noisisedaudiofullyconnected = istft(STFTFullyConnected)…“窗口”,赢了,“OverlapLength”重叠,…“FFTLength”ffTLength,“ConjugateSymmetric”,真正的);(STFTFullyConvolutional;…“窗口”,赢了,“OverlapLength”重叠,…“FFTLength”ffTLength,“ConjugateSymmetric”,真正的);
绘制干净、有噪声和去噪的音频信号。
t = (1/fs) * (0: nummel (noisisedaudiofullyconnected)-1);figure subplot(4,1,1) plot(t,cleanAudio(1:numel(denoisedAudioFullyConnected))) title(“干净的演讲》网格)在subplot(4,1,2) plot(t,noisyAudio(1:numel(denoisedAudioFullyConnected)))) title(《吵闹的演讲》网格)在subplot(4,1,3) plot(t,denoisedAudioFullyConnected) title(去噪语音(全连接层)网格)在subplot(4,1,4) plot(t,denoisedAudioFullyConvolutional) title(去噪语音(卷积层)网格)在包含(“时间(s)”)
绘制干净、噪声和去噪的谱图。
H =图;次要情节(4 1 1)谱图(ffTLength cleanAudio,赢,重叠,fs);标题(“干净的演讲》网格)在次要情节(1、2)谱图(ffTLength noisyAudio,赢,重叠,fs);标题(《吵闹的演讲》网格)在次要情节(4 1 3)谱图(ffTLength denoisedAudioFullyConnected,赢,重叠,fs);标题(去噪语音(全连接层)网格)在次要情节(4,4)谱图(ffTLength denoisedAudioFullyConvolutional,赢,重叠,fs);标题(去噪语音(卷积层)网格)在P = get(h,“位置”);集(h,“位置”,[p(1) 65 p(3) 800]];
听嘈杂的演讲。
声音(noisyAudio fs)
听来自全连接层网络的去噪语音。
声音(denoisedAudioFullyConnected fs)
听来自卷积层网络的去噪语音。
声音(denoisedAudioFullyConvolutional fs)
听干净的语言。
声音(cleanAudio fs)
可以从数据存储中测试更多文件testDenoisingNets
。该函数生成上面突出显示的时域和频域图,并且还返回干净的、有噪声的和去噪的音频信号。
[cleanAudio,noisyAudio, noisisedaudiofullyconnected, noisisedaudiofullyconvolutional] = testDenoisingNets(adsTest,denoiseNetFullyConnected,denoiseNetFullyConvolutional,noisyMean,noisyStd,cleanMean,cleanStd);
上一节中的过程将噪声信号的整个频谱传递给预测
。这不适用于要求低延迟的实时应用程序。
运行speechDenoisingRealtimeApp
关于如何模拟流、实时版本的去噪网络的示例。该应用程序使用具有完全连接层的网络。音频帧长等于STFT跳大小,即0.25 * 256 = 64个样本。
speechDenoisingRealtimeApp
启动设计用于与模拟交互的用户界面(UI)。UI使您能够调整参数,结果立即反映在模拟中。您还可以启用/禁用对去噪输出进行操作的噪声门,以进一步降低噪声,以及调整噪声门的攻击时间、释放时间和阈值。您可以从UI中收听嘈杂的,干净的或去噪的音频。
示波器绘制干净、有噪声和去噪信号,以及噪声门的增益。
[1]https://voice.mozilla.org/en
[10]“基于深度学习的语音去噪实验”,Liu Ding, Paris Smaragdis, Minje Kim, INTERSPEECH, 2014。
[10]“基于全卷积神经网络的语音增强”,李振元,INTERSPEECH, 2017。