参考文档:http://www.cnblogs.com/welen/p/7485151.html
写在前面,本文虽然对大多数脚本进行了解释,但只是初学者的理解,如果你认为读起来不知所云,建议从 kaldi 官方文档 读起,两边配合理解,可以解决很多看起来好像很难理解的东西。(官方文档地址: http://www.kaldi-asr.org/doc/data_prep.html )
今天开始了kaldi脚本的学习,首先从kaldi 最简单的demo开始。
路径: kaldi-trunk/egs/yesno/s5
运行: ./run.sh
目前代码:
1 #!/bin/bash
2
3 #====
4 # run.sh 的代码会展现清晰的语音处理的过程,目前学习到 数据准备阶段的 prepare_data.sh
5 #====
6
7 train_cmd="utils/run.pl"
8 decode_cmd="utils/run.pl"
9
10 #====
11 #waves_yesno 用于存储语音文件,检测到本目录不存在,就去指定链接下载语音包
12 #====
13 if [ ! -d waves_yesno ]; then
14 wget http://www.openslr.org/resources/1/waves_yesno.tar.gz || exit 1;
15 # was:
16 # wget http://sourceforge.net/projects/kaldi/files/waves_yesno.tar.gz || exit 1;
17 tar -xvzf waves_yesno.tar.gz || exit 1;
18 fi
19
20
21 train_yesno=train_yesno
22 test_base_name=test_yesno
23
24 #====
25 # -r :rm 命令参数,递归删除
26 # -f :rm 命令参数,递归删除
27 # but I don not know why remove them
28 #====
29 rm -rf data exp mfcc
30
31 # Data preparation
32
33 #====
34 # 目前只看到这里
35 #====
36 local/prepare_data.sh waves_yesno
1. local/prepare_data.sh
#!/bin/bash #egs/yesno/s5/local/prepare_data.sh #==== # -p means make all dirs if there is no such dir #==== mkdir -p data/local local=`pwd`/local scripts=`pwd`/scripts export PATH=$PATH:`pwd`/../../../tools/irstlm/bin echo "Preparing train and test data" #==== # $1 : shell 命令,指的是文件运行时传进来的第一个参数,自然 $2,$3就指的是第2,3个命令行参数 # 这里 $1 = waves_yesno #==== train_base_name=train_yesno test_base_name=test_yesno waves_dir=$1 #==== # ls -1 : 所有的文件和目录都单行显示,每个文件或者目录占一行 # > : 指的是重定向输出,后面跟的就是输出到哪一个文件。具体可以查找Linux重定向相关内容
# 形式: ls [option] > outputfile # 这里是把刚才下载的语音包的所有文件名都提取出来,写进data/local/waves_all.list # 目的是为了保存语音文件的文件名,后面建立语音ID与语音文本的对应还会用到这些文件名 #==== ls -1 $waves_dir > data/local/waves_all.list #==== # 下面几行代码创建的文件(比如waves.test waves.train)都在这个文件夹下面 #==== cd data/local. #==== # waves_all.list 的文件名被分成了两个部分, # 一部分放进了waves.test,用于测试 # 一部分放进了waves.train,用于训练
# 具体代码在下一小节 #==== ../../local/create_yesno_waves_test_train.pl waves_all.list waves.test waves.train #==== # ../../local/create_yesno_wav_scp.pl waves_yesno waves.test > test_yesno_wav.scp # test_yesno_wav.scp 保存的是语音ID 以及 对应的语音文件名 # 就像这样 : 0_0_0_0_1_1_1_1 waves_yesno/0_0_0_0_1_1_1_1.wav # > : 重定向输出。 # ../../local/create_yesno_wav_scp.pl 里面有一个输出语句,输出语句的输出内容被重定向输出到了test_yesno_wav.scp #==== ../../local/create_yesno_wav_scp.pl ${waves_dir} waves.test > ${test_base_name}_wav.scp ../../local/create_yesno_wav_scp.pl ${waves_dir} waves.train > ${train_base_name}_wav.scp #==== # test_yesno.txt 保存的是语音ID以及语音文本的对应关系 # 像这样 : 0_0_1_1_1_1_0_0 NO NO YES YES YES YES NO NO #==== ../../local/create_yesno_txt.pl waves.test > ${test_base_name}.txt ../../local/create_yesno_txt.pl waves.train > ${train_base_name}.txt cp ../../input/task.arpabo lm_tg.arpa cd ../.. # This stage was copied from WSJ example #==== # 大概就是生成了两个文件 # spk2utt 发音人 - 发音id 对应关系 # utt2spk 发音id - 发音人 对应关系 #==== for x in train_yesno test_yesno; do mkdir -p data/$x cp data/local/${x}_wav.scp data/$x/wav.scp cp data/local/$x.txt data/$x/text cat data/$x/text | awk '{printf("%s global ", $1);}' > data/$x/utt2spk utils/utt2spk_to_spk2utt.pl <data/$x/utt2spk >data/$x/spk2utt do
至此,prepare_data.sh 代码阅读完毕
目录结构是这样的:
目录结构如下:(来自WELEN的博客)
data
├───train_yesno 训练文件夹
│ ├───text (发音
id
发音文本)
│ ├───utt2spk (发音
id
发音人)
│ ├───spk2utt (发音人 发音
id
)
│ └───wav.scp (发音
id
发音文件)
└───test_yesno
├───text
├───utt2spk
├───spk2utt
└───wav.scp
1 #!/usr/bin/env perl 2 3 #==== 4 # ARGV[]:perl 的命令行参数数组 5 # 根据prepare_data.sh 知道三个参数分别是 6 # waves_all.list , waves.test , waves.train 7 #==== 8 $full_list = $ARGV[0]; 9 $test_list = $ARGV[1]; 10 $train_list = $ARGV[2]; 11 12 #==== 13 # open : perl文件操作函数, 文件操作都是通过文件句柄 14 # 形式: open FILEHANDLE FILENAME 15 # <filehandle> 是文件信息读取运算符,按行读取 16 # 这个循环得到了语音文件的总数,为下一步把文件名分成两部分做准备 17 #==== 18 open FL, $full_list; 19 $nol = 0; 20 while ($l = <FL>) 21 { 22 $nol++; 23 } 24 close FL; 25 26 #==== 27 # > : open 函数中对文件的操作方式,具体可以查找网上 perl open函数 相关内容 28 # chomp: 大概的作用就是去掉当前行的末尾的换行符 29 #==== 30 $i = 0; 31 open FL, $full_list; 32 open TESTLIST, ">$test_list"; 33 open TRAINLIST, ">$train_list"; 34 #==== 35 # this while(){} shows what this .pl file do: 36 # divide all data into two part, 37 # first part will put in waves.train 38 # the second 30 files' name will put in waves.test 39 #==== 40 while ($l = <FL>) 41 { 42 chomp($l); 43 $i++; 44 if ($i <= $nol/2 ) 45 { 46 print TRAINLIST "$l "; 47 } 48 else 49 { 50 print TESTLIST "$l "; 51 } 52 }
3. create_yesno_wav_scp.pl
#!/usr/bin/env perl #==== # $ARGV[0] = waves_yesno # ARGV[1] = waves.test #==== $waves_dir = $ARGV[0]; $in_list = $ARGV[1]; open IL, $in_list; #==== # 把语音ID-语音文件的对应关系输出到 对应文件 # final output like this : 0_0_0_0_1_1_1_1 waves_yesno/0_0_0_0_1_1_1_1.wav # line 2: # "." :应该只是连接符号,但是不确定,没有查到 # “/”: 是转义字符,所以full_path应该是这样的:waves_yesno/0_0_0_0_1_1_1_1.wav # line 3: =~ s/// 是perl的替换符 # 形式: =~ s/pattern/replacement/[option] # 具体可以查看网上 perl s/// 操作符的内容 # line 4: print strings will rewrite into test_yesno_wav.scp which have been gave in prepare_data.sh #==== while ($l = <IL>) { chomp($l); $full_path = $waves_dir . "/" . $l; $l =~ s/.wav//; print "$l $full_path "; }
4.create_yesno_txt.pl
#!/usr/bin/env perl #==== # ARGV[0] = waves.test # in_list = waves.test #==== $in_list = $ARGV[0]; open IL, $in_list; #==== # here will replace all 0 as NO ,all 1 as YES, all — as sapce # print will rewrite into test_yesno.txt which has been gave in prepare_data.sh # g : option of =~ s/patterns/replacement/[option] # replace all patterns in string as replacement #==== while ($l = <IL>) { chomp($l); $l =~ s/.wav//; $trans = $l; $trans =~ s/0/NO/g; $trans =~ s/1/YES/g; $trans =~ s/\_/ /g; print "$l $trans "; }
5. 阶段性小结
5.1
综上,数据准备阶段的 prepare_data.sh 的工作,主要集中在 egs/yesyno/s5/data 目录,完成的工作基本上可以看做是数据标注,将语音包分成两部分,一部分用于训练,一部分用于测试训练的模型。
比如 text 文件,就将语音进行了标注,指出每一个语音对应的文字内容。wav.scp 就标注出了语音对应的语音文件。
5.2
数据准备阶段完成的工作包括,
下载语音包,
建立必要的工作目录以及文件,
包括训练使用的目录,
建立的文件用于建立对应关系便于训练
发音ID - 发音文本
发音ID - 发音文件
发音ID - 发音人
发音人 - 发音ID
包括测试使用的目录,
建立的文件用于建立对应关系便于测试,检查测试结果
发音ID - 发音文本
发音ID - 发音文件
发音ID - 发音人
发音人 - 发音ID