WEBRTC中VAD算法及思想的數學解析

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。

0 概述

VAD(Voice Activity Detection), 活動語音檢測技術在語音信號處理有着重要的作用,如語音增強中估計噪聲、語音識別中進行語音端點檢測,在語音信號傳輸中用於非連續傳輸等等。

本文全面解析WEBRTC 中VAD 算法,該算法主要原理是將信號在頻譜上進行子帶劃分爲 80~ 250Hz,250~ 500Hz,500Hz~ 1K, 1~ 2K,2~ 3K,3~4KHz ,6個頻帶,計算每個頻帶能量爲特徵;通過假設檢驗,構建了噪聲和語音兩個假設,從而對每個子帶構建由2個高斯分佈組合的噪聲和語音的混合高斯分佈模型。通過極大似然估計對模型進行自適應學習優化,並通過概率比判決推斷。

該算法重點涉及了子帶劃分技術、極大似然估計、假設檢驗等知識,本文將通過信號與系統、統計學習的知識解析代碼中所蘊含的數學。

1 子帶劃分濾波器 SplitFilter

1.1 全通濾波器 AllPassFilter

在這裏插入圖片描述
從函數中
tmp32 = state32 + filter_coefficient * *data_in;
state32 = (*data_in * (1 << 14)) - filter_coefficient * tmp16;
x(n)x(n)爲輸入序列,y(n)y(n)爲輸出序列,cc爲實係數filter_coefficient ,可知表示 :
y(n)=x(n1)cy(n1)+cx(n) y(n) =x(n-1)-c*y(n-1) + c*x(n)
但由於函數中data_in +=2, 即反饋是隻與歷史第二值相關的,其表示爲:
y(n)=x(n2)cy(n2)+cx(n) y(n) =x(n-2)-c*y(n-2) + c*x(n)
則傳輸函數爲:
H(z)=c+z21+cz2 H(z) = \frac{c+z^{-2}}{1+cz^{-2}}
是一個2階全通濾波器,H(w)=1|H(w)| = 1

1.2 IIR 傳輸函數的並聯全通實現

根據IIR傳輸函數通過全通函數並聯性質【參考文獻《數字信號處理–基於計算機的方法》】:

  • 一對互補的低通和高通傳輸函數可有兩個穩定的全通濾波器並聯組成:
    F(z)=12(A0(z)+A1(z))F(z)= \frac{1}{2}(A_0(z)+A_1(z))G(z)=12(A0(z)A1(z)) G(z)= \frac{1}{2}(A_0(z)-A_1(z))F(z)2+G(z)2=1|F(z)|^2 + |G(z)|^2 = 1

  • 對於低通高通濾波器對,傳輸函數的階數N必須是奇數,其中A0(z)A_0(z)A1(z)A_1(z)的階數相差1。

由上述可知,一對互補的低通高通傳輸函數可以有兩個相同階數的全通函數多相分解得出:
E(z)=E0(z2)+z1E1(z2)E(z) = E_0(z^2) + z^{-1}E_1(z^2)
E0(z2)E_0(z^2)E1(z2)E_1(z^2)爲相同階數的全通函數,z1z^{-1}使輸入序列移位。
在這裏插入圖片描述
webrtc vad 中SplitFilter 正是通過上述IIR傳輸函數多相分解成全通函數並聯技術實現,並實現對輸出2倍下抽樣。

第一個全通濾波器的參數kAllPassCoefsQ15[0] = 20972, 即c=20972/(215)=0.64c=20972/(2^{15})=0.64,則傳輸函數:
A0(z)=0.64+z21+0.64z2 A_0(z) = \frac{0.64+z^{-2}}{1+0.64*z^{-2}}
第二個全通濾波器的參數kAllPassCoefsQ15[0] = 5571, 即c=5571/(215)=0.17c=5571/(2^{15})=0.17, 則傳輸函數:
A1(z)=0.17+z21+0.17z2 A_1(z) = \frac{0.17+z^{-2}}{1+0.17*z^{-2}}
第一個全通濾波器的輸入序列data_in[n]是第二個全通函數輸入序列data_in[n+1]的移位,且全通濾波器中輸出是 tmp16 = (int16_t) (tmp32 >> 16); // Q(-1) *data_out++ = tmp16;Q(-1), 即1/21/2的輸出,則傳輸函數爲
H(z)=12(A1(z)+z1A0(z))H(z)= \frac{1}{2}(A_1(z) _-^+ z^{-1}A_0(z))

