Tensorflow Speech Recognition Competition 参加記

はじめまして。Maxwell氏の大学の後輩、Ireko8です。今回、tensorflow speech recognition competitionに参加して72位になり銅メダルを取得しました。Titanicをのぞけば初めてのKaggle competitionでしかもはじめて自分がまともにディープのモデルを使うコンペでした。

経緯

Maxwell氏の会社のもとでインターンをしていたとき、Google Speech APIなどを用いたSpeech to Textの簡単なシステムを作ったりしていたので、今回のコンペで音声認識の勉強になると思い参加しました。

コンペ概要

  • タスク 約1秒の音声が指定された十種類のコマンド("on"や"left", "stop"など)+silence(音声が入っていない)+unknown(音声は入っているが指定されたコマンドではない)のどれにあたるかを分類します。
    • Rasberry Piで実行できるモデルで一番精度のよかったものをつくるPrizeもあったのですが割愛します。
  • ルール

    • External Dataは禁止。Pretrained Modelの使用も禁止。
  • データ

    • 訓練データ

      30種類程度の単語(分類対象のコマンドを含む)を録音した音声データおよそ6万件。ファイル名にはその音声を録音した人のidがついています。

      • 同一の人物が発した音声は似通っているため、学習の際に訓練データと検証データの両方に同じ人物の発した音声が含まれているとleakになるので、データはidが重複しないように分割する必要があります。

      訓練データにはsilenceと明確にラベリングされたデータはありません。silenceのデータは自分で生成する必要があります。ただし、訓練データを作成するときに用いられたバックグラウンドノイズ(ホワイトノイズなど)があり、External Data禁止なので基本的にこれを用いることになります。

    • テストデータ 音声データ16万件弱。訓練データより多い上に、訓練データには含まれていない単語の音声(Unknown Unknowns)が存在します。

手法

  • モデル スペクトログラムを入力とするVGG-likeな二次元CNNとwaveデータをそのまま入力とするVGG-likeな一次元CNNの二つを作りました。
  • augmentation リアルタイムに行ったaugmentationは、音声をフリップする(wavデータを-1倍する、波形の正負を反転する)のとノイズを付加する、ボリュームを変える、一秒ではない音声についてはランダムで切り出すor前後に無を追加の3つです。ピッチを半音の範囲でずらす、スピードを変えるといったaugmentationもofflineで行いました。librosaが重すぎて、ピッチシフトとスピードチェンジはリアルタイムにできませんでした。
    • 音声フリップに関しては実は効果がなかったのでは、という疑いがあります。
  • silence data 訓練データのバックグラウンドノイズから切り出したwavデータにランダムにボリュームを変えた20000件を用いました。いま考えると、バックグラウンドノイズを連結させたwavからEpochごとに必要なぶんだけ切り出したほうがよかった気がします。
  • Pseudo-Labeling & Ensemble 5foldで学習させたあと、Cross Pseudo-Labelingを行い、入力データやaugmentationを変えたモデルをaveragingしました。
    • Pseudo-Labelingとはテストデータの予測ラベルのうち予測スコアが高いデータを訓練データに追加することでデータ数を増やす半教師あり学習です。このままですと自分自身の出力を訓練データに含めていることになるため、若干leakyになってしまいます。そこでまず独立に学習させた複数のモデルからテストデータの予測スコアを計算し、特定のモデルのpseudo-labelを作る際はそのモデル以外のモデルから計算された予測スコアをensembleした予測に基いてラベルを作ります。これをCross Pseudo-Labelingと呼ぶそうです。たとえば5foldで独立に学習させたモデルがあった際、1fold目のモデルのためのpseudo-labelを作る際には2~5fold目のモデルの予測を用いる、といった具合です。詳細はiwiwi氏のstatefarm解説スライドを参照してください。 修正(2018/2/1)statefarmの解説を読み直したら、自分の手法はCross Pseudo-Labelingではないですね……。

敗因

僕の用意したモデルはpublic LB 0.85強 ~ 0.86程度の性能だったのですが、終盤3日くらいで上位陣がsingle 0.87 ~ 0.88の性能のモデルを使っていることがわかったので、単体の性能で負けました。完全に見込みが甘かったとしかいえないです。

また、締め切り一週間前にトップがpublic LB 0.86のモデルを3つdiscussionに投下してきたのですが、そのコードが手元で再現できなかったのも痛いです。今回、discussionで上げられていた一次元CNNのモデルは手元で全く再現できませんでした。

最後の一週間くらい、一次元CNNのほうが二次元CNNより性能が悪かったので、そっちの性能向上に注力したのですが、うまくいかなくて諦めたのですが、二次元CNNのほうが性能向上は簡単だったので、これも誤算だったとしかいえません。

細かいところだと、

上位陣のsolution

基本的にたくさんモデルを作ってensembleするのが主流でした。

  • 1位

    raw wave・melspectrogram・MFCCの三種類のモデル(single public LB 0.86)をpseudo-labelingしてensembleというオーソドックスな手法でした。ただし、他のチームメンバーはsingle LB 0.88程度の強いモデルを持っていたみたいです。

  • 2位

    log-mel filterbankを使った特徴量でCNNでした。この方はsingleでprivate 90.9%の精度を出すというすごいことをやっていました。wavを20~50のchunkに切り出しstandizationする、VLTPを使う、というaugmentationが大変有効だったそうです。

  • 3位

    pseudo-labelingに加えて、pseudo-labelだけでまず学習させてその重みを初期値としてtraining dataでfine-tuningするという手法を使っていました。ねじゅみさんのメモにもある手法ですね。

  • 4位

    stacking。silenceに関しては、confidence scoreの低いものでpowerが小さいものをtest dataから取ってきたみたいですね。

  • mixup(7位)

    ベータ関数からサンプリングしたweightで二つのデータを混ぜるmixupが有効だったみたいです。このチームではシンプルにwavを混ぜていたみたいです。

使わなかった手法

  • RNN

    16000Hzでサンプリングされた約1秒のデータということもあってRNNは使いませんでした。実際、上位陣の多くはCNNで畳み込んでいました。

  • silenceとそれ以外のclassifierを作る

    テストデータの分布が訓練データの分布が異なりunknownやsilenceに偏っていたため、silenceやunknownに特化したclassifierとそれ以外のclassifierの二つを作る、というアイデアは考えてました。しかし、特定のlabelにoverfitするかも、と日和って採用しませんでした。今回一位のHeng氏も同様のアイデア(https://www.kaggle.com/c/tensorflow-speech-recognition-challenge/discussion/47083#latest-268685)を出していましたが実装はしていなかったみたいです。

  • test time augmentation

    一位のHeng氏が使って(ピッチシフトなど)有効だったみたいです。

今回参考にしたサイト

  • ねじゅみさんのメモ

    Kaggleのテクニックについて簡潔にまとまっている素晴らしいメモです。

  • iwiwi氏のstatefarmコンペのスライド

    人物でvalidationを分けるなど、今回のコンペで類似するところが多々ありました。ただ各々のテストデータが似通っているわけではなかったので、test dataに対するunsupervisedは行いませんでした。

  • kaggler-jaのログ

装備

GTX 1080とTesla K80(コンペで配られたGCPの500ドルクレジットを使いました)

終わりに

今回はいろいろ見込みが甘かったので、次はもっと上位に行きたいです。