Kaldi命令词识别

kaldi

刚刚拿到一个简单语料库练手,发现只有语音和对应文字, 这篇文章记录了从数据预处理到kaldi对数据进行训练和测试的全过程,这里首先训练单音节模型,其他模型后面再补充。

语料库处理

task 0: 观察语料库

语料库主要用于命令词识别,包括200个词汇,2000条语音,10个说话者分别对200个词汇进行录音。语音目录以说话者id标识:

$ tree -d
├── speaker001
├── speaker002
├── speaker003
├── speaker004
├── speaker005
├── speaker006
├── speaker007
├── speaker008
├── speaker009
├── speaker010
└── Levoice.list

每个说话者文件夹目录下包含对应的200条语音:

└┤ tree speaker001
speaker001
├── 00001.wav
├── 00002.wav
├── 00003.wav
...
└── 00200.wav

语音文字说明文件Levoice.list 格式为<语音id> <文字> <录音时长>,例如:

└┤ head -n 2 Levoice.list 
speaker001/00001.wav    三六零通讯录  5.6
speaker001/00002.wav    三六五日历   2.8

语料库所给的资源应用到kaldi还需要汉字发音词典,这里只能自己准备,下面会参考thchs30语料库的词典准备自己的词典。

task 1: 预处理语料库

为方便后续操作,需要对语料库文件进行预处理,这部分包括:

  • 重新重命名语音文件,使2000个语音文件具有唯一标识(speakerid_voiceid.wav)
  • 划分训练、测试、验证数据集
  • 根据Levoice.list生成utt2words.txt ,进行文件名对应汉字映射。

上述过程脚本(注意rname命令在Ubuntu和Centos中有细微差别):

 !/bin/bash

#if need cv or not
needcv=true

# rename wav files by add prefix by "speaker"
start_path=`pwd`
for dirname in $(ls | grep "speaker")
do
    #get first filename
    filename=$(ls $dirname | head -n 1)
    if [[ $filename =~ "speaker" ]]; then
        echo "files in $dirname have already renamed, passing..."
    else
        echo "now rename flies with prefix speakers"
        echo $dirname
        cd $dirname
        #in centos rename
        rename "00" $dirname"_00" "00"*
        # ubuntu using follows
        #rename "s/00/$dirname""_00/" 00*
        cd ..
    fi
done

# devide file to train, cv and test
cd $start_path
rm -rf  test train cv  && mkdir test train cv

i=1
for dirname in $(ls | grep "speaker")
do
    if [ $i -lt 9 ];then
        cp $dirname/* train
    else
        cp $dirname/* test
    fi
    let i=$i+1

done

function rand(){
    min=$1
    max=$(($2-$min+1))
    num=$(($RANDOM+1000000000))
    echo $(($num%$max+$min))  
}

count=0
array=("0" "0" "0" "0")
#ls -al train
if [ needcv ]; then
    for file in $(ls train | grep "speak")
    do
        array[$count]=$file
        let count=$count+1
        if [ $count -eq 4 ];then
            rnd=$(rand 0 3)
            mv train/${array[$rnd]} cv
            #echo ${array[$rnd]}
            let count=0
        fi
    done
    echo "cv files prepared over, examples number is $(ls cv | wc -l)"
fi
echo "train files number is $(ls train | wc -l)"
echo "test files number is $(ls test | wc -l)"

语料库对训练集、验证集、测试集参考thchs30,这里将说话人9、10语音作为测试集,再从1-8语音集中的1600百条语音文件四条语音为组随机选择一条语音归入验证集,剩下的作为训练集。划分结果训练集、验证集、测试集比例6:2:2。

在语料库目录运行上脚本,会在该目录下产生trian、test和cv目录,这些目录及文件将被后面使用。

最后直接将Levoice.list中的信息进行简单字符替换即可:

speaker001/00001.wav    三六零通讯录  5.6
---->
speaker001_00001.wav    三六零通讯录  5.6

可以在vi或其他编辑器中替换即可。

应用Kaldi

task0 : 构建kaldi项目结构

参照其他项目,首先复制创建项目结构目录,配置文件以及项目需要使用的依赖工具,这里多参考thchs30部分结构。在egs 目录下建立/wakeup/s5作为项目目录,在该目录下准备以下文件:

$ tree -L 1
|-- cmd.sh // 运行配置目录
|-- conf  // 配置文件目录
|-- local //存放run.sh 中调用的脚本工具,需要自己编写
|-- path.sh //Kaldi 工具和库目录添加到PATH
|-- run.sh // top层脚本,运行该脚本训练数据和测试, 需要自己编写
|-- steps // kaldi 脚本工具, 复制到工程目录下
|-- tools // kaldi 脚本工具, 复制到工程目录下
`-- utils // kaldi 脚本工具, 复制到工程目录下

这里cmd.sh里根据自己运行方式配置运行参数,这里配置成单机运行

export train_cmd=run.pl
export decode_cmd="run.pl --mem 4G"
export mkgraph_cmd="run.pl --mem 8G"

conf 目录包含一些配置文件,这里主要将系统采样频率与语料库的采样频率设置为一致:

$ ls
decode_dnn.config  fbank.conf  mfcc.conf
$ more mfcc.conf 
--use-energy=false   # only non-default option.
--sample-frequency=8000
$ more decode_dnn.config 
beam=18.0 # beam for decoding.  Was 13.0 in the scripts.
lattice_beam=10.0 # this has most effect on size of the lattices.
$ more fbank.conf 
--sample-frequency=8000
--num-mel-bins=40

task1 : 准备训练文件

参照kaldi数据准备部分文档,该部分需要自己根据语料库分别就train,test,cross validation目录生成以下文件:

  • text : < uttid > < word >
  • wav.scp : < uttid > < utter_file_path >
  • utt2spk : < uttid > < speakid >
  • spk2utt : < speakid > < uttid >
  • word.txt : 同 text

编写local/data_pre.sh脚本供run.sh调用(下面会涉及run.sh脚本的编写),传入参数运行目录以及语料库目录:

#!/bin/bash
# 2017-3-23 by zqh 

# This file prepares files needed in kaldi
# including text, wav.scp, utt2spk, spk2utt
# output: 
#   data/train dir include infomation of train data
#   data/test dir include infomation of test data
#   data/cv dir include infomation of cross validation data

run_dir=$1
dataset_dir=$2

cd $run_dir
echo "prepare data in data/{train, test, cv}"
mkdir -p data/{train,test,cv}

#create text, wav.scp, utt2spk, spk2utt
(
i=0
for dir in train cv test; do
    echo "clean dir data/$dir"
    cd $run_dir/data/$dir
    rm -rf wav.scp utt2spk spk2utt word.txt text  
    #phone.txt
    for data in $(find $dataset_dir/$dir/*.wav | sort -u | xargs -i basename {} .wav);do
        let i=$i+1
        spkid=$(echo $data | awk -F"_" '{print "" $1}')
        uttid=$data
        echo $uttid $dataset_dir/$dir/$data.wav >> wav.scp
        echo $uttid $spkid >> utt2spk
        # gen word.txt
        echo $uttid $(cat $dataset_dir/utt2word.txt | grep $uttid | awk '{print "" $2}') >> word.txt
        # gen phone.txt TODO
    done
    cp word.txt text
    sort wav.scp -o wav.scp
    sort utt2spk -o utt2spk
    sort text -o text
    # sort phone.txt -o phone.txt
done
echo "all file number is $i"
) || exit 1

utils/utt2spk_to_spk2utt.pl data/train/utt2spk > data/train/spk2utt
utils/utt2spk_to_spk2utt.pl data/cv/utt2spk > data/cv/spk2utt
utils/utt2spk_to_spk2utt.pl data/test/utt2spk > data/test/spk2utt

task2 : 训练语言模型

由于这里仅仅需要对语料库中的200个命令词进行识别,大而全的汉语词典并不必要,这里需要根据自己的语料建立词典并且生成语言模型。

task 2.1 : 准备词典

根据kaldi的要求,需要准备的词典包括以下文件(我这里和语料库放在同个目录下,后面kaldi从该目录下读取):

[username@hostname dict]$ pwd
/home/username/dataset_wakeup/resource/dict
[username@hostname dict]$ ls
extra_questions.txt  lexiconp.txt  lexicon.txt  nonsilence_phones.txt  optional_silence.txt  silence_phones.txt

对上面文件简单说明:

  • lexicon.txt: 词典,包括语料中涉及的词汇与发音,与单字及其发音。
  • silence_phones.txt:静音标识,这里为sil。
  • nonsilence_phones.txt : 非静音标识,与silence_phones.txt共同组成lexicon.txt中的发音。
  • extra_questions.txt : 包含重音音调标记,这里没有用到
  • lexiconp.txt : 如果一个词有不同发音,则会在不同行中出现多次。如果你想使用发音概率,你需要建立 exiconp.txt 而不是 lexicon.txt,这里未使用

以上文件可以参考复制thchs30的resource资源,只要替换lexicon.txt为自己的字典,并且追加thchs30中lexicon.txt中所有的单字及其发音(简单awk命令即可)。此外该语料库仅仅提供了汉字无对应发音,需要自己参考thchs30中的词典准备,(心想只有200条,觉得手打的会很快,事实用了2-3个小时,心累,回头想可以写程序完成)。
lexicon.txt 文件内容大致为:

$ more lexicon.txt 
SIL sil
<SPOKEN_NOISE> sil
三六零通讯录 s an1 l iu4 l ing2 t ong1 x vn4 l u4
三六五日历 s an1 l iu4 uu u3 r iz4 l i4
三D图库    s an1 d i4 t u2 k u4
task 2.2: 生成语言模型

语言模型训练需要使用n-gram算法,借助sirlm工具可以简单实现,并进行语言模型生成:

安装
  • 下载sirlm安装包(官网下载速慢,也可通过在github上找到相应资源下载),解压后进入最上层目录进行安装。
  • export SRILM=pwd
  • make
  • 把$make_dir/bin/i686-m64/加入PATH以便使用其中脚本
生成语言模型

在语料库目录下创建lm_word文件夹(方便管理),复制上面的字典lexicon.txt,并删除前两行,保存为作为words.txt作语料输入文件进行n-gram语言模型生成(由于只是词汇识别设置n=1):

ngram-count -order 1 -text words.txt -lm word.arpa

其他参数可以参考:

-order  指定n-gram的n是多少,默认是3
-text   提供输入的语料文件,统计该语料中的n-gram
-lm     指定输出的lm文件
-vocab  用来指定对哪些词进行n-gram统计
-wbdiscount1 表示1gram Witten-Bell discounting 
Note:参数顺序无所谓

该命令生成arpa格式的语言模型文件,后面由kaldi的其他工具转换为FST格式使用。

完成语言模型的生成后,对应的可以在run.sh脚本中利用该部分的语言模型,通过kaldi提供的工具构建语言模型的FST格式文件,这部分 主要创建了data/{dict,lang,graph}目录及相应文件,并在后面的构建解码图的过程中使用。run.sh脚本该部分代码:

#gen lang dir 
(
    echo "create new dir data/dict,lang,graph"
    cd $run_path
    mkdir -p data/{dict,lang,graph} && \
    cp $dataset//resource/dict/{extra_questions.txt,nonsilence_phones.txt,optional_silence.txt,silence_phones.txt} data/dict && \
    cat $dataset/resource/dict/lexicon.txt | \
    grep -v '<s>' | grep -v '</s>' | sort -u > data/dict/lexicon.txt || exit 1;
    utils/prepare_lang.sh --position_dependent_phones false data/dict "<SPOKEN_NOISE>" data/local/lang data/lang || exit 1;
    gzip -c $dataset/King-ASR-M-005/lm_word/word.arpa > data/graph/word.arpa.gz || exit 1;
    utils/format_lm.sh data/lang data/graph/word.arpa.gz $dataset/King-ASR-M-005/lm_word/lexicon.txt data/graph/lang || exit 1;
)

这里主要包括utils/prepare_lang.sh 、 和utils/format_lm.sh 两个脚本的调用,不作具体分析。

考虑篇幅,未完待续,原创文章,转载注明出处。原文到博客
更多关注公众号:

wechat

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,386评论 25 707
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,118评论 9 467
  • 好奇的我 我是一个好奇的孩纸,因为看见好多的订阅号啊,公众号啊,所以,想自己为什么不可以试试呢?好奇其实不可怕。 ...
    龙七七阅读 231评论 0 0
  • 01 之所以会经常想起小学时光,是因为每年冬天,我那异常肥厚粗壮的右手食指都要疼上几天,那几天伸不敢伸,握不敢握,...
    善下归海阅读 316评论 0 6
  • 光影间 楼上的红漆掉了几片 远处你的身影渐行渐远 打开你的世界 是所有的期盼 我只想 将飞过的小叶折成两半 一片我...
    画者eic阅读 125评论 0 0