CTC Segmentationの紹介

Chief Research Officerの西鳥羽 二郎です。

今回は音声認識において長い音声データとその書き起こしから学習データを作成する際に役立つツールの紹介です。

書き起こしから学習データを作る時の問題点

音声認識は最近は Kaldi, Mozilla DeepSpeech, wav2letter, ESPnetなどのツールが整備されてきて割と試しやすくなりました。 これらとTEDLIUMLibriSpeechのようなオープンで大規模なコーパスと組み合わせると自分たちでも比較的高精度な音声認識を行うことができます。 日本語でも有償ですが日本語話し言葉コーパス(CSJ)などを用いれば日本語の音声認識を行うことができます。

また、より精度が高い音声認識を作るために独自のデータを用いて学習したモデルを構築したいという場合があるかと思います。 こういった場合自分で学習データを用意する必要がありますが、音声認識の学習データは通常数秒~十数秒程度の短い音声または音声区間とそれに対応するテキストという形式が一般的です。 はじめから音声認識の学習データを作成するつもりで短い音声データを用意している場合は良いのですが、講演や会話など長い音声に対する書き起こしを用意した場合に長い音声データに対して書き起こしのみがあり、細かく区切られてはいないというケースもあるかと思います。 例えばLibriVoxでは著作権が切れてパブリックドメインになったテキストに対しての読み上げデータが集められています。 音声とテキストの組があるので一見すると音声認識の学習データにできそうですが、前述の通り数秒から十数秒に対応する1文~2文が音声のどの区間に対応するかを求める必要があります。

書き起こしと音声区間のアライメントをとってみる

長い音声データとそれに対する書き起こしがある時、音声認識を利用して対応を取る(アライメントを取るともいいます)ことができます。*1。このうちCTC SegmentationについてはESPnetに実装されていて試すことができます。 元々は英語のみの対応でしたが、日本語用の音声認識学習モデルを用意するとともに、日本語の音声でも対応を取ることを可能にしました。 今回は先程も触れたLibriVoxに登録されている『虻のおれい』(音声テキスト)で試します。 事前準備としてインストールに従い、gitレポジトリのmasterブランチからESPnetをインストールしてください。

作業ディレクトリを作成します。クローンしたESPnetのディレクトリに移動して下記のコマンドを実行します。

$ cd egs/csj/align1/
$ mkdir -p data/demo

次に作業ディレクトリに移動し、音声データを取得します。Librivox上のデータはmp3ですが、ESPnetではWAVである必要があるのでffmpegなどでWAV形式に変換します。

$ cd data/demo
$ wget http://www.archive.org/download/multilingual_shorts_004_1210_lv/msw004_09_abunoorei_yumeno_um_64kb.mp3
$ ffmpeg -i "msw004_09_abunoorei_yumeno_um_64kb.mp3" -vn  -ar 16000 -acodec pcm_s16le -f wav "Abuno_Orei.wav"

テキストを取得します。元は青空文庫にあるテキストですが、音声認識の学習データに用いるために適当なところで文単位に区切ります。ここでは「。」ごとに区切っていますが厳密ではありません。

cat << EOF > utt_text
Abuno_Orei チエ子さんは今年六つになる可愛いお嬢さんでした。 
Abuno_Orei ある日裏のお庭で一人でおとなしく遊んでいますと、「ブルブルブルブル」 と変な歌のような声がきこえました。
Abuno_Orei 何だろうとそこいらを見まわしますと、そこの白壁によせかけてあったサイダーの瓶に一匹の虻が落ち込んで、ブルンブルンと狂いまわりながら、「ドウゾ助けて下さい。ドウゾ助けて下さい」 と言っています。
Abuno_Orei チエ子さんはすぐに走って行ってその瓶を取り上げて、口のところからのぞきながら、「虻さん虻さん、どうしたの」 と言いました。
Abuno_Orei 虻は狂いまわってビンのガラスのアッチコッチへぶつかりながら、「どうしてか、落ち込みましたところが、出て行かれなくなりました。
Abuno_Orei 助けて下さい、助けて下さい」 と泣いて狂いまわります。
Abuno_Orei チエ子さんは笑い出しました。「虻さん、お前はバカだねえ。
Abuno_Orei 上の方に穴があるじゃないか。
Abuno_Orei そう、あたしの声が聞こえるでしょう。
Abuno_Orei その方へ来れば逃げられるよ。
Abuno_Orei 横の方へ行ってもダメだよ。ガラスがあるから」 と言いましたが、虻はもう夢中になって、「どこですか、どこですか」 と狂いまわるばかりです。
Abuno_Orei チエ子さんは虻が可哀そうになりました。どうかして助けてやりたいと思って、そこいらに落ちていた棒切れを拾って上から突込んで上の方へ追いやろうとしましたが、虻はどうしても上の方へ来ません。
Abuno_Orei うっかりすると棒にさわって殺されそうになります。
Abuno_Orei チエ子さんは困ってしまいました。どうして助けてやろうかといろいろ考えました。
Abuno_Orei 上から息を吹きこんだり、瓶をさかさまにして打ちふったりしましたが、虻はなかなか口の方へ来ません。
Abuno_Orei やっぱり横の方へ横の方へと飛んでは打かり、打かっては飛んで、死ぬ程苦しんでいます。
Abuno_Orei チエ子さんは又考えました。
Abuno_Orei どうかして助けたいと一所懸命に考えましたが、とうとう一つうまいことを考え出しまして、瓶を手に持ったままお台所の方へ走って行きました。
Abuno_Orei チエ子さんは台所に行って、サイダーを飲むときの麦わらとコップを一つお母さまから貸していただきました。
Abuno_Orei そのコップに水を入れて麦わらで吸い取って、虻がジッとしているときにすこしずつ瓶の中に吹き込んでやりますと、虻は水がこわいので段々上の方へやって来ました。
Abuno_Orei チエ子さんは喜んでもう一いき水を吹いてみますと、どうしたものか虻は又あわて出してブルブルと飛ぶ拍子に水の中へ落ち込んでしまいました。
Abuno_Orei チエ子さんはあわてて瓶をさかさまにしますと、水と一諸に虻も流れ出て、ビショビショに濡れた羽根を引きずりながら苦しそうに地べたの上をはい出しましたが、やがて水のないところへ来て羽根をブルブルとふるわしたと思うと、「ありがとう御座います。チエ子さん。このおれいはいつかきっといたします」と言ううちにブーンと飛んで行きました。
Abuno_Orei 「お母さん、お母さん。チエ子は虻を助けました。サイダーの瓶の中に落ちていたのを水を入れて外に出してやりました」 とチエ子さんは大喜びをしながらお母さんにお話しました。
Abuno_Orei 「そう。チエ子さんはお利口ね。けれども虻は刺しますから、これからいじらないようになさい」 と言われました。
Abuno_Orei 「いいえ。お母さん。あの虻は、チエ子にありがとうってお礼を言って逃げて行きましたのよ。ですからもうあたしは刺さないのよ」 とまじめになって言いました。 
Abuno_Orei お母さんはこれをおききになって大そうお笑いになりました。チエ子さんは虻とお話したことをいつまでも本当にしておりました。
Abuno_Orei それからいく日も経ってから、チエ子さんがお座敷でうたたねをしていた間にお母さまはちょっとお買物に行かれました。 その留守の事でした。
Abuno_Orei お台所の方から一人の泥棒が入って来まして、チエ子さんが寝ているのを見つけますと、つかつかと近寄ってゆすぶり起しました。
Abuno_Orei チエ子さんはビックリして眼をさましますと、眼の前に気味の悪い顔をした大きな男がニヤニヤ笑って立っております。
Abuno_Orei チエ子さんは眼をこすりながら、「おじさんだあれ」 と言いました。
Abuno_Orei 泥棒はやっぱりニヤニヤ笑いながら、「可愛いお嬢さんだね。いい子だからお金はどこに仕舞ってあるか教えておくれ」 と言いました。
Abuno_Orei チエ子さんは眼をパチパチさせて泣き出しそうな顔をしながら、「あたし知らない。おじさんはどこの人?」 と尋ねました。
Abuno_Orei 泥棒はこわい顔になってふところからピカピカ光る庖丁を出して見せながら、「泣いたらきかないぞ。
Abuno_Orei さ、お前のお母さんはお金をどこに仕舞っているか。言わないとこれで殺してしまうぞ」 と言いました。
Abuno_Orei チエ子さんは、「お母さん」 と泣きながら逃げ出しました。
Abuno_Orei 「このやつ、逃げたな」 と泥棒はいきなり追っかけてチエ子さんを捕まえようとしました。
Abuno_Orei その時ブーンと唸って一匹の虻が飛んで来て、泥棒の眼の前でブルンブルンブルンとまわり始めました。
Abuno_Orei 泥棒は邪魔になるので、「こんちくしょう、こんちくしょう」 と払い除けようとしましたが、なかなか払い除けられません。
Abuno_Orei そのうちにチエ子さんは、「お母さん、お母さん」 と叫びながら障子を開けてお縁の方に逃げて行きます。
Abuno_Orei 「逃がしてなるものか」 と泥棒は一所懸命となって、とうとう虻をタタキ落として追っかけてゆきました。
Abuno_Orei そうすると虻はタタキ落とされてちょっと死んだようになりましたが、又飛び上って泥棒の足へ飛びついて力一パイ喰いつきました。
Abuno_Orei 「アイタッ」 と泥棒はうしろ向きに立ち止まる拍子にお縁から足を辷らして、石の上に落っこちて頭をぶって眼をまわしてしまいました。
Abuno_Orei そのうちにチエ子さんは表へ出て、通りがかりのお巡査さんにこの事を言いましたので、泥棒はすぐに縛られてしまいました。
Abuno_Orei お母さんがお帰りになってこのお話をおききになると、涙をこぼしてチエ子さんを抱きしめておよろこびになりました。
Abuno_Orei その時にチエ子さんはお縁側を見ると一匹の虻が死んで落ちておりました。
Abuno_Orei 「お母さん、御覧なさい。この間の虻が泥棒を刺したのよ。あたしが助けてやったお礼をしてくれたのよ」 と言いました。
Abuno_Orei お母さんはおうなずきになりました。そうして晩方お父さんがお帰りになってお母さんがこのお話をされますと、お父さまはチエ子の頭を撫でながら、「あぶとお話した子は世界中でチエ子一人だろう」 とお笑いになりました。
Abuno_Orei チエ子さんは虻のお墓を作ってやりました。
EOF

ここまでで必要なデータは作成されたので実行に移ります。

$ cd ../../
$ echo "batchsize: 0" > data/demo/align.yaml
$ cp ../../wsj/asr1/conf/no_preprocess.yaml ./conf
$ ../../../utils/asr_align_wav.sh \
    --models csj.rnn.v1 \
    --align_dir data/demo \
    --align_config data/demo/align.yaml \
    --ngpu 0 \
    data/demo/Abuno_Orei.wav data_demo/utt_text

無事実行が完了すれば data/demo/aligned_segment というファイルが作成されます。

$ head data/demo/aligned_segments
Abuno_Orei Abuno_Orei 7.70 11.54 -1.311640302
Abuno_Orei Abuno_Orei 12.34 20.38 -1.053379135
Abuno_Orei Abuno_Orei 21.22 35.90 -1.981905761
Abuno_Orei Abuno_Orei 36.46 45.82 -0.773697951
Abuno_Orei Abuno_Orei 46.86 55.54 -1.995335005
Abuno_Orei Abuno_Orei 55.94 60.00 -1.091320961
Abuno_Orei Abuno_Orei 60.58 65.34 -0.512905411
Abuno_Orei Abuno_Orei 65.54 67.46 -0.410571633
Abuno_Orei Abuno_Orei 67.74 69.80 -0.081433271
Abuno_Orei Abuno_Orei 70.02 72.10 -0.148678789

data/demo/utt_textの各行が音声ファイルの何秒から何秒の部分に該当するかを data/demo/aligned_segmentsに出力しています。例えば1行目はAbuno_Oreiという音声ファイルのAbuno_Orei(こちらはutt_textの1列目で指定している名前)というエントリは7.70秒から11.54秒に対応しており、その信頼度は対数尤度で-1.311640302 であるという結果になっています。この情報により長い音声データに対して全体の書き起こしがあるといった場合でも音声認識の学習データを作成することができます。

ちなみに、CSJをお持ちの方であれば、CSJの評価データにおいてESPnetによる対応付と実際の人手によるアノテーションとの比較を作成することができます。egs/csj/align1/ のレシピで試すことができるので興味ある方は試していただければと思います。