由於全通函數的輸出只與當前輸入值和歷史第二輸入值、輸出值有關,而全通濾波器的當前輸入是2倍下抽樣輸入x(2n+1),即data_in[1]、data_in[3]、data_in[5]……,則輸出也只計算得到data_out[1]、data_out[3]、data_out[5]……, 相當於對原輸出y(n)進行2倍下抽樣的數據y(2n+1)。

函數中*hp_data_out++ -= *lp_data_out;對應的高通傳輸函數:
Hhigh=12(A1(z)z1A0(z))H_{high} =\frac{1}{2}(A_1(z) - z^{-1}A_0(z))Hhigh=12(0.17+z21+0.17z2z10.64+z21+0.64z2)H_{high} =\frac{1}{2}( \frac{0.17+z^{-2}}{1+0.17*z^{-2}} - z^{-1}\frac{0.64+z^{-2}}{1+0.64*z^{-2}})
函數中*lp_data_out++ += tmp_out;對應的低通傳輸函數:
Hlow=12(A1(z)+z1A0(z))H_{low} =\frac{1}{2}(A_1(z) + z^{-1}A_0(z))Hlow=12(0.17+z21+0.17z2+z10.64+z21+0.64z2)H_{low} =\frac{1}{2}( \frac{0.17+z^{-2}}{1+0.17*z^{-2}} + z^{-1}\frac{0.64+z^{-2}}{1+0.64*z^{-2}})

通過matlab畫出上述低通濾波器和高通濾波器:

%sliptfilter

clc; close all; clear all;
%20972 5571
% 全通濾波器 z^-1* A1(z)
c = 20972/(2^15);
B =[0 c 0 1];
A =[1 0 c];
[A1,W1] = freqz(B,A,1024,'whole',4000);
A1f =abs(A1);
A1a = angle(A1);
x = 4000/512;
%plot((1:512)*x,A1f(1:512));

% 全通濾波器  A0(z)
c = 5571/(2^15);
B =[c 0 1];
A =[1 0 c];
[A0,W0] = freqz(B,A,1024,'whole',4000);
A0f =abs(A0);
A0a = angle(A0);
%plot((1:512)*x,A0f(1:512));

% 高通濾波
H = A1 - A0;
H = H/2;
Hf=abs(H);
Ha= angle(H);
plot((1:512)*x,Hf(1:512)); hold on;
%低通濾波
L = A1 + A0;
L = L/2;
Lf = abs(L);
La = angle(L);
plot((1:512)*x,Lf(1:512));

在這裏插入圖片描述
上圖正顯示一對互補的低通高通濾波器。

2 高斯正態分佈(Gaussian normal distribution)概率的數值計算

在這裏插入圖片描述
在這裏插入圖片描述

高斯正太分佈的概率計算公式爲:
12πse(xm)22s2\frac{1}{ \sqrt{2\pi}s} * e^{ \frac{-(x - m)^2} {2 * s^2} }

WebRtcVad_GaussianProbability 正是用於計算上述概率值的函數,該函數採用巧妙計算機數值計算,近似地求解浮點指數下的概率值。

由於之後求解是求解似然比,因此該函數不考慮計算尺度12π\frac{1}{ \sqrt{2\pi}}

  // Calculate |inv_std| = 1 / s, in Q10.
  // 131072 = 1 in Q17, and (|std| >> 1) is for rounding instead of truncation.
  // Q-domain: Q17 / Q7 = Q10.
  tmp32 = (int32_t) 131072 + (int32_t) (std >> 1);
  inv_std = (int16_t) WebRtcSpl_DivW32W16(tmp32, std);

