这几天对webrtc中的VAD算法做了一些研究,有了一些心得
VAD算法中核心的是使用了GMM进行分类,noise作为一类,speech作为一类,两类求后验概率,并且实时的更新GMM的参数
但是这个GMM的具体情况是怎么样的,它的均值,方差,都是多少,使用特征的维度是多少?参数又是怎么更新的
一、GMM的 权重,均值,方差分析
频率域上分成了6个子带(channel),对每个子带求能量,初始值有一个GMM参数,包括 权重,均值,方差。
对噪声、语音,每个子带上的GMM的混合系数2,即2个高斯的mixture,一共6(6个channel,或者子带)*2(noise和speech)*2(每个GMM和混合系数是2)
你比如噪声的权重,kNoiseDataWeights,前六个和后六个分别是两个GMM的两组权重,Q7量化为2^7 =128
显然, 34/128 + 94/128 = 1; 62/128+66/128 = 1;...依次类推
使用的特征是6维的,但是这6维不是直接就使用一个GMM模型,而是裂解成6个子特征(6个channel),每个一维度的特征作为一个混合高斯模型的输入,每个高斯混合模型的混合系数是2
问题比较简单了,既然不是多维度的,那么协方差矩阵的 正定问题 就归在一个方差值上面,也就这一个方差数大于0即可。
二、高斯混合模型的后验概率
函数 WebRtcVad_GaussianProbability 就是专门求这个后验概率,它先计算
1 / s 然后计算 1 / s^2 再 |tmp32| = (x - m)^2 / (2 * s^2), 再计算 exp(-(x - m)^2 / (2 * s^2)),最后算出来 (1 / s) * exp(-(x - m)^2 / (2 * s^2))
暂时还没有看出来 1/sqrt(2*pi) 在哪里,估计在后面,或者忽略了。
注意输入的input是Q4的。实际上求后验概率,就这么简单,但是程序中非出来一个判断函数,这个主要是为了如果要求 x-> exp(-x)
当 |tmp32| = (x - m)^2 / (2 * s^2) (x是均值)这个值非常大的时候,超过一个范围 kCompVar = 22005的时候,认为 exp(-|tmp32|) 接近是 0直接给成0
这下面 令 x = |tmp32| (下图中的红色的X), 主要目的是使用了 ”换底公式” ,以前是求exp的幂,现在是求2的幂【2的幂在定点代码中比e的幂好算】
我们主要方向,x --- > exp(-x) 考虑到Q值,Q10的量 另外 log2(exp(1)) = 1.4427
x是Q10的,最后算出来的exp表达式也得是Q10,所以真正的 x到exp(-x),应该是
exp(-x/1024)*1024 = 1024*( 2 ^ [log2(e))*(-x/1024)]) = 1024* 2^ (-1.4427* x/1024)
这里面用了一些技巧,主要是为了方便求2个幂,我们给这个表达式上下都乘以一个数,2^[fix(x*1.4427/1024)+1]
2^10 * 2^ (-1.4427* x/1024) = 2^ (10 + -1.4427*x/1024) = 2^(10 + -1.4427*x/1024 +1 + fix(x*1.4427 /1024) ) /2^[fix(x*1.4427/1024)+1]
乘以这个数的主要目的是为了使得 -1.4427*x/1024 + 1 + fix(x*1.4427 /1024) 能是一个不大于1的一个纯小数【小学概念】
上面那个除法表达式分子记为 Y(就是代码中的exp_value),所以有 Y = 2^(10 + -1.4427*x/1024 + 1 + fix(x*1.4427 /1024) ),另外 分母记为2^[fix(x*1.4427/1024)+1],最后分子准备右移(除法)就得结果了
对Y除2^10, Y/1024 = 2^(-1.4427*x/1024 + 1 + fix(x*1.4427 /1024) )
上面也说了,这个括号里面的数,应该为 1 + α , α是一个接近0比较小的数,我们利用二项式展开公式,分解2的幂
Y/1024 ≈ 2 + -1.4427*x/1024 + fix(x*1.4427 /1024) 那么Y有
Y = 1024 *(2 + fix(x*1.4427 /1024)-1.4427*x/1024)
= 1024* ( 1-x* 1.4427/1024+ fix( x * 1.4427 / 1024)+1)
= 1024- x * 1.4427+1024* fix( x * 1.4427 / 1024)+1024
= 2^16 - x * 1.4427 - 1024 * (63) + 1024* fix( x * 1.4427 / 1024) + 1024
= 2^16 - x * 1.4427 - 1024 * (63 - fix( x * 1.4427 / 1024) ) + 1024
而 fix(( 2^16 - x * 1.4427) / 1024)= 63 - fix( x * 1.4427 / 1024); 【的确不是64减】
故 Y = 2^16 - x * 1.4427 - 1024*fix(( 2^16 - x * 1.4427) / 1024) + 1024 = 2^16 - x * 1.4427- 1024*fix(( 2^16 - x * 1.4427) / 1024) + 1024
2^16 - x * 1.4427 是求相反数,代码中的刚从 WEB...MUL...RSFT算出来的 tmp16 = 1.4427* x ;
1024*fix(( 2^16 - x * 1.4427) / 1024) 为了抛掉整数,只留小数部分(我知道这个蓝色表达式求得是整数部分),所以前面要加个“-”
1024 加上1024
再来看代码
分母就不用多说了: 2^[fix(x*1.4427/1024)+1],除法(Y/tmp16)相当于右移 (exp_value>>tmp16)
22005 是这样算出来的 fix(22005*1.4427/1024)+1 = 32(最大右移数) ,那么 31 * 1024 /1.4427 = 2.2003e+004(倒算出来的,先有32-1)
Tips: 这个整体思路是要计算 x 到 exp(-x/1024)*1024,其中tmp16的值为 tmp16(最终值) = 2^(fix(x*1.4427/1024)+1)
我已经计算出来了 exp(-x/1024)*1024 = Y/tmp16 (令Y为分子,tmp16为分母)
我先计算Y/1024, 为的是这个数比较能用2的幂级数展开近似求,求出来之后, 得到Y,一个除法(Y/tmp16)就是最终结果
代码中为什么是 exp_value >> = tmp16? 它相当于 是在求一个除法,Y >>fix(x*1.4427/1024)+1
也就是 Y / 2^[fix(x*1.4427/1024)+1]
为什么exp的算法最后是个除法?原因是用了“换底公式”都换成2的幂去计算了
三、高斯混合模型的参数更新
参数更新问题,很多博客已经给出了很详细的说明,比如http://blog.csdn.net/shichaog/article/details/52399354 【这里感谢博主】
噪声均值的更新 参考代码 vad_sp.c中的 WebRtcVad_FindMinimum 函数
对缩小,放的比较开,这是为了保证噪声跟进能比较准确
对于下面的参数更新,我这里做一些啰嗦的说明,以便大家理解的更好一些,【先放成果照片,拜大神,后面我再详细说明】
偏导公式太复杂了,我也记不住,用matlab来算吧
代码最核心的更新部分:GmmProbability函数中