1. 程式人生 > >【Kaldi 新手入門】手把手教你搭建簡易英文數字ASR系統

【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 命令解壓檔案,然後進入檔案目錄。

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上了,如果有需要的話可以去下載。