stage -1
- local/download_and_untar.shell脚本解析
-
可以选择下载解压之后删除压缩包, --remove-archive 使用该标记实现
-
下载的文件存放目录:$data,如果不存在则返回错误
-
aishell一共有两个part,可以选择一个part下载,但只能在这两个范围之内选择:data_aishell resource_aishell,会判断下载的part在不在list中, 如果不在,则返回错误
-
下载之前会判断url的长度是否为0,如果长度为0则返回错误
-
下载解压完成之后有一个complete标记: d a t a / data/ data/part/.complete
-
两个part的大小分别为:15582913665 1246920字节
-
下载的文件和位置: d a t a / data/ data/part.tgz,如果存在,则判断大小,读取大小的命令为:
$(/bin/ls -l d a t a / data/ data/part.tgz | awk ‘{print $5}’) -
如果大小在列表中,则ok,否则rm掉
-
如果 d a t a / data/ data/part.tgz不存在,则,下载。首先查看wget是否安装了。
-
if ! which wget > /dev/null; 这个是判断是否为空,为空时会进去。
-
full下载链接:$url/part.tgz
-
这里面有个技巧,执行不成功返回null,执行成功返回true,来判断是否下载和解压成功:
if ! weget $full_url;then echo error exit 1; fi
if ! tar xvf $full_url; then echo error exit 1 fi
到此,下载和解压都完成了。
-
生成完成标记: 生成方法为touch语句
touch d a t a / data/ data/part/.complete -
如果是data_aishell数据,则进入到wav目录:data/$part/wav,解压下面的全部tar.gz文件
for wav in ./*.tar.gz;do echo .... tar -zxf $wav && rm wav done
-
最后显示成功下载并解压文件日志
-
如果有标记要remove掉压缩包,则移除压缩包
-
最后退出
exit 0
-
stage 0
-
local/aishell_data_prep.sh
-
首先检查参数是否是两个
if [ $# != 2 ];then ... fi
-
第一个参数,wav目录
-
第二个参数,取得text: $2/aishell_transcript_v0.8.txt
-
定义并创建
train_dir=data/loca/train dev_dir = data/local/dev test_dir=data/local/test tmp_dir=data/local/tmp
-
如果wav目录不存在或者翻译文稿不存在,则报错 exit1
-
find $aishell_audio_dir -iname “*.wav” > $tmp/wav.flist
- 忽略大小写,查找.wav结尾的文件,存放到临时文件夹中
n=cat $tmp/wav.flist | wc -l
[$n -ne 141925] && echo “error” 一共141925个文件,此处用-ne或者!=都是可以的
- 忽略大小写,查找.wav结尾的文件,存放到临时文件夹中
-
按照文件夹区分train test dev文件列表,存储到对应的文件夹下
-
grep -i “wav/train” $tmp/wav.flist > $train_dir/wav.flist || exit 1; 如果不成功则退出,都是这么写的, 自己平时很少这么写。
其他两个同理 grep -i 的i表示忽略大小写 这样就把 train test dev的wav.flist都整理好了 -
删除tmp文件夹
-
获取utt_id,然后拼接起来
```bash sed -e "s/\.wav//" $train_dir/wav.flist | awk -F '/' '{print $NF}' > $train_dir/utt.list paste -d' ' $dir/utt.list $dir/wav.flist > $dir/wav.scp_all tools/filter_scp.pl -f 1 $dir/utt.list $aishell_text > $dir/transcripts.txt ```
-
tools/filter_scp.pl这是一个专门的脚本,第一个文件的第一个字段 是 uttid,用于过滤出第二个文件中,第n个字段包含uttid的行
- -f 1 表示第二个文件的第一个字段作为匹配项
-
sed -e ‘s/.wav//’ data/local/dev/wav.flist | awk -F ‘/’ ‘{print $NF}’ > utt.list
- 存在一些没有翻译的utt,这个也都过滤掉了。首先通过wav过滤出utt,然后使用utt过滤出text,然后再找到uttnew,使用新的utt过滤wav.scp最后两者都要排序
-
最后把整理好的wav.scp text放到data/train/dev/test下面为训练做准备
-
stage 1
- 把transcript取掉空格,重新生成text,原来的变成text.org
- 使用wav.scp计算cmvn,存放到train目录下面
stage 2
-
生成字典data/dict/lang_char.txt
-
tools/text2token.py 把拼接在一起的文本变成单字,例如:处理后的例子如下
- BAC009S0723W0493 钢 筋 穿 过 后 排 座 位 并 顶 出 车 外
-
tools/text2token.py -s 1 -n 1 data/train/text 跳过第一列,-n表示组内个数
grep -a -v -e '^\s*$' -a 在文件是二进制文件时使用 -v 输出不符合条件的 -e 字符串为范本样式
- NR表示行数,第几行的意思,和awk结合使用才有效
- \s表示非空白字符
- 所以上面的是去掉多个字符的情况
stage 3
-
把wav.scp 和 text准备成data.list
-
需要区分是shard还是raw,raw的话,适合小数据,直接将音频和对应的文本整合为一条,key,wav,txt
- 如果shard首先会对数据进行分组打包,打包成压缩格式,然后data.list中存放的就是shard的路径
在数据读取时,会根据shard还是raw进行区分读取。
tools/make_shard_list.py 脚本参数除了上面的输入和输出文件外,还需要一个shard路径,还需要shard的分组大小,还需要有一个线程数
tools/make_raw_list.py
- 如果shard首先会对数据进行分组打包,打包成压缩格式,然后data.list中存放的就是shard的路径
state 4 训练
-
获取执行命令之后的内容这样写:numgpu=$(echo $CUDA_VISIBLE_DEVICES| awk -F ‘,’ ‘{print NF}’)
-
计算world_size,表示总共有多少个gpu进行训练,方法是num_gpu * num_node, num_node默认是1
-
数学计算用命令:world_size=
expr $num_gpus \* $num_nodes
-
可以用个&&表示,第一部分成功的话,执行第二部分
-
空的定义为:cmvn_opt=这样就可以
-
如果cmvn ture,则,cmvn_opt重新赋值为"–cmvn $dir/global_cmvn"
-
for循环的写法,是两个括号
for((i=0;i<$num_gpu; ++i));do {} & done
- &表示一起执行,不用等待for循环里面命令结束再开始下一个
-
当前训练的gpuid=$(echo C U D A V I S I B L E D E V I C E S ∣ c u t − d ′ , ′ − f CUDA_VISIBLE_DEVICES | cut -d',' -f CUDAVISIBLEDEVICES∣cut−d′,′−f[$i+1])
-
:+ 当前面变量不为空时,用后面的替换
${checkpoint:±-checkpoint $checkpoint} -
有一个world_size,还有一个rank
-
world_size是所有分布式机器的gpu个数,rank是当前排名,计算方法需要用到node_rank这个是传入的,
-
rank = node_rank * num_gpus + $i
-
[ ] 里面可以直接进行数学运算,比如 []里面可以直接进行数学运算,比如 []里面可以直接进行数学运算,比如[$i+1]
state 5 测试
- 如果average_checkpoint标记是true,默认为true,则进行average操作,需要传入最终保存的模型,模型文件目录,平均数,默认为5,是按照last平均还是best平均
- 计算脚本为:wenet/bin/average_model.py,该脚本会对模型文件目录下的模型文件按照要求进行平均
如果标记是false,需要制定decode_checkpoint,默认是final.pt - 下面就是根据不同的解码模式,使用解码脚本进行解码。
- 解码模式有四种;ctc_greedy, ctc_beam_search, attention, attention_rescore
- 解码脚本为:wenet/bin/recognize.py ,解码有很多参数
- gpu 表示解码时使用的gpu,mode时解码模式,data_type和训练时一样,testdata传入test.data.list
- checkpoint 传入解码使用的模型,beam_size时ctc搜索时的beam size, batchsize默认为1, penalty默认为0.0,不知道干嘛的
- dict为使用的字典文件,ctc_weight默认在解码时使用0.5,在训练时使用0.3, reverse_weight,默认为0.0,在双向解码时,一般设置为0.3
- result_file存放最后识别的text,decoding_chunk_size默认为空,在流式解码时传入流式解码的参数
- 最后使用tools/compute-wer.py脚本计算wer,需要传入两个参数,一个是解码text,一个是标准text,最后输出到wer文件中。另外有两个参数
- –char 1 --v=1这两个一直没有变动过。第一个参数表示变成char,第二个参数表示是否输出verbose:
最后使用命令:bash run.sh --stage 5 --stop-stage 5 --average_checkpoint false
stage 6 导出模型
-
导出libtorch模型,zip模型
-
脚本:wenet/bin/export_jit.py
-
参数 train conf,要导出的模型,checkpoint,导出的模型 output_file, 导出的量化模型,output_quant_file
-
bash run.sh --stage 6 --stop-stage 6
stage 7 runtime解码
跑lm模型,解码时添加LM模型,使用runtime去跑
-
准备工作
报错:
srilm tools are not found, please download it and install it from:
http://www.speech.sri.com/projects/srilm/download.html
Then add the tools to your PATH解决方法:kaldi、wenet的tools文件夹里都有install_srilm.sh脚本可以直接执行安装srilm,
cd tools bash install_srilm.sh cd .. source tools/env.sh whichi srilm
- source tools/env.sh 将里面的路径,添加到path中,这样以后就不用source这个了,直接. ./path.sh就可以
-
第一部分:生成lexicon
- dict=data/dict/lang_char.txt
- 定义unit_file=$dict
- 创建文件夹 data/loca/dict ,用于存放字典相关的东西
- 将字典文件存放到文件data/local.dict/units.txt
- 运行prepare_dict的脚本,具体脚本为tools/fst/prepare_dict.py,根据字典,原始语料的词汇表生成新的词汇表
- 生成结果类似:
左膝 左 膝
左眼 左 眼
左玉 左 玉
作报 作 报
本地文件可以自己编写对应的prepare dict脚本运行,生成输入的dict
-
第二部分训练lm
-
训练结果存放在目录data/local/lm中
-
首先把训练文本放到里面,data/local/lm/text内容如下:
BAC009S0002W0135 银行 业金 融机 构 执行 首套 房贷 款 政策
BAC009S0002W0136 这个 后来 被 称 为 九三零 新政 策 的 措施 -
训练lm
tools/filter_scp.pl data/train/text
$data/data_aishell/transcript/aishell_transcript_v0.8.txt > $lm/text -
local/aishell_train_lms.sh跑训练
-
脚本解析:
-
首先需要依赖两个文件 data/local/lm/text也就是训练文本
-
然后依赖词典,data/local/dict/lexicon.txt
-
检查两个文件在不在,不在提示不在,然后退出
-
然后检查srilm工具在不在,通过which命令完成
```bash if ! which ngram-count >/dev/null;then 找不到则进入该分支,打印提示,退出 fi ```
-
当前工作目录dir=data/local/lm/text
-
准备好干净的text cleantext=$dir/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("<SPOKEN_NOISE> ");} } printf("\n"); }'
-
awk -v
- -v表示把shell里的变量传进awk
-
-
begin中的内容,把见过的词都添加到seen中
-
接下来对text的每一行处理,
cat $cleantext | awk '{for(n=2;n<=NF;n++) print $n; }' | sort | uniq -c | \ sort -nr > $dir/word.counts || exit 1;
-
-c表示计数
-
sort -nr 表示倒叙根据数值排序,结果为
48876 的 11114 在 10171 一 9609 了
-
也就是对所有的分词结果进行排序计数去重复,按照倒叙排序
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 -表示标准输入流 - 上面命令把所有cleantext的内容打印出来,再打印第二项内容,然后和上面的明林狗一样,进行排序计数去重复按照倒叙排序 该文件在cleantext有的词计数上+1 ,其他的cleantext没有的(lexicon大的多),计数都为1 结果存放在了unigram.counts中
-
-
data/local/lm/wordlist 中存放unigram.counts第一列,最后添加两个标记
-
生成data/local/lm/heldout和data/local/lm/train
-
heldout是clean data的前10000行
-
train是clean的除了前9999的全部行,两个文件有一个叠加数据
-
heldout和train回去掉第一个无用字符
-
head -5 表示输出前5个
-
tail -n +5 表示输出第5个及后面的数据
ngram-count -text $dir/train -order 3 -limit-vocab -vocab $dir/wordlist -unk \ -map-unk "<UNK>" -kndiscount -interpolate -lm $dir/lm.arpa ngram -lm $dir/lm.arpa -ppl $dir/heldout
- 这两个命令负责训练lm模型,很快就训练完了
ngram-count -text $dir/train -order 3 -limit-vocab -vocab $dir/wordlist -unk \ -map-unk "<UNK>" -kndiscount -interpolate -lm $dir/lm.arpa
输入参数text表示训练的语料;order表示语言模型阶数;limit-vocab不知道 vocab wordlist是所有字典第一列,外加两个标记<s> </s> -unk不知道 -map-unk “<UNK>” -kndiscount不知道 -interpolate是插值的意思 lm data/local/lm/lm.arpa是生成的语言模型 ngram命令不知道是干嘛的,heldout也不知道是干啥的 最后word.count好像没有用,其他的用了
-
-
-
生成TLG
tools/fst/compile_lexicon_token_fst.sh
data/local/dict data/local/tmp data/local/lang
tools/fst/make_tlg.sh data/local/lm data/local/lang data/lang_test || exit 1;
报错:
tools/fst/compile_lexicon_token_fst.sh: line 61: fstcompile: command not found
tools/fst/compile_lexicon_token_fst.sh: line 62: fstarcsort: command not found
解决方法,重新安装openfst
https://blog.csdn.net/HEYUDAGE/article/details/128271395
如果遇到libfstscript.so.8 错误
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
或者重新编译:mkdir build && cd build && cmake -DGRAPH_TOOLS=ON … && cmake --build .
模型加载错误:
torch::jit::ErrorReport
下载的libtorch文件版本为:
https://download.pytorch.org/libtorch/cpu/libtorch-shared-with-deps-1.13.0%2Bcpu.zip
报错:torch::jit::ErrorReport 原因是加载错误,根本原因是版本libtorch和torch的版本不匹配,更多的时候,libtorch的版本低于torch版本,解决方法,提升libtorch的版本或者降低torch的版本。比如libtorch1.12,则torch版本为1.12或者1.11.
遇到问题需要打印日志,修改:
…/…/core/bin/decoder_main.cc
例如打印方式:
std::cout << “wav.utt” << wav.first << std::endl;
std::cout << “wav_name:” << wav.second << std::endl;
train lm ,with data/lcoal/lm/text (annotation with space), and data/loca/dict/lexicon.txt
(所有的词)
local/aishell_train_lms.sh
- 生成wfst图
tools/fst/compile_lexicon_token_fst.sh data/local/dict data/local/tmp data/local/lang
tools/fst/make_tlg.sh data/local/lm data/local/lang data/lang_test || exit1;
decoder_main如果找不到就 . ./path.sh