online的操作介紹

bbzz2發表於2017-06-15

在windows下裝虛擬機器UBuntu的機器上玩。你的虛擬機器記憶體要大些(4g--8g最好)。不然很痛苦。


找感覺一定要從中文開始,我們直接拿那個清華大學的中文例子(thchs30)開練,

1 首先進入githab下載的原始碼中egs目錄下找到這個例子,然後要看一遍它的介紹。在readme裡面有語料庫的下載方式。把它下完(3個壓縮包全下)。4個多G比較大。

2 考到對應的資料夾下(與s5同級別即可),取名叫thchs30-openslr,將所有壓縮包解壓到這下面

3 開啟s5目錄,編輯cmd.sh.  如下:()

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

export cuda_cmd=run.pl


4 開啟run.sh,看到#data preparation這句,在它之後就全是shell的命令。建議一條一條的跑。不然中間會有莫名奇妙的斷檔和錯誤。如何一條條跑呢? 使用註釋:

<<!EOF!            !EOF!   這兩句相當於C語言的/*  */.   但願這個你能看懂。在此謝謝小凡妹妹的線上指導。


5按照上面的一句一句的來。它大概有幾個過程:資料準備,monophone單音素訓練, tri1三因素訓練, trib2進行lda_mllt特徵變換,trib3進行sat自然語言適應,trib4做quick(這個我也不懂),後面就是dnn了


6當執行到dnn時候會報錯,因為預設dnn都是用GPU來跑的。它會檢查一下,發現只在CPU下,就終止了。這裡建議不要跑dnn了。因為我試過,改成CPU之後跑了7,8天,才迭代17,18次。太慢了。而一次訓練怎麼的也得20多次。還要訓練好幾回。所以,想跑dnn的話還是找GPU吧。


7.下面說說前面的幾個步驟幹啥用。其實每一次都會有個模型,這些模型都可以用了,你可以看它的exp目錄,所有的步驟都在這裡面輸出。我們先簡單看一個,tri1,開啟後,有個decode_test_word,裡面有個scoring_kaldi,best_wer就是它的錯誤率,36.15%.好回到tri1下,看到final.mdl了嗎,這個就是有用的東西,學出來的模型。另外還得到graph_word下面,找到words.txt,和HCLG.fst,一個是字典,一個是有限狀態機。有這3個檔案,就可以來使用你的識別功能了。


8.這步是比較好玩的了。我們用這個模型來識別自己的語言。(是非常的不準,只是感受一下而已),回到原始碼的src下。make ext  編譯擴充套件程式。(在這之前確定你的tools資料夾下的portaudio已經裝好)之後,會看到onlinebin資料夾。裡面有兩個程式,online-wav-gmm-decode-faster 用來回放wav檔案來識別的,online-gmm-decode-faster用來從麥克風輸入聲音來識別的。

8.1 這裡開個小差:portaudio 裝好後,有可能收不到聲音,可以裝個audio recoder(用apt-get),之後用它錄音試試,測測是否有聲音,只要能錄音,portaudio就沒問題,一般裝完就好了,不行就再重啟一下。不知道為啥。

8.2 介紹下online。很鄙視kaldi在這塊的設計,居然搞出來兩個版本。online是不再維護了。但是非常好用,online2是新的。引數n多。也不支援從麥克風採集。特別不人性化,如果有那個朋友把online2研究好了,請把經驗也分享一下。


9.我們找一個例子吧:去egs下,開啟voxforge,裡面有個online_demo,直接考到thchs30下。在online_demo裡面建2個資料夾online-data  work,在online-data下建兩個資料夾audio和models,audio下放你要回放的wav,models建個資料夾tri1,把s5下的exp下的tri1下的final.mdl和35.mdl(final.mdl是快捷方式)考過去。把s5下的exp下的tri1下的graph_word裡面的words.txt,和HCLG.fst,考到models的tri1下。


10。開啟online_demo的run.sh

a)將下面這段註釋掉:(這段是voxforge例子中下載現網的測試語料和識別模型的。我們測試語料自己準備,模型就是tri1了)

if [ ! -s ${data_file}.tar.bz2 ]; then
    echo "Downloading test models and data ..."
    wget -T 10 -t 3 $data_url;


    if [ ! -s ${data_file}.tar.bz2 ]; then
        echo "Download of $data_file has failed!"
        exit 1
    fi
fi

b) 然後再找到如下這句,將其路徑改成tri1

 # Change this to "tri2a" if you like to test using a ML-trained model
ac_model_type=tri2b_mmi

# Alignments and decoding results  

---------------------------------------------------------------

改成:

 # Change this to "tri2a" if you like to test using a ML-trained model
ac_model_type=tri1

c)

online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85\
            --max-active=4000 --beam=12.0 --acoustic-scale=0.0769 \
            scp:$decode_dir/input.scp $ac_model/model 

————————————————————————————

改成:online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85\
            --max-active=4000 --beam=12.0 --acoustic-scale=0.0769 \
            scp:$decode_dir/input.scp $ac_model/final.mdl



11。直接./run.sh吧,就是開始回放識別了。裡面有提示,./run.sh --test-mode live命令就是從麥克風識別。

12.昇華部分在這裡。我們試完tri1的模型後,一定很想試試tri2或3.但當你操作時,會遇到如下的問題:

ERROR (online-wav-gmm-decode-faster:LogLikelihoods():diag-gmm.cc:533) DiagGmm::ComponentLogLikelihood, dimension mismatch 39vs. 40

怎麼解決? 仔細看看這個原始檔,它是dieta的。如果要是ldp還得加matrix引數(拿tri2b舉例)。

於是修改run.sh成如下這個樣子  :(就是把final.mat考過來,引入命令中)

if [ -s $ac_model/matrix ]; then
    trans_matrix=$ac_model/12.mat
fi

同時把把s5下的exp下的tri2b下的12.mat考到models的tri2b下。

13 再次修改run.sh成如下這個樣子(新增2個引數--left-context=3 --right-context=3)

 online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85 \
            --max-active=4000 --beam=12.0 --acoustic-scale=0.0769 --left-context=3 --right-context=3\
            scp:$decode_dir/input.scp $ac_model/final.mdl $ac_model/HCLG.fst \
            $ac_model/words.txt '1:2:3:4:5' ark,t:$decode_dir/trans.txt \
            ark,t:$decode_dir/ali.txt $trans_matrix;;

14 執行./run.sh,結果如下。怎麼樣,有點酷不? 如果想使用tri2等模型做麥克風線上的,也同理修改就可以了。

online-wav-gmm-decode-faster --verbose=1 --rt-min=0.8 --rt-max=0.85 --max-active=4000 --beam=12.0 --acoustic-scale=0.0769 --left-context=3 --right-context=3 scp:./work/input.scp online-data/models/tri2b/final.mdl online-data/models/tri2b/HCLG.fst online-data/models/tri2b/words.txt 1:2:3:4:5 ark,t:./work/trans.txt ark,t:./work/ali.txt online-data/models/tri2b/12.mat 
File: D4_750
蘇北 軍禮 下跪 將 是 馬 湛 殺人 裡 杜 唐 據 五 蘇 並 案 但 甜美 但 也 分析 抗戰 


基於kaldi的線上中文識別,online的操作介紹

科大訊飛的語音識別庫,感覺只有後半部分,想實現前半部分的採集卻找不到可以用的東西。於是自己做一個。

可以隨便拿個開源的程式碼改一下,我們就使用arecord吧。


這只是個簡單的端點檢測,根據聲音能量,也就是分貝大小來調整的。


下載原始碼:

http://www.alsa-project.org/main/index.PHP/Main_Page


arecord在alsa的alsa-utils裡面。當然依賴於Library (alsa-lib),所以兩個都要下下來

下載頁在這:

http://www.alsa-project.org/main/index.php/Download


然後在本地就可以編譯了。編譯時會提示需要各種工具,直接按提示下載即可。 我用的是ubuntu直接apt-get,就得到了。


編好之後,進入aplay資料夾下。可以看到arecord其實是一個連結鏈到aplay了。aplay.c就是原始碼,我們直接改它就行


一:定義變數:

95行左右,

static char *command;
static snd_pcm_t *handle;
static struct {
snd_pcm_format_t format;
unsigned int channels;
unsigned int rate;
} hwparams, rhwparams;
static int timelimit = 0;
//add by ljh 
static int silflag = 0;     //出現靜音情況
static off64_t silcount= 0; //j靜音時要收到的包
static int enablesil = 0; //啟用靜音檢測狀態


1648行,修改函式如下

static void print_vu_meter_mono(int perc, int maxperc)//因為是單音輸入,所以我們只改這一塊,呼叫時配合mono設定。
{
const int bar_length = 50;
char line[80];
int val;

//add by ljh
if(perc>=2)//speak
{
enablesil = 1;// sel open
silflag = 0;
}else if(silflag==0)//into silflag
{
//init silcount  1s
silcount = 2* snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
silflag =1; 
}else // already silence
{
}


for (val = 0; val <= perc * bar_length / 100 && val < bar_length; val++)
line[val] = '#';
for (; val <= maxperc * bar_length / 100 && val < bar_length; val++)
line[val] = ' ';
line[val] = '+';
for (++val; val <= bar_length; val++)
line[val] = ' ';
if (maxperc > 99)
sprintf(line + val, "| MAX");
else
sprintf(line + val, "| %02i%%", maxperc);
fputs(line, stderr);
if (perc > 100)
fprintf(stderr, _(" !clip  "));
}


最後是capture函式,分別在 3050和3103處修改:

static void capture(char *orig_name)
{
int tostdout=0;/* boolean which describes output stream */
int filecount=0;/* number of files written */
char *name = orig_name;/* current filename */
char namebuf[PATH_MAX+1];
off64_t count, rest;/* number of bytes to capture */
struct stat statbuf;


/* get number of bytes to capture */
count = calc_count();
if (count == 0)
count = LLONG_MAX;
/* compute the number of bytes per file */
max_file_size = max_file_time *
snd_pcm_format_size(hwparams.format,
   hwparams.rate * hwparams.channels);
/* WAVE-file should be even (I'm not sure), but wasting one byte
  isn't a problem (this can only be in 8 bit mono) */
if (count < LLONG_MAX)
count += count % 2;
else
count -= count % 2;


/* display verbose output to console */
header(file_type, name);


/* setup sound hardware */
set_params();


/* write to stdout? */
if (!name || !strcmp(name, "-")) {
fd = fileno(stdout);
name = "stdout";
tostdout=1;
if (count > fmt_rec_table[file_type].max_filesize)
count = fmt_rec_table[file_type].max_filesize;
}
init_stdin();


//add by ljh
silcount = 2* snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);


do {
/* open a file to write */
if(!tostdout) {
/* upon the second file we start the numbering scheme */
if (filecount || use_strftime) {
filecount = new_capture_file(orig_name, namebuf,
    sizeof(namebuf),
    filecount);
name = namebuf;
}

/* open a new file */
if (!lstat(name, &statbuf)) {
if (S_ISREG(statbuf.st_mode))
remove(name);
}
fd = safe_open(name);
if (fd < 0) {
perror(name);
prg_exit(EXIT_FAILURE);
}
filecount++;
}


rest = count;
if (rest > fmt_rec_table[file_type].max_filesize)
rest = fmt_rec_table[file_type].max_filesize;
if (max_file_size && (rest > max_file_size)) 
rest = max_file_size;


/* setup sample header */
if (fmt_rec_table[file_type].start)
fmt_rec_table[file_type].start(fd, rest);


/* capture */
fdcount = 0;
while (rest > 0 && recycle_capture_file == 0 && !in_aborting) {
size_t c = (rest <= (off64_t)chunk_bytes) ?
(size_t)rest : chunk_bytes;
size_t f = c * 8 / bits_per_frame;
if (pcm_read(audiobuf, f) != f)
break;
if (write(fd, audiobuf, c) != c) {
perror(name);
prg_exit(EXIT_FAILURE);
}
count -= c;
rest -= c;
fdcount += c;


//add by ljh
if(silflag == 1 && enablesil == 1)//silence  sil open
{
silcount -= c;
if(silcount <= 0)// after 1s 
{
// game over
count = 0;
rest = 0;
}
}
}


/* re-enable SIGUSR1 signal */
if (recycle_capture_file) {
recycle_capture_file = 0;
signal(SIGUSR1, signal_handler_recycle);
}


/* finish sample Container */
if (fmt_rec_table[file_type].end && !tostdout) {
fmt_rec_table[file_type].end(fd);
fd = -1;
}


if (in_aborting)
break;


/* repeat the loop when format is raw without timelimit or
* requested counts of data are recorded
*/
} while ((file_type == FORMAT_RAW && !timelimit) || count > 0);
}



然後再編譯就好了。配合到程式碼裡使用可以這樣:

system("./arecord -D hw:0,0 -f S16_LE -V mono -r8000 -c 2 -t wav  wav/mytest.wav");

把aplay拷過去,建立個連結ln ./alplay  arecord

記得把依賴的庫libasound.so.2也得考過去哦。


原理:capture中,每次會把讀到的資料做個能量計算並列印出來,通過其計算的部分來設定個靜音開始狀態,同時在設定接下來需要讀多少個位元組,這部分可以仿照其原有的count計算方法。這樣,當進入靜音狀態開始時,系統再讀入設定好的位元組後,就自動退出了。當然在此期間,假如使用者又說話了。那麼靜音狀態關閉,silcount將不會生效。直到再此進入靜音狀態開始  

還有一個點是當錄音剛啟動時,如果使用者不說話,我們不希望它退出,要一直等在那裡,於是又需要個變數就是啟用錄音狀態enablesil ,預設是不啟用,一旦有使用者說話,就把它啟用。這樣silflag就開始生效了。

如果想直接拿來用,這裡有編譯好的程式,和原始碼可以下載:

http://download.csdn.NET/detail/lijin6249/9580171

 

在linux上如何做一個簡單的vad功能,即錄音時說話停止即錄音停止。



相關文章