首先上面代碼求方差倒數1/s1/s,其做法是對小數進行四捨五入(rounding)而不是直接截斷(truncation), 所以加了(std >> 1), 即在Q7的std的一半,也就是0.5(小數部分加0.5 強轉整形下截斷就得四捨五入結果)。

接下來計算得到式子|tmp32| = (x - m)^2 / (2 * s^2), in Q10.,當tmp32 < kCompVar時(如果大於,則趨於0)求它的ee自然指數解,這裏通過計算機數值計算巧妙近似求解。

  // If the exponent is small enough to give a non-zero probability we calculate
  // |exp_value| ~= exp(-(x - m)^2 / (2 * s^2))
  //             ~= exp2(-log2(exp(1)) * |tmp32|).
  if (tmp32 < kCompVar) {
    // Calculate |tmp16| = log2(exp(1)) * |tmp32|, in Q10.
    // Q-domain: (Q12 * Q10) >> 12 = Q10.
    tmp16 = (int16_t)((kLog2Exp * tmp32) >> 12);
    tmp16 = -tmp16;
    exp_value = (0x0400 | (tmp16 & 0x03FF));
    tmp16 ^= 0xFFFF;
    tmp16 >>= 10;
    tmp16 += 1;
    // Get |exp_value| = exp(-|tmp32|) in Q10.
    exp_value >>= tmp16;
  }

上面自然指數函數:
e(xm)22s2e^{ \frac{-(x - m)^2} {2 * s^2} }
可以通過換爲2的指數函數(代碼中exp2是2指數:2x2^x)化爲:
2log2e(xm)22s22^{-log_2 e*{ \frac{(x - m)^2} {2 * s^2}}}

// Q-domain: (Q12 * Q10) >> 12 = Q10.
    tmp16 = (int16_t)((kLog2Exp * tmp32) >> 12);
    tmp16 = -tmp16;

上面得到tmp16log2e(xm)22s2-log_2 e*{ \frac{(x - m)^2} {2 * s^2}}, 是一個負的Q10值。

exp_value = (0x0400 | (tmp16 & 0x03FF));
tmp160x03FF按位與操作,取低10位,即Q10的小數部分記ff,由於tmp16是個負值,機器碼爲反碼錶示,那麼得與的結果記爲finv=1ff_{inv} = 1-f, 比如tmp16爲-3.6(Q0), 那麼tmp16 & 0x03FF爲0.4(Q0)。那麼再或上0x0400,exp_value在Q0就是一個1.x的值:
exp_value=1+finvexp\_value = 1+ f_{inv}

	tmp16 ^= 0xFFFF;
    tmp16 >>= 10;
    tmp16 += 1;

接着,tmp16 異或0xFFFF, 即求反碼,由於tmp16 是個負值,機器碼求反碼就變其絕對值(這裏應該是求補碼,即反碼再加1,但這裏省略近似,因爲Q10的1 很是很小值)。接下來tmp16 >>= 10轉爲Q0的實數部分記爲rr, 那麼原來

2log2e(xm)22s22^{-log_2 e*{ \frac{(x - m)^2} {2 * s^2}}}
便可記爲:
2c,crf,c=r+f2^{-c}, c爲實數部分r 和小數f 組成的值,即c=r+f
=2(r+f)=2(r+1)21f=2(r+1)2finv= 2^{-(r+f)}= 2^{-(r+1)}2^{1-f}= 2^{-(r+1)}2^{f_{inv}}
r+1r+1便是tmp16 += 1這時候的tmp16值,即tmp16=r+1tmp16= r+1,。

這裏利用近似解:
2finv1+finv2^{f_{inv}} \approx 1+f_{inv}
通過matlab畫圖:

clc; clear all; close all;
x = (0:0.01:1);
y = 2.^x;
z = 1 + x;
plot(x,y ,x, z);

在這裏插入圖片描述

// Get |exp_value| = exp(-|tmp32|) in Q10.
   exp_value >>= tmp16;

看上面最終代碼,以及tmp16=r+1tmp16 = r+1exp_value=1+finvexp\_value = 1+ f_{inv}, 所以

