希望这篇是这一轮最后一篇关于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才是真正保存高斯混合模型参数的地方。上面的框图写的比较清楚了。