HTKbook翻译之第十二章网络、词典及语言模型

在写viterbi解码那篇博客时需要参考HTK book的相关章节,就想着把它们翻译下,以供其他读者参考。而目前能找到的HTKbook的中文翻译资料只有前三章。现在翻译的第十二章是我第一尝试去翻译这样比较长的英文文档,遣词造句很多有待商榷。如果读者看到哪里 有错翻错或者不合适的地方写在评论里,我会逐一改正。明天(2020年11月12号)开始翻译第十三章 Decoding with HVite,也作为语音识别之HTK入门(十)——维特比(viterbi)解码博客的部分内容。

百度网盘的连接欢迎下载:

链接:https://pan.baidu.com/s/1FJbSoyeMdIhX2zXsTNthfQ 
提取码:inr3 
 

 

 

第十二章 网络、词典及语言模型

 

前几章描述了如何处理语音数据和训练各种HMM模型参数。本章及后面几章将集中描述如何通过HTK工具构建语音识别器。本章聚焦在网络和字典。一个词级网络描述了一系列可能被识别的词;对子词(sub word)模型而言,词典描述了构成每个词的系列子词。词级网络的典型代表是通过任务语法(Task Grammar)显示定义的所有合法词序列,或者一个“词循环(Word-loop)”,即把所有词放置于一个循环中,允许任意词之间相连接。词循环网络通常会被扩展会随机语言模型。网络还可以被用来定义音子(phone)识别器和各式各样的关键词识别系统(word-spotting systems)。

 

HTK的标准网格格式(Standard Lattice Format SLF)方式可以用来描述词网络。该格式将在第二十章描述更多细节。它是一个通用的文本格式,用来表示多个可能的识别输出,以及词的网络拓扑结构。SLF格式是基于文本,可以直接对它进行编辑,但是这样做是非常费力不讨好的。HTK提供了两个更高抽象表示的工具。第一个是HParse,它允许通过编写诺尔范式(BNF格式)来生成SLF格式的文件。HTK的以前版本只提供这样的方式生成SLF文本。Hparse的任务语法很容易编写,缺点是不利于识别器精细控制它生成的词网络。而HBuild直接在SLF层面上提供词网络的精细控制。它的主要功能是让一个巨大的词网络被分解成一组小的、独立的子网络,该大网络是通过SLF格式输入的。这能有效的提升设计处理过程,避免了不必要的重复。

 

Hbuild还能实现其他功能。首先,它自动构建词循环和词对语法。第二,它可以在网络中包含随机二元文法语言模型。这些可以由HLStats通过标注文本产生。然后,HTK支持标准的ARPA MIT-LL文本格式的N元文法语言模型,因此可以从不同来源构建网络。

 

无论使用哪种方法来产生词网络,有一点是肯定的,就是要确定产生的词网络能表达设计人员想要的语法。能有效的衡量识别任务的复杂度也是非常有用的,为达此目的,HSGen可以提供帮助。它能根据SLF网络随机产生语句实例,同时评估网络的复杂度。

 

当识别器导入一个网络时,它需要同时参考发音字典,来把网络中的词展开成音子模型序列。发音字典中可以包括多音字,这样在词网络中包括平行序列。有开关选项是否自动将字典里的词汇转换成上下文相关的三音子模型,无论是词内还是跨词间的三音子。发音字典对语音识别系统是至关重要的。实际上,词的发音内容可以通过多个源来获得。HTK提供的HDMan工具能完成上述功能。每个源都可以单独的编辑,也可以合并转换来构建统一的HTK格式的字典。

 

HTK模型HNet实现了上述的多种描述词网络以及把这些网络扩展成子词级别的HMM网络的方法,以便构建识别器。HDict实现了加载和操作字典的方法。HLM实现了加载和操作语言模型的方法。这些实现方法以及HParse、HBuild、HSGen、HLStats和HDMan是本章的主题。

 

12.1 如何使用词网络

在吃透词网络和字典之前,理解图12.1描述的识别过程是非常有用的。一个网络是HTK的标准网格格式(SLF)来定义的。它是一个文本文件,可以被编辑器直接编写,也可以使用HTK的工具——HParse和HBuild。它们的输入都是一个文本文件,输出一个SLF文件。无论哪个方法,SLF根据都是通过离线生成,是语音识别系统处理过程的一部分。

 

SLF文件包括一系列的代表“词”的节点,和一系列代表词间转移的边。转移的概率值不同,用来表征语法网络的偏好。它们同时可以表示二元文法的语言模型,HBuild可以自动产生这样的二元网络。除了SLF文件,HTK识别器还需要发音字典为网络中的每个词提供发音信息,也即组成所有发音集合的音子模型(phone)。可以通过HTK提供的接口工具HDict来输入字典。

 

发音字典、phone模型集合和词网络作为HNet的输入,以产生对应的子词模型网络。字典中的词可能有多个发音,这样的话,网络将为每个发音构建一个分支。每个发音要么由一系列音子构成,要么由一系列子词HMM名称构成。如果是前一种情况,HNet可以通过词内或者词间三音子模型来扩展HMM网络。一旦HMM网络构建完成,就可以作为解码模块Hrec的输入,对语音进行识别。注意一点,必须在识别时在线构建HMM网络,它是初始化过程的一部分。

 

为方便起见,HTK提供Hvite工具来调用HNet和HRec的一些识别功能。HVite适合执行测试性的语音文件识别任务。但是,应用开发人员需要知道,Hvite只是一个命令行工具,它可以通过命令行参数调用加载词网络、发音字典和模型,然后产生识别网络,对每一段输入语音进行重复性的识别操作。如果想把识别功能嵌入到某应用中,最适当的方式是直接调用HNet和Hrec的相应功能。关于Hvite的使用功能将在下一章介绍。

 

12.2 词网络和SLF格式

本节简单的介绍下HTK提供的标准网格格式(SLF)。SLF文件有多种用途,有些超过了HTK的范畴。这里描述的仅仅是作为Hnet输入的词网络的一些特性。接下来的章节将介绍作为识别器输出的一些特性。更多、更全的关于SLF的信息请参考第二十章。

 

在SLF文件中描述的词网络有一系列的节点和边构成。节点表示词、边表示词之间的转移。每个节点和边各单独占一行,每行由多个“域”(field)组成,而每个“域”都是一个“name=value”对。域的名字可以任意长,但一般都是只由一个字母表示。惯例上,域的名字首字母大写的说明是强制要求,而如果它是小写的则为可选。以#号开头的行是注释,可以忽略。

 

下面的例子说明了SLF词网络的基本格式。它与图12.2相对应。上图表示以“start”开始,以“end”结尾,然后中间由“bit”和“but”构成。后面会看到,开头和结尾的词会被映射为“静音”,也就是为“空”。所以这段语法允许说话者说“bit but bit bit … etc”。

# Define size of network: N=num nodes and L=num arcs

N=4 L=8

# List nodes: I=node-number, W=word

I=0 W=start

I=1 W=end

I=2 W=bit

I=3 W=but

# List arcs: J=arc-number, S=start-node, E=end-node

J=0 S=0 E=2

J=1 S=0 E=3

J=2 S=3 E=1

J=3 S=2 E=1

J=4 S=2 E=3

J=5 S=3 E=3

J=6 S=3 E=2

J=7 S=2 E=2

 

注意第一行定义了网络的大小,它必须放在最开头。没有前项的节点就是初始节点,没有后项的就是结尾节点,它们都是唯一的。上述SLF描述的语法中,节点0就是初始节点,节点1就是结尾节点。给它们起“start”和“end”并没有特别的意义,只是便于理解。

 

 

词网络也可以包含空节点,在SLF中以“!NULL”表示。空节点有利于减少边的数量。例如在上述的Bit-But网络中,可以这么定义:

 

# Network using null nodes

N=6 L=7

I=0 W=start

I=1 W=end

I=2 W=bit

I=3 W=but

I=4 W=!NULL

I=5 W=!NULL

J=0 S=0 E=4

J=1 S=4 E=2

J=2 S=4 E=3

J=3 S=2 E=5

J=4 S=3 E=5

J=5 S=5 E=4

J=6 S=5 E=1

 

 

 

在这个例子中,减少的并不明显。但是,如果有很多个并行单词,边的总数会因为空节点的引入而下降明显。

 

默认情况下,所有边都是相似的。但是可选项“l=x”可以被附在任意边上,x表示转移概率的log值。例如,单词but的可能性是bit的两倍,那么在第一、第二条边可以表示为:

 

J=1 S=4 E=2 l=-1.1

J=2 S=4 E=3 l=-0.4

 

这里的概率被归一化了,但不是必须的。识别器把这个对数概率值加到路径的得分上,作为最优路径选择时的词转移的惩罚值。

 

12.3 利用HParse构建词网络

手工建立词级SLF网络文件并不困难,但是有点繁琐。在早期HTK版本中,建立在巴克斯-诺尔范式基础上的抽象语法标记被用来定制识别语法。这个HParse格式测词网络被识别器实时读入,并编译成有限状态识别网络。

 

HTK3.4依旧支持这种方式,来驱动识别器,但是得在离线编译成SLF词级网络后。HParse格式语法由扩展的正则表达式组成,它们被包含在圆括号内。表达式由一系列的词和元字符组成。

| 表示不同分支

[]表示0或1次重复

{}表示0或者多次重复

<>表示1或者多次重复

<<>>表示环境相关的循环

下面的例子说明上述元字符的用法,除了最后一个,它是在上下文相关的建模环境下使用。

 

作为第一个语法实例,用最简单的孤立词网络举例,每个词就是一个数字。合法的语法如下面这样的:

 

(

one | two | three | four | five |

six | seven | eight | nine | zero

)

 

 

这个语法会被转换成图12.4(a)描述的词网络。如果把该语法文件保存在文件digitsys中,对应SLF词网络文件名称为digitnet,那么可以用如下命令完成转换:

 

HParse digitsyn digitnet

 

 

上述语法假定输入的数字有合适的结尾。可以通过在上述语法规则中,在表达式的首尾添加静音模型来消除上述假设。

 

(

sil ( one | two | three | four | five |

six | seven | eight | nine | zero ) sil

)

 

对应如图12.4(b)所示。可能的后续模型为静音+数字+静音。如果需要识别一连串数字,那么需要用尖括号把它们括起来,如下:

 

(

sil < one | two | three | four | five |

six | seven | eight | nine | zero > sil

)

 

对应如图12.4(c)所示。

 

 

HParse语法可以定义变量来表示子表达式。变量名以$字符开头,变量定义格式如下:

$var = expression ;

例如,上面连结数字的语法可以这么重写。

 

$digit = one | two | three | four | five |

six | seven | eight | nine | zero;

(

sil < $digit > sil

)

 

$digit就是一个变量,它的值就赋值号右边的表达式的值。在其他表达式中,变量出现的地方,都可以用变量定义右边的部分代替。变量定义必须在使用之前,因此可以递归定义。

 

再优化下上面的数字语法,语句的开始和结尾的静音节点可以设置成可选的,用方括号括起来。

 

$digit = one | two | three | four | five |

six | seven | eight | nine | zero;

(

[ sil ] < $digit > [ sil ]

)

 

图12.4(d)描述的就是这个版本的词网络示意。

 

HParse格式的语法非常便于描述语音交互接口的任务语法(task grammar)。最后,利用HParse语法定义一个简单的用语音来控制电话拨号的任务语法。

 

 

$digit = one | two | three | four | five |six | seven | eight | nine | zero;

$number = $digit { [pause] $digit};

$scode = shortcode $digit $digit;

$telnum = $scode | $number;

$cmd = dial $telnum |enter $scode for $number |redial | cancel;

$noise = lipsmack | breath | background;

( < $cmd | $noise > )

 

 

字典中的pause、lipsmack、breath和background是利用对应的音频训练的噪音HMM模型,它们在输出时都为空。

 

最后需要注意一点,在早期的HTK版本中,词的发音被嵌入在词语法中。它们是通过保留节点WD_BEGIN和WD_END作为词的分界符。为了向后兼容,HP可以处理旧风格的网络,但是会输出一个发音字典和一个新语法格式的词网络。相关的引用章节详细的定义了这个兼容模式,环境变量V1COMPAT或者-c设为true。

 

在讨论词网络主题的最后一点,需要注意,任何包括tee模型的循环网络会报错。例如,sp模型是一单状态的tee模型,用来表示短暂停,下面这个网络就会报错。

 

( sil < sp | $digit > sil )

 

这个语法的意图是让识别器识别可能被短暂定分开的一系列数字。但是该语法允许sp模型无限循环,识别器可能会一直在某状态循环而不消耗任何输入序列。解决方法就是重新组织网络。如下:

 

( sil < $digit sp > sil )

 

 

12.4 二元文法语言模型

 

在继续描述如何生成网络之前,例如通过工具HBuild,需要聊一聊二元文法语言模型的使用。HTK提供HLM模块来支持统计语言模型。虽然HLM支持N-gram语言模型,但是HTK相关的功能是被限定在二元的。

 

可以调用HLStats生成二元文法语言模型,假定所有训练标注语料被存在MLF文件labs中。命令如下:

 

HLStats -b bigfn -o wordlist labs

 

 

标注文件中涉及所有词都罗列在wordlist中。这个命令读取所有标注文件labs,在内存中构建一个二元表,然后输出一个备用的表示二元语法文件bigfn。用到的公式在介绍HLS章节给出。二元文法的思想可以通过下面的公式展示。

p(i,j)=\left\{\begin{matrix} (N(i,j) -D)/N(i)& if N(i,j)>t\\ b(i)p(j)& otherwise \end{matrix}\right.

N(i,j)表示单词i后接单词j,也即词对\left \langle w_{i},w_{j} \right \rangle在文本中出现的次数,而N(i)表示单词w_{i}在文本出现的次数。实际上,会从概率大的配对项数值中减去一部,分配给出现次数少的项。这一过程被叫做“discounting”——打折。默认这个减去的数值D为0.5,它是可以调节的,通过环境变量DISCOUNT来配置。当二元文法模型中某项概率值小于门槛值(threshold),这个二元项就会退化为一个一元模型,并经过一个退化系数的缩放,使得概率值总是为1。

 

导出的二元模型以ARPA MIT-LL格式保存在文本文件中。如下面所示:

 

\data\

ngram 1=<num 1-grams>

ngram 2=<num 2-ngrams>

\1-grams:

P(!ENTER)       !ENTER   B(!ENTER)

P(W1)               W1           B(W1)

P(W2)               W2           B(W2)

...

P(!EXIT)            !EXIT       B(!EXIT)

\2-grams:

P(W1 | !ENTER)                !ENTER            W1

P(W2 | !ENTER)                !ENTER            W2

P(W1 | W1)                        W1                    W1

P(W2 | W1)                        W1                    W2

P(W1 | W2)                        W2                    W1

....

P(!EXIT | W1)           W1                    !EXIT

P(!EXIT | W2)           W2                    !EXIT

\end\

 

 

所有概率数值都是以对数形式保存的。默认的开始/结束标记,!ENTER和!EXIT可以通过HLStats –s选项来改变。

 

对有些应用来说,简单的矩阵风格来表示二元文法可能更合适。如果调用HLS命令的-o选项被剔除,那么简单的二元矩阵输出就如下所示:

 

!ENTER

W1

W2

……

!ENTER

0

P(W1 | !ENTER)

P(W2 | !ENTER)

……

W1

0

P(W1 | W1)

P(W2 | W1)

……

W2

0

P(W1 | W2)

P(W2 | W2)

……

……

 

 

 

 

!EXIT

0

PN

PN

……

 

其中p(wj|wi)表示单词i后面接单词j的概率,它的值在矩阵的第i行,第j列。如果总共有N个单词,那么PN的值就是 1/(N+1)。这就保证了每行的概率值和为1。使用一种非常粗糙的平滑技术,就是设置概率的最低值,通过选项-f minp来指定,防止某项概率低于minp。然后,第一列永远是0。最后,在高斯混合绑定和离散概率中,通过“游程编码”方案表示,它后接星号和重复次数。

 

12.5 通过HBuild构建词网络

 

在本章开始的介绍部分提到,HB的主要功能是允许通过主网格(main lattice)和一系列子网格(sub-lattice)来构造词级网络。任意网格都可以包含节点定义,它指向其他网格。这使得词级识别网络可以被分解成多个子网络,然后在不同节点重复使用。

 

例如,假设要处理小数的输入。适合的网络结构如图12.5所示。但是用SLF文件来直接写这个语法,在数字部分循环写了两次。这是可以避免的,如果定义数字循环为子网络,然后在小数网络中引用它。定义如下:

 

# Digit network

SUBLAT=digits

N=14 L=21

# define digits

I=0 W=zero

I=1 W=one

I=2 W=two

...

I=9 W=nine

# enter/exit & loop-back null nodes

I=10 W=!NULL

I=11 W=!NULL

I=12 W=!NULL

I=13 W=!NULL

# null->null->digits

J=0 S=10 E=11

J=1 S=11 E=0

J=2 S=11 E=1

...

J=10 S=11 E=9

# digits->null->null

J=11 S=0 E=12

...

J=19 S=9 E=12

J=20 S=12 E=13

# finally add loop back

J=21 S=12 E=11

.

# 接上一栏

# Decimal netork

N=5 L=4

# digits -> point -> digits

I=0 W=start

I=1 L=digits

I=2 W=pause

I=3 L=digits

I=4 W=end

# digits -> point -> digits

J=0 S=0 E=1

J=1 S=1 E=2

J=2 S=2 E=3

J=3 S=3 E=4

 

 

子网络是通过文件开头的SUBLAT关键来识别,它单独一行的点号结束。一旦定义好,子网络就可以替代更高层级网络中通过字符“L”来引用它的部分。如在小数网络中,节点1和节点3指向L=digits。

 

这个过程还可以继续引申下去,小数网络(decimal network)还可以被更高抽象的网络引用。

 

 

最常用的识别网络就是词循环网络。词汇表中的任一个词平行放置在一个大循环中,允许任意词序列被识别。这是最常用的听写或者转写系统的安排方式。HBuild可以从词表中自动构建这样的网络。它还可以读入ARPA MIT-LL格式或者HTK矩阵格式的二元文法模型,把它们附在二元词转移的概率上。需要注意一点就是,这样的二元模型允许每个词的自循环,这极大的增加网络的大小,减慢了识别速度。备份(backed-off)的二元模型可以共享循环转移。图12.6就说明了这个想法。当读入ARPA MIT-LL格式的二元模型时,HBuild可以利用这样的转移共享。

 

最后HB可以自动的构建词对语法,就像在ARPA Naval Resource Management task。

 

12.6 用HSGen测试词网络

 

当设计一个任务语法时,如果能测试所定义的语言是否与预期的一致是非常有用的。一个简答的方法就是随机的遍历任务网络来产生词序实例。HTK工具HSGen就是这样的做的。

 

一个例子,如果bnet表示上述的Bit-But语法文件,而bdic是对应的字典文件,那么命令:

 

HSGen bnet bdic

 

就会随机产生一系列由bnet定义的语言的词序列,如下:

 

start bit but bit bit bit end

start but bit but but end

start bit bit but but end

.... etc

 

也许这个例子还不太明显,如果语法比较复杂的话,这样的输出会非常有启发意义。

 

HSG也可以通过记录句子的生成概率来评估它的经验熵。为了使用该功能,最好禁止句子输出,然后随机产生大量例子。如执行:

 

HSGen -s -n 1000 -q bnet bdic

 

-s选项指示命令执行统计,-q选项禁止输出,-n指定产生句子数量,该命令的执行结果如下:

Number of Nodes = 4 [0 null], Vocab Size = 4 Entropy = 1.156462, Perplexity = 2.229102

1000 Sentences: average len = 5.1, min=3, max=19

 

12.7 构造字典

 

在12.1节提到过,HNet利用词网络扩展子词HMM模型网络供识别器使用。用到的方法就是对词网络中的每个词查找发音字典,获得它的发音信息。

 

HTK中使用到的字典格式非常简单,每行表示一个单词,格式如下:

 

WORD     [ ’[’OUTSYM’]’ ]       [PRONPROB] P1 P2 P3 P4 ....

 

 

WORD表示指定单词,后接可选的OUTSYM和PRONPROB。其中OUTSYM表示识别时输出的字符串,它必须包括在方括号内。PRONPROB表示发音概率(0.0~1.0)。P1,P2,… 表示该单词对应的发音音子序列。如果一个单词的发音符号没有指定,默认为单词自身。如果一个单词的发音概率没有指定,默认为1.0。空的方括号,[],表示识别时禁止输出。例如,一个字典可能包括下面的内容:

 

 

bit            b ih t

but           b ah t

dog          [woof] d ao g

cat           [meow] k ae t

start        [] sil

end          [] sil

 

 

如果某个单词是多音字,那么该单词在字典中占多行,例如:

 

the           th iy

the           th ax

 

对应单词“the”的重音和非重音形式。

 

字典里的发音一般是音子级别的,就像上面例子里的一样。如果是上下文相关模型,这些模型将直接包含在字典中。以Bit-But为例:

 

bit            b+ih b-ih+t ih-t

but           b+ah b-ah+t ah-t

 

 

原则上,这是没必要的,因为HNet可以根据上下文自动构建。但是离线处理可以节约计算时间,把它作为字典的一部分。当然这只能是“词内上下文相关”的,“跨词依赖”只能通过HNet生成。因为无法预知每个词的上个、下个是哪个词,因此也就无法预先构建跨词上下文依赖多音子绑定模型。

 

发音字典是非常有价值的数据源,如果手工制定,需要非常大的投入。有不少商业化的开防域字典,但是一般它们的格式和使用的音子集都不相同。为帮助构建发音字典,HTK提供了HDMan工具,它可以用来为不同源字典构建格式统一的发音字典。图12.7显示HDMan的工作方式。

 

源字典文件的每一行都表示一个单词发音,而且这些单词必须得按字母顺序排好序。字典里的单词必须是有效的HTK字符串,这在4.6节定义过。如果运行任意字符串,那么输入的编辑脚本应该在第一行加上RAW指令。

HDMan的基本操作就是读入输入流并扫描,遇到新词,就把它连同发音信息拷贝到输出文件中。图12.7中,还看到了词列表。它是可选的,但是如果提供了,HDMan就复制列表中的内容。正常情况下,HDMan只复制找到的第一个单词的发音。那么多个源字典的排序是以“可靠性”来排的,例如Src1里的信息就比后面Src2更准确,也就是说Src2中可能包含一些词的发音是错的,在Src1中给予了纠正。最后Src3是一个很大的字典,但是发音信息的准确度和可靠性是比较低的,(例如,它可能是通过基于规则的文本到音子转换系统生成的)但是包含了很多不在主流词典中的单词。

 

如图中所示,HDMan可以对每个源字典应用一系列的编辑指令,还可以对输出流进行编辑。相关指令在引用章节详细的介绍。与HLEd相类似,指令集都是以文本方式写在脚本文件中的,每行一个指令。每个输入的指令脚本跟它对应的源字典相同,唯一的差别是后缀名不同,为“ded”。输出文件的编辑脚本保存在“global.ded”文件中。指令包括词级和音子级的添加和删除指令,还有上下文相关的替换以及自动转换为左、右二元音子和词内的三音子指令。

 

当HDMan加载字典时,会在词的开始和结尾分别加上分割标记,并在把它们写入新的字典时删除掉。默认标记是#号,但是可以通过选项-b来重新指定。这样做的原因是在上下文依赖相关的命令在执行时,可以定位到发音标记的初始和结尾位置。下面会给出演示例子。

 

给出HDMan的典型操作,而不是全面介绍HDMan的每一个编辑指令可能更高效些。首先,假设某字典中,非重读的“-ed”标记为“ih0 d”,但是新构建的字典没有重音标记,而是通过弱读来表示,那么就需要经过下面转换。

 

ih0 d #     ->    ax d

ih0            ->    ih (其他情况)

 

这个转换功能可以通过下面的3个指令实现。

 

MP axd0 ih0 d #

SP axd0 ax d #

RP ih ih0

 

 

上下文关联替换MP指令把所有“ih0 d #”合并为“axd0”,然后SP指令把它分拆为“ax d #”。最后RP指令把所有的ih0替换为ih。还有一个相似的例子,如果音子序列“ax l”后面跟的是辅音字符,那么把它们都替换成“el”,就像bottle词中的。这可以使用DC指令完成辅音的定义,然后MP指令将所有“ax l”合并为“axl”,再通过CR指令将满足“axl nonv”模式的字符串替换为“el+原字符”,最后把剩下的axl拆解为“ax l”还原。

 

DC nonv l r w y .... m n ng #

MP axl ax l

CR el * axl nonv

SP axl ax l

 

 

最后一个例子是关于输出的,它的指令脚本为global.ded,可以通过下面的指令组合将上下文相关的形式,并且在每个发音序列后面添加sp(短暂停)。

 

 

TC

AS sp

 

它会把字典的数据改为:

 

BAT b ah t   —> BAT b+ah b-ah+t ah-t sp

 

 

最后,如果设置-l选项,HDMan会产生一个log文件,包含了一些汇总信息,比如每个源字典有多个音子,有多少个单词等等统计信息。设置-n选项的话,HDMan就会产生一个音子列表。在这个例子里,HDMan会记录每个音子的使用次数,以及哪些音子在发音字典中出现,但是在音子集合中缺没有它们。这有助于检测错误和删除字典中不期望出现的符号。

 

12.8词网络扩展

 

到目前为止,我们已经了解了词网络和发音字典,接着要介绍的就是如果将词网络扩展为(子词/音子)模型识别网络。如图12.1所示,这个扩展过程是由HNet自动完成的。默认情况下,HNet尝试根据字典的内容和音子模型列表来推理词网络的扩展。但是,有5个配置参数可以用来精确控制这个扩展过程:ALLOWCXTEXP, ALLOWXWRDEXP, FORCECXTEXP, FORCELEFTBI和FORCERIGHTBI。这个过程可以分为四步。

第一步,上下文定义

首先需要确定字典中的模型名称是如何组织的,以及是否构建“跨词扩展”。扫描字典每个不同的音子并给它们分类:

  1. 上下文无关的

这类音子会被跳过,当确立上下文时,例如短暂停(sp)。它一般被插入在每个单词发音的末尾,因为持续时间很短暂,所以在处理跨词的三音子模型时,跳过它们不做处理。

  1. 上下文独立的

这样的音子只存在上下文独立的形式,典型的是sil(静音)模型。HNet区别sil和sp的地方在于,两者都能出现在上下文独立的形式,而sil可以出现在其他音子的上下文中,而sp不可以。

  1. 上下文相关的

这一类音子看它是否出现在某音子的上下文部分,且该符号属于系统的音子集合。上下文相关音子受到模型扩展的支配的。

 

第二步,确定网络的类型

一般情况下,是要建立尽可能简化的网络。如果字典是封闭的(所有音子名称都属于音子HMM集合),那么并不需要太多的扩展,只需要直接用字典中的发音序列替换掉网络中的单词即可。但是如果字典不是封闭的,且词内上下文扩展后的模型都能在HMM集合中找到,那么就应用词内上下文扩展。否则,执行跨词的上下文扩展。

 

网络类型可以通过前面提到的配置环境参数来修改。默认情况下,ALLOWCXTEXP设置为true。如果它被设置为false,那么音子名称是没有扩展的,都对应到音子集合中。ALLOWXWRDEXP默认是false,以阻止跨词边界的上下文扩展。这样,只能对字典中的词内音子序列进行上下文绑定。如果FORCECXTEXP设为true,那么上下文扩展会被执行。例如,HMM集合包含了所有的单音子、双音子和三音子模型,那么给定一个单子序列构成的发音字典,那么Hnet默认情况下会生成单音子的识别网络,因为该发音字典是封闭的。但是如果FORCECXTEXP设为true,ALLOWXWRDEXP设为false,那么HNet会执行词内扩展。如果FORCECXTEXP设为true,ALLOWXWRDEXP设为true,那么会执行跨词的上下文扩展。

 

第三步,网络扩展

词网络中的每个词会被转换为一个以一系列节点。这个系列节点的尾部是叫“词尾”节点,它之前是该词对应在发音字典中的一系列发音音子构成的节点。对于跨词上下文扩展情形,初始和结尾的上下文音子直接复制下来,去来区分不同的上下文环境。每一个复制后的尾音子,都接着一个“尾词”节点。空节点只有“尾词”节点,而无前置的发音音子序列。

 

第四步,连接模型构成网络节点

每个模型节点都被连接到相应的HMM定义。HMM模型的名字是有相应的音子名称和它的上下文来决定的。对应的算法如下:

  1. 构造上下文依赖的模型名称,看它是否已经存在;
  2. 构造上下文独立的模型名称,看它是否已经存在。

如果环境变量ALLOWCXTEXP设置为false,那么(a)跳过,如果FORCECXTEXP设为true,(b)跳过。如果遇到不能匹配的模型,系统报错。当某音子的右边是词边界时,或者FORCELEFTBI设为true,上下文相关的模型名称则为左结合的二音子。也就是,设词的最后一个单音子名称为p,左边的音子为l,那么关于的p的上下文相关模型名称则为l-p。对称的,如果音子p是一个词的开始,也就是它的左边是边界,右边为音子r,那么p的上下文相关音子名称为p+r。其他情况,就是按照普通的上下文相关的三音子够造方法,其名称标记为l-p+r。上下文无关的音子在这一过程中直接跳过。下面举个例子说明是如何操作的。

有一个音子序列:

 

sil aa r sp y uw sp sil

 

依据上下文信息可以扩展为:

 

sil sil-aa+r aa-r+y sp r-y+uw y-uw+sil sp sil

 

其中sil是上下文问无关的(context-independent),而sp是上下文独立的(context-free)。对词内扩展系统来说,还可以通过变量CFWORDBOUNDARY进行控制。默认设置为true,上下文独立音子也被认为是词边界,因此

 

aa r sp y uw sp

 

可以被扩展为:

 

aa+r aa-r sp y+uw y-uw sp

 

如果设置变量CFWORDBOUNDARY为false:

 

aa+r aa-r+y sp r-y+uw y-uw sp

 

 

注意在实际应用中,步骤三和四是同时处理的,对第一个和最后一个上下文相关的模型,如果不同逻辑模型对应同一个物理模型会被合并。

 

已经就模型的扩展过程进行了细致的描述,结合几个简单的例子会对这一过程理解的更清晰。所以例子都是基于图12.2所示的Bit-But相关的词网络的。首先,假设发音字典包含单音子发音信息,也即:

 

bit            b i t

but           b u t

start        sil

end          sil

 

所有的单音子集合为:

 

b  i  t  u  sil

 

 

在这个例子中,输入到HNet中的是一个封闭的字典。因此并没有模型扩展,只是直接替换网络中的词,如图12.8所示。图中圆框图表示模型节点,方框图表示“词尾”节点。

 

相类似的,如果字典中包含词内三音子发音音子,如下所示:

 

bit            b+i b-i+t i-t

but           b+u b-u+t u-t

start        sil

end          sil

 

HMM音子集合包含所有的模型:

 

b+i  b-i+t  i-t b+u  b-u+t  u-t  sil

 

然后HNet根据封闭的字典实现识别模型网络,如图12.9所示。

 

如果字典里只包含单音子模型,而模型集包含完整的三音子模型,那么HNet将生产跨词的三音子扩展网络,如图12.10所示。

 

现在仍然假设字典中使用单音子模型,而模型集合中给出所有的单音子、双音子和三音子模型。这个例子中,默认情况下会产生图12.8所示的单音子识别网络。如果FORCECXTEXP为true,ALLOWXWRDEXP为false,那么产生词内扩展网络,如图12.9。如果FORCECXTEXP和ALLOWXWRDEXP都为true,那么将产生跨词的上下文相关网络,如图12.10。

 

12.9其他类型的识别网络

 

尽管HTK提供的模块主要目标是实现子词连接的语音识别网络,但是它还支持其他形式的识别系统。

 

为了建立音素识别器,一般通过SLF文件定义词级网络。但是这里的“词”是一个单独的音子。这个网络的结构图通常是一个循环,所有音子首尾相互连接。

 

这样的发音字典包含了系统涉及的所有词,并且给出它们的发音信息和词本身是一样的。例如下面所示:

 

ih     ih

eh   eh

ah   ah

... etc

 

 

音素识别器经常使用二元音子来提供上下文依赖的判别。前提是HMM集合中包含了所有必要的二元音子模型。如果FORCELEFTBI或者FORCERIGHTBI设置true,HNet将把简单的音子循环网络扩展为上下文相关的二元音子循环网络。

 

词识别器也是通过类似的方式建立的(以词为单位构建识别模型)。它与通过子词模型来构建识别网络类似,只是这里的发音字典的发音信息只有词本身而已,代替了子词发音序列模型。

 

最后,关键词识别系统是通过在词网络中平行放置关键词,还有些过滤模型。这里的关键词可以是词HMM模型或者子词模型。注意一点,这里的词转移惩罚值可以用来调节虚警率(false alarm rate)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值