Kaldiに関する処理を日本語のドキュメントでまとめてみた（特徴量抽出編）3

#前回

１：http://qiita.com/GushiSnow/items/cc1440e0a8ea199e78c5

２：http://qiita.com/GushiSnow/items/a24cad7231de341738ee

スクリプト解説の続きです。

#MFCC特徴量を抽出します。

MFCC
音声認識に必要な特徴量を取得する処理。

解説記事
http://recognition.web.fc2.com/tips/mfcc.html

```shell-session
for x in train test; do                                                     
  steps/make_mfcc.sh --cmd "$train_cmd" --nj $njobs \                        
  data/$x exp/make_mfcc/$x $mfccdir || exit 1;                                
  steps/compute_cmvn_stats.sh data/$x exp/make_mfcc/$x $mfccdir || exit 1;   
done
```

上記のスクリプトを動作させることで、MFCC特徴量を出力することが可能

##１：データが正しいかを調査

```shell-session
check_sorted_and_uniq $data/utt2spk
```

ユーザーの発話が対応しているファイルが正しいか判断


```shell-session
check_sorted_and_uniq $data/spk2utt  
```

話者と発話が正しいかを判断

```shell-session
num_utts=`cat $tmpdir/utts | wc -l`
if [ -f $data/text ]; then
  check_sorted_and_uniq $data/text
  text_len=`cat $data/text | wc -l`
  illegal_sym_list="
 
 #0"
  for x in $illegal_sym_list; do
    if grep -w "$x" $data/text > /dev/null; then
      echo "$0: Error: in $data, text contains illegal symbol $x"
      exit 1;
    fi
  done
  awk '{print $1}' < $data/text > $tmpdir/utts.txt
  if ! cmp -s $tmpdir/utts{,.txt}; then
    echo "$0: Error: in $data, utterance lists extracted from utt2spk and text"
    echo "$0: differ, partial diff is:"
    partial_diff $tmpdir/utts{,.txt}
    exit 1;
  fi
fi
```

ユーザーの発話とテキストが一致しているかをチェック、もし一致していない場合は不一致の部分を出力

```shell-session
if [ -f $data/wav.scp ]; then
  check_sorted_and_uniq $data/wav.scp

  if [ -f $data/segments ]; then

    check_sorted_and_uniq $data/segments
    # We have a segments file -> interpret wav file as "recording-ids" not utter
ance-ids.
    ! cat $data/segments | \
      awk '{if (NF != 4 || ($4 <= $3 && $4 != -1)) { print "Bad line in segments
 file", $0; exit(1); }}' && \
      echo "$0: badly formatted segments file" && exit 1;
    
    segments_len=`cat $data/segments | wc -l`
    if [ -f $data/text ]; then
      ! cmp -s $tmpdir/utts <(awk '{print $1}' <$data/text) && \
        echo "$0: Utterance list differs between $data/text and $data/segments "
 && \
        echo "$0: Lengths are $segments_len vs $num_utts";
    fi

    cat $data/segments | awk '{print $2}' | sort | uniq > $tmpdir/recordings
    awk '{print $1}' $data/wav.scp > $tmpdir/recordings.wav
    if ! cmp -s $tmpdir/recordings{,.wav}; then
      echo "$0: Error: in $data, recording-ids extracted from segments and wav.scp"
      echo "$0: differ, partial diff is:"
      partial_diff $tmpdir/recordings{,.wav}
      exit 1;
    fi
```
１：セグメントファイルが正常データかを判定
２：セグメントファイルのフォーマットが適切かを判定
３：セグメントとテキストを比較
４：セグメントファイルのidとwavファイルのidが一致するかを見る。


```shell-session
    if [ -f $data/reco2file_and_channel ]; then
      # this file is needed only for ctm scoring; it's indexed by recording-id.
      check_sorted_and_uniq $data/reco2file_and_channel
      ! cat $data/reco2file_and_channel | \
        awk '{if (NF != 3 || ($3 != "A" && $3 != "B" )) { 
                if ( NF == 3 && $3 == "1" ) {
                  warning_issued = 1;
                } else {
                  print "Bad line ", $0; exit 1; 
                }
              }
            } 
            END {
              if (warning_issued == 1) {
                print "The channel should be marked as A or B, not 1! You should change it ASAP! "
              }
            }' && echo "$0: badly formatted reco2file_and_channel file" && exit 1;
      cat $data/reco2file_and_channel | awk '{print $1}' > $tmpdir/recordings.r2fc
      if ! cmp -s $tmpdir/recordings{,.r2fc}; then
        echo "$0: Error: in $data, recording-ids extracted from segments and reco2file_and_channel"
        echo "$0: differ, partial diff is:"
        partial_diff $tmpdir/recordings{,.r2fc}
        exit 1;
      fi
    fi
  else
    # No segments file -> assume wav.scp indexed by utterance.
    cat $data/wav.scp | awk '{print $1}' > $tmpdir/utts.wav
    if ! cmp -s $tmpdir/utts{,.wav}; then
      echo "$0: Error: in $data, utterance lists extracted from utt2spk and wav.scp"
      echo "$0: differ, partial diff is:"
      partial_diff $tmpdir/utts{,.wav}
      exit 1;
    fi

    if [ -f $data/reco2file_and_channel ]; then
      # this file is needed only for ctm scoring; it's indexed by recording-id.
      check_sorted_and_uniq $data/reco2file_and_channel
      ! cat $data/reco2file_and_channel | \
        awk '{if (NF != 3 || ($3 != "A" && $3 != "B")) { print "Bad line ", $0; exit 1; }}' && \
        echo "$0: badly formatted reco2file_and_channel file" && exit 1;
      cat $data/reco2file_and_channel | awk '{print $1}' > $tmpdir/utts.r2fc
      if ! cmp -s $tmpdir/utts{,.r2fc}; then
        echo "$0: Error: in $data, utterance-ids extracted from segments and reco2file_and_channel"
        echo "$0: differ, partial diff is:"
        partial_diff $tmpdir/utts{,.r2fc}
        exit 1;
      fi
    fi
  fi
fi
```

１：音声ファイルとチャネルのチェックを行う（ユニークでソートされているか）
２：セグメントファイルと音声ファイルのidを抽出し、合致しているかを確認する。
３：レコードファイルがソートされているかユニークかをチェックする。
４：セグメントファイルとレコードファイルのidを抽出し、合致しているかを確認する。

```shell-session
-f $data/feats.scp #特徴量
-f $data/cmvn.scp #cmvn
-f $data/spk2gender #話者の性別
-f $data/spk2warp #不明
-f $data/utt2warp #不明
-f $data/$f         #その他データ
```

特徴量とcmvn、話者の性別などをチェックしたい場合は下記オプションを追加する必要あり


```shell-session
for n in $(seq $nj); 
do                   
    # the next command does nothing unless $mfccdir/storage/ exists, see      
    # utils/create_data_link.pl for more info.                                
    utils/create_data_link.pl $mfccdir/raw_mfcc_$name.$n.ark                  
 done      
```

##2：シンボリックリンクの作成を行う。

```shell-session
for n in $(seq $nj); do
  # the next command does nothing unless $mfccdir/storage/ exists, see
  # utils/create_data_link.pl for more info.
  utils/create_data_link.pl $mfccdir/raw_mfcc_$name.$n.ark
done

```

##3：mfccファイルの作成を行う。
セグメントありかなしかでオプションがつくかどうかが異なる。

```shell-session
#セグメントあり
if [ -f $data/segments ]; then
  echo "$0 [info]: segments file exists: using that."

  split_segments=""
  for n in $(seq $nj); do
    split_segments="$split_segments $logdir/segments.$n"
  done

#scpファイルを分割 
  utils/split_scp.pl $data/segments $split_segments || exit 1;
  rm $logdir/.error 2>/dev/null

  $cmd JOB=1:$nj $logdir/make_mfcc_${name}.JOB.log \
    extract-segments scp,p:$scp $logdir/segments.JOB ark:- \| \
    compute-mfcc-feats $vtln_opts --verbose=2 --config=$mfcc_config ark:- ark:- \| \
    copy-feats --compress=$compress ark:- \
      ark,scp:$mfccdir/raw_mfcc_$name.JOB.ark,$mfccdir/raw_mfcc_$name.JOB.scp \
     || exit 1;
```

```shell-session
#セグメントなし
else
  echo "$0: [info]: no segments file exists: assuming wav.scp indexed by utterance."
  split_scps=""
  for n in $(seq $nj); do
    split_scps="$split_scps $logdir/wav_${name}.$n.scp"
  done

  utils/split_scp.pl $scp $split_scps || exit 1;


  # add ,p to the input rspecifier so that we can just skip over
  # utterances that have bad wave data.

  $cmd JOB=1:$nj $logdir/make_mfcc_${name}.JOB.log \
    compute-mfcc-feats  $vtln_opts --verbose=2 --config=$mfcc_config \
     scp,p:$logdir/wav_${name}.JOB.scp ark:- \| \
      copy-feats --compress=$compress ark:- \
      ark,scp:$mfccdir/raw_mfcc_$name.JOB.ark,$mfccdir/raw_mfcc_$name.JOB.scp \
      || exit 1;
fi

```