HCompV源码再解析续三

希望这篇是这一轮最后一篇关于HCompV的博客。

上一篇续二分析到通过LoadFile函数加载所有特征向量(MFCC_0_D_A)文件,然后把它们按维度累计分别列举measSum和squareSum。

接下来就是如何计算单独的mean和variance了,其实就是一个简单的除法,再有一些加减。已经统计过了totalCount是计数一共多少帧(特征向量),看SetCovs函数。

/* SetCovs: set covariance values in hmm */
void SetCovs(void)
{
   int i,s,m;
   StateElem *se;
   StreamElem *ste;
   MixtureElem *me;
   MixPDF *mp;

   CalcCovs();

   for (i=2,se=hmmLink->svec+2; i < hmmLink->numStates; i++,se++)
      for (s=1,ste=se->info->pdf+1; s <= hset.swidth[0]; s++,ste++)
         for (m=1,me = ste->spdf.cpdf+1; m<=ste->nMix; m++, me++) {
            mp = me->mpdf;
            if (meanUpdate && !IsSeenV(mp->mean)){      /* meanSum now holds mean */
               CopyVector(accs[s].meanSum,mp->mean); 
               TouchV(mp->mean);
            }
            if (!IsSeenV(mp->cov.var)){
               if (mp->ckind==FULLC)
                  CopyMatrix(accs[s].fixed.inv,mp->cov.inv);
               else if (fullcNeeded[s])  /* dont need full cov, but its all we have */                
                  TriDiag2Vector(accs[s].fixed.inv,mp->cov.var);
               else
                  CopyVector(accs[s].fixed.var,mp->cov.var);
               TouchV(mp->cov.var);
            }
         }
   ClearSeenFlags(&hset,CLR_ALL);
}

CalcCovs()函数就是计算累加器中的均值和方差。

/* CalcCovs: calculate covariance of speech data */
void CalcCovs(void)
{
   int x,y,s,V;
   float meanx,meany,varxy,n;
   Matrix fullMat;   

   n = (float)totalCount;     /* to prevent rounding to integer below */
   for (s=1; s<=hset.swidth[0]; s++){  /* For each stream   */
      V = hset.swidth[s];
      for (x=1; x<=V; x++)            /* For each coefficient ... */
         accs[s].meanSum[x] /= n;         /* ... calculate mean */
      for (x=1;x<=V;x++) {
         meanx = accs[s].meanSum[x];      /* ... and [co]variance */

         varxy = accs[s].squareSum.var[x]/n - meanx*meanx;
         accs[s].fixed.var[x] = (varxy > minVar) ? varxy :minVar;

      }
   }
}

非常简单,就是计算acc中均值就OK了。所有数据的均值方差推导公式如下图所示,再看代码就比较清楚了。meanx就是各维度的μ。

接着看下面的代码,有多层for循环,看似非常复杂,其实逻辑非常非常简单,就是执行几次CopyVector。

抽丝剥茧,把它简化后,就是看下面这个图。

一个HMM包含五个状态StateElem,每个状态可能建模几个stream,我们这个例子就一个StreamElem。所以多了一层废话,让代码结构无谓的复杂了一点。 

每个StreamElem包含一个高斯混合,混合数由nMix指定,而对应的nMix个概率密度函数由MixtureVector向量spdf指定。每个MixtureElem包含weight和MixPDF结构,它描述了当前高斯的均值和方差、方差类型等等。理顺这些Elem之间的关系,并在头脑里勾勒出它们之间的指向关系,理解代码就非常简单了。一个HMM模型包含5个状态,但是只有状态2、3和4是emission state,就是最外层的循环。通过指针svec访问。而每个StateElem节点只包含一个指针,就是StateInfo。它是描述HMM状态信息的一个结构,比如该状态包含多少流,每个流的权重,以及最重要的每个流的元素信息——StreamElem。

StreamElem本质就是高斯混合模型,它需要制定高斯个数nMix,以及每个高斯概率密度函数的均值、协方差矩阵等,在MixtureElem中描述。再往下,MixtureElem包含该混合高斯的权重和MixPDF。终于到了最底层,就是MixPDF才是真正保存高斯混合模型参数的地方。上面的框图写的比较清楚了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值