下面看看函数Network *ExpandWordNet(MemHeap *heap,Lattice *lat,Vocab *voc,HMMSet *hset)的处理流程。

Network *ExpandWordNet(MemHeap *heap,Lattice *lat,Vocab *voc,HMMSet *hset)
{HMMSetCxtInfo *hci;Network *net;NetNode *node,*wordNode,*chainNode;NetLink netlink;Word thisWord;PronHolder *pInst;LNode *thisLNode;net=(Network*) New(heap,sizeof(Network));net->heap=heap;net->vocab=voc;net->numNode=net->numLink=0;net->chain=NULL;hci=GetHMMSetCxtInfo(hset,TRUE); // 创建/获取HMMSetCXTInfo对象/* First create context arrays and pronunciation instances */nNull=InitPronHolders(net,lat,hci,voc,&holderHeap,frcSil);/* Need to find out the phonetic contexts for all NULL words */if (hci->xc>0 && nNull>0)SetNullContexts(lat,hci->xc);/* Count xwrd links and create word ends */ProcessCrossWordLinks(net->heap,lat,hci->xc);/* Build models on basis of contexts seen */net->teeWords=FALSE;for (i=0; i < lat->nn; i++) {thisLNode = lat->lnodes+i;thisWord = thisLNode->word;if (thisWord==NULL) thisWord=voc->nullWord;if (trace&T_CST) {printf("Building word %s\n",thisWord->wordName->name);}for(pInst=(PronHolder*)thisLNode->sublat;pInst!=NULL;pInst=pInst->next) {/* !NULL consists only of word ends */if (pInst->nphones==0) {/* Flawed */if (hci->xc==0) {/* But we need a pointer for xc==0 cases */wordNode = FindWordNode(NULL,pInst->pron,pInst,n_word);pInst->starts = wordNode;pInst->nstart = 0; /* Stops us adding node into chain twice */}continue;}/* Determine which bits of word are l and r cd */if (hci->xc>0) {for (p=0;p<pInst->nphones;p++)if (GetHCIContext(hci,pInst->phones[p])>=0) break;for (q=pInst->nphones-1;q>=0;q--)if (GetHCIContext(hci,pInst->phones[q])>=0) break;}else {p=0;q=pInst->nphones-1;}pInst->tee=TRUE;/* Make wrd-int cd phones (possibly none!) */CreateWIModels(pInst,p,q,net,hci);if (hci->xc==0) {/* Word internal context only */CreateIEModels(thisWord,pInst,p,q,net,hci);}/* Cross word context */else if (pInst->clen==1) {/* Single phone word means that we need to *//*  build a complete cross-bar of contexts */CreateX1Model(pInst,p,q,net,hci,&holderHeap);}else {/* Cross word context and more than one phone */CreateXEModels(pInst,p,q,net,hci,&holderHeap);}}}   /* Allocate NetLinks from hash table stats. Zero counters */for (i=0; i<WNHASHSIZE; i++) {/* Build links for each word end model */for (node=wnHashTab[i];node!=NULL;node=node->chain) {if (node->nlinks>0){node->links=(NetLink*) New(net->heap,sizeof(NetLink)*node->nlinks);}elsenode->links=NULL;nxl+=node->nlinks;node->nlinks=0;node->aux=0;}}/* Finally put in the cross word links */ProcessCrossWordLinks(NULL,lat,hci->xc);/* First disassemble wnHashTab and link to end nodes as necessary */AddInitialFinal(lat,net,hci->xc); for (i=0; i<WNHASHSIZE; i++) {AddChain(net,wnHashTab[i]);}/* Finally chain all nodes together */for (i=0; i < lat->nn; i++) for (pInst=(PronHolder*)lat->lnodes[i].sublat;pInst!=NULL;pInst=pInst->next) {if (pInst->nstart>0)AddChain(net,pInst->starts);AddChain(net,pInst->chain);AddChain(net,pInst->ends);}/* And then clear up after ourselves */for (i=0; i < lat->nn; i++) lat->lnodes[i].sublat = NULL;DeleteHeap(&holderHeap);/* Count the initial/final nodes/links */net->numLink=net->initial.nlinks;net->numNode=2;/* now reorder links and identify wd0 nodes */for (chainNode = net->chain, ncn=0; chainNode != NULL; chainNode = chainNode->chain,net->numNode++,ncn++) {chainNode->inst=NULL;chainNode->type=chainNode->type&n_nocontext;net->numLink+=chainNode->nlinks;/* Make !NULL words really NULL */if (chainNode->type==n_word && chainNode->info.pron!=NULL &&net->nullWord!=NULL && chainNode->info.pron->word==net->nullWord)chainNode->info.pron=NULL;/* First make n_wd0 nodes */if (chainNode->type & n_hmm)for (i = 0; i < chainNode->nlinks; i++)if ( IsWd0Link(&chainNode->links[i]) ) {chainNode->type |= n_wd0;break;}/* Then put all n_tr0 nodes first */for (i = 0; i < chainNode->nlinks; i++) {/* Don't need to move any initial n_tr0 links */if (chainNode->links[i].node->type & n_tr0) continue;/* Find if there are any n_tr0 ones to swap with */for (j = i+1; j < chainNode->nlinks; j++)if (chainNode->links[j].node->type & n_tr0) break;/* No, finished */if (j >= chainNode->nlinks) break;/* Yes, swap then carry on */netlink = chainNode->links[i];chainNode->links[i] = chainNode->links[j];chainNode->links[j] = netlink;}}return(net);
}   

