HCompV源码再解析

 

在还没开始讲解源码之前,我先贴出一个数据结构HMMSet:

/* ---------------------- HMM Sets ----------------------------- */

typedef struct _HMMSet{
   MemHeap *hmem;          /* memory heap for this HMM Set */   
   Boolean *firstElem;     /* first element added to hmem during MakeHMMSet*/
   char *hmmSetId;         /* identifier for the hmm set */
   MILink mmfNames;        /* List of external file names */
   int numLogHMM;          /* Num of logical HMM's */
   int numPhyHMM;          /* Num of distinct physical HMM's */
   int numFiles;           /* total number of ext files */
   int numMacros;          /* num macros used in this set */
   MLink * mtab;           /* Array[0..MACHASHSIZE-1]OF MLink */
   PtrMap ** pmap;         /* Array[0..PTRHASHSIZE-1]OF PtrMap* */
   Boolean allowTMods;     /* true if HMMs can have Tee Models */
   Boolean optSet;         /* true if global options have been set */
   short vecSize;          /* dimension of observation vectors */
   short swidth[SMAX];     /* [0]=num streams,[i]=width of stream i */
   ParmKind pkind;         /* kind of obs vector components */
   DurKind dkind;          /* kind of duration model (model or state) */
   CovKind ckind;          /* cov kind - only global in V1.X */
   HSetKind hsKind;        /* kind of HMM set */
   TMixRec tmRecs[SMAX];   /* array[1..S]of tied mixture record */
   int numStates;          /* Number of states in HMMSet */
   int numSharedStates;    /* Number of shared states in HMMSet */
   int numMix;             /* Number of mixture components in HMMSet */
   int numSharedMix;       /* Number of shared mixtures in HMMSet */
   int numTransP;          /* Number of distinct transition matrices */
   int ckUsage[NUMCKIND];  /* Number of components using given ckind */
   InputXForm *xf;         /* Input transform of HMMSet */
   AdaptXForm *semiTied;   /* SemiTied transform associated with model set */
   short projSize;         /* dimension of vector to update */

   /* Adaptation information accumulates */
   Boolean attRegAccs;   /* have the set of accumulates been attached */
   Boolean attXFormInfo; /* have the set of adapt info been attached */
   Boolean attMInfo;     /* have the set of adapt info been attached */
   AdaptXForm *curXForm;
   AdaptXForm *parentXForm;
   
   /* Added to support LogWgts */
   Boolean logWt;       /* Component weights are stored as Logs */

   /* Added to support delayed loading of the semi-tied transform */
   char *semiTiedMacro;  /* macroname of semi-tied transform */

} HMMSet;

要想掌握HTK工具,必须对这个结构里的每一项了如指掌,并能清晰的勾勒出画面。随着程序调试的进行,我们逐渐接近这个结构,并把它弄清楚。

 

在HCompV工具中,调试时直接跳到函数InitShell处。它调用SaveCommandLine函数,它的功能在静态变量char* saveCommandLine中保持命令行参数,后续解析会用到。

static char *savedCommandLine;
/* SaveCommandLine: Stores all command line arguments in 1 string */
static void SaveCommandLine(int argc, char **argv)
{
   int i, len=0;

   for (i=0;i<argc;i++)
      len+=strlen(argv[i])+1;
   savedCommandLine = (char *) malloc(len);
   savedCommandLine[0]='\000';
   strcat(savedCommandLine, argv[0]);
   for (i=1;i<argc;i++)
      sprintf(savedCommandLine,"%s %s", savedCommandLine, argv[i]);
}

现在它的内容是“D:\\vscode\\source\\repos\\HTK\\HCompV\\Debug\\HCompV.exe -C .\\src\\config\\config1 -f 0.01 -m -S .\\src\\train.scp -M .\\src\\hmms\\hmm0 .\\src\\proto” 一共11个参数,即argc值为11.

接下来的循环处理-A -C -S -V -D标志,并保存相应的参数:

-A表示打印信息,infoPrint设为True

-C是指定配置文件,它的位置有-C后面的命令参数指定;然后是读取配置文件并解析;

    函数 ReadConfigFile读取配置文件,并解析,构造confList,它是一个链表,节点是ConfigEntry。

typedef struct _ConfigEntry{
   ConfParam param;
   struct _ConfigEntry *next;
}ConfigEntry;

typedef struct {     /* Configuration Parameter */
   char *user;          /* name of module/tool to use this param */
   char *name;          /* name of param - upper case always */
   ConfKind kind;       /* kind of config param value */
   ConfVal val;         /* value */
   Boolean seen;        /* set true when read by any module */
} ConfParam;

param内容包含了配置项的名字、值类型和值本身。

-S指定文件列表文件,后面是文件的具体位置。通过 SetScriptFile函数计算包含多少个文件。

然后就是系统的初始化,其中跟后面密切相关的是内存的初始化。它会分配gstack这个全局的内存空间。在后面的CreateHMMSet函数中,将会看到,HMM集合对象hset有个属性mem,它指向gstack的内存空间,而它上面保存了所有的HMM参数。它们是以哈希表的数据结构存储的。

 

CreateHMMSet函数,第一个参数hset,就是HMMSet的指针,它指向一个对象包含了当前HMM集合的一些信息,比如:包含多少个hmm(物理的、逻辑的各多少)、而且hmm还分不同类型(这点还没弄明白),以及存储的内存位置。HTK还支持多数据流通道,每个数据流的维度等等信息。

这个结构是像是一个理解HTK的地图,时刻要关注它。关注每一步操作对这个结构产生哪些影响。或者这个结构连接上的其他信息发生哪些变化。

另外,非常重要的一点是,它还包含一个hashtable;而且这个hashtable的位置就在CreateHMMSet函数的第二个参数MemHeap* heap指向的内存块上。

这个hashtable的总槽数由下面这个宏确定:

#define MACHASHSIZE 250007   /* Size of each HMM Set macro hash table */

待会儿会看到如何创建这个hashtable,以及如何给这个table添加第一个hmm对象。下面的代码是CreateHMMSet函数,前面都是初值设定,没有什么难的。重点是看到中间那部分,调用一个特征重要的函数MakeHashTab(hset, MACHASHSIZE)。

/* EXPORT->CreateHMMSet: create the basic HMMSet structure */
void CreateHMMSet(HMMSet *hset, MemHeap *heap, Boolean allowTMods)
{
   int s;

   /* set default values in hset structure */
   hset->hmem = heap;
   hset->hmmSetId = NULL;
   hset->mmfNames = NULL; hset->numFiles = 0;
   hset->allowTMods = allowTMods;  hset->optSet = FALSE;
   hset->vecSize = 0; hset->swidth[0] = 0;
   hset->dkind = NULLD; hset->ckind = NULLC; hset->pkind = 0;
   hset->numPhyHMM = hset->numLogHMM = hset->numMacros = 0;
   hset->xf = NULL; hset->logWt = FALSE;
   for (s=1; s<SMAX; s++) {
      hset->tmRecs[s].nMix = 0; hset->tmRecs[s].mixId = NULL;
      hset->tmRecs[s].probs = NULL; hset->tmRecs[s].mixes = NULL;
   }
   /* initialise the hash tables */
   hset->mtab = (MLink *)MakeHashTab(hset,MACHASHSIZE);
   hset->pmap = NULL;
   /* initialise adaptation information */
   hset->attRegAccs = FALSE;
   hset->attXFormInfo = FALSE;
   hset->attMInfo = FALSE;
   hset->curXForm = NULL;
   hset->parentXForm = NULL;
   hset->semiTiedMacro = NULL;
   hset->semiTied = NULL;
   hset->projSize = 0;
}

下面可以看出函数MakeHashTab(hset, MACHASHSIZE)在创建一个哈希表。

/* MakeHashTab: make a macro hash table and initialise it */
void ** MakeHashTab(HMMSet *hset, int size)
{
   void **p;
   int i;
   
   p = (void **) New(hset->hmem,sizeof(void *)*size);
   for (i=0; i<size; i++)
      p[i] = NULL;
   return p;
}

这个hash表的每个元素都可能填上一个MLink,它是指向MacroDef的指针。

typedef struct _MacroDef *MLink;

typedef struct _MacroDef{
   MLink next;             /* next cell in hash table */
   char type;              /* type of macro [hluvixdtmps*] */
   short fidx;             /* idx of MMF file (0 = SMF) */
   LabId id;               /* name of macro */
   Ptr structure;          /* -> shared structure or HMM Def */
} MacroDef;

这个MacroDef是描述了HMM更上层的结构,它的Ptr structure会指向具体的hmm对象。

到此,我们头脑中,应该有张清晰的地图,就是HTK这个隐马尔可夫(HMM)开源软件最重要的结构图。

 

继续往下, Initialise()函数会调用 MakeOneHMM(HMMSet *hset, char *hname)函数,就是在hset这个hmm集合中创建一个名为hname的hmm对象。然后里面调用 InitHMMSet(HMMSet *hset, char *fname, Boolean isSingle),继续往下调用 CreateHMM(HMMSet *hset, LabId lId, LabId pId),就是最终创建hmm的地方,它里面有这行代码:

hmm = (HLink)New(hset->hmem,sizeof(HMMDef));

它表示在hset指向的内存空间上,new一个大小为sizeof(HMMDef)的空间,然后初始化这个hmm的一些参数,大部分都是空或者0,因为还没计算和赋值。

上图就变成下面这个样子了。

在mem堆上建立了hmm实体,并初始化。接着理解NewMacro函数也很关键。

NewMacro(hset,0,'h',pId,hmm)

它的作用就是在mtab这个list中建立一个MLink指向的对象MacroDef,并把相关的值赋到这个对象上,例如Ptr structure指向刚建立的hmm对象,type为“h”,fidx为0,即为首个hmm对象,Labid指向的NameCell字符串为“proto”。

到这里,HCompV已经为HMMSet添加了名为proto的空hmm模型。HCompV工具的功能几乎完成了一半了,剩下的就是给这个hmm根据训练数据计算全局均值和方差赋值上去就完成使命了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值