这篇文章主要介绍如何进行RNNoise的训练和测试
1、 网络结构
RNNoise分为三层嵌入式网络,由全连接层,GRU网络构成
包含了215个units
4个hidden layers
Largest:96 units
GRU VS LSTM
GRU容易执行去噪任务,需要更少的计算资源,较少参数,训练快
RNN可以对时间序列建模,而噪声抑制需要在时域获得良好的噪声估计。很长时间以来,RNN的能力受到很大的限制,因为它们长期不能保存信息,并且由于当通过时间反向传播时涉及的梯度下降过程是非常低效的(消失的梯度问题)。门控单元(LSTM和GRU)解决了简单RNN不能长期保存信息和梯度消失的问题。与简单RNN相比,GRU有两个额外的门。复位门决定是否将状态(记忆)用于计算新状态,而更新门决定状态将根据新输入改变多少。当更新门关闭时,GRU可以长时间地记住信息。
网络顺序
首先对语音数据分帧,每帧10ms,160个数据点,对这个数据点提取42维的特征,输入网络,经过第一层GRU,输出24维与原始输入42维concate进入第二层GRU,输出48维与原始输入concate,进入第三层GRU,最终输出22维的点(每个点是语音还是噪声的概率),将这个结果作为权重对原始输入进行加权,最后拟合出256的频谱用于恢复时域语音信号,这个信号就是降噪后的结果。
2、 数据集
需要准备好训练集,在本项目中可以很简单的准备训练数据和label,只需要两个长文件,一个是仅包含噪声的文件,noise_only.wav,一个是仅包含语音的文件speech_only.wav,在训练开始前,需要将噪声加入到语音,合成含噪语音noisy.wav,
将这个noisy.wav的特征送入到网络。
噪声Noise_only:人群噪声(30min),街道汽车噪声(20min),路面噪声(10min), 16k
语音Speech_only : Haitian_data 60min, 16k (可以尝试各种开源语音数据集,讯飞有很多)
需要去噪的语音Noisy:lumian,tingche, gaojia 16k(用于测试)
3、 更改采样率
工程的音频限制为单声道,16bit,采样率48k,需要修改训练数据
**下载语音转换工具:
~/rnnoise$ git clone https://github.com/smallmuou/wavutils
~/rnnoise/wav$ …/wavutils/bin/wavinfo speech_only.wav**
- 1
- 2
- 3
运行以下脚本
xx@ubuntu::~/bin$ wavinfo no_music.wav
- 1
+++++++++++++++++++++++++
+ WAVEFORM INFORMATION +
+++++++++++++++++++++++++
Audio Format: 1 (0x0001)
Num Channels: 1 (0x0001)
Sample Rate: 16000 (0x00003e80)
its Per Sample: 16 (0x0010)
PCM Size: 140592720 (0x08614650)
xx@ubuntu:~/bin$ pcm2wav 1 48000 16 noise_only.pcm noise_only.wav
- 1
+++++++++++++++++++++++++
+ WAVEFORM INFORMATION +
+++++++++++++++++++++++++
Audio Format: 1 (0x0001)
Num Channels: 1 (0x0001)
Sample Rate: 48000 (0x0000bb80)
Bits Per Sample: 16 (0x0010)
PCM Size: 365189238 (0x15c45876)
4、 训练过程
(1) step 1 转换为pcm格式
~/rnnoise/wavutils/bin/wav2pcm speech_only.wav speech_only.pcm
~/rnnoise/wavutils/bin/wav2pcm noise_only.wav noise_only.pcm
~/rnnoise/wavutils/bin/wav2pcm speech_noise.wav speech_noise.pcm
[INFO] Successfully convert noise_only.pcm to noise_only.wav.
- 1
- 2
- 3
- 4
- 5
(2) step2 提取特征
Ubuntu下,运行在RNNoise工程目录中的src中的compile.sh,编译denoise.c :获得denoise_training可执行文件。在Windows下直接生成解决方案,debug文件夹有denoise.exe 可执行文件。
需要修改stdout->fout,否则会在shell里乱输出乱码。修改原始count=50000000,太多帧运行时间是10倍,但是我们没有10倍的语音,效果不明显。修改为500000。
~/rnnoise/src/ $./compile.sh
~/rnnoise/src/denoise_training speech_only.pcm noise_only.pcm output.f32
-> "matrix size: 500000 x 87"
- 1
- 2
- 3
输入noise_only.wav和speech_only.wav,利用denoise.c 提取特征,设定500000帧,可读取83分钟音频,获得文件output.f32,包含500000x87的矩阵,占用170M,运行时间大约为5分钟
(3) step 3 数据转换
RNNoise的training文件夹下,利用bin2hdf5.py 将二进制数据转换成h5数据。方便在python下训练。文件大小170M几乎不变,仅 增加2k。
~/rnnoise/training/bin2hdf5.py output.f32 500000 87 denoise_data9.h5
- 1
(4) step 4 训练神经网络
在tensorflow框架下,keras和theano作为后端训练一个包含三个GRU的神经网络。输入42维特征,输出22维的G(类似概率值的增益系数,用于调整原始输入的频谱数据)。500000帧的数据分为250个sequence,每个sequence包含2000个42维的数据。其中225份为训练集,25份为验证集。迭代120次。每次迭代用时40秒。整个过程80分钟。输出权重文件newweights9i.hdf5,占用1079k。
~/rnnoise/training/rnn_train.py
-> denoise_data9.h5 -> newweights9i.hdf5
Using Theano backend.
Build model…
Loading data…
done.
250 sequences
250 train sequences. x_shape = (250, 2000, 42) y_shape = (250, 2000, 22)
Train…
Train on 225 samples, validate on 25 samples
Epoch 1/120
32/225 [===>…] - ETA: 39s - loss: 9.1596 - denoise_output_loss: 0.6047 - vad_output_loss: 6.22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
(5)step 5 获取权重数据
~/rnnoise/training/dump_rnn.py newweights9i.hdf5 rnn_data.c rnn_data.h
- 1
将训练获得的权重数据写入到工程文件rnn_data.c,rnn_data.h
但是作者给出的 dump_rnn.py 无法运行,需要修改python文件并添加函数,得到修改后的dump_rnn.py
++++++++++++++++++++++++++++++++++++
from keras.constraints import Constraint
def mean_squared_sqrt_error(y_true, y_pred):
return K.mean(K.square(K.sqrt(y_pred) - K.sqrt(y_true)), axis=-1)
- 1
- 2
- 3
+++++++++++++++++++++++++++++++++++++
def my_crossentropy (y_true, y_pred):
return K.mean(2*K.abs(y_true-0.5) * K.binary_crossentropy(y_pred, y_true), axis=-1)
- 1
- 2
++++++++++++++++++++++++++++++++++++++
def mymask (y_true):
return K.minimum(y_true+1., 1.)
- 1
- 2
++++++++++++++++++++++++++++++++++++++
def msse (y_true, y_pred):
return K.mean(mymask(y_true) * K.square(K.sqrt(y_pred) - K.sqrt(y_true)), axis=-1)
- 1
- 2
++++++++++++++++++++++++++++++++++++++
def mycost(y_true, y_pred):
return K.mean(mymask(y_true) * (10K.square(K.square(K.sqrt(y_pred) - K.sqrt(y_true))) + K.square(K.sqrt(y_pred) -
K.sqrt(y_true)) + 0.01K.binary_crossentropy(y_pred, y_true)), axis=-1)
- 1
- 2
- 3
+++++++++++++++++++++++++++++++++++++++
def my_accuracy (y_true, y_pred):
return K.mean(2*K.abs(y_true-0.5) * K.equal(y_true, K.round(y_pred)), axis=-1)
- 1
- 2
+++++++++++++++++++++++++++++++++++++++
class WeightClip (Constraint):
def init (self, c=2,name='WeightClip'):
self.c = c
- 1
- 2
- 3
+++++++++++++++++++++++++++++++++++++++
获得rnn_data.c, 409kb,rnn_data.h 1kb。
4、 降噪
用新的rnn_data.c和rnn_data.h替换工程文件rnn_data.c, rnn_data.h。
在denoise_demo.c 里修改音频输入为48k,输出为16k。(用一个简单的方法每三个点取一个值,进行降采样)
将if training =1,denoise.c的if training=0。
然后make clean,make。
得到降噪后的wav文件。5分钟的语音降噪过程为1分钟。
Ubuntu:rnnoise_demo speech_noise.pcm denoised_speech_noise.pcm
Windows:denoise.exe speech_noise.pcm denoised_speech_noise.pcm。
- 1
- 2
参考文档
RNNoise: Learning Noise Suppression
https://people.xiph.org/~jm/demo/rnnoise/
1、 The code: RNNoise Git repository :
https://git.xiph.org/?p=rnnoise.git;a=summary
(Github mirror:https://github.com/xiph/rnnoise/)
2、J.-M. Valin, A Hybrid DSP/Deep Learning Approach to Real-Time Full-Band Speech Enhancement, arXiv:1709.08243 [cs.SD], 2017.
https://arxiv.org/pdf/1709.08243.pdf
3、 Jean-Marc’s blog post for comments
https://jmvalin.dreamwidth.org/15210.html