2log2e(xm)22s22^{-log_2 e*{ \frac{(x - m)^2} {2 * s^2}}}=2(r+1)2finv=2^{-(r+1)}2^{f_{inv}}2(r+1)(1+finv)=2(r+1)exp_value\approx 2^{-(r+1)}(1+ f_{inv})=2^{-(r+1)}*exp\_value=exp_value&gt;&gt;tmp16 = exp\_value &gt;&gt; tmp16

  // Calculate and return (1 / s) * exp(-(x - m)^2 / (2 * s^2)), in Q20.
  // Q-domain: Q10 * Q10 = Q20.
  return inv_std * exp_value;

exp_valueinv_std相乘,最終返回當前概率值。

3 基於假設的似然檢驗(LRT with hypothesis)

3.1 計算似然概率及判決

在這裏插入圖片描述

每一個頻帶的特徵所對應是由兩個高斯分佈組合的高斯混合模型(這裏不講解高斯混合模型,比較基礎,無非就是僅一個高斯分佈不足以表示數據的真正分佈情況,所以用多個加權組合來擬合)。

判決的方法就是計算每一個頻帶特徵分別在噪聲概率分佈假設和語音概率分佈假設的似然概率Pr{X|H0} Pr{X|H1}的比值來來判斷:當其中某一子頻帶的似然比滿足閾值或者整體頻道似然比滿足閾值則判決爲語音,如下代碼:
在這裏插入圖片描述
在這裏插入圖片描述
但代碼中直接用似然概率表達並不準確,因爲XH0發生的可能性大,可能是因爲H0發生的可能性大,即先驗概率Pr{H0} Pr{H1}無法比較。
對於統計分類判決,該特徵樣本發生後屬於哪個類別是其後驗概率,即Pr{H0|X} Pr{H1|X}, 也即最大後驗概率準則的判決。通過貝葉斯定理:
p(xH)=p(Hx)p(x)p(H)p(x|H) = \frac{p(H|x)p(x)}{p(H)}
由此可見,代碼直接使用似然概率是假設了噪聲和語音的先驗概率一樣,即在一段音頻數據大概一半是噪聲一半有語音。

3.2 極大似然估計更新參數

對模型的參數更新,這裏使用極大似然估計的方法。所謂極大似然簡單地說就是當樣本出現的這個事件時,認爲該樣本具有最大的概率,那麼希望概率分佈的參數使得該樣概率最大:
L(θ;x1x2xn)=p(xk;θ)L(\theta; x_1、x_2……x_n) = \prod{p(x_k;\theta )}
上面假設了樣本獨立發生,假設有θ^\hat{\theta}使得L(θ,x1x2xn)L(\theta, x_1、x_2……x_n)最大,那麼θ^\hat{\theta}是參數θ\theta的極大似然估計值。

這裏每次發生的樣本的只有一個,即通過3.1節的特徵樣本經過上一次的模型概率分佈判決事件,由於該模型爲高斯混合模型,其極大似然函數可設計爲:
L(θ)=p(G0)logG0(x;θ0)+p(G1)logG1(x;θ1)L(\theta) = p(G_0)*logG_0(x;\theta_0)+p(G_1)*logG_1(x;\theta_1)
上式中,θ0,θ1\theta_0,\theta_1分別代表各自高斯函數參數,如μ0σ0,μ1σ1\mu_0、\sigma_0,\mu_1、\sigma_1θ\theta爲其中某一參數,對其更新便是在樣本xx出現,並被判決爲某個結果(噪聲或語音)時,求得θ^\hat{\theta} 使得L(θ)L(\theta)極大。p(Gk)p(G_k)爲對應高斯分佈的權重。其取對數是計算似然的常用技術,對數函數是單調性,保持原來函數的極值位置和函數值的大小關係,可以化簡指數函數,可以將乘除變爲加減,大大簡化計算,這裏loglog表示取對數,顯然高斯分佈是自然指數,所以爲ee爲底的lnln