看函数签名,输入包括Lattice、Vocab以及HMMSet,返回Network。

1)创建Network实例;并初始化

2)创建HMMSetCxtInfo对象,由函数GetHMMSetCxtInfo函数完成。

看一下代码:

HMMSetCxtInfo *GetHMMSetCxtInfo(HMMSet *hset, Boolean frcCxtInd)
{HMMSetCxtInfo *hci;LabId labid;MLink ml;if (frcCxtInd)labid=GetLabId("@HCI-CI@",TRUE);elselabid=GetLabId("@HCI-CD@",TRUE);ml=FindMacroName(hset,'@',labid);if (ml==NULL) {hci=NewHMMSetCxtInfo(hset,frcCxtInd);if (!frcCxtInd) DefineContexts(hci);NewMacro(hset,0,'@',labid,hci);}elsehci=(HMMSetCxtInfo *) ml->structure;return(hci);
}

其中GetLabId会在一个全局的hashtable中查找是否有“@HCI-CI@”的字符串对象NameCell,如果没有创建一个,并返回对象指针,也就是labid。

有了这个labid之后,在hset的一个对象mtab中查找是否有同名的宏Macro,这个Macro指向这个名称对应的实体信息,例如“@HCI-CI@”对应的就HMMSetCxtInfo对象,其他的还有如HMM对象。

如果在hset的mtab中没有找到labid也就是“@HCI-CI@”对应的宏,就调用NewHMMSETCxtInfo函数创建一个,但是并不包含具体的信息,然后通过NewMacro(hset,0,'@',labid, hci)来把该对象赋值并添加到hset的mtab上。

GetHMMSetCxtInfo功能完成。

3)初始化发音对象,调用函数InitPronHolder。

它的输入Netword,Lattice,HMMSetCxtInfo,Vocab等,最终的效果是在Lattice的每个节点附着一系列的sublat,每个sublat就是这个单词的发音信息构建的实体PronHolder,之所以有多个是多音字的缘故。

         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;

代码太长,很多都是些检查、设置,关键的地方就是上面这几行。首先New一个PronHolder,传递的参数是对应的LNode--Word以及发音词典收集的信息,比较包含多少个phone,以及phone序列还有多少个发音。然后把这pInst赋给LNode-->sublat,完成扩展。

4)计算跨词连接和word end节点,由ProcessCrossWordLinks函数完成。

在Lattice中,有LNode和LArc两种元素,其中LNode包含了Word、节点类型tag、后续arc和前向arc;LArc包含开始和结束LNode以及开始节点后面的LArc、结束节点前面的LArc。在第三步,可以认为根据节点中Word携带的pron和phones信息构建了一个子图序列,赋值给了sublat项。但是词之间的信息还没有关联上。这一步就是为了完成这个功能。

也就是根据Lattice构建Network,其中NetNode包含的信息有:

/* The network nodes themselves just store connectivity info */
struct _NetNode {NetNodeType type;    /* Type of this node (includes context) */union {HLink  hmm;       /* HMM (physical) definition */Pron   pron;      /* Word represented (may == null) */}info;                /* Extra information specific to type of node */char    *tag;        /* Semantic tagging information */int nlinks;          /* Number of nodes connected to this one */NetLink *links;      /* Array[0..nlinks-1] of links to connected nodes */NetInst *inst;       /* Model Instance (if one exists, else NULL) */   NetNode *chain;int aux;
};

而NetLink信息相对少很多:

struct _NetLink{NetNode *node;       /* Node in network */LogFloat like;       /* Transition likelihood */
};

一般情况下,lattice中的每个arc会生成一个NetNode,包含这个arc的【开始节点】的发音信息,开始节点的类型,有多少个连接。这个连接数,是由arc的起始节点的发音个数决定的。一般情况下,一个NetNode一个link,除非有多音字。所以可以总结一下,不考虑多音字情况下,Lattice中有多少个LArc,对应的Network中就有多个NetNode,且每个NetNode的nlink为1。

构建好了这些NetNode保存在全局的hash表中:

NetNode *wnHashTab[WNHASHSIZE];

5)接下来,处理在Lattice中的LNode中的词内扩展操作。

在3)中,我们已经为每个LNode的sublat构建一个PronHolder对象。回顾下这个对象包含哪些信息。它与lattice中的节点是对应的,因此它包含了LNode,以及这个LNode节点中Word的发音Pron,有多少个音子等等。不妨把PronHolder的结构再贴在这里,删除些暂时不涉及的数据项。

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 ic;          /* Initial context - cache saves finding for all links */int fc;          /* Final context - cache saves finding for all links */int clen;        /* Number of non-cf phones in pronunciation */NetNode **lc;    /* Left contexts - linked to word initial models */NetNode **rc;    /* Right contexts - linked to word end nodes */int nstart;      /* Number of models in starts chain */int nend;        /* Number of models in ends chain */struct pronholder *next;
}
PronHolder;

下面来看看,要对这个PronHolder进行什么操作,目的是什么。会判断这个LNode对应的Word,有几个phone组成,是否包含Tee模型等。

然后通过函数CreateIEModels函数也创建一个NetNode(与LArc一样,不过包含的信息不同),它的内容是该节点对应的Word的发音PronHolder的实例,而LArc对应的NetNode是LArc的开始节点的发音信息。

循环nn(Lattice中的节点数)次,构建nn个NetNode,并且都保存在wnHashTable中,其hash值与pIns以及pIst-pron和n_word有关。

6)接下要分配NetLink对象了。前面两步骤中,4)是分配跨词(LAct)的NetNode;5)是根据LNode构建NetNode,但是Network中的NetLink一直没有被构建。

HVite源码解析续相关推荐

  1. 【多线程】ThreadPoolExecutor类源码解析----续(二进制相关运算)

    前言 在之前阅读 ThreadPoolExecutor 源码的时候,发现代码里用到了一些二进制相关的位运算之类的代码,看起来有些费劲了,所以现在大概总结了一些笔记,二进制这东西吧,不难,就跟数学一样, ...

  2. elementui组件_elementui 中 loading 组件源码解析(续)

    上一篇我们说了elementui如何将loading组件添加到 Vue 实例上,具体内容见上期 elementui 中 loading 组件源码解析. 这一篇我们开始讲讲自定义指令 自定义指令 关于自 ...

  3. elementui table某一列是否显示_elementui 中 loading 组件源码解析(续)

    上一篇我们说了elementui如何将loading组件添加到 Vue 实例上,具体内容见上期 elementui 中 loading 组件源码解析. 这一篇我们开始讲讲自定义指令 自定义指令 关于自 ...

  4. 注册中心 Eureka 源码解析 —— 应用实例注册发现(五)之过期

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-evict/ ...

  5. Redis进阶- Redisson分布式锁实现原理及源码解析

    文章目录 Pre 用法 Redisson分布式锁实现原理 Redisson分布式锁源码分析 redisson.getLock(lockKey) 的逻辑 redissonLock.lock()的逻辑 r ...

  6. QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者"tingsking18"和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消 ...

  7. iOS即时通讯之CocoaAsyncSocket源码解析一

    申明:本文内容属于转载整理,原文连接 前言: CocoaAsyncSocket是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的.强大的异步套接字库,向上封装 ...

  8. Retrofit源码解析

    本文分析的源码基于retrofit:2.9.0. 前言 Retrofit基于OkHttp,网络请求的工作本质上是 OkHttp 完成,而 Retrofit 仅负责 网络请求接口的封装,它们的关系示意图 ...

  9. Eureka Server源码解析(服务故障下线剔除流程)

    原创不易,转载注明出处 系列文章目录 <SpringCloud Eureka Server源码解析(启动流程)> <Eureka Server源码解析(服务注册流程)> < ...

  10. Eureka 源码解析 —— EndPoint 与 解析器

    1. 概述 本文主要分享 EndPoint 与 解析器. EndPoint ,服务端点.例如,Eureka-Server 的访问地址. EndPoint 解析器,将配置的 Eureka-Server ...

最新文章

  1. linux mailx 发邮件
  2. ITK:迷你管道MiniPipeline
  3. PHP中的魔术方法总结 :__construct, __destruct , __call, __callStatic,__get, __set, __isset, __uns
  4. python 文件遍历
  5. searchsploit漏洞查找工具使用指南(exploit-db.com 离线工具)
  6. tp6 使用lock_tp5 lock的使用
  7. python opencv pdf脚本之家_OpenCV 3和Qt5计算机视觉应用开发 PDF 影印含源码版
  8. 使用ztree展示树形菜单结构
  9. Bootstrap Table API 中文版 说明文档
  10. hibernate中基于主键映射1-1关联关系和基于外键映射1-1关联关系的不同
  11. ZeroClipboard插件——复制到剪切板
  12. Error:Could not find common.jar (android.arch.core:common:1.0.0)
  13. RRRR_wys' Blog 3.0 准备上线啦!
  14. 安科瑞ACY100油烟浓度在线监控仪在浙江省某市餐饮油烟监测治理项目中的应用
  15. 混合线性模型笔记2:概念解释
  16. 电脑屏幕总是晚上定时开启护眼模式,变成黄色?教你这招解决它
  17. DVWA安装教程(懂你的不懂·详细)
  18. 青龙->微信机器人 搭建保姆级教程
  19. Python GIS神器shapely 2.0新版本来了
  20. 优雅地使用 C++ 制作表格:tabulate

热门文章

  1. 什么软件可以查月经周期,检测月经周期的软件
  2. 【数据结构 严蔚敏版】 链表基本操作
  3. open 读Txt文件
  4. html 车牌号输入代码,html中车牌号省份简称输入键盘的示例代码
  5. .aspx(或.asp)文件与.html(.htm)文件的区别与联系
  6. WebView交互架构项目实战(三)(1),老罗android开发视频教程
  7. 使用R语言做极大似然估计
  8. 不用再为机翻头疼!sci论文写作翻译神器推荐
  9. unity及unity hub下载官网
  10. Java快逸报表展现demo_快逸报表操作积累.docx