RNNoise算法论文阅读
摘要
尽管噪声抑制是信号处理中的一个相对成熟的领域,但它仍然依赖于估计器算法和参数微调。本文中,作者展示了一种噪声抑制的混合DSP/深度学习方法。作者在保证输出高质量增强语音的同时,尽可能地降低算法的复杂性。利用四层隐含层的深度循环神经网络估计理想的临界频带增益,并采用传统的基音滤波器衰减基音谐波间的噪声。与传统的最小均方误差频谱估计器相比,该方法实现了更高的增强语音质量,且保持了足够低的复杂性,以便以48KHz的采样率实时在低功耗CPU上操作。
关键词:噪声抑制、循环神经网络
Ⅰ.介绍
从上世纪70年代开始,噪声抑制一直是一个令人感兴趣的话题。顾名思义,该想法是获取噪声信号并消除尽可能多的噪声,同时使目标语音失真最小。传统形式的频谱估计技术依赖于噪声频谱估计器(Noise Spectral Estimation,NSE),如图2.1所示:
语音活性检测器(Voice Activity Detection,VAD)及其相关算法是噪声频谱估计器的驱动。图中的语音活性检测器(Voice Activity Detection,VAD)、噪声频谱估计器(Noise Spectral Estimation,NSE)、谱减器(Spectral Subtraction,SS)这三个都需要准确的估计算法且难以调整。例如,原始的初始噪声估计器和基于谱减法的谱估计器[1]已被更准确的谱估计器[2,3]和谱幅度估计器[4]所取代。尽管有所改进,但这些估计器仍然难以设计,并且需要大量的手动调整工作。这就是为什么深度学习的最新进展对噪声抑制很有吸引力。
深度学习技术已经被用于噪声抑制[5,6,7,8,9]。许多提议的方法针对无需低延迟的自动语音识别(Automatic speech recognition,ASR)应用程序。此外,在许多情况下,神经网络的大尺寸使得没有GPU的设备实时实现变得困难。在本文所提及的方法中,作者专注于具有低复杂性的实时应用程序(如视频会议)。作者同样专注于全频段(48KHz)语音。为了实现这些目标,作者选择了一种混合方法(第Ⅱ节),依靠经过验证的信号处理技术并使用深度学习(第Ⅲ节)来替代传统方法难以调整的估计器。这种方法与所谓的端到端系统形成鲜明对比,后期大部分或全部信号处理操作都被机器学习所替代。这些端到端系统已经清晰地展现了深度学习的能力,但它们通常以显著增加复杂性为代价。
作者在第Ⅳ节中表明了所提及的方法具有可接受的复杂性,并比传统的方法具有更高的增强语音质量(见第Ⅴ节)。在第Ⅵ节总结并展望了进一步改进该方法的方向。
Ⅱ.信号模型
作者提出了一种混合噪声抑制方法:始终保留所有必需的基本信号处理(没有神经网络尝试对其进行仿真),但是让神经网络学习所有需要在信号处理旁边进行无休止调整的棘手部分。
主处理循环基于具有50%混叠(10ms帧移)的20ms窗口。分析和综合都使用相同的Vorbis传函数[10],满足PrincenBradley准则[11]。窗函数定义为:
w
(
n
)
=
sin
[
π
2
sin
2
(
π
n
N
)
]
(2.1)
w(n)=\sin[\frac{\pi}{2}\sin^{2}(\frac{\pi n}{N})] \tag{2.1}
w(n)=sin[2πsin2(Nπn)](2.1)
其中,
N
N
N表示窗口长度。
该系统的信号级框图如图2所示。大部分抑制是使用从循环神经网络(RNN)计算得出的增益在低分辨率频谱包络上执行的。这些增益只是理想比值掩膜(Ideal Ratio Mask,IRM)的平方根,为了进一步进行噪声抑制,使用FIR滤波器进行基音滤波,去除基音谐波间的噪声。
A.频带结构
在[5]采取的方法中,神经网络用于直接估计频率区间的大小,总共需要6144个隐藏单元和接近1000万个权重来处理8KHz语音。扩展到48KHz语音(使用20ms的帧长)需要400个输出(0到20KHz)的网络,这显然会导致算法达到无法承受的复杂度。
避免该问题的一种方法是假设语音和噪声的频谱包络足够平坦,以使用比频率区间(frequency bin)更粗糙的分辨率。此外,该方法不直接估计频谱幅度,而是估算关键频带的最优增益,该增益具有限制在0到1之间的显著优势。该方法使用与Opus编码器[13]中使用的巴克刻度(Bark Scale)[12]近似版本相同的划分方法将频带进行划分。也就是说,这些频带在高频段遵循巴克刻度,但在低频段始终至少为4个频率区间。作者使用三角窗函数而不是矩形窗,峰值响应位于频带间的边界。共使用了22个频带,因此,网络仅需输出22个范围在[0,1]之间的值。
设
w
b
(
k
)
w_{b}(k)
wb(k)为频带
b
b
b在频率
k
k
k上的振幅,且
∑
b
w
b
(
k
)
=
1
\sum_{b}w_{b}(k)=1
∑bwb(k)=1。对于变换后的信号
X
(
k
)
X(k)
X(k),频带中的能量由下式给出:
E
(
b
)
=
∑
b
w
b
(
k
)
∣
X
(
k
)
∣
2
(2.2)
E(b)=\sum_{b}w_{b}(k)\lvert X(k) \rvert^2 \tag{2.2}
E(b)=b∑wb(k)∣X(k)∣2(2.2)
每个频带增益定义为
g
b
g_{b}
gb:
g
b
=
E
s
(
b
)
E
x
(
b
)
(2.3)
g_{b}=\sqrt \frac{E_{s}(b)}{E_{x}(b)} \tag{2.3}
gb=Ex(b)Es(b)(2.3)
其中,
E
s
(
b
)
E_{s}(b)
Es(b)为纯净语音(标注数据、基准数据)的能量,而
E
X
(
b
)
E_{X}(b)
EX(b)为输入语音(含噪声)的能量。考虑到最优频带增益
g
^
b
\hat g_{b}
g^b,以下内插增益用于各个频带
k
k
k:
r
(
k
)
=
∑
b
w
b
(
k
)
g
^
b
(2.4)
r(k)=\sum_{b}w_{b}(k)\hat g_{b} \tag{2.4}
r(k)=b∑wb(k)g^b(2.4)
B.基音滤波
使用巴克刻度衍生频带来计算增益的主要缺点是我们无法在频谱中对更精细的细节进行建模。实际上,这不利于抑制基音谐波间的噪声。作为替代方案,我们可以在基音周期使用梳状滤波器来消除谐波间噪声,这与语音编码器后置滤波器的工作方式[14]相似。因为语音信号的周期性严重依赖于频率(尤其是采样率为48KHz的语音信号),因此基音滤波器基于每个频带滤波系数
α
b
\alpha_{b}
αb应用在频域。设
P
(
k
)
P(k)
P(k)为加时延的基音信号
x
(
n
−
T
)
x(n-T)
x(n−T)的加窗截取后的DFT,通过计算
X
(
k
)
+
α
b
P
(
k
)
X(k)+\alpha_{b}P(k)
X(k)+αbP(k)来执行滤波,然后将结果重新归一化,使得每个频带的信号具有和原始信号
X
(
k
)
X(k)
X(k)相同的能量。
频带
b
b
b的基音相关性定义为:
p
(
b
)
=
∑
k
w
b
(
k
)
R
[
X
(
k
)
P
∗
(
k
)
]
∑
k
w
b
(
k
)
∣
X
(
k
)
∣
2
⋅
∑
k
w
b
(
k
)
∣
P
(
k
)
∣
2
(2.5)
p(b)=\frac{\sum_{k}w_{b}(k)\mathcal{R}[X(k)P^{*}(k)]}{\sqrt{\sum_{k}w_{b}(k)\lvert X(k) \rvert^2 \cdot \sum_{k}w_{b}(k)\lvert P(k) \rvert^2}} \tag{2.5}
p(b)=∑kwb(k)∣X(k)∣2⋅∑kwb(k)∣P(k)∣2∑kwb(k)R[X(k)P∗(k)](2.5)
其中,
R
[
⋅
]
\mathcal{R}[\cdot]
R[⋅]表示复数值的实部,
⋅
∗
\cdot^{*}
⋅∗表示复共轭。注意,对于单个频带,(2.5)等效于时域上的基音相关性。
推导滤波系数
α
b
\alpha_{b}
αb的最佳值是困难的,并且最小化均方误差(Minimize Mean Squared Error,MMSE)在感知上并不是最佳的。相反,我们使用基于以下约束和观察的启发式方法。由于噪声会导致基音相关性降低,正常来说,
p
b
p_{b}
pb会小于
g
b
g_{b}
gb,因此,对于任何
p
b
≥
g
b
p_{b} \geq g_{b}
pb≥gb的频带,
α
b
=
1
\alpha_{b}=1
αb=1。在没有噪声的情况下,由于不想让信号失真,所以当
g
b
=
1
g_{b}=1
gb=1时,
α
b
=
0
\alpha_{b}=0
αb=0。类似地,当
p
b
=
0
p_{b}=0
pb=0时,由于不需要增强基音,因此
α
b
=
0
\alpha_{b}=0
αb=0。考虑所有上述约束及它们间的平滑行为,使用下述表达式来计算滤波器系数:
α
b
=
min
(
p
b
2
(
1
−
g
b
2
)
(
1
−
p
b
2
)
g
b
2
,
1
)
(2.6)
\alpha_{b}=\min(\sqrt{\frac{p_{b}^2(1-g_{b}^2)}{(1-p_{b}^2)g_{b}^2}},1) \tag{2.6}
αb=min((1−pb2)gb2pb2(1−gb2),1)(2.6)
即使我们在这里使用了FIR基音滤波器,也可以基于形式为
H
(
z
)
=
1
/
(
1
−
β
z
−
T
)
H(z)=1/(1-\beta z^{-T})
H(z)=1/(1−βz−T)的IIR基音滤波器来计算
P
k
P_{k}
Pk,这会更好地抑制基音谐波间的噪声,代价是失真会稍微增加。
C.特征提取
神经网络的输入和输出的频带数目应该相等,且应包含噪声信号的对数谱。为了改善训练数据的条件,我们将对数频谱进行离散余弦变换(Discrete Cosine Transform,DCT),得到22个巴克频率倒谱系数(Bark-Frequency Cepstral Coefficients,BFCC )。除此之外,还包括前6个BFCC的时域上的一阶二阶导数。由于我们已经需要在(2.5)中计算基音,因此,我们计算整个频带上基音相关性的DCT,并将其前6个系数包括到特征集中。最后, 加上基音周期以及有助于语音检测的频谱非平稳性度量。总共使用42个输入特征(22+6x2+6+1+1)。与语音识别中通常使用的特征不同, 这里不使用倒谱均值归一化, 并且包含第一倒谱系数。这是有意的选择, 因为我们需要跟踪噪声的绝对水平, 这确实会使特征对信号的绝对幅度和信道频率响应敏感。这将在章节III-A中讨论。
Ⅲ.深度学习架构
神经网络紧密遵循噪声抑制算法的传统结构,如图3所示。该设计基于以下假设:三个循环层分别构成图1中的基本组成部分之一。在实践中,神经网络可以完全不是如我们所规划这样的承担这些功能 (并且可能在某种程度上确实如此)。它总共包括215个单元,4个隐藏层,最大的一层有96个单元。我们发现增加单元数量并不能显着提高噪声抑制的质量。但是,损失函数和我们构造训练数据的方式对最终质量有很大影响。我们发现门控循环单元(GRU)[15]在此任务上略胜于LSTM,同时也更简单。
H
(
z
)
=
1
+
r
1
z
−
1
+
r
2
z
−
2
1
+
r
3
z
−
1
+
r
4
z
−
4
(2.7)
H(z)=\frac{1+r_{1}z^{-1}+r_{2}z^{-2}}{1+r_{3}z^{-1}+r_{4}z^{-4}} \tag{2.7}
H(z)=1+r3z−1+r4z−41+r1z−1+r2z−2(2.7)
其中,
r
1
、
.
.
.
、
r
4
r_{1}、... 、r_{4}
r1、...、r4中的都是均匀分布在
[
−
3
/
8
,
3
/
8
]
[-3/8, 3/8]
[−3/8,3/8] 之间的随机值。对信号幅度的鲁棒性是通过改变混合信号的最终幅度来实现的。
我们总共有6个小时的语音和4个小时的噪声数据,通过使用各种增益和滤波器组合并将数据重新采样到40 kHz至54 kHz之间的频率,可以产生140个小时的嘈杂语音。
B.优化过程
在训练过程中, 因为无法精确确定正确的增益, 损失函数被用于判断衰减是过多了还是少了。尽管二值交叉熵函数在优化[0,1] 中的值用的很普遍, 但因为它与感知效果不匹配, 在获取合适增益值时结果并不好。我们使用以下损失函数进行训练:
L
(
g
b
,
g
^
b
)
=
(
g
b
γ
−
g
^
b
γ
)
2
(2.8)
L(g_{b},\hat g_{b})=(g_{b}^{\gamma}-\hat g_{b}^{\gamma})^2 \tag{2.8}
L(gb,g^b)=(gbγ−g^bγ)2(2.8)
其中,
g
^
b
\hat g_{b}
g^b为增益估计,
g
b
g_{b}
gb为对应的实际的增益。指数
γ
\gamma
γ为一个感知参数,控制抑制噪声的程度,由于
lim
γ
→
0
x
γ
−
1
γ
=
log
(
x
)
\lim_{\gamma \rightarrow 0}\frac{x^{\gamma}-1}{\gamma}=\log (x)
limγ→0γxγ−1=log(x),因此
lim
γ
→
0
L
(
g
b
,
g
^
b
)
\lim_{\gamma \rightarrow 0}L(g_{b},\hat g_{b})
limγ→0L(gb,g^b)最小化对数能量的均方误差,如果增益太小会导致抑制得太狠。实际中发现,
γ
=
1
/
2
\gamma =1/2
γ=1/2是个比较合适的值,此时相当于在能量是原有能量的1/4的情况下的最小化均方误差。有时, 在特定频段中可能没有噪音且没有语音, 比如当输入为无声时或在高频段上的信号被低通滤掉时。如果发生这种情况,则将标记数据的增益明确标记为undefined,并且忽略该增益的损失函数,以避免破坏训练过程。
对于神经网络的VAD输出,我们使用标准的交叉熵损失函数。训练在Tensorflow后台和Keras库上进行。
C.增益平滑
当使用估计增益
g
^
b
\hat g_{b}
g^b抑制噪声时,有时输出噪声听起来很干,缺少预期的混响成分。通过限制跨帧的
g
^
b
\hat g_{b}
g^b衰减,可以很轻易解决该问题。平滑增益
g
~
b
\tilde{g}_{b}
g~b通过下式获得:
g
~
b
=
max
(
λ
g
~
b
(
p
r
e
v
)
,
g
^
b
)
(2.9)
\tilde g_{b} = \max(\lambda \tilde{g}_{b}^{(prev)},\hat g_{b}) \tag{2.9}
g~b=max(λg~b(prev),g^b)(2.9)
其中,
g
~
b
(
p
r
e
v
)
\tilde{g}_{b}^{(prev)}
g~b(prev)为前一帧滤波增益,衰减因子
λ
=
0.6
\lambda=0.6
λ=0.6等效于135ms的混响时间。
Ⅳ.复杂度分析
为了使噪声抑制算法易于部署,希望保持大小和复杂度都较低。可执行文件的大小由代表神经网络中215个单位所需的87503个权重决定。为了使尺寸尽可能小,可以将权重量化为8位而不会降低性能。这样就可以将所有权重放入CPU的L2缓存中。
由于每个权重在每帧的乘加运算中恰好使用一次,因此神经网络每帧需要175,000个浮点运算(我们将乘加算作两个运算),因此实时使用需要17.5 M flops。每帧的IFFT和两个FFT大约需要7.5 M flops,基音搜索(以12 kHz的频率运行)大约需要10 M flops。该算法的总复杂度约为40 M flops,与全频带语音编码器相当。
该算法的非矢量C实现方式需要大约1.3%的单个x86内核(Haswell i7-4800MQ)来执行单个通道的48 kHz噪声抑制。在1.2 GHz ARM Cortex-A53内核(Raspberry Pi 3)上,相同浮点代码的实时复杂度为14%。作为比较,[9]中的16 kHz语音增强方法使用3个隐藏层,每个隐藏层具有2048个单位。这需要1250万的重量,导致1600 M flops的复杂度。即使量化为8位,其权重也不适合大多数CPU的缓存,其实时操作需要大约800 MB/s的内存带宽。
Ⅴ.结果
作者使用训练集中未使用的语音和噪声数据测试噪声抑制的质量。我们将其与SpeexDSP库(https://www.speex.org/downloads/)中基于MMSE的噪声抑制器进行了比较。尽管噪声抑制工作在48 kHz,但由于宽带PESQ的局限性,必须将输出重新采样至16 kHz [16]。图2.5中的客观结果表明,通过使用深度学习(特别是对于非平稳噪声类型),质量得到了显着改善。通过随意聆听样品可以确认这种改进。图2.4展示了示例中的噪声抑制的效果。
图中为在SNR为15 dB, 对嘈杂噪声 (babble Noise) 的噪声抑制示例语音的语谱图, (上:noisy,中:RNNoise,下:clean speech), 为了清楚起见,仅显示0-12 kHz频带。
Ⅵ.结论
本文演示了一种噪声抑制方法,该方法将基于DSP的技术与深度学习相结合。通过仅将深度学习用于难以调节的噪声抑制方面,将问题简化为仅计算22个最优的关键频带增益,而且只需几个单元就可以有效地完成。然后,通过使用简单的基音滤波器解决频段的粗分辨率。所产生的低复杂度使该方法适用于移动或嵌入式设备,并且时延足够低,可用于视频会议系统。我们还证明,其质量明显高于基于纯信号处理的方法。该技术可以轻松扩展到残留回声抑制,例如,通过将远端信号或滤波后的远端信号的倒频谱添加到输入特征中。同样,它应适用于麦克风阵列后过滤,方法是像[17]那样通过泄漏(leakage)估计来增强输入特征。