由於每次更新只有一個樣本特徵,得到當前的似然極大估計並不準確(過擬合),希望在每次更新步進朝極大值更新,這裏使用梯度下降法來迭代實現最優化:
θ^=θc(L(θ))\hat\theta = \theta - c*\nabla(L(\theta))
由於梯度下降法是對具有極小值的代價函數(誤差函數)的優化,我們這是有極大值的分佈函數,故這裏是梯度提升:
θ^=θ+c(L(θ))\hat\theta = \theta+c*\nabla(L(\theta))
上式(L(θ))\nabla(L(\theta))LLθ\theta梯度,cc爲步進因子,該值較小時更新到最優的速度較慢,該值較大時可能不能得到極值,而是在極值附近振盪。

3.2.1 高斯分佈均值更新

現在我們來求第一個高斯模型均值參數 u0u_0的更新:
L(u0)=p(G0)logG0(x;u0)+p(G1)logG1(x;θ1)L(u_0) = p(G_0)*logG_0(x;u_0)+p(G_1)*logG_1(x;\theta_1)
上式對u0u_0 求導,p(G1)logG1(x;θ1)p(G_1)*logG_1(x;\theta_1) 該項與u0u_0無關,導數爲0,故忽略。則
p(G0)logG0(x;u0)p(G_0)*logG_0(x;u_0)=p(G0)log12πσ0e(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}\sigma_0} * e^{ \frac{-(x - u_0)^2} {2 * \sigma_0^2} }=p(G0)log12πσ0+p(G0)loge(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}\sigma_0} + p(G_0)*loge^{ \frac{-(x - u_0)^2} {2 * \sigma_0^2} }=p(G0)log12πσ0+p(G0)(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}\sigma_0} + p(G_0)*\frac{-(x - u_0)^2} {2 * \sigma_0^2}
上式繼續忽略與u0u_0無關項,則
(L(u0))=(p(G0)(xu0)22σ02)\nabla(L(u_0)) = \nabla(p(G_0)*\frac{-(x - u_0)^2} {2 * \sigma_0^2} )=p(G0)xu0σ02=p(G_0)*\frac{x-u_0}{\sigma_0^2}
u0u_0的更新:
u^0=u0+p(G0)xu0σ02c\hat u_0 = u_0 + p(G_0)*\frac{x-u_0}{\sigma_0^2}*c

同理,對第二高斯分佈以及語音的兩個高斯分佈的均值更新的一般函數爲:
u^=u+p(G)xuσ2c\hat u = u + p(G)*\frac{x-u}{\sigma^2}*c

如下面代碼,對噪聲的均值參數更新:
在這裏插入圖片描述

3.2.2 高斯分佈方差更新

接下來我們來求第一個高斯模型方差參數 σ0\sigma_0的更新:
L(σ0)=p(G0)logG0(x;σ0)+p(G1)logG1(x;θ1)L(\sigma_0) = p(G_0)*logG_0(x;\sigma_0)+p(G_1)*logG_1(x;\theta_1)
上式對σ0\sigma_0 求導,p(G1)logG1(x;θ1)p(G_1)*logG_1(x;\theta_1) 該項與σ0\sigma_0無關,導數爲0,故忽略。則
p(G0)logG0(x;σ0)p(G_0)*logG_0(x;\sigma_0)=p(G0)log12πσ0e(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}\sigma_0} * e^{ \frac{-(x - u_0)^2} {2 * \sigma_0^2} }=p(G0)log12πσ0+p(G0)loge(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}\sigma_0} + p(G_0)*loge^{ \frac{-(x - u_0)^2} {2 * \sigma_0^2} }=p(G0)log12π+p(G0)log1σ0+p(G0)(xu0)22σ02=p(G_0)*log\frac{1}{ \sqrt{2\pi}} + p(G_0)*log\frac{1}{\sigma_0}+ p(G_0)*\frac{-(x - u_0)^2} {2 * \sigma_0^2}
上式繼續忽略與σ0\sigma_0無關項,則
(L(u0))=p(G0)(log1σ0+(xu0)22σ02)\nabla(L(u_0)) = p(G_0)*\nabla(log\frac{1}{\sigma_0} + \frac{-(x - u_0)^2} {2 * \sigma_0^2} )=p(G0)(1σ0+(xu0)2σ03) =p(G_0)* (-\frac{1}{\sigma_0} + \frac{(x - u_0)^2} { \sigma_0^3} )=p(G0)1σ0((xu0)2σ021)=p(G_0)* \frac{1}{\sigma_0}( \frac{(x - u_0)^2} { \sigma_0^2} - 1 )
σ0\sigma_0的更新:
σ^0=σ0+p(G0)1σ0((xu0)2σ021)c\hat \sigma_0 = \sigma_0 + p(G_0)* \frac{1}{\sigma_0}( \frac{(x - u_0)^2} { \sigma_0^2} - 1 )*c
同理,對第二高斯分佈以及語音的兩個高斯分佈的均值更新的一般函數爲:
σ^=σ+p(G)1σ((xu)2σ21)c\hat \sigma = \sigma + p(G)* \frac{1}{\sigma}( \frac{(x - u)^2} { \sigma^2} - 1 )*c
如下面代碼,對噪聲的方差參數更新:
在這裏插入圖片描述

