本节使用HTK来完成一个拨号系统,虽然拨号系统本身非常简单,但是它却是一个通用的模型,能够帮助我们迅速掌握HTK的用法。
为了完成该系统,需要了解如何进行准备以下的操作:
1. 去相关变换的用法;
2. 大词汇量解码器的使用;
3. 识别训练;
4. 说话人适配;
5. 学习使用循环神经网络做语言模型;
6. 学习用深度神经网络做HMM状态分布的估计和语音特征的产生;
* * * * *
### 第一部分 准备识别任务的语法
我们所要构建的拨号系统应该能够识别如下的命令,例如
* Dial three three two six five four
* Dial nine zero four one oh nine
* Phone Woodland
* Call Steve Young
我们用一个EBNF来表示上面语言的语法
~~~
$digit = ONE | TWO | THREE | FOUR | FIVE |
SIX | SEVEN | EIGHT | NINE | OH | ZERO;
$name = [ JOOP ] JANSEN |
[ JULIAN ] ODELL |
[ DAVE ] OLLASON |
[ PHIL ] WOODLAND |
[ STEVE ] YOUNG;
( SENT-START ( DIAL <$digit> | (PHONE|CALL) $name) SENT-END )
~~~
该语法表示你可以说dail+电话号码,也可以说phone/call+人名。该EBNF对应的词网络如下图:

**利用HPrase,我们可以完成将EBNF转化为词网络的任务**,输出的词网络以SLF规定的形式表示并存储。
具体的,我们将语法文件存储在`worksource/grammar.ebnf`中,然后执行如下命令:
~~~bash
bin.cpu/HParse worksource/grammar.ebnf worksource/wordnet.slf
~~~
查看`worksource/wordnet.slf`文件,其中的内容如下:
~~~
VERSION=1.0
N=31 L=62
I=0 W=SENT-END
I=1 W=YOUNG
I=2 W=!NULL
I=3 W=STEVE
······
I=28 W=SENT-START
I=29 W=!NULL
I=30 W=!NULL
J=0 S=2 E=0
J=1 S=16 E=0
······
J=60 S=30 E=28
J=61 S=0 E=29
~~~
该文件第一行声明了文件的版本,第二行`N=31 L=62`表示该文件所描述的图中包含**31个节点,62条弧**,接下来的部分描述节点含义,例如`I=0 W=SENT-END`表示**编号为0的节点表示的意义是SENT-END**,最后一部分描述弧的信息,例如`J=0 S=2 E=0`表示**编号为0的弧,起点是编号为2的节点,终点是编号为0的节点**。
* * * * *
### 第二部分 准备词典
建立词典的主要任务是收集所有需要用到的单词和发音。HTK所需要的发音词典的内容如下所示:

每一行是一个单词的发音,第一个项表示单词,方括号是可选的,表示当遇到该单词的时候,已经产生的句子需要被输出,最后的一串是该单词的发音。
我们需要找到发音词典来查找需要用到的词的信息,并将其组织为上述形式。
这里我们先介绍一个工具HDMan,使用它可以方便地将多个发音词典合成为一个发音词典。其最基本的用法如下:
~~~bash
HDMan out in1 in2 ···
~~~
该命令的作用是将词典in1,in2等合并为一个词典out。另外以下参数对我们来说是非常有用的:
* 参数-w wlist可以使输出词典中只包含wlist(下文写作word list)中列出的单词,wlist的单词必须按词典序排列。
* 参数-m 则会把一个单词的所有发音全部输出,默认情况下HDMan命令只会输出第一个发音。
**了解了任务的目标,我们接下来完成项目所需的词典的创建。**
在worksource中创建文件夹dict,根据HTKBook的要求,我们从[这里](ftp://svr-ftp.eng.cam.ac.uk/pub/comp.speech/dictionaries/beep.tar.gz)下载beep词典,将解压后的内容放到dict中,此时dict中的内容如图所示:

词典的主要内容是两个文件case.txt和beep-1.0,其中beep-1.0中包括了单词的发音。
利用Shell命令,我们可以从词网络文件生成word list,具体地,在`~/htk/worksource/dict`目录下执行如下命令:
~~~bash
cat ../wordnet.slf | grep W= | sed 's/.*W=\(.*\)/\1/g'| sort -u > wlist
~~~
就可以在此目录下生成名为wlist的word list文件。
另外,beep词典是由许多组织共同编写的,其中的单词表示形式不同,而HDMan要求的词典必须按照字典序排列,使用如下命令从beep中生成符合输入要求的词典simple_beep,simple_beep中包含所有全部由大写字母组成的单词。
~~~
cat beep/beep-1.0 | grep -E ^[A-Z]+[[:space:]] | sort -t $' ' -k 1,1 -c > simple_beep
~~~
根据HDMan的要求,还需要再执行HDMan的目录下编辑global.ded的脚本文件,来空时输出词典的格式,内容如下:
~~~
AS sp
RS cmu
MP sil sil sp
~~~
使用HDMan命令就可以生成需要的词典。命令如下
~~~
../../bin.cpu/HDMan -w wlist special_dict simple_beep
~~~
最后向special_dict中添加缺失的数据如下:

* * * * *
### 第三部分 准备录音数据
利用HSLab,可以录取音频文件,运行命令`.\HSLab.exe noname`可以创建一个新的音频文件,而运行命令`.\HSLab.exe file`可以打开名为file的音频文件。HSLab的主界面如下:

为了方便数据的录制,HTK另外提供了工具HSGen,用于按照词网络的规则随机地产生句子。用法如下:
~~~bash
HSGen [options] wdnet dictfile
~~~
其中option支持如下选项:
* -l 给每个句子添加行号;
* -n N 一共产生N个句子;
* -q 抑制输出;
* -s 计算词网络的特征;
执行命令
~~~
HSGen -l -n 10 wordnet.slf special_dict > textprompts
~~~
就可以生成10句样本文本,其中的内容如下(该内容是随机的):

整体的获取音频数据的流程如下图:

* * * * *
### 第四部分 获取抄本文件
为了方便使用,我们还需要将上述句子文件`textprompts`转化为MLF文件,HTK提供了一个脚本将上述文件转化为MLF文件,脚本的位置在simples/HTKTutorial下,名为prompts2mlf,用法如下:
~~~bash
./prompts2mlf text.mlf textprompts
~~~
生成的`text.mlf`文件的内容如下:

紧接着,还需要用工具`HLEd`将词级别的mlf文件转换为音素级别的mlf文件,`HLEd`也支持编辑脚本文件来对音素做一些处理,这里需要创建脚本`mkphones0.led`,内容如下(*该文件的作用后面再详细解释*)

然后执行如下命令就可以产生音素级别的mlf文件(*参数之后再详细解释*):
~~~bash
HLEd -l '*' -d special_dict -i phones0.mlf mkphones0.led text.mlf
~~~
生成的phones0.mlf文件如下:

* * * * *
### 第五部分 提取音频的特征
最后,我们还需要将录制的数据转化为音频特征文件,HTK提供了`HCopy`工具来帮助我们完成任务,`HCopy`根据所提供的配置文件和一组列表文件来完成音频特征的提取过程。
为了完成这个过程,我需要首先准备一个列表文件,文件的内容结构如下:

每一行是一个音频文件的名称和特征文件的名称,中间用空格隔开。
还需要准备一个配置文件,用来指示HCopy如何提取特征,HTKBook给出的范例如下(*具体参数的意义后面再解释*):
~~~
# Coding parameters
TARGETKIND = MFCC_0
TARGETRATE = 100000.0
SAVECOMPRESSED = T
SAVEWITHCRC = T
WINDOWSIZE = 250000.0
USEHAMMING = T
PREEMCOEF = 0.97
NUMCHANS = 26
CEPLIFTER = 22
NUMCEPS = 12
ENORMALISE = F
~~~
**现在**我们来对一个音频文件simple.wav(!**需要说明的是,这里的simple.wav文件仅仅是后缀名是wav,而非标准的windows平台的wav文件,它实际上是由HSLab录制的声音文件,一般扩展名写作.sig**)进行特征提取,这里假设文件就存放在执行命令的文件夹下,因此编写waves.scp文件内容如下
~~~txt
simple.wav simple.mfc
~~~
然后将上述配置文件存储为`config`文件。
接着可以运行如下命令:
~~~bash
HCopy -T 1 -C config -S waves.scp
~~~
执行成功后得到如下输出:

至此数据准备工作就完成了。
* * * * *