Kaldi命令詞識(shí)別

kaldi

剛剛拿到一個(gè)簡(jiǎn)單語(yǔ)料庫(kù)練手,發(fā)現(xiàn)只有語(yǔ)音和對(duì)應(yīng)文字, 這篇文章記錄了從數(shù)據(jù)預(yù)處理到kaldi對(duì)數(shù)據(jù)進(jìn)行訓(xùn)練和測(cè)試的全過(guò)程,這里首先訓(xùn)練單音節(jié)模型,其他模型后面再補(bǔ)充。

語(yǔ)料庫(kù)處理

task 0: 觀察語(yǔ)料庫(kù)

語(yǔ)料庫(kù)主要用于命令詞識(shí)別,包括200個(gè)詞匯,2000條語(yǔ)音,10個(gè)說(shuō)話者分別對(duì)200個(gè)詞匯進(jìn)行錄音。語(yǔ)音目錄以說(shuō)話者id標(biāo)識(shí):

$ tree -d
├── speaker001
├── speaker002
├── speaker003
├── speaker004
├── speaker005
├── speaker006
├── speaker007
├── speaker008
├── speaker009
├── speaker010
└── Levoice.list

每個(gè)說(shuō)話者文件夾目錄下包含對(duì)應(yīng)的200條語(yǔ)音:

└┤ tree speaker001
speaker001
├── 00001.wav
├── 00002.wav
├── 00003.wav
...
└── 00200.wav

語(yǔ)音文字說(shuō)明文件Levoice.list 格式為<語(yǔ)音id> <文字> <錄音時(shí)長(zhǎng)>,例如:

└┤ head -n 2 Levoice.list 
speaker001/00001.wav    三六零通訊錄  5.6
speaker001/00002.wav    三六五日歷   2.8

語(yǔ)料庫(kù)所給的資源應(yīng)用到kaldi還需要漢字發(fā)音詞典,這里只能自己準(zhǔn)備,下面會(huì)參考thchs30語(yǔ)料庫(kù)的詞典準(zhǔn)備自己的詞典。

task 1: 預(yù)處理語(yǔ)料庫(kù)

為方便后續(xù)操作,需要對(duì)語(yǔ)料庫(kù)文件進(jìn)行預(yù)處理,這部分包括:

  • 重新重命名語(yǔ)音文件,使2000個(gè)語(yǔ)音文件具有唯一標(biāo)識(shí)(speakerid_voiceid.wav)
  • 劃分訓(xùn)練、測(cè)試、驗(yàn)證數(shù)據(jù)集
  • 根據(jù)Levoice.list生成utt2words.txt ,進(jìn)行文件名對(duì)應(yīng)漢字映射。

上述過(guò)程腳本(注意rname命令在Ubuntu和Centos中有細(xì)微差別):

 !/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)"

語(yǔ)料庫(kù)對(duì)訓(xùn)練集、驗(yàn)證集、測(cè)試集參考thchs30,這里將說(shuō)話人9、10語(yǔ)音作為測(cè)試集,再?gòu)?-8語(yǔ)音集中的1600百條語(yǔ)音文件四條語(yǔ)音為組隨機(jī)選擇一條語(yǔ)音歸入驗(yàn)證集,剩下的作為訓(xùn)練集。劃分結(jié)果訓(xùn)練集、驗(yàn)證集、測(cè)試集比例6:2:2。

在語(yǔ)料庫(kù)目錄運(yùn)行上腳本,會(huì)在該目錄下產(chǎn)生trian、test和cv目錄,這些目錄及文件將被后面使用。

最后直接將Levoice.list中的信息進(jìn)行簡(jiǎn)單字符替換即可:

speaker001/00001.wav    三六零通訊錄  5.6
---->
speaker001_00001.wav    三六零通訊錄  5.6

可以在vi或其他編輯器中替換即可。

應(yīng)用Kaldi