3.2.3 假設檢驗(hypothesis-test)

在這裏插入圖片描述
在此小結之前,主要講解如何通過頻帶劃分提取特徵、高斯分佈概率的數值求解、模型的自適應更新等技術,如上述代碼註釋的第一句:Calculates the probabilities for both speech and background noise using Gaussian Mixture Models (GMM)。這只是該算法的技術層面,接下來將講解算法的核心思想:A hypothesis-test is performed to decide which type of signal is most probable.

假設檢驗可以用來判斷樣本與樣本或樣本與總體的差異是由抽樣誤差引起的還是由於本質上差別引起的,其基本原理是對總體的特徵做出某種假設,然後通過抽樣研究的統計推理,進而做出對此假設是接受還是拒絕的推斷。

提出假設的形式:

   原假設 H0H0 : 樣本與總體或樣本與樣本間的差異是由抽樣誤差引起的;
   備擇假設 H1H1:樣本與總體或樣本與樣本間存在本質差異。
那麼算法中原假設H0H0 :噪聲,備擇假設H1H1:語音 ;而原假設的總體特徵,即噪聲的特徵是頻帶能量相對小的,其實現就是WebRtcVad_FindMinimum函數:
在這裏插入圖片描述
該函數邏輯比較簡單,就是跟蹤16個最小值,每個最小值最長更新期限爲100幀,並返回5個最小值的中值的平滑值。(爲什麼不跟蹤5個就行了?首先最小值需要在最近一定時期內更新,噪聲可能只在短時間平穩;如果只有5個,那麼更新很容易將目前一個較大的值記錄5個最小值之一,因此需要跟蹤更多,允許使一些期限到的最小更新當前值,同時還有候選的真正小最值重新形成5個最小值。)

通過上述函數獲得最小值做爲調整噪聲模型均值:
在這裏插入圖片描述
上述約代碼束調整噪聲的整體分佈的均值向feature_minimun比例調整,從而保持原假設H0H0,維持誤差。

在這裏插入圖片描述
更新噪聲分佈的整體均值後,上面的代碼又比例調整噪聲分佈和語音分佈的均值的相對距離,即保證備擇假設H1H1,使有本質差異。

也就是通過WebRtcVad_FindMinimum的最小值,進行噪聲假設,從而構建了噪聲和語音兩個分佈模型。

對於語音信號增強,如降噪,通常都是跟蹤信號中能量較小值來假設爲噪聲,只是具體實現方法不同。這部分是基礎,影響兩個分佈的準確度,可在這裏進行算法優化。

假設檢驗標準:

統計推斷假設檢驗判決標準,一般就是最大後驗概率準則:
p(H0x)p(H1x)&gt;1,\frac{p(H0|x)}{p(H1|x)}&gt;1, 則接受爲原假設
算法中在3.1節所說,轉換爲似然概率比進行判斷。

4 魯棒性輸出

在這裏插入圖片描述
上面代碼爲整個算法最後的判決輸出,爲了保證判決爲語音魯棒性及連貫性,採用非語音滯後判決,使短時間隔的噪聲也判決輸出爲語音標誌。

本文自此結束,本文爲博主原創文章,未經允許不得轉載,但歡迎救濟
                     在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章