[轉]Kaldi命令詞識別

轉自:git

http://www.jianshu.com/p/5b19605792ab?utm_campaign=maleskine&utm_content=note&utm_medium=pc_all_hots&utm_source=recommendationgithub

http://www.jianshu.com/p/6338fab6bd0a算法

剛剛拿到一個簡單語料庫練手,發現只有語音和對應文字, 這篇文章記錄了從數據預處理到kaldi對數據進行訓練和測試的全過程,這裏首先訓練單音節模型,其餘模型後面再補充。

語料庫處理

task 0: 觀察語料庫

語料庫主要用於命令詞識別,包括200個詞彙,2000條語音,10個說話者分別對200個詞彙進行錄音。語音目錄以說話者id標識:

$ tree -d
├── speaker001
├── speaker002
├── speaker003
├── speaker004
├── speaker005
├── speaker006
├── speaker007
├── speaker008
├── speaker009
├── speaker010
└── Levoice.list
每一個說話者文件夾目錄下包含對應的200條語音:

└┤ tree speaker001
speaker001
├── 00001.wav
├── 00002.wav
├── 00003.wav
...
└── 00200.wav
語音文字說明文件Levoice.list 格式爲<語音id> <文字> <錄音時長>,例如:

└┤ head -n 2 Levoice.list 
speaker001/00001.wav    三六零通信錄    5.6
speaker001/00002.wav    三六五日曆    2.8
語料庫所給的資源應用到kaldi還須要漢字發音詞典,這裏只能本身準備,下面會參考thchs30語料庫的詞典準備本身的詞典。

task 1: 預處理語料庫

爲方便後續操做,須要對語料庫文件進行預處理,這部分包括:

從新重命名語音文件,使2000個語音文件具備惟一標識(speakerid_voiceid.wav)
劃分訓練、測試、驗證數據集
根據Levoice.list生成utt2words.txt ,進行文件名對應漢字映射。
上述過程腳本(注意rname命令在Ubuntu和Centos中有細微差異):

 !/bin/bash

#if need cv or not
needcv=true

# rename wav files by add prefix by "speaker"
start_path=`pwd`
for dirname in $(ls | grep "speaker")
do
    #get first filename
    filename=$(ls $dirname | head -n 1)
    if [[ $filename =~ "speaker" ]]; then
        echo "files in $dirname have already renamed, passing..."
    else
        echo "now rename flies with prefix speakers"
        echo $dirname
        cd $dirname
        #in centos rename
        rename "00" $dirname"_00" "00"*
        # ubuntu using follows
        #rename "s/00/$dirname""_00/" 00*
        cd ..
    fi
done

# devide file to train, cv and test
cd $start_path
rm -rf  test train cv  && mkdir test train cv

