HERest源码解析

前面解析了HCompV源码,它主要功能是初始化hmm模型,定义一些宏。还没接触到模型训练。HMM模型训练算法是语音识别的算法中的难点之一。

它的输入包括所有hmm模型的名称、以及初始的模型参数(在hmmdefs设置好了),特征参数文件(train.scp)和特征文件对应的MLF转写文件(phone0.mlf)。输出就是包含更新后的hmmdefs。

先写最重要,也是比较难的,然后再分析一些程序流程、配置等。这样避免喧宾夺主。

/* Load data and call FBFile: apply forward-backward to given utterance */
void DoForwardBackward(FBInfo *fbInfo, UttInfo *utt, char * datafn, char * datafn2)
{
   char datafn_lab[MAXFNAMELEN];

   utt->twoDataFiles = twoDataFiles ;
   utt->S = fbInfo->al_hset->swidth[0];

   /* Load the labels - support for label masks */
   if (labFileMask) {
      if (!MaskMatch (labFileMask, datafn_lab, datafn))
         HError(2319,"HERest: LABFILEMASK %s has no match with segemnt %s", labFileMask, datafn);
   }
   else
      strcpy (datafn_lab, datafn);
   LoadLabs(utt, lff, datafn_lab, labDir, labExt);
   /* Load the data */
   LoadData(fbInfo->al_hset, utt, dff, datafn, datafn2);

   if (firstTime) {
      InitUttObservations(utt, fbInfo->al_hset, datafn, fbInfo->maxMixInS);
      firstTime = FALSE;
   }
  
   /* fill the alpha beta and otprobs (held in fbInfo) */
   if (FBFile(fbInfo, utt, datafn)) {
      /* update totals */
      totalT += utt->T ;
      totalPr += utt->pr ;
      /* Handle the input xform Jacobian if necssary */
      if (fbInfo->al_hset->xf != NULL) {
         totalPr += utt->T*0.5*fbInfo->al_hset->xf->xform->det;
      }

   }
}

上面函数顾名思义,就是实现了前向后向算法。LoadLabs函数加载音素级标注文件,构建transaction对象,它包含了这些音素串的所有信息。接着是LoadData函数,将特征向量的数据加载到内存中,并保存相关的信道信息、数据流宽度信息,时间片长度等等。

/* FBFile: apply forward-backward to given utterance */
Boolean FBFile(FBInfo *fbInfo, UttInfo *utt, char * datafn)

已经有了data和Label以及hmm设置作为输入,接下来就是计算前向后向算法。由上面声明的函数FBFile完成。

/* FBFile: apply forward-backward to given utterance */
Boolean FBFile(FBInfo *fbInfo, UttInfo *utt, char * datafn)
{
   Boolean success;

   if ((success = StepBack(fbInfo,utt,datafn)))
      StepForward(fbInfo,utt);
#ifdef PDE_STATS
   PrintPDEstats();
#endif

   ResetStacks(fbInfo->ab);

   return success;
}

看看代码结构,一目了然。

就是StepBacke和StepForward两个函数完成后向和前向算法。下面首先看看StepBack函数的过程。

/* StepBack: Step utterance from T to 1 calculating Beta matrix*/
static Boolean StepBack(FBInfo *fbInfo, UttInfo *utt, char * datafn)
{
   LogDouble lbeta;
   LogDouble pruneThresh;
   AlphaBeta *ab;
   PruneInfo *p;
   int qt;
   
   ResetObsCache();  
   ab = fbInfo->ab;
   pruneThresh=pruneSetting.pruneInit;
   do
      {
         ResetStacks(ab);
         InitPruneStats(ab);  
         p = fbInfo->ab->pInfo;
         p->pruneThresh = pruneThresh;
         qt=CreateInsts(fbInfo,ab,utt->Q,utt->tr);

         CreateBeta(ab,utt->T);
         SetBeamTaper(p,ab->qDms,utt->Q,utt->T);
         CreateOtprob(ab,utt->T);
         lbeta=SetBeta(ab,fbInfo,utt);
         if (lbeta>LSMALL) break;
         pruneThresh+=pruneSetting.pruneInc;

      }
   while(pruneThresh<=pruneSetting.pruneLim);

   return TRUE;
}

这个函数的参数,有FBInfo和UttInfo两个指针。它们指向的对象,包含了计算前向、后向算法非常重要的数据结构——AlphaBeta、ParmBuf和Observation。

/* structure for the forward-backward alpha-beta structures */
typedef struct {
  
  MemHeap abMem;      /* alpha beta memory heap */
  PruneInfo *pInfo;   /* pruning information */
  HLink *up_qList;    /* array[1..Q] of active HMM defs */
  HLink *al_qList;    /* array[1..Q] of active align HMM defs */
  LabId  *qIds;       /* array[1..Q] of logical HMM names (in qList) */
  short *qDms;        /* array[1..Q] of minimum model duration */
  DVector *alphat;    /* array[1..Q][1..Nq] of prob */
  DVector *alphat1;   /* alpha[t-1] */
  DVector **beta;     /* array[1..T][1..Q][1..Nq] of prob */
  float *****otprob;  /* array[1..T][1..Q][2..Nq-1][0..S][0..M] of prob */
  LogDouble pr;       /* log prob of current utterance */
  Vector occt;        /* occ probs for current time t */
  Vector *occa;       /* array[1..Q][1..Nq] of occ probs (trace only) */

} AlphaBeta;

看一下AlphaBeta的结构体什么样子,分析下它包含哪些部分,分别叫什么名字,以及它在内存是什么样子。因为后面的代码跟它关系密切。

主要有HLink* up_qList它指向HMM指针,也就是包含一系列HMM指针,它是即将训练的句子文本对应的hmm模型序列。DVector*alphat矩阵,包含了alpha的概率值,以及DVector**beta。

这是内存中alphat的样子,beta包含一系列指向类似alphat的指针,它是alphat的数组,这样一步一步嵌套的。Q代表了这句话包含多少个hmm,Nq表示每个hmm包含多少个状态。它是由多个hmm串起来构成一个大的hmm。下图是《Speech and Language Processing》第九章的一个图,示意了嵌入式训练中hmm结构。

再看一下StepForward函数,运行StepBack然后再运行一次StepForward就完成了一句话的嵌入式训练(Embeded training)。

/* StepForward: Step from 1 to T calc'ing Alpha columns and 
   accumulating statistic */

static void StepForward(FBInfo *fbInfo, UttInfo *utt)
{
   int q,t,start,end,negs;
   DVector aqt,aqt1,bqt,bqt1,bq1t;
   HLink al_hmm, up_hmm;
   AlphaBeta *ab;

   /* reset the memory heap for alpha for a new utterance */
   /* ResetHeap(&(fbMemInfo.alphaStack)); */
  
   ab = fbInfo->ab;
   CreateAlpha(ab,fbInfo->al_hset,utt->Q); /* al_hset may be idential to up_hset */
   InitAlpha(ab,&start,&end,utt->Q,fbInfo->skipstart,fbInfo->skipend);
   ab->occa = NULL;
   if (trace&T_OCC) 
      CreateTraceOcc(ab,utt);
   for (q=1;q<=utt->Q;q++){             /* inc access counters */
      up_hmm = ab->up_qList[q];
      negs = (int)up_hmm->hook+1;
      up_hmm->hook = (void *)negs;
   }

   ResetObsCache();

   for (t=1;t<=utt->T;t++) {

      GetInputObs(utt, t, fbInfo->hsKind);

      if (fbInfo->hsKind == TIEDHS)
         PrecomputeTMix(fbInfo->al_hset,&(utt->ot),pruneSetting.minFrwdP,0);

      if (t>1)
         StepAlpha(ab,t,&start,&end,utt->Q,utt->T,utt->pr,
                   fbInfo->skipstart,fbInfo->skipend);
    
      if (trace&T_ALF && NonSkipRegion(fbInfo->skipstart,fbInfo->skipend,t)) 
         TraceAlphaBeta(ab,t,start,end,utt->pr);
    
      for (q=start;q<=end;q++) { 
         /* increment accs for each active model */
         al_hmm = ab->al_qList[q];
         up_hmm = ab->up_qList[q];
         aqt = ab->alphat[q];
         bqt = ab->beta[t][q];
         bqt1 = (t==utt->T) ? NULL:ab->beta[t+1][q];
         aqt1 = (t==1)      ? NULL:ab->alphat1[q];
         bq1t = (q==utt->Q) ? NULL:ab->beta[t][q+1];
         SetOcct(al_hmm,q,ab->occt,ab->occa,aqt,bqt,bq1t,utt->pr);
         /* accumulate the statistics */
         if (fbInfo->uFlags&(UPMEANS|UPVARS|UPMIXES|UPXFORM))
            UpMixParms(fbInfo,q,up_hmm,al_hmm,utt->ot,utt->ot2,t,aqt,aqt1,bqt,
                       utt->S, utt->twoDataFiles, utt->pr);
         if (fbInfo->uFlags&UPTRANS)
            UpTranParms(fbInfo,up_hmm,t,q,aqt,bqt,bqt1,bq1t,utt->pr);
      }
   }
}

然后就是再次读入下一个文件、不断循环直至文件结尾。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值