task0 : 構(gòu)建kaldi項(xiàng)目結(jié)構(gòu)

參照其他項(xiàng)目,首先復(fù)制創(chuàng)建項(xiàng)目結(jié)構(gòu)目錄,配置文件以及項(xiàng)目需要使用的依賴工具,這里多參考thchs30部分結(jié)構(gòu)。在egs 目錄下建立/wakeup/s5作為項(xiàng)目目錄,在該目錄下準(zhǔn)備以下文件:

$ tree -L 1
|-- cmd.sh // 運(yùn)行配置目錄
|-- conf  // 配置文件目錄
|-- local //存放run.sh 中調(diào)用的腳本工具,需要自己編寫(xiě)
|-- path.sh //Kaldi 工具和庫(kù)目錄添加到PATH
|-- run.sh // top層腳本,運(yùn)行該腳本訓(xùn)練數(shù)據(jù)和測(cè)試, 需要自己編寫(xiě)
|-- steps // kaldi 腳本工具, 復(fù)制到工程目錄下
|-- tools // kaldi 腳本工具, 復(fù)制到工程目錄下
`-- utils // kaldi 腳本工具, 復(fù)制到工程目錄下

這里cmd.sh里根據(jù)自己運(yùn)行方式配置運(yùn)行參數(shù),這里配置成單機(jī)運(yùn)行

export train_cmd=run.pl
export decode_cmd="run.pl --mem 4G"
export mkgraph_cmd="run.pl --mem 8G"

conf 目錄包含一些配置文件,這里主要將系統(tǒng)采樣頻率與語(yǔ)料庫(kù)的采樣頻率設(shè)置為一致:

$ 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 : 準(zhǔn)備訓(xùn)練文件

參照kaldi數(shù)據(jù)準(zhǔn)備部分文檔,該部分需要自己根據(jù)語(yǔ)料庫(kù)分別就train,test,cross validation目錄生成以下文件:

  • text : < uttid > < word >
  • wav.scp : < uttid > < utter_file_path >
  • utt2spk : < uttid > < speakid >
  • spk2utt : < speakid > < uttid >
  • word.txt : 同 text

編寫(xiě)local/data_pre.sh腳本供run.sh調(diào)用(下面會(huì)涉及run.sh腳本的編寫(xiě)),傳入?yún)?shù)運(yùn)行目錄以及語(yǔ)料庫(kù)目錄:

#!/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 : 訓(xùn)練語(yǔ)言模型

由于這里僅僅需要對(duì)語(yǔ)料庫(kù)中的200個(gè)命令詞進(jìn)行識(shí)別,大而全的漢語(yǔ)詞典并不必要,這里需要根據(jù)自己的語(yǔ)料建立詞典并且生成語(yǔ)言模型。

task 2.1 : 準(zhǔn)備詞典

根據(jù)kaldi的要求,需要準(zhǔn)備的詞典包括以下文件(我這里和語(yǔ)料庫(kù)放在同個(gè)目錄下,后面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

對(duì)上面文件簡(jiǎn)單說(shuō)明:

  • lexicon.txt: 詞典,包括語(yǔ)料中涉及的詞匯與發(fā)音,與單字及其發(fā)音。
  • silence_phones.txt:靜音標(biāo)識(shí),這里為sil。
  • nonsilence_phones.txt : 非靜音標(biāo)識(shí),與silence_phones.txt共同組成lexicon.txt中的發(fā)音。
  • extra_questions.txt : 包含重音音調(diào)標(biāo)記,這里沒(méi)有用到
  • lexiconp.txt : 如果一個(gè)詞有不同發(fā)音,則會(huì)在不同行中出現(xiàn)多次。如果你想使用發(fā)音概率,你需要建立 exiconp.txt 而不是 lexicon.txt,這里未使用

以上文件可以參考復(fù)制thchs30的resource資源,只要替換lexicon.txt為自己的字典,并且追加thchs30中l(wèi)exicon.txt中所有的單字及其發(fā)音(簡(jiǎn)單awk命令即可)。此外該語(yǔ)料庫(kù)僅僅提供了漢字無(wú)對(duì)應(yīng)發(fā)音,需要自己參考thchs30中的詞典準(zhǔn)備,(心想只有200條,覺(jué)得手打的會(huì)很快,事實(shí)用了2-3個(gè)小時(shí),心累,回頭想可以寫(xiě)程序完成)。
lexicon.txt 文件內(nèi)容大致為:

$ 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圖庫(kù)    s an1 d i4 t u2 k u4
task 2.2: 生成語(yǔ)言模型

語(yǔ)言模型訓(xùn)練需要使用n-gram算法,借助sirlm工具可以簡(jiǎn)單實(shí)現(xiàn),并進(jìn)行語(yǔ)言模型生成:

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

在語(yǔ)料庫(kù)目錄下創(chuàng)建lm_word文件夾(方便管理),復(fù)制上面的字典lexicon.txt,并刪除前兩行,保存為作為words.txt作語(yǔ)料輸入文件進(jìn)行n-gram語(yǔ)言模型生成(由于只是詞匯識(shí)別設(shè)置n=1):

ngram-count -order 1 -text words.txt -lm word.arpa

其他參數(shù)可以參考:

-order  指定n-gram的n是多少,默認(rèn)是3
-text   提供輸入的語(yǔ)料文件,統(tǒng)計(jì)該語(yǔ)料中的n-gram
-lm     指定輸出的lm文件
-vocab  用來(lái)指定對(duì)哪些詞進(jìn)行n-gram統(tǒng)計(jì)
-wbdiscount1 表示1gram Witten-Bell discounting 
Note:參數(shù)順序無(wú)所謂

該命令生成arpa格式的語(yǔ)言模型文件,后面由kaldi的其他工具轉(zhuǎn)換為FST格式使用。

完成語(yǔ)言模型的生成后,對(duì)應(yīng)的可以在run.sh腳本中利用該部分的語(yǔ)言模型,通過(guò)kaldi提供的工具構(gòu)建語(yǔ)言模型的FST格式文件,這部分 主要?jiǎng)?chuàng)建了data/{dict,lang,graph}目錄及相應(yīng)文件,并在后面的構(gòu)建解碼圖的過(guò)程中使用。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 兩個(gè)腳本的調(diào)用,不作具體分析。

考慮篇幅,未完待續(xù),原創(chuàng)文章,轉(zhuǎn)載注明出處。原文到博客
更多關(guān)注公眾號(hào):

wechat

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,314評(píng)論 25 708
  • Ubuntu的發(fā)音 Ubuntu,源于非洲祖魯人和科薩人的語(yǔ)言,發(fā)作 oo-boon-too 的音。了解發(fā)音是有意...
    螢火蟲(chóng)de夢(mèng)閱讀 99,571評(píng)論 9 467
  • 好奇的我 我是一個(gè)好奇的孩紙,因?yàn)榭匆?jiàn)好多的訂閱號(hào)啊,公眾號(hào)啊,所以,想自己為什么不可以試試呢?好奇其實(shí)不可怕。 ...
    龍七七閱讀 238評(píng)論 0 0
  • 01 之所以會(huì)經(jīng)常想起小學(xué)時(shí)光,是因?yàn)槊磕甓欤夷钱惓7屎翊謮训挠沂质持付家凵蠋滋欤菐滋焐觳桓疑欤詹桓椅眨?..
    善下歸海閱讀 342評(píng)論 0 6
  • 光影間 樓上的紅漆掉了幾片 遠(yuǎn)處你的身影漸行漸遠(yuǎn) 打開(kāi)你的世界 是所有的期盼 我只想 將飛過(guò)的小葉折成兩半 一片我...
    畫(huà)者eic閱讀 174評(píng)論 0 0