i=1
for dirname in $(ls | grep "speaker")
do
    if [ $i -lt 9 ];then
        cp $dirname/* train
    else
        cp $dirname/* test
    fi
    let i=$i+1

done

function rand(){
    min=$1
    max=$(($2-$min+1))
    num=$(($RANDOM+1000000000))
    echo $(($num%$max+$min))  
}

count=0
array=("0" "0" "0" "0")
#ls -al train
if [ needcv ]; then
    for file in $(ls train | grep "speak")
    do
        array[$count]=$file
        let count=$count+1
        if [ $count -eq 4 ];then
            rnd=$(rand 0 3)
            mv train/${array[$rnd]} cv
            #echo ${array[$rnd]}
            let count=0
        fi
    done
    echo "cv files prepared over, examples number is $(ls cv | wc -l)"
fi
echo "train files number is $(ls train | wc -l)"
echo "test files number is $(ls test | wc -l)"
語料庫對訓練集、驗證集、測試集參考thchs30,這裏將說話人九、10語音做爲測試集,再從1-8語音集中的1600百條語音文件四條語音爲組隨機選擇一條語音納入驗證集,剩下的做爲訓練集。劃分結果訓練集、驗證集、測試集比例6:2:2。

在語料庫目錄運行上腳本,會在該目錄下產生trian、test和cv目錄,這些目錄及文件將被後面使用。

最後直接將Levoice.list中的信息進行簡單字符替換便可:

speaker001/00001.wav    三六零通信錄    5.6
---->
speaker001_00001.wav    三六零通信錄    5.6
能夠在vi或其餘編輯器中替換便可。

應用Kaldi

task0 : 構建kaldi項目結構

參照其餘項目,首先複製建立項目結構目錄,配置文件以及項目須要使用的依賴工具,這裏多參考thchs30部分結構。在egs 目錄下創建/wakeup/s5做爲項目目錄,在該目錄下準備如下文件:

$ tree -L 1
|-- cmd.sh // 運行配置目錄
|-- conf  // 配置文件目錄
|-- local //存放run.sh 中調用的腳本工具,須要本身編寫
|-- path.sh //Kaldi 工具和庫目錄添加到PATH
|-- run.sh // top層腳本,運行該腳本訓練數據和測試, 須要本身編寫
|-- steps // kaldi 腳本工具, 複製到工程目錄下
|-- tools // kaldi 腳本工具, 複製到工程目錄下
`-- utils // kaldi 腳本工具, 複製到工程目錄下
這裏cmd.sh里根據本身運行方式配置運行參數,這裏配置成單機運行

export train_cmd=run.pl
export decode_cmd="run.pl --mem 4G"
export mkgraph_cmd="run.pl --mem 8G"
conf 目錄包含一些配置文件,這裏主要將系統採樣頻率與語料庫的採樣頻率設置爲一致:

$ ls
decode_dnn.config  fbank.conf  mfcc.conf
$ more mfcc.conf 
--use-energy=false   # only non-default option.
--sample-frequency=8000
$ more decode_dnn.config 
beam=18.0 # beam for decoding.  Was 13.0 in the scripts.
lattice_beam=10.0 # this has most effect on size of the lattices.
$ more fbank.conf 
--sample-frequency=8000
--num-mel-bins=40
task1 : 準備訓練文件

參照kaldi數據準備部分文檔,該部分須要本身根據語料庫分別就train,test,cross validation目錄生成如下文件:

text : < uttid > < word >
wav.scp : < uttid > < utter_file_path >
utt2spk : < uttid > < speakid >
spk2utt : < speakid > < uttid >
word.txt : 同 text
編寫local/data_pre.sh腳本供run.sh調用(下面會涉及run.sh腳本的編寫),傳入參數運行目錄以及語料庫目錄:

#!/bin/bash
# 2017-3-23 by zqh 

# This file prepares files needed in kaldi
# including text, wav.scp, utt2spk, spk2utt
# output: 
#   data/train dir include infomation of train data
#   data/test dir include infomation of test data
#   data/cv dir include infomation of cross validation data

run_dir=$1
dataset_dir=$2

cd $run_dir
echo "prepare data in data/{train, test, cv}"
mkdir -p data/{train,test,cv}

#create text, wav.scp, utt2spk, spk2utt
(
i=0
for dir in train cv test; do
    echo "clean dir data/$dir"
    cd $run_dir/data/$dir
    rm -rf wav.scp utt2spk spk2utt word.txt text  
    #phone.txt
    for data in $(find $dataset_dir/$dir/*.wav | sort -u | xargs -i basename {} .wav);do
        let i=$i+1
        spkid=$(echo $data | awk -F"_" '{print "" $1}')
        uttid=$data
        echo $uttid $dataset_dir/$dir/$data.wav >> wav.scp
        echo $uttid $spkid >> utt2spk
        # gen word.txt
        echo $uttid $(cat $dataset_dir/utt2word.txt | grep $uttid | awk '{print "" $2}') >> word.txt
        # gen phone.txt TODO
    done
    cp word.txt text
    sort wav.scp -o wav.scp
    sort utt2spk -o utt2spk
    sort text -o text
    # sort phone.txt -o phone.txt
done
echo "all file number is $i"
) || exit 1

utils/utt2spk_to_spk2utt.pl data/train/utt2spk > data/train/spk2utt
utils/utt2spk_to_spk2utt.pl data/cv/utt2spk > data/cv/spk2utt
utils/utt2spk_to_spk2utt.pl data/test/utt2spk > data/test/spk2utt
task2 : 訓練語言模型

因爲這裏僅僅須要對語料庫中的200個命令詞進行識別,大而全的漢語詞典並沒必要要,這裏須要根據本身的語料創建詞典而且生成語言模型。

task 2.1 : 準備詞典

根據kaldi的要求,須要準備的詞典包括如下文件(我這裏和語料庫放在同個目錄下,後面kaldi從該目錄下讀取):

[username@hostname dict]$ pwd
/home/username/dataset_wakeup/resource/dict
[username@hostname dict]$ ls
extra_questions.txt  lexiconp.txt  lexicon.txt  nonsilence_phones.txt  optional_silence.txt  silence_phones.txt
對上面文件簡單說明:

lexicon.txt: 詞典,包括語料中涉及的詞彙與發音,與單字及其發音。
silence_phones.txt:靜音標識,這裏爲sil。
nonsilence_phones.txt : 非靜音標識,與silence_phones.txt共同組成lexicon.txt中的發音。
extra_questions.txt : 包含重音音調標記,這裏沒有用到
lexiconp.txt : 若是一個詞有不一樣發音,則會在不一樣行中出現屢次。若是你想使用發音機率,你須要創建 exiconp.txt 而不是 lexicon.txt,這裏未使用
以上文件能夠參考複製thchs30的resource資源,只要替換lexicon.txt爲本身的字典,而且追加thchs30中lexicon.txt中全部的單字及其發音(簡單awk命令便可)。此外該語料庫僅僅提供了漢字無對應發音,須要本身參考thchs30中的詞典準備,(心想只有200條,以爲手打的會很快,事實用了2-3個小時,心累,回頭想能夠寫程序完成)。
lexicon.txt 文件內容大體爲:

$ more lexicon.txt 
SIL sil
<SPOKEN_NOISE> sil
三六零通信錄 s an1 l iu4 l ing2 t ong1 x vn4 l u4
三六五日曆 s an1 l iu4 uu u3 r iz4 l i4
三D圖庫    s an1 d i4 t u2 k u4
task 2.2: 生成語言模型

語言模型訓練須要使用n-gram算法,藉助sirlm工具能夠簡單實現,並進行語言模型生成:

安裝

下載sirlm安裝包(官網下載速慢,也可經過在github上找到相應資源下載),解壓後進入最上層目錄進行安裝。
export SRILM=pwd
make
把$make_dir/bin/i686-m64/加入PATH以便使用其中腳本
生成語言模型

在語料庫目錄下建立lm_word文件夾(方便管理),複製上面的字典lexicon.txt,並刪除前兩行,保存爲做爲words.txt做語料輸入文件進行n-gram語言模型生成(因爲只是詞彙識別設置n=1):

ngram-count -order 1 -text words.txt -lm word.arpa
其餘參數能夠參考:

-order  指定n-gram的n是多少,默認是3
-text   提供輸入的語料文件,統計該語料中的n-gram
-lm     指定輸出的lm文件
-vocab  用來指定對哪些詞進行n-gram統計
-wbdiscount1 表示1gram Witten-Bell discounting 
Note:參數順序無所謂
該命令生成arpa格式的語言模型文件,後面由kaldi的其餘工具轉換爲FST格式使用。

完成語言模型的生成後,對應的能夠在run.sh腳本中利用該部分的語言模型,經過kaldi提供的工具構建語言模型的FST格式文件,這部分 主要建立了data/{dict,lang,graph}目錄及相應文件,並在後面的構建解碼圖的過程當中使用。run.sh腳本該部分代碼:

#gen lang dir 
(
    echo "create new dir data/dict,lang,graph"
    cd $run_path
    mkdir -p data/{dict,lang,graph} && \
    cp $dataset//resource/dict/{extra_questions.txt,nonsilence_phones.txt,optional_silence.txt,silence_phones.txt} data/dict && \
    cat $dataset/resource/dict/lexicon.txt | \
    grep -v '<s>' | grep -v '</s>' | sort -u > data/dict/lexicon.txt || exit 1;
    utils/prepare_lang.sh --position_dependent_phones false data/dict "<SPOKEN_NOISE>" data/local/lang data/lang || exit 1;
    gzip -c $dataset/King-ASR-M-005/lm_word/word.arpa > data/graph/word.arpa.gz || exit 1;
    utils/format_lm.sh data/lang data/graph/word.arpa.gz $dataset/King-ASR-M-005/lm_word/lexicon.txt data/graph/lang || exit 1;
)
這裏主要包括utils/prepare_lang.sh 、 和utils/format_lm.sh 兩個腳本的調用,不做具體分析。

做者:zqh_zy
連接:http://www.jianshu.com/p/5b19605792ab
來源:簡書
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

 

task4 : 特徵提取(FMCC)

完成了語言模型的構建,下面開始生成聲學模型部分,首先對語音文件進行特徵提取,這裏用到了上面準備的文件,包括:text, wav.scp, utt2spk, spk2utt 。
run.sh中完成特徵提取,並對語音進行歸一化處理:

#gen MFCC features
rm -rf data/mfcc && mkdir -p data/mfcc &&  cp -R data/{train,cv,test} data/mfcc || exit 1;
for x in train cv test; do
   #make  mfcc 
   steps/make_mfcc.sh --nj $n --cmd "$train_cmd" data/mfcc/$x exp/make_mfcc/$x mfcc/$x || exit 1;
   #compute cmvn
   steps/compute_cmvn_stats.sh data/mfcc/$x exp/mfcc_cmvn/$x mfcc/$x || exit 1;
done
生成的特徵提取相關文件保存在data/mfcc目錄下,真實的數據保存在mfcc/目錄下。

task5 : 訓練聲學模型、 構建解碼圖

該部分調用kaldi腳本,訓練單音節模型,後面測試證實,單個詞彙的識別,該模型一樣能保證良好的識別效果,一樣run.sh腳本中:

#monophone
#steps/train_mono.sh --boost-silence 1.25 --nj $n --cmd "$train_cmd" data/mfcc/train data/lang exp/mono || exit 1;
聲學模型的訓練結果文件保存在exp/mono目錄下。下面構建解碼圖,這部分調用utils/mkgraph.sh, 利用先前建立的語言模型和上步訓練的聲學模型構建HCLG解碼圖,該部分生成的解碼圖保存在exp/mono/graph_word文件夾下:

utils/mkgraph.sh --mono --nj $n  data/graph/lang exp/mono exp/mono/graph_word  || exit 1;
task6: 測試

在local目錄下建立data_decode.sh 腳本對解碼步驟進行封裝:

#!/bin/bash
#decoding wrapper
#run from ../
nj=2
mono=false
. ./cmd.sh ## You'll want to change cmd.sh to something that will work on your system.
. ./path.sh ## Source the tools/utils (import the queue.pl)
. utils/parse_options.sh || exit 1;

decoder=$1
srcdir=$2
datadir=$3

if [ $mono = true ];then
  echo  "using monophone to generate graph"
  opt="--mono"
fi

#decode word
$decoder --cmd "$decode_cmd"  $srcdir/graph_word $datadir/test $srcdir/decode_test_word || exit 1
在run.sh腳本中調用上腳本:

#test mono model
local/data_decode.sh --nj 2 "steps/decode.sh" exp/mono data/mfcc &
這裏注意因爲測試集只有兩個說話者,併發度設置爲2,不然會出現文件分割數與併發數不匹配的狀況,解碼過程主要用到特徵提取後的test文件,上部分生成的解碼圖,測試結果在exp/mono/decode_test_word文件夾中查看。

爲了對測試結果進行評估,還需在local目錄下完成打分腳本相關的代碼,這裏參考thchs30,拷貝文件:score.sh、wer_output_filter 。

下面給出完整的run.sh腳本,以後運行腳本:

#!/bin/bash

. ./cmd.sh
. ./path.sh

run_path=`pwd`
n=8 #parallel jobs

#dataset path
dataset=~/dataset_wakeup

#data prepare
#gen text, wav.scp, utt2spk, spk2utt
local/data_prep.sh $run_path $dataset/King-ASR-M-005 || exit 1


#gen lang dir 
(
    echo "create new dir data/dict,lang,graph"
    cd $run_path
    mkdir -p data/{dict,lang,graph} && \
    cp $dataset//resource/dict/{extra_questions.txt,nonsilence_phones.txt,optional_silence.txt,silence_phones.txt} data/dict && \
    cat $dataset/resource/dict/lexicon.txt | \
    grep -v '<s>' | grep -v '</s>' | sort -u > data/dict/lexicon.txt || exit 1;
    utils/prepare_lang.sh --position_dependent_phones false data/dict "<SPOKEN_NOISE>" data/local/lang data/lang || exit 1;
    gzip -c $dataset/King-ASR-M-005/lm_word/word.arpa > data/graph/word.arpa.gz || exit 1;
    utils/format_lm.sh data/lang data/graph/word.arpa.gz $dataset/King-ASR-M-005/lm_word/lexicon.txt data/graph/lang || exit 1;
)

#gen MFCC features
rm -rf data/mfcc && mkdir -p data/mfcc &&  cp -R data/{train,cv,test} data/mfcc || exit 1;
for x in train cv test; do
   #make  mfcc 
   steps/make_mfcc.sh --nj $n --cmd "$train_cmd" data/mfcc/$x exp/make_mfcc/$x mfcc/$x || exit 1;
   #compute cmvn
   steps/compute_cmvn_stats.sh data/mfcc/$x exp/mfcc_cmvn/$x mfcc/$x || exit 1;
done

#monophone
steps/train_mono.sh --boost-silence 1.25 --nj $n --cmd "$train_cmd" data/mfcc/train data/lang exp/mono || exit 1;
#decode word

# make decoder graph
utils/mkgraph.sh --mono  data/graph/lang exp/mono exp/mono/graph_word  || exit 1;

#test mono model
local/data_decode.sh --nj 2 "steps/decode.sh" exp/mono data/mfcc &
運行腳本,因爲數據量不大,並不須要很長時間,運行測試結束查看效果:

[uesrname@hostname scoring_kaldi]$ ls
best_wer  log  penalty_0.0  penalty_0.5  penalty_1.0  test_filt.txt  wer_details
[uesrname@hostname scoring_kaldi]$ more best_wer 
%WER 5.57 [ 100 / 1795, 19 ins, 4 del, 77 sub ] exp/mono/decode_test_word/wer_17_1.0
錯詞率爲5.57%,在penalty_1.0中能夠查看最好的識別結果。

小結

文章記錄了從拿到語料庫,到應用Kaldi的全過程,主要想對流程進行總結,對語音識別相關的原理沒有涉及太多。另外這裏僅僅訓練了單音節模型,其餘模型能夠參照thchs30完成,這裏再也不補充。
過程當中遇到的小問題不少,一個比較典型的,一開始想偷懶直接使用thchs30的詞典,後來識別結果不好,單詞均爲一個或兩個絕不相干的字。考慮本身語料庫中的詞彙在thchs30的詞典中並未涉及,仍是經過本身標註詞典解決問題。

做者:zqh_zy
連接:http://www.jianshu.com/p/6338fab6bd0a
來源:簡書
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
相關文章
相關標籤/搜索