ESPnet による音声認識入門 ~AN4データセットによる学習・評価~

こんにちは、製品企画部リサーチャーの古谷(@kk_fry_)です。 レトリバでは、主に音声認識の研究開発を行っています。

前回に引き続き、音声認識が実行できるオープンソースのツールキット ESPnet を触ってみる記事を書いていこうと思います。

前回は ESPnet Model Zoo を用いて、学習済みモデルを用いての推論を行いましたが、今回は ESPnet 本体を用いて音声認識の学習と評価をしてみたいと思います。

前回の最後に「学習済みモデルを用いた推論」と書きましたが、せっかくなので学習もやってみます。

ESPnet のインストール

ESPnet 公式ドキュメントのインストールガイドに従って、環境を用意しインストールします。

私は、Kaldi の Dockerfile を参考にして、次のような Dockerfile を作成しました。

この Dockerfile からビルドされたイメージのコンテナ内で作業しています。

FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu18.04

RUN apt update && \
    apt install -y --no-install-recommends \
        g++ \
        make \
        cmake \
        automake \
        autoconf \
        bzip2 \
        unzip \
        wget \
        sox \
        libtool \
        git \
        subversion \
        python2.7 \
        python3 \
        python3-dev \
        python3-distutils \
        zlib1g-dev \
        gfortran \
        ca-certificates \
        patch \
        ffmpeg \
        libsndfile1-dev \
        flac \
        vim \
        curl \
        nkf \
        libfreetype6-dev && \
    rm -rf /var/lib/apt/lists/*

RUN ln -s /usr/bin/python3 /usr/bin/python

RUN curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
RUN python get-pip.py

RUN pip3 install torch numpy kaldiio humanfriendly soundfile typeguard espnet

コンテナを起動する、もしくは必要なライブラリのインストールが完了したら、ESPnet を clone します。

git clone https://github.com/espnet/espnet.git

続いて、ビルドと Python の設定をします。 今回はシステムの Python を使用します。 conda, venv 等を使う場合はインストールガイドを参照してください。

なお、今回は Kaldi 依存を無くした ESPnet2 を使用するため、インストールガイドの Step1 は飛ばします。

cd espnet/tools
make
./setup_python.sh $(command -v python3)

これで ESPnet のインストールは完了です。

egs2/an4/asr1 のレシピを実行

ESPnet では、音声認識音声合成を行う手順が「レシピ」としてまとめられています。

音声認識のレシピには、データの準備・言語モデルの学習・音響モデルの学習・評価などが含まれています。

今回は、初めての音声認識ということで、AN4 という無償で使える小さいデータセットを用いて学習と評価を試してみます。

AN4 は、名前・住所・生年月日などが発話された英語のデータセットです。

早速、レシピを実行してみましょう。 ESPnet2 のレシピは egs2 ディレクトリにまとめられています。このディレクトリには様々な音声コーパスに対するディレクトリが存在します。 今回は AN4 ディレクトリで音声認識を行うので、egs2/an4/asr1 ディレクトリのレシピ(run.sh)を実行します1

cd <espnet-root>/egs2/an4/asr1/
./run.sh

しばらくすると結果が出ます。

...
<!-- Generated by scripts/utils/show_asr_result.sh -->
# RESULTS
## Environments
- date: `Fri Feb 19 02:56:26 UTC 2021`
- python version: `3.6.9 (default, Oct  8 2020, 12:12:24)  [GCC 8.4.0]`
- espnet version: `espnet 0.9.7`
- pytorch version: `pytorch 1.4.0+cu100`
- Git hash: `2cfecaf70a2d2826e43db9a2aa34305c6628c5a5`
  - Commit date: `Thu Feb 18 18:14:52 2021 +0000`

## asr_train_raw_en_bpe30
### WER

|dataset|Snt|Wrd|Corr|Sub|Del|Ins|Err|S.Err|
|---|---|---|---|---|---|---|---|---|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test|130|773|89.3|9.3|1.4|0.9|11.6|43.8|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev|100|591|80.5|15.1|4.4|0.8|20.3|59.0|

### CER

|dataset|Snt|Wrd|Corr|Sub|Del|Ins|Err|S.Err|
|---|---|---|---|---|---|---|---|---|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test|130|2565|93.6|2.9|3.5|0.6|7.0|43.8|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev|100|1915|87.8|4.9|7.3|1.1|13.3|59.0|

### TER

|dataset|Snt|Wrd|Corr|Sub|Del|Ins|Err|S.Err|
|---|---|---|---|---|---|---|---|---|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test|130|2695|93.9|2.7|3.4|0.6|6.7|43.8|
|inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev|100|2015|88.4|4.6|6.9|1.0|12.6|59.0|

2021-02-19T02:56:26 (asr.sh:1377:main) Skip the uploading stages
2021-02-19T02:56:26 (asr.sh:1380:main) Successfully finished. [elapsed=585s]

上に記載した結果の部分は Markdown 形式で出力されるので、Markdown に対応したエディタにコピペすると読みやすい表になります。

Environment には実行環境の情報が記載されています。

asr_train_raw_en_bpe30 というのは、今回学習したモデルの名前です。

続いて、WER, CER, TER の表があります。これが評価結果です。

評価結果の見方

出力された評価結果の表を以下にコピペしました。

WER

dataset Snt Wrd Corr Sub Del Ins Err S.Err
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test 130 773 89.3 9.3 1.4 0.9 11.6 43.8
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev 100 591 80.5 15.1 4.4 0.8 20.3 59.0

CER

dataset Snt Wrd Corr Sub Del Ins Err S.Err
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test 130 2565 93.6 2.9 3.5 0.6 7.0 43.8
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev 100 1915 87.8 4.9 7.3 1.1 13.3 59.0

TER

dataset Snt Wrd Corr Sub Del Ins Err S.Err
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/test 130 2695 93.9 2.7 3.4 0.6 6.7 43.8
inference_lm_lm_train_lm_en_bpe30_valid.loss.ave_asr_model_valid.acc.ave/train_dev 100 2015 88.4 4.6 6.9 1.0 12.6 59.0

表の各列は以下の値を表します。

  • dataset: データセットの名前
  • Snt: セグメント(ひとまとまりの発話)の個数
  • Wrd: 単語数(CER, TER の場合は文字数、以下も同じ)
  • Corr: 出力と正解で一致する単語数
  • Sub: Substitute 置換回数を単語数で割った値の平均値
  • Del: Deletion 削除回数を単語数で割った値の平均値
  • Ins: Insertion 挿入回数を単語数で割った値の平均値
  • Err: エラー率(Sub+Del+Ins)
  • S.Err: セグメント単位のエラー率(完全一致しなかったセグメントの割合)

WER とは、Word Error Rate の略で、日本語では「単語誤り率」と呼ばれます。これは「編集距離」を元に計算されます。

2 つの文について、片方の文に以下の 3 つの操作を繰り返してもう片方に一致させるために必要な操作の最小回数を「編集距離」といいます。

  • 挿入:2 つの隣り合う単語の間に新たな単語を 1 つ挿入する
  • 削除:1 つの単語を削除する
  • 置換:1 つの単語を削除する

WER は出力文と正解文の編集距離(挿入回数と削除回数と置換回数の和)を、正解文の単語数で割った値です。

CER は Character Error Rate の略で、日本語では「文字誤り率」と呼ばれます。

これは WER の計算で単語単位で考えていた部分を文字単位で考えて計算される値です。

例えば、"word" と "world" は、WER においては完全に別物ですが、CER においては 1 文字違い(4 文字正解)になります。

TER は、Translation Edit Rate の略で、日本語では「翻訳編集率」と呼ばれるようです。

これは、WER で考慮する操作に加えてもう一つ「移動」を考慮するものです。

移動とは、出力文の連続した一部分をまるっと別の箇所に移すことです。つまり、"ABCDE" を "ADEBC" にする操作が、WER だと 4 回の置換(もしくは削除と挿入を 2 回ずつ)になりますが、TER では "BC" (または "DE")の移動 1 回とカウントします。

一般的に、英語の音声認識では WER、日本語の音声認識では WER か CER が用いられることが多いです2

今回の結果は WER が test セットで 11.6 %、train_dev セットで 20.3 % となりました。

実際には train_dev セットでの WER が最小になるようにハイパーパラメータを調整して、そのときの test セットの WER を精度として採用することになりますが、今回はこれでとりあえず実験完了とします。

おわりに

今回は、ESPnet 本体を用いて小さいデータセットでの音声認識の学習と評価を行いました。

データセットがシンプルなので学習はすぐ終わります3が、10-20% 程度の WER が達成できており、近年の音声認識技術の向上を感じますね。

次回があるかはわかりませんが、あればモデル変更やハイパーパラメータ調整か、日本語データセットでの音声認識をやってみようかなと考えています。


  1. ASR は Automatic Speech Recognition: 自動音声認識の略です

  2. 日本語は単語の間に空白を入れないため、WER を計算するためには単語分割の処理が必要になります。そのため、CER のみを確認することがあります。

  3. 学習にかかった時間は出力結果の最終行に記載されており、 585 秒となっています。実行環境は TITAN RTX が 1 枚刺さったサーバーです。