wenet aishell脚本解析

文章目录

  • stage -1
  • stage 0
  • stage 1
  • stage 2
  • stage 3
  • state 4 训练
  • state 5 测试
  • stage 6 导出模型
  • stage 7 runtime解码

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或者!=都是可以的
    • 按照文件夹区分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

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 CUDAVISIBLEDEVICEScutd,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((getline0){ seen[$1]=1; } }
          
            {for(n=1; n<=NF; n++) 
               {  if (seen[$n]) 
                     { printf("%s ", $n); } 
                  else 
                     {printf(" ");} 
               } 
             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 "" -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 "" -kndiscount -interpolate -lm $dir/lm.arpa
      
      输入参数text表示训练的语料;order表示语言模型阶数;limit-vocab不知道
      vocab wordlist是所有字典第一列,外加两个标记 
      -unk不知道 -map-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

你可能感兴趣的:(语音识别)