我将通过几篇文章来介绍一下RNN用于降噪的实例。
综合论文和项目介绍及代码编写
原文链接:https://people.xiph.org/~jm/demo/rnnoise/
上图显示了前后音频噪声抑制的频谱图。(上:noisy,中:RNNoise,下:clean speech)
该演示介绍了RNNoise项目,显示了如何将深度学习应用于噪声抑制。主要思想是将经典信号处理与深度学习结合起来,创建一个小而快速的实时噪声抑制算法。不需要昂贵的GPU,可以在Raspberry Pi上轻松运行。结果更简单(更容易调谐),听起来比传统的噪音抑制系统更好。
噪音抑制是言语处理中的一个相当古老的话题,至少可以追溯到70年代。顾名思义,这个想法是采取嘈杂的信号,并尽可能多地消除噪音,同时对感兴趣的言论造成最小的失真。
这是传统噪声抑制算法的概念图。语音活动检测(VAD)模块检测信号何时包含声音以及何时只是噪声。这被噪声谱估计模块用于计算噪声的频谱特性(每个频率多少功率)。然后,知道噪声模型,它可以从输入音频“减去”(并没有那么简单)。
从上图看,噪声抑制看起来很简单:只是三个概念上简单的任务,任何本科EE学生都可以写一个噪音抑制算法,有时困难的是在任何时间,各种噪音情况下使系统工作正常。这需要非常仔细地调整算法中的每个参数,需要在包含大量特殊信号的情况下进行大量的测试。speexdsp库中的噪音抑制器做了一些工作,但不是很好。
深度学习是一种新思想的新版本:人工神经网络。虽然自六十年代以来一直存在,但近年来有了很多重要的新发展:
我们现在知道如何使使用比两个隐藏层更深的网络;
我们知道如何让循环网络在过去很长时间记住状态;
我们有实际训练他们的计算资源。
循环神经网络(RNN)很好地应用于语音处理,因为它们可以对时间序列建模,而不是仅仅考虑输入和输出帧。这对噪声抑制特别重要,因为我们需要在时间尺度上获得较好的噪声估计。长期以来,RNN的能力受到很大的限制,因为它们长期不能保存信息,并且通过时间进行反向传播时涉及的梯度下降过程是非常低效的(梯度消失问题)。这两个问题都通过门控单元来解决,如长期记忆(LSTM),门控循环单元(GRU)及多种变体。
RNNoise使用门控循环单元(GRU),因为它在此任务上执行得比LSTM略好,并且需要更少的资源(CPU和权重的存储容量)。与简单的循环单元相比,GRU有两个额外的门。复位门决定是否将当前状态(记忆)用于计算新状态,而更新门决定当前状态将根据新输入改变多少。这个更新门(关闭时)使得GRU可以长时间地记住信息,这是GRU(和LSTM)比简单的循环单元执行得更好的原因。
上图将简单的循环单元与GRU进行比较。区别在于GRU的r和z门,这使得有可能学习更长期的模式。两者都是基于整个层的先前状态和输入计算的软开关(0到1之间的值),具有S形激活功能。当更新门z在左边时,状态可以在很长一段时间内保持恒定,直到一个条件使z转向右边。
由于深入学习的成功,在工程问题中引入深层神经网络是普遍做法。这些方法称为端到端——神经元接受并传递信息,使得误差最小化。端对端方法已被应用于语音识别和语音合成等工程问题,这些端到端系统已经证明了深度神经网络的强大。另一方面,这些系统有时可能不是最优的,而且在资源方面是浪费的。例如,噪声抑制的一些方法使用具有数千个神经元和数千万个权重的层来执行噪声抑制。缺点是模型运行网络所需的计算成本巨大,模型本身的规模也难以控制,因为存储了数千行代码以及几十兆字节的神经元权重。
这就是为什么我们采用不同的方法原因:保持所有必要的基本信号处理(未经过神经网络的仿真),紧接着让神经网络学习所有需要反复调整参数的棘手繁杂的工作。与现有的深度学习噪音抑制工作不同的是,我们的目标是实时通信而不是语音识别,所以处理的语音帧不能超过几毫秒(在这种情况下为10毫秒)。
为了避免因大量的神经元产生大量的输出,我们决定不直接使用样本或频谱。相反,我们考虑遵循bark scale采用的频段,一个与我们感知到的声音相匹配的频率比例。在此基础上我们使用总共22个频段,而不是复杂的480个频谱值。
Opus混合式编码的布局与实际的Bark比例频段的比较。对于RNNoise,我们使用与Opus相同的基本布局。由于我们使频段重叠,Opus编码的频段之间的分界成为重叠的RNNoise频段的中心。由于耳朵在高频部分的分辨率较差,所以在高频段较宽,在低频下,频段较窄,但并不像巴克比例那样窄,因为我们没有足够的数据来做出很好的估计。
当然,我们无法从22个频段的能量重建音频。我们可以做的是计算一个应用于每个频带信号的增益。可以将其视为使用22频段均衡器,并快速更改每个频段的电平,以便衰减噪声,但让信号通过。
计算频带增益的优点:首先,它使模型变得非常简单,只需要很少的频带计算。第二,不会产生所谓的音乐噪声伪影(musical noise artifacts),因为在周围被衰减的同时只有单音调能通过。这些伪影在噪声中很常见且很难处理,频带足够宽时,要么让整个频带通过,要么剪掉。第三个优点来自于如何优化模型。由于增益总是在0和1之间,所以简单地使用S形激活函数(其输出也在0和1之间)来计算它们确保计算方法的正确,比如不在一开始就引入认为噪声。
分频段计算的缺点是在没有语音的足够分辨率时不能在基频谐波之间很细致的抑制噪声。但是这并不是那么重要,因为可以通过额外的手段来处理,比如使用一个类似于语音编码增强的后滤波方法:使用梳状滤波器在一个基频周期(pitch period)内消除间谐波噪声(inter-harmonic noise)。
由于我们计算的输出是基于22个频段的,所以输入频率分辨率更高是没有意义的,因此我们使用相同的22个子带将频谱信息提供给神经网络。因为音频具有很大的动态范围,所以计算能量的log而不是直接传送能量值。更进一步,我们使用这个对数值进行DCT对特征进行去相关。所得到的数据是基于Bark量表的倒谱,其与语音识别中非常常用的梅尔倒谱系数(MFCC)密切相关。
纯粹的RNN模型处理8khz语音,计算增益使用了6144个隐藏单元,大概1000万个权重
对于20ms的48khz语音需要400个输出。
使用一个较粗的范围尺度比按照频率尺度计算更有效。选取了音频编码较常使用的bark scale
Ex的计算:
求平方和,并做一次指数平滑,每个频带的增益:
Ex是clean speech,Es是noisy信号,频率尺度的插值增益:
我们使用的网络架构灵感源于传统的噪音抑制方法。大部分工作是由3个GRU层完成的。下图显示了我们用于计算频带增益的层次,以及架构如何映射到噪声抑制中的传统步骤。当然,与普通神经网络一样,我们没有实际的证据证明网络如我们期望的一样运行,而是设计成这样的结构确实证明能比其他的网络做的效果更好。
上图的展示的是次项目所使用的拓扑结构,每个方框代表了一层中的神经元,指出了数目,标记了激活函数。一个VAD输出,在某些方面有用,但是并没有用于噪声抑制网络的乘数。另一个22维的输出,将增益用于不同的频率,可以进行噪声抑制。
在噪声抑制的情况下,我们不能仅仅收集可用于监督学习的输入/输出数据,因为我们很少同时获得干净的语音和噪音。相反,我们必须从清晰的语音和噪音的单独录音中人为地创建数据。 棘手的部分是获得各种各样的噪音数据,以增强语音。 我们还必须确保覆盖各种录音条件。 例如,仅在全频段音频(0-20 kHz)下训练的早期版本在8 kHz低通滤波时失效。作者使用McGill TSP speech database1 (Frenchand English) 和NTT Multi-Lingual Speech Database for Telephonometry 。噪声有computer fans, office, crowd, airplane, car,train, construction。使用6小时的clean speech和4小时的noise,产生了140个小时的noisy speech,并在40khz-54khz范围内重采样。
与语音识别常见的不同,我们选择不将倒谱平均归一化应用于特征,并保留代表能量的第一个倒谱系数。 因此,我们必须确保数据包括所有现实级别的音频。 我们还对音频应用随机滤波器,使系统对各种麦克风频率响应(通常由倒谱均值归一化处理)具有鲁棒性。
在(0,1)范围内,交叉熵是神经网络训练普遍使用的损失函数,但是对于语音信号,结果并不能很好的匹配感知影响(perceptual effect),采用了平方误差L(g_b,g ̂_b)=(〖g_b〗^γ-〖g ̂_b〗^γ )^2
γ是一个感知参数,控制了在多大程度抑制噪声。使用时不采用log域的mean square loss防止过度抑制,折中的做法是取γ=1/2。
VAD 的输出使用的是标准交叉熵损失。
训练过程使用tensorflow backend和keras library。
使用增益g来抑制噪声会使声音听起来很枯燥,缺少回响。可以通过限制g在帧之间的衰减来修复:
g(prev)是前一帧的过滤后的增益,λ=0.6可以相当于回响时间是135ms。
由于我们的频带的频率分辨率不足,无法滤除音调谐波之间的噪声,所以我们使用基本的信号处理。 这是混合方式的另一部分。当有相同变量的多个测量值时,提高精度(降低噪声)的最简单方法就是计算平均值。显然,只是计算相邻音频样本的平均值不是我们想要的,因为它会导致低通滤波。然而,当信号是周期性的(例如语音)时,我们可以计算由基频周期偏移的采样的平均值。引入梳状滤波器,使基频谐波通过,同时衰减它们之间的频率这是含有噪声的部分。为了避免信号失真,梳状滤波器被独立地应用于每个频带,并且其滤波器强度取决于基频相关性和神经网络计算的频带增益。
我们目前使用FIR滤波器进行pitch filtering,但也可以使用IIR滤波器,如果强度太高,则会导致更高的失真风险,从而产生较大的噪声衰减。
神经网络的所有设计和训练都是使用Keras深度学习库在Python中完成的。由于Python通常不是实时系统的首选语言,所以我们必须在C中实现代码。幸运的是,运行神经网络比训练一个神经网络简单得多,所以我们只需要实现一次前向传播经过GRU层,输出22维的增益。为了更好的调整出权重的合理步幅,我们在训练期间将权重的大小限制为+/- 0.5,这使得可以使用8位值轻松存储它们。所得到的模型仅85 kB(而不是将权重存储为32位浮点数所需的340 kB)。
C代码可以使用BSD许可证。代码在x86 CPU上的运行速度比实时的要快60倍。它甚至比Raspberry Pi 3上的实时速度快7倍。具有良好的矢量化(SSE / AVX),应该可以比现在快四倍。
该代码仍然在积极的开发(没有封装API),但已经在应用程序中使用。它目前的应用场景是VoIP /视频会议应用程序,但通过一些调整,它可能适用于许多其他任务。一个容易想到的任务是自动语音识别(ASR),虽然我们只能去除嘈杂的语音并将输出发送到ASR,但这不是最优的,因为它丢弃了一些不确定性的有用信息。ASR不仅需要clean speech,而且还可以依赖于这一估计值时,这是更有用的。 RNNoise的另一个可能的目标是为电子乐器创造一个更加智能的噪音门。所有它应该采用的是良好的训练数据和一些代码的变化,将一个Raspberry Pi变成一个真正好的吉他噪音门。
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