1. 程式人生 > >說話人識別中的VAD

說話人識別中的VAD

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

本文根據kaldi中的vad的演算法 kaldi/src/ivector/voice-activity-detection.cc以及網上的一些資源來總結一下這個知識點。

首先VAD的全稱是:Voice Activity Detection (語音啟用檢

測), 能夠區分傳輸語音訊號中的語音訊號和背景噪音, 當然還能在通訊中區分語音和靜默段能夠區分傳輸語音訊號中的語音訊號和背景噪音,

避免頻寬資源的浪費,這裡我們只討論在說話人識別中需要區分背景噪音來構建UBM模型。

下面直接看kaldi的原始碼,注意看註釋

run.sh中呼叫下面computer_vad_decision.sh

Usage: $0 [options] <data-dir> <log-dir> <path-to-vad-dir>

[plain] 
view plain
 copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. sid/compute_vad_decision.sh --nj 40 --cmd "$train_cmd" \  
  2.     data/train exp/make_vad $vaddir  
computer_vad_decision.sh呼叫的是

Usage: compute-vad [options] <feats-rspecifier> <vad-wspecifier>

輸入的是每一個feats檔案,由於上邊的nj是40,所以這JOB: 1~40, 輸入mfcc.ark 輸出vad.ark

compute-vad --config=$vad_config scp:$sdata/JOB/feats.scp ark,scp:$vaddir/vad_${name}.JOB.ark,$vaddir/vad_${name}.JOB.scp

computer-vad是  kaldi/src/ivectorbin/compute-vad.cc 下面是 computer-vad.cc中的邏輯:

[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. for (;!feat_reader.Done(); feat_reader.Next()) {  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #讀取每一句話  
  2. std::string utt = feat_reader.Key();  
  3. Matrix<BaseFloat> feat(feat_reader.Value());  
  4. if (feat.NumRows() == 0) {  
  5.   KALDI_WARN << "Empty feature matrix for utterance " << utt;  
  6.   num_err++;  
  7.   continue;  
  8. }  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1.   #宣告一個vector, 維數 = 一句話的幀數  
  2.   Vector<BaseFloat> vad_result(feat.NumRows());  
  3.   #然後是計算vad,一個可選引數集合,mfcc的matrix, 返回的結果vertor, 看下一個的原始碼片段  
  4.   ComputeVadEnergy(opts, feat, &vad_result);  
  5.     
  6.   double sum = vad_result.Sum();  
  7.   if (sum == 0.0) {  
  8.     KALDI_WARN << "No frames were judged voiced for utterance " << utt;  
  9.     num_unvoiced++;  
  10.   } else {  
  11.     num_done++;  
  12.   }  
  13.   tot_decision += vad_result.Sum();  
  14.   tot_length += vad_result.Dim();  
  15.   
  16.   if (!(omit_unvoiced_utts && sum == 0)) {  
  17.     vad_writer.Write(utt, vad_result);  
  18.   }  
  19. }  

下面這個是計算vad結果的函式:  kaldi / src / ivector / voice-activity-detection.cc

[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #include "ivector/voice-activity-detection.h"  
  2. #include "matrix/matrix-functions.h"  
  3.   
  4.   
  5. namespace kaldi {  
  6.   
  7. void ComputeVadEnergy(const VadEnergyOptions &opts,  
  8.                       const MatrixBase<BaseFloat> &feats,  
  9.                       Vector<BaseFloat> *output_voiced) {  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #feats是mfcc的特徵矩陣  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. int32 T = feats.NumRows();  
  2. output_voiced->Resize(T);  
  3. if (T == 0) {  
  4.   KALDI_WARN << "Empty features";  
  5.   return;  
  6. }  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #定義一個維度為T的vector  
  2. Vector<BaseFloat> log_energy(T);  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #激昂feats的第0列as log_energy的value  
  2. log_energy.CopyColFromMat(feats, 0); // column zero is log-energy.  
  3. #讀取配置檔案中的噪聲的閾值: <span style="font-family: Menlo; font-size: 11px;">--vad-energy-threshold=5.5, 若小於這個值則為噪音,若大於則為語音訊號  
  4. BaseFloat energy_threshold = opts.vad_energy_threshold;  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. #讀取配置檔案中:  
[cpp]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1.  if (opts.vad_energy_mean_scale != 0.0) {  
  2.     KALDI_ASSERT(opts.vad_energy_mean_scale > 0.0);  
  3.     energy_threshold += opts.vad_energy_mean_scale * log_energy.Sum() / T;  
  4.   }  
  5.     
  6.   KALDI_ASSERT(opts.vad_frames_context >= 0);  
  7.   KALDI_ASSERT(opts.vad_proportion_threshold > 0.0 &&  
  8.                opts.vad_proportion_threshold < 1.0);  
  9.   for (int32 t = 0; t < T; t++) {  
  10.     const BaseFloat *log_energy_data = log_energy.Data();  
  11.     int32 num_count = 0, den_count = 0, context = opts.vad_frames_context;  
  12.     for (int32 t2 = t - context; t2 <= t + context; t2++) {  
  13.       if (t2 >= 0 && t2 < T) {  
  14.         den_count++;  
  15.         if (log_energy_data[t] > energy_threshold)  
  16.           num_count++;  
  17.       }  
  18.     }  
  19.     if (num_count >= den_count * opts.vad_proportion_threshold)  
  20.       (*output_voiced)(t) = 1.0;  
  21.     else  
  22.       (*output_voiced)(t) = 0.0;  
  23.   }  
  24. }    
  25.   
  26. }  

下面我將給出一個實際的計算過程的demo:

其中raw_mfcc_train1.txt 和 vad_train1.txt分別是在mfcc目錄下執行:

./../../../../src/bin/copy-vector ark:vad_train.1.ark ark,t:- > vad_train1.txt 

./../../../../src/featbin/copy-feats ark:raw_mfcc_train.1.ark ark,t:- > raw_mfcc_train1.txt 

[python]  view plain  copy   在CODE上檢視程式碼片 派生到我的程式碼片
  1. import numpy as np  
  2.   
  3. def read_feats(filename):  
  4.     f = open(filename, 'r')  
  5.     all_xs = []  
  6.     arr = []  
  7.     for line in f:  
  8.         temp = []  
  9.         if '[' in line:  
  10.             pass  
  11.         else:  
  12.             l = line.strip().split(' ')  
  13.             #print "l->",len(l)  
  14.             if ']' in l:  
  15.                 l_temp = l[:-1]  
  16.                 for i in range(len(l_temp)):  
  17.                     if l_temp[i] != '':  
  18.                         temp.append(eval(l_temp[i]))  
  19.                 #print "temp->",len(temp)  
  20.                 arr.append(temp)  
  21.                 all_xs.append(arr)  
  22.                 arr = []  
  23.             else:  
  24.                 for i in range(len(l)):  
  25.                     if l[i] != '':  
  26.                         temp.append(eval(l[i]))  
  27.                 #print "temo->",len(temp)  
  28.                 arr.append(temp)  
  29.     return all_xs  
  30. mfcc_filename = 'raw_mfcc_train1.txt'  
  31. all_feats = read_feats(mfcc_filename)  
  32. vad_energy_threshold = 5.5  
  33. vad_energy_mean_scale = 0.5  
  34. vad_frames_context = 5  
  35. vad_proportion_threshold =