HTK中!NULL节点和SENT_END以及SENT_START的区别与联系

SENT_START和SENT_END是词典dict中的存在的,有发音的"sil",且是个单音子词,在词典voc->wtab中的取"SENT_START"的hash对象。而!NULL是词格Lattice网络中的虚拟节点,在voc中也有对应该虚拟节点的对象,但是它不发音。在字典的初始化时会在voc->wtab中加入“!NULL"的LabId对应的Word,但是Word指向的DictEntry有发音信息和发音个数,这时它们分别为null和0,表明不发音。

typedef struct _DictEntry{
   LabId wordName;  /* word identifier */
   Pron pron;       /* first pronunciation */
   int nprons;      /* number of prons for this word */
   Word next;       /* next word in hash table chain */
   void *aux;       /* hook used by HTK library modules for temp info */
} DictEntry;

一个字典是由一系列上述结构体组成,每个结构体包含wordName、Pron和nprons(int)。

当构建Lattice网络时,每个网络描述里的节点,就是一个LNode,它的构成基础就是词典中的DictEntry。它包含Word,它是指向DictEntry的指针,还有sublat,它是根据word、pron、voc等信息扩展的具体的phone子网络,它是构成识别网络的基础。

这sublat指向的实体就是下面的结构:

typedef struct pronholder
{
   LNode *ln;       /* Node that created this instance */
   Pron pron;       /* Actual pronunciation */
   short nphones;   /* Number of phones for this instance */
   LabId *phones;   /* Phone sequence for the instance */
   
   int nstart;      /* Number of models in starts chain */
   int nend;        /* Number of models in ends chain */
   NetNode *starts; /* Chain of initial models */
   NetNode *ends;   /* Chain of final models */
   NetNode *chain;  /* Chain of other nodes in word */
   struct pronholder *next;
}
PronHolder;

它就是NetWork的基石。PronHolder包含了starts、ends以及chain指针。它分为几个类型,比如是词节点还是hmm节点等等。

static int InitPronHolders(Network *net,Lattice *lat,HMMSetCxtInfo *hci,
                           Vocab *voc,MemHeap *heap,char *frcSil)
{
   PronHolder *pInst;
   NetNode *wordNode;
   Pron thisPron;
   Word thisWord;
   LNode *thisLNode;
   PInstInfo *pii;
   LabId silPhones[MAXPHONES],addPhones[MAXPHONES],labid;
   int i,j,k,l,n,t,lc,type,nNull,npii,nSil,nAdd;
   char *ptr,*p,*nxt,name[MAXSTRLEN],st;

   /* Reset hash table prior to processing lattice */
   for (i=0; i<WNHASHSIZE; i++)
      wnHashTab[i]=NULL;

   /* Determine if we have a real !NULL word */
   net->nullWord = GetWord(voc,GetLabId("!NULL", TRUE),TRUE);
   for (thisPron=net->nullWord->pron;thisPron!=NULL;thisPron=thisPron->next)
      if (thisPron->nphones!=0) {
         net->nullWord=NULL;
         break;
      }
   if (net->nullWord!=NULL) {
      if (net->nullWord->pron==NULL)
         NewPron(voc,net->nullWord,0,NULL,net->nullWord->wordName,1.0);
   }

      nSil=nAdd=0;

   /* Create instance for each pronunciation in lattice */
   for (i=0,nNull=0,t=0; i < lat->nn; i++) {
      thisLNode = lat->lnodes+i;
      thisWord = thisLNode->word;
      if (thisWord==NULL) thisWord=voc->nullWord;
      if (thisWord==voc->subLatWord)
         HError(8220,"InitPronHolders: Expand lattice before making network");
      thisLNode->sublat=NULL;
      if (thisWord->nprons<=0)
         HError(8220,"InitPronHolders: Word %s not defined in dictionary",
                thisWord->wordName->name);

      pii=(PInstInfo *) New(&gstack,(thisWord->nprons+1)*(nAdd+1)*sizeof(PInstInfo));
      pii--;
      /* Scan current pronunciations and make modified ones */
      for (j=1,thisPron=thisWord->pron,npii=0; thisPron!=NULL;
           j++,thisPron=thisPron->next) {
         if (thisPron->nphones==0) n=0;

         if (thisPron->nphones==0 || nAdd==0 || n==0) {
            /* Just need one pronunciation */
            if (thisPron->nphones==0) {
               if (thisWord!=net->nullWord && (trace&T_CXT)) 
                  printf("InitPronHolders: Word %s has !NULL pronunciation\n",
                         thisWord->wordName->name);
               nNull++;
            }
            if (n==0) n=thisPron->nphones;
            pii[++npii].pron=thisPron; pii[npii].silId=-1;
            pii[npii].n=n;pii[npii].t=n;
            pii[npii].phones=thisPron->phones;
         }

      }

      /* Now make the PronHolders */
      for (j=1; j<=npii; j++) {
         /* Don't add duplicates */
         if (pii[j].pron==NULL) continue;
         /* Build inst for each pron */
         pInst=NewPronHolder(heap,hci,pii[j].pron,pii[j].t,pii[j].phones);
         pInst->ln = thisLNode;
         pInst->next = (PronHolder*)thisLNode->sublat;
         thisLNode->sublat = (SubLatDef*) pInst;
         if (pInst->nphones<=0) pInst->fct = 0.0;
         else pInst->fct = thisLNode->score/pInst->nphones;

         /* Fake connections from SENT_[START/END] */

         if (thisLNode->foll==NULL) {
            wordNode = FindWordNode(net->heap,pInst->pron,pInst,n_word);
            wordNode->tag=SafeCopyString(net->heap,thisLNode->tag);
            wordNode->nlinks = 0;
         }
      }
      Dispose(&gstack,++pii);
   }
   
   return(nNull);
}

我删除了很多对理解原理无关的代码,剩下精简的,试着来详细分析,这个函数是如何初始化PronHolder对象的。

刚开始,将net-nullword指向voc->nullword,它是"!NULL"对应的word,我们之前分析知道,它在初始化时,pron为NULL;所这里会走到NewPron这句,使得pron是有效,只是nphones为0,phones=null。这个信息在下面会使用到。

接着就是nSil和nAdd为0,什么作用,我目前不是很清楚。

再然后,就是循环对Lattice的LNode创建发音实例——PronHolder。

这一步,它又分两步走,首先是把发音信息保存在PInstInfo列表中,因为一个节点(对应一个Word)可能有多个发音,所以循环处理。

第一步:[如果发音对象的nphones为0,则对nNull增一,计数一共包含多少个!NULL节点。]把发音信息例如:音子数,音子序列pron对象都保存起来,在下一步中使用;

第二步:就是调用NewPronHolder,传递进去的参数包括发音对象、音子个数、音子序列。然后把pInst与产生该对象的Lattice的节点关联起来,pInst放入LNode的sublat中,而pInst的ln指向该node。

到这一步,Lattice中出现的LNode都以及初步扩展过了,比较特殊的节点,一是SENT_START/END和!NULL也做了说明。

但是呢,我们只有单词内部(每个单词有0、1或多个发音音子)的发音信息,和组网框架,但是对词之间,以及词内部的starts、chain和ends之间的指向关系还没构建呢。

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值