上一篇分析了HERest工具的大体流程,其中最主要的是它运用了前向后向算法来更新模型参数。只是捡了最重要的流程来梳理,目的是先有个大体的印象。这一篇就来详细的、从头到尾的解读HERest代码。分析它主要涉及的数据结构、StepBack和StepForward算法的具体计算过程,以及它们的背后的公式推导。
UttInfo *utt; /* utterance information storage */
FBInfo *fbInfo; /* forward-backward information storage */
HMMSet hset; /* Set of HMMs to be re-estimated */
看前面几行注释,就告知我们什么是最重要的数据结构了。下面就来看一下它们分别是什么样子、有哪些数据项以及各自的功能是什么。其中HMMSet已经分析过了,重点是看UttInfo和FBInfo。
/* structure for the utterance information */
typedef struct {
MemHeap transStack; /* utterance transcript information heap */
MemHeap dataStack; /* utterance data information heap */
MemHeap dataStack2; /* utterance data2 information heap */
int Q; /* number of models in transcription */
Transcription *tr; /* current transcription */
Boolean twoDataFiles; /* Using two data files */
int S; /* number of data streams */
int T; /* number of frames in utterance */
ParmBuf pbuf; /* parameter buffer */
ParmBuf pbuf2; /* a second parameter buffer (if required) */
Observation ot; /* Observation at time t ... */
Observation ot2; /* Cepstral Mean Normalised obervation, used in
single pass re-training */
LogDouble pr; /* log prob of current utterance */
} UttInfo;
UttInfo这个数据结构保存了输入的观察值和对应文本(转录)的信息,分别对应Observation和Transcription。再往下追,看看这两个结构是什么样子。
typedef struct {
Boolean eSep; /* Energy is in separate stream */
short swidth[SMAX]; /* [0]=num streams,[i]=width of stream i */
ParmKind bk; /* parm kind of the parm buffer */
ParmKind pk; /* parm kind of this obs (bk or DISCRETE) */
short vq[SMAX]; /* array[1..swidth[0]] of VQ index */
Vector fv[SMAX]; /* array[1..swidth[0]] of Vector */
} Observation;
它记录了观察向量的信息,包括有多个流、观察值是离散还是连续的、以及参数本身fv[SMAX]。并且我们看到UttInfo中,是以Transcription指针 *tr 的形式保存的,其实它是一个向量,包含了所有观察值包装后的Transcription元素。在LoadData里会看到。