零、SRILM安装与使用 srilm的安装与使用(标贝科技)
SRILM安装教程
kaldi/tools/install_srilm.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 # !/usr/bin/env bash current_path=`pwd` current_dir=`basename "$current_path"` if [ "tools" != "$current_dir" ]; then echo "You should run this script in tools/ directory!!" exit 1 fi if [ ! -d liblbfgs-1.10 ]; then echo Installing libLBFGS library to support MaxEnt LMs bash extras/install_liblbfgs.sh || exit 1 fi ! command -v gawk > /dev/null && \ echo "GNU awk is not installed so SRILM will probably not work correctly: refusing to install" && exit 1; if [ $# -ne 3 ]; then echo "SRILM download requires some information about you" echo echo "Usage: $0 <name> <organization> <email>" exit 1 fi srilm_url="http://www.speech.sri.com/projects/srilm/srilm_download.php" post_data="WWW_file=srilm-1.7.3.tar.gz&WWW_name=$1&WWW_org=$2&WWW_email=$3" if ! wget --post-data "$post_data" -O ./srilm.tar.gz "$srilm_url"; then echo 'There was a problem downloading the file.' echo 'Check you internet connection and try again.' exit 1 fi mkdir -p srilm cd srilm if [ -f ../srilm.tgz ]; then tar -xvzf ../srilm.tgz # Old SRILM format elif [ -f ../srilm.tar.gz ]; then tar -xvzf ../srilm.tar.gz # Changed format type from tgz to tar.gz fi major=`gawk -F. '{ print $1 }' RELEASE` minor=`gawk -F. '{ print $2 }' RELEASE` micro=`gawk -F. '{ print $3 }' RELEASE` if [ $major -le 1 ] && [ $minor -le 7 ] && [ $micro -le 1 ]; then echo "Detected version 1.7.1 or earlier. Applying patch." patch -p0 < ../extras/srilm.patch fi# set the SRILM variable in the top-level Makefile to this directory. cp Makefile tmpf cat tmpf | gawk -v pwd=`pwd` '/SRILM =/{printf("SRILM = %s\n", pwd); next;} {print;}' \ > Makefile || exit 1 rm tmpf mtype=`sbin/machine-type` echo HAVE_LIBLBFGS=1 >> common/Makefile.machine.$mtype grep ADDITIONAL_INCLUDES common/Makefile.machine.$mtype | \ sed 's|$| -I$(SRILM)/../liblbfgs-1.10/include|' \ >> common/Makefile.machine.$mtype grep ADDITIONAL_LDFLAGS common/Makefile.machine.$mtype | \ sed 's|$| -L$(SRILM)/../liblbfgs-1.10/lib/ -Wl,-rpath -Wl,$(SRILM)/../liblbfgs-1.10/lib/|' \ >> common/Makefile.machine.$mtype make || exit cd .. ( [ ! -z "${SRILM}" ] && \ echo >&2 "SRILM variable is aleady defined. Undefining..." && \ unset SRILM [ -f ./env.sh ] && . ./env.sh [ ! -z "${SRILM}" ] && \ echo >&2 "SRILM config is already in env.sh" && exit wd=`pwd` wd=`readlink -f $wd || pwd` echo "export SRILM=$wd/srilm" dirs="\${PATH}" for directory in $(cd srilm && find bin -type d ) ; do dirs="$dirs:\${SRILM}/$directory" done echo "export PATH=$dirs" ) >> env.sh echo >&2 "Installation of SRILM finished successfully" echo >&2 "Please source the tools/env.sh in your path.sh to enable it"
一、准备 srilm是一个语言模型训练工具,SRILM的主要目标是支持语言模型的估计和评测。估计是从训练数据(训练集)中得到一个模型,包括最大似然估计及相应的平滑算法;而评测则是从测试集中计算其困惑度。其最基础和最核心的模块是n-gram模块,这也是最早实现的模块,包括两个工 具:ngram-count和ngram,相应的被用来估计语言模型和计算语言模型的困惑度。
在训练模型之前需要对文本数据进行处理,得到分好词的文本数据 。
同时,我们还需要准备一个词典lexicon.txt,大家可以自行建立自己的词典或者获取其他已经建立好的词典作为lexicon。词典在这里的作用是我们在训练模型之前需要对文本数据中出现的词进行一个统计。统计每一个词在文本中出现的频率。
然后我们还需要测试数据集,我们需要准备一些测试数据集用于测试我们训练的模型性能。
二、词频统计 在得到了分好词的文本后,需要对文本中的每个词进行一个词频统计,具体的步骤如下:
1.处理输入文本:将输入文本(分好词)中没有出现在lexicon中的词替换成,然后生成文件text.no_oov。生成的文本大概如下图。
实现这一步所需要的shell代码:$text
代表我们的文本数据的路径,$lexicon
是词典的路径。
1 2 cat $text | awk -v lex=$lexicon 'BEGIN{while((getline<lex) >0){ seen[$1]=1; } } {for(n=1; n<=NF;n++) { if (seen[$n]) { printf("%s ", $n); } else {printf("<UNK> ");} } printf("\n");}' > $cleantext || exit 1;
2.统计text.no_oov (cleantxt)中出现的词的词频并按词频降序排列,生成word.counts。结果如下图:
从图中可以看到第一列为我们的词频,第二列为文本中所有出现过的词。都已经按照降序排好顺序了。代码如下,这步我指定了一个临时文件夹$tmp, 用于存储中间过程的临时文件,使用之前需要创建。
1 cat $cleantext | awk '{for(n=1;n<=NF;n++) print $n; }' | sort -T $tmp | uniq -c | sort -T $tmp -nr > $dir/word.counts || exit 1;
3.把text.no_oov(cleantext)中的所有词和lexicon中的非静音词合并,统计词频,按照词频降序排列,生成unigram.counts。
1 cat $cleantext | awk '{for(n=1;n<=NF;n++) print $n; }' | cat - <(grep -w -v '!SIL' $lexicon | awk '{print $1}') | sort -T $tmp | uniq -c | sort -nr > $dir/unigram.counts || exit 1;
4.用kaldi/tools/kaldi_lm中的get_word_map.pl工具将unigram.counts中的词改成一种简短的形式,生成word_map
1 cat $dir/unigram.counts | awk '{print $2}' | kaldi/tools/kaldi_lm/get_word_map.pl "<s>" "</s>" "<UNK>" > $dir/word_map || exit 1;
至此我们就得到了word_map,可以用它来进行ngram语言模型的训练了。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # !/usr/bin/env bash text=./data/train/word.txt lexicon=./data/train/lexicon.txt dir=srilm mkdir $dir cleantext=$dir/text.no_oov cat $text | awk -v lex=$lexicon 'BEGIN{while((getline<lex) >0){ seen[$1]=1; } } {for(n=1; n<=NF;n++) { if (seen[$n]) { printf("%s ", $n); } else {printf("<UNK> ");} } printf("\n");}' > $cleantext || exit 1; cat $cleantext | awk '{for(n=2;n<=NF;n++) print $n; }' | sort | uniq -c | sort -nr > $dir/word.counts || exit 1; cat $cleantext | awk '{for(n=2;n<=NF;n++) print $n; }' | \ cat - <(grep -w -v '!SIL' $lexicon | awk '{print $1}') | \ sort | uniq -c | sort -nr > $dir/unigram.counts || exit 1; cat $dir/unigram.counts | awk '{print $2}' | get_word_map.pl "<s>" "</s>" "<UNK>" > $dir/word_map || exit 1; cat $cleantext | awk -v wmap=$dir/word_map 'BEGIN{while((getline<wmap)>0)map[$1]=$2;} { for(n=2;n<=NF;n++) { printf map[$n]; if(n<NF){ printf " "; } else { print ""; }}}' | gzip -c >$dir/train.gz \ || exit 1;
三、训练
详情代码见github:https://github.com/baixf-xyz/ASR_work/tree/master/07-LM
在针对小文本数据量的语言模型训练中,我们可以直接调用srilm中的n-gram-count工具进行训练,但针对大数据量文本数据进行训练时,可能会造成内存溢出的问题,所以针对这个问题,srilm工具箱同时集成了另一个工具make-big-lm,它可以达到把文本数据分块进行处理的功能,节省了内存空间提升了训练效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ngram-count ##功能 #读取分词后的text文件或者count文件,然后用来输出最后汇总的count文件或者语言模型 ##参数 #输入文本: # -read 读取count文件 # -text 读取分词后的文本文件 #词典文件: # -vocab 限制text和count文件的单词,没有出现在词典的单词替换为<unk>; # 如果没有加该选项,所有的单词将会被自动加入词典 # -limit-vocab 只限制count文件的单词(对text文件无效); # 没有出现在词典里面的count将会被丢弃 # -write-vocab 输出词典 #语言模型: # -lm 输出语言模型 # -write-binary-lm 输出二进制的语言模型 # -sort 输出语言模型gram排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ngram ##功能 #用于评估语言模型的好坏,或者是计算特定句子的得分,用于语音识别的识别结果分析。 ##参数 #计算得分: # -order 模型阶数,默认使用3阶 # -lm 使用的语言模型 # -ppl 后跟需要打分的句子(一行一句,已经分词),ppl表示所有单词,ppl1表示除了</s>以外的单词 # -debug 0 只输出整体情况 # -debug 1 具体到句子 # -debug 2 具体每个词的概率 #产生句子: # -gen 产生句子的个数 # -seed 产生句子用到的random seed ngram -lm ${lm} -order 3 -ppl ${file} -debug 1 > ${ppl}
运行THCHS30的kaldi 代码,在数据准备阶段,data/train 和 test文件夹中会生成 word.txt 文件,去除第一列 id 标识,使用train作LM model的计数和训练文件,Test做测试。
问题1:使用SRILM 获得经过 interpolation 式的 Kneser Ney 平滑的 3gram 以上的语言模型执行
共有两种训练方法:
训练文本 ——> count文件 ——> lm模型
训练文本——> lm模型
1 2 # 计数功能——生成计数文件 ngram-count -text -order 3 newtrainword.txt -limit-vocab lexicon.txt -no-sos -no-eos -write trainword.text.count –debug 1
1 2 # 从计数文件构建语言模型 ngram-count -read trainword.text.count -order 3 -lm LM_train -interpolate –kndiscount –debug 1
1 2 # 直接结合上面两步,接利用训练语料构建语言模型 ngram-count -text newtrainword.txt -order 3 -lm train.lm -interpolate –kndiscount –debug 1
问题2:使用 SRILM SRILM 计算在识别测试集上计算计算在识别测试集上计算 PPL=32266.1
1 ngram -ppl testword.text.count -order 3 -lm train.lm
1 2 file testword.text.count: 12025 sentences, 37441 words, 22213 OOVs 2577 zeroprobs, logprob= -111258 ppl= 32266.1 ppl1= 6.22859e+08
四、Tips 使用nrgam-count,主要是三要素:训练文件,计数文件,语言模型,流程都是“训练文件–>计数文件–>语言模型”。
先将训练文件进行计数,通过-gtnmin mincount来控制哪些计数丢弃,即计数为0,然后再得到中间计数文件,再针对中间计数文件进行折扣算法,得到最终的语言模型。计数文件是一个中间文件,可以通过-write count_file来输出保存。
较为常用的使用方法如下:
最简单(Good-Turing折扣算法)1 ngram-count -text train -lm LM.ARPA
输出词典和计数文件:1 ngram-count -text train -write-vocab VOCAB -write COUNT -lm LM.ARPA
对语言模型剪枝:1 ngram-count -text train -prune 0.2 -lm LM.ARPA
设置计数最小阈值:1 ngram-count -text train -gt1min 1 -gt2min 1 -gt3min 2 -lm LM.ARPA
使用经过插值的修正Kneser-Ney折扣算法:1 ngram-count -text train -kndiscount -interpolate -lm LM.ARPA
将debug信息输出来:1 ngram-count -text train -kndiscount -interpolate -lm LM.ARPA -debug 1 2>DEBUG
参考