【Kaldi 新手入门】手把手教你搭建简易英文数字ASR系统

* 写作本文的目的:一方面是为了帮助Kaldi的新手更好的入门这个语音识别工具,另一方面是为自己的学习做一个笔记,也方便日后的学习查阅. *

Kaldi的下载安装

备注: 虽然Kaldi可以同时运行在Windows和Linux两个平台上,但大多数人还是使用Linux系统进行运行,原因是安装运行时的错误相对较少,而在Linux操作系统中应用最广的是Ubuntu,在这里我就仅叙述Kaldi在Ubuntu 18.04中的安装步骤。

一、下载

 由于我本篇博文的主要目的不是讲解Kaldi的安装,所以就简略带过了,Kaldi是git上开源的,可以直接利用git命令进行下载,然后再编译即可。
  具体在linux终端中执行如下命令:

sudo apt install git
git clone https://github.com/kaldi-asr/kaldi.git kaldi-trunk --origin golden

其中第一个命令是安装git,第二个命令是从git上下载Kaldi,这可能需要等待一段时间。

二、编译

  从git上下载完成后,利用 tar -zxvf 命令解压文件,然后进入文件目录。

  确保你的linux系统安装了相应依赖软件后(依赖软件可参照官方文档)
            http://kaldi-asr.org/doc/kaldi_for_dummies.html
  之后,在./tool目录下执行以下命令:

make

  然后进入src目录中,依次执行下述命令:

./configure
make depend
make

  其中的make命令可以由make -j ?来代替,?表示你CPU的核数,-j 是一个并行处理机制,可以加快编译的速度。执行完上述命令之后,你的Kaldi就安装完成啦!接下来,就可以在语音识别的海洋(深坑)中尽情玩耍(受虐)了!

搭建你的第一个ASR系统

  接下来,进入正题,对于一个初次认识语音识别的小白来说,学习Kaldi,首先要做的肯定是去看Kaldi的入门文档,在此也附上链接 http://kaldi-asr.org/doc/index.html,而文档中首先给了一篇文章,名字也起的很好 Kaldi for Dummies tutorial (Kaldi傻瓜教程),但不尽如人意的就是他是全英文的,我也是对着这个教程搞了几天才弄出了我的第一个ASR系统。

  在最开始我要提一句,我之后的实现步骤是建立在工程文件存放位置在kaldi/trunk/egs/digits中的,之后所说的工程根目录就是指的digits,是我在egs/目录下新建的一个目录,如果你将工程文件建立在别的地方,有些地方可能要有所更改,比如一些相对路径

一、语音素材准备

  对于这个傻瓜教程,最开始我的困难就是没法找到语音素材,毕竟就是苦逼大学生,也没人给你提供素材,自己要是去各处征集的话那时间可就不只几天了,好在经过我长时间的网上查阅,找到一个github上提供好的语音素材,虽然和官方文档要求的不太一样,但同样可以满足ASR的要求。

附上英文数字的语音链接:英文数字语音包
  这个语音包中包含128个语音文件,wav格式的,分别是两个人朗读,其中每个文件只包含三个数字。这 128 文件中 80 个用于训练, 48 个用于测试。并且训练数据和测试数据都被分成了 8 部分(可以假装成 8 个人),每部分分别 10 个 和 6 个。训练集和测试集的前五个目录是男士朗读,后面三个是女士朗读的。
  下载的语音包目录结构如下:

├── test
│   ├── 1
│   ├── 2
│   ├── 3
│   ├── 4
│   ├── 5
│   ├── 6
│   ├── 7
│   └── 8
└── train
    ├── 1
    ├── 2
    ├── 3
    ├── 4
    ├── 5
    ├── 6
    ├── 7
    └── 8

  当然,如果你有条件,有自己的录音的话,也可以将其分类后应用。
  

二、数据准备

  语音素材准备好之后,开始进行数据的准备,这里也是ASR系统构建的依据,是必不可少的一环。
  在根路径下新建目录data/。
  声学数据
    在这里主要就是spk2gender、wav.scp、text、utt2spk这四个文件的准备以及语料库的准备,先在data/目录
  下新建三个子目录train/、test/、local/
    然后分别在train/和test/目录下新建以下四个文件夹
    1.spk2gender
      这个文件主要为Kaldi描述了说话人的编号以及性别的对应关系,具有这样的格式: [speakerID] [gender]
    中间用空格隔开,每一条数据关系用一行表示,在我所使用的训练集中,是8个人,前面5个是男士,后面
    3个是女士,则我所使用的spk2gender如下:
    
  spk2gender

1 m
2 m
3 m
4 m
5 m
6 f
7 f
8 f

    2.wav.scp
      这个文件主要存放了的内容表示了语音编号和语音文件存放位置的对应关系,具有这样的格式:     
    [utterranceID] [full_path_to_audio_file],同样中间用空格隔开,每一条数据关系用一行表示,这个文件一般
    比较长,这篇博文就不全部贴了,若有需要可去我的git上直接下载全部代码。

   wav.scp部分内容

1_040 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-040.wav
1_089 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-089.wav
1_104 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-104.wav
1_231 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-231.wav
1_327 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-327.wav
1_413 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-413.wav
1_462 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-462.wav
1_468 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-468.wav
1_470 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-470.wav
1_560 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/1/1-560.wav
2_012 /home/johnson/桌面/Kaldi/kaldi-trunk/egs/digits/digits_audio/train/2/2-012.wav

    3.text
      这个文件主要是用来存放语音编号与语音内容之间的对应关系,具有这样的格式: [utterranceID]
    [text_transcription],与上述一样,下面附上我的文件部分内容。
    
   text部分内容

1_040 zero four zero
1_089 zero eight nine
1_104 one zero four
1_231 two three one
1_327 three two seven
1_413 four one three
1_462 four six two
1_468 four six eight
1_470 four seven zero
1_560 five six zero
2_012 zero one two

    4.utt2spk
     这个文件主要是用来存放语音编号和说话人编号之间的对应关系,具有这样的格式: [utterranceID]
   [speakerID],下面提供部分内容。
   
   utt2spk部分内容

1_040 1
1_089 1
1_104 1
1_231 1
1_327 1
1_413 1
1_462 1
1_468 1
1_470 1
1_560 1
2_012 2

注意:上述的四个文件要在data/train/和data/test/目录下分别建立,要与train和test的语音文件相对应,相比于data/train下的四个文件,在data/test中需要修改的文件有如下三个:utt2spk、text、wav.scp

  语料库
  
  接下来我们来构建我们的语料库,在这里面要包含所有训练集和测试集的数据,在我们的语音素材中也就是在语料库中要有128条数据,在data/local/目录下新建corpus.txt文件,输入内容格式如下:
  
corpus.txt

zero four zero
zero eight nine
one zero four
two three one
three two seven
four one three
four six two
four six eight
four seven zero
five six zero

  至此,我们的声学数据就准备完成了,在此声明一下,由于本次索要构建的ASR系统比较小,只有0 ~ 9这几个数字,所以声学数据的文件也相对较小,但之后的语音识别可能会有很大的升学数据文件,到那个时候,手动的编写这些文件并不是一个很好的选择,麻烦还容易出错,到时候可以采用脚本文件进行处理,当然这也都是后话了。现在,我们接下来的任务,是继续完善这个ASR,为它准备语言数据。

  语言数据

  语言数据主要包括 lexicon.txt 、optional_silence.txt、nonsilence_phones.txt、silence_phones.txt,并在 data/local/ 目录下新建文件夹 dict,将这四个文件保存到那里,同样,我接下来对他们分别进行解释:
  
  1.lexicon.txt
    这个文件中包括了你语音中出现的所有词的发音,也就是音素表达,不知道什么是音素?没关系,我也不知
  道,对于初学者,这些就当做是现有的资料就好了,在这里我也将这个ASR用到的音素列到下面:
  
  lexicon.txt

!SIL sil
<UNK> spn
eight ey t
five f ay v
four f ao r
nine n ay n
one hh w ah n
one w ah n
seven s eh v ah n
six s ih k s
three th r iy
two t uw
zero z ih r ow
zero z iy r ow

   2.nonsilence_phones.txt
     这个文件包括了上面所有的非静音音素,同样,下面提供本次所需要的非静音音素。
     
   nonsilence_phones.txt

ah
ao
ay
eh
ey
f
hh
ih
iy
k
n
ow
r
s
t
th
uw
w
v
z

   3.silence_phones.txt
    这个文件中包括了上面所有的静音音素。
  
   silence_phones.txt

sil
spn

    
   4.optional_silence.txt
     这个文件里面只有可选的静音音素。
     
   optional_silence.txt

sil

三、脚本文件编写

  现在,让我们回到项目的根路径,我们需要在这个目录下建立如下文件:
  
  1.cmd.sh

export train_cmd="run.pl"
export decode_cmd="run.pl"
export mkgraph_cmd="run.pl"

  2.path.sh

export train_cmd="run.pl"
export decode_cmd="run.pl"
export cuda_cmd="run.pl"
export mkgraph_cmd="run.pl"
cat path.sh 
export KALDI_ROOT=`pwd`/../..
[ -f $KALDI_ROOT/tools/env.sh ] && . $KALDI_ROOT/tools/env.sh
export PATH=$PWD/utils/:$KALDI_ROOT/tools/openfst/bin:$KALDI_ROOT/tools/irstlm/bin/:$PWD:$PATH
[ ! -f $KALDI_ROOT/tools/config/common_path.sh ] && echo >&2 "The standard file $KALDI_ROOT/tools/config/common_path.sh is not present -> Exit!" && exit 1
. $KALDI_ROOT/tools/config/common_path.sh
export LC_ALL=C

  3.run.sh

#!/bin/bash

. ./path.sh || exit 1
. ./cmd.sh || exit 1

nj=4
lm_order=1

. utils/parse_options.sh || exit 1
[[ $# -ge 1 ]] && { echo "Wrong arguments!"; exit 1; }


# Removing previously created data (from last run.sh execution)
rm -rf exp mfcc data/train/spk2utt data/train/cmvn.scp data/train/feats.scp \
      data/train/split1 data/test/spk2utt data/test/cmvn.scp data/test/feats.scp \
      data/test/split1 data/local/lang data/lang data/local/tmp \
      data/local/dict/lexiconp.txt


echo
echo "===== PREPARING ACOUSTIC DATA ====="
echo

# Making spk2utt files
utils/utt2spk_to_spk2utt.pl data/train/utt2spk > data/train/spk2utt
utils/utt2spk_to_spk2utt.pl data/test/utt2spk > data/test/spk2utt

echo
echo "===== FEATURES EXTRACTION ====="
echo

# Making feats.scp files
mfccdir=mfcc
# Uncomment and modify arguments in scripts below if you have any problems with data sorting
# utils/validate_data_dir.sh data/train     # script for checking prepared data - here: for data/train directory
# utils/validate_data_dir.sh data/test
# utils/fix_data_dir.sh data/train          # tool for data proper sorting if needed - here: for data/train directory
# utils/fix_data_dir.sh data/test

steps/make_mfcc.sh --nj $nj --cmd "$train_cmd" data/train \
                        exp/make_mfcc/train $mfccdir
steps/make_mfcc.sh --nj $nj --cmd "$train_cmd" data/test \
                        exp/make_mfcc/test $mfccdir

# Making cmvn.scp files
steps/compute_cmvn_stats.sh data/train exp/make_mfcc/train $mfccdir
steps/compute_cmvn_stats.sh data/test exp/make_mfcc/test $mfccdir

echo
echo "===== PREPARING LANGUAGE DATA ====="
echo
# Preparing language data
utils/prepare_lang.sh data/local/dict "<UNK>" data/local/lang data/lang

echo
echo "===== LANGUAGE MODEL CREATION ====="
echo "===== MAKING lm.arpa ====="
echo

loc=`which ngram-count`;
if [ -z $loc ]; then
    if uname -a | grep 64 >/dev/null; then
        sdir=$KALDI_ROOT/tools/srilm/bin/i686-m64
    else
        sdir=$KALDI_ROOT/tools/srilm/bin/i686
    fi
    if [ -f $sdir/ngram-count ]; then
        echo "Using SRILM language modelling tool from $sdir"
        export PATH=$PATH:$sdir
    else
        echo "SRILM toolkit is probably not installed. Instructions: tools/install_srilm.sh"
        exit 1
    fi
fi

local=data/local
mkdir $local/tmp
ngram-count -order $lm_order -write-vocab $local/tmp/vocab-full.txt \
            -wbdiscount -text $local/corpus.txt -lm $local/tmp/lm.arpa

echo
echo "===== MAKING G.fst ====="
echo

lang=data/lang
arpa2fst --disambig-symbol=#0 --read-symbol-table=$lang/words.txt \
                          $local/tmp/lm.arpa $lang/G.fst

echo
echo "===== MONO TRAINING ====="
echo

steps/train_mono.sh --nj $nj --cmd "$train_cmd" data/train data/lang exp/mono  || exit 1

echo
echo "===== MONO DECODING ====="
echo

utils/mkgraph.sh --mono data/lang exp/mono exp/mono/graph || exit 1
steps/decode.sh --config conf/decode.config --nj $nj --cmd "$decode_cmd" \
                exp/mono/graph data/test exp/mono/decode
local/score.sh data/test data/lang exp/mono/decode/

echo
echo "===== MONO ALIGNMENT ====="
echo

steps/align_si.sh --nj $nj --cmd "$train_cmd" data/train data/lang exp/mono exp/mono_ali || exit 1

echo
echo "===== TRI1 (first triphone pass) TRAINING ====="
echo

steps/train_deltas.sh --cmd "$train_cmd" 2000 11000 data/train data/lang exp/mono_ali exp/tri1 || exit 1

echo
echo "===== TRI1 (first triphone pass) DECODING ====="
echo

utils/mkgraph.sh data/lang exp/tri1 exp/tri1/graph || exit 1
steps/decode.sh --config conf/decode.config --nj $nj --cmd "$decode_cmd" \
                exp/tri1/graph data/test exp/tri1/decode
local/score.sh data/test data/lang exp/tri1/decode

echo
echo "===== run.sh script is finished ====="
echo

  这三个文件看不懂没有关系,但我感觉要是稍微有一点linux基础的,看起来应该不是很费劲,而且也有注释是不是?不过也不需要看懂,前面的过程如果没有出错的话,按照我的步骤应该就可以成功搭建啦!

四、环境以及一些配置文件

  只有以上那三个脚本文件还是不够滴,我们还需要借助一些Kaldi已经帮我们编写好的东西,这些文件一般可以从Kaldi根目录下的egs/目录下的其他例程中找到,这里直接用linux命令将其链接过来即可,在你工程的根目录下输入以下命令:

$ ln -s ../timit/s5/steps steps
$ ln -s ../timit/s5/utils utils

  
  然后为了你自己可以看懂ASR训练的结果,还需要在工程根目录下新建一个local/目录,将其他例程中的score.sh文件拷贝过来,可以显示训练的错误率等等。(虽说如此… 但我并没有成功 大家自求多福哈!)

  最后,养成好习惯,在工程根目录下新建conf/目录,用于存放配置文件,在这里我们可以直接拷贝egs/voxforge/s5/conf/中的两个配置文件,内容不用更改。

五、运行run.sh

  终于到了最激动人心的时候了!在按照我的步骤完成以上的配置之后,就可以直接运行run.sh文件了,接下来你就会看到一大串一看不太懂的东西啦!只要其中没有出现ERROR字段,那就说明你成功了,如果你够幸运的话,你还可以看到提示的错误率是多少。

提示:在执行run.sh的时候最好用sudo进行执行,因为其中涉及到文件的修改删除,可能会有权限不够的情况。还有就是,如果你在执行的时候,提示你没有这个命令,或无法执行,可以首先执行$ chmod u+x local/.sh .sh这个命令,把.sh文件修改为可执行文件,再执行。

  最后,为了方便大家验证自己是否操作正确,我附上我的工程文件结构图供大家参考。

.
├── cmd.sh
├── conf
│   ├── decode.config
│   └── mfcc.conf
├── data
│   ├── local
│   │   ├── corpus.txt
│   │   └── dict
│   │       ├── lexicon.txt
│   │       ├── nonsilence_phones.txt
│   │       ├── optional_silence.txt
│   │       └── silence_phones.txt
│   ├── test
│   │   ├── spk2gender
│   │   ├── text
│   │   ├── utt2spk
│   │   └── wav.scp
│   └── train
│       ├── spk2gender
│       ├── text
│       ├── utt2spk
│       └── wav.scp
├── local
│   └── score.sh
├── path.sh
├── run.sh
├── steps -> ../../timit/s5/steps
└── utils -> ../../timit/s5/utils

我的工程已经放到我的github上了,如果有需要的话可以去下载。
  

猜你喜欢

转载自blog.csdn.net/m0_38055352/article/details/82416633