Freesound General-Purpose Audio Tagging Challenge
最近、Kaggle始めました。登録自体は2年前にしてたのですが、興味起きなくてタイタニックやった後にずっと放置してました (^^;
今回、取り組んだのはFreesound General-Purpose Audio Tagging Challengeという効果音に対して3つのタグをつけるコンペです。2秒ほどの音に対してギター、犬の鳴き声、チャイム、シンバルなどの41個のタグから3つを付与します。1つの音声に対する正解のタグは1つなのですが、3つのうち正解のタグが上位にあるほどスコアが高くなります。
Deep Learningで音を分類したり、生成したりする技術に興味があったので取り組んでみました。Private Leader Board上では比較的上位に食いこめたので参考までにアプローチをまとめておこうと思います!
Kaggleにはカーネルという実験をまとめたJupyter Notebookを公開できる仕組みがあり、Zafarさんという方がKerasを使って Beginner's Guide to Audio Dataというとてもよいチュートリアルをまとめています。このカーネルは @daisukelab さんが翻訳されてました。感謝!
入力特徴量
入力特徴量は、メルスペクトログラム (64次元)またはMFCC(64次元)です。
手前味噌ですが、MFCCは以前、自分で実装したことあります。
最近は、librosaという音を扱うのにとても便利なPythonパッケージができたため、自分で実装しなくても簡単に計算できます。以下の記事はlibrosaでメルスペクトログラムやMFCCを抽出する方法をlibrosaの実装にまで踏み込んで開設されていて参考になります。
アーキテクチャ
音の分類で使われるモデルアーキテクチャは、画像分類で使われるのと同じ畳み込みニューラルネットワーク(CNN)が一般的です。ただ、画像と違ってあまり深いネットワークは使われません。また音は時系列データでもあるためLSTMなどのRNN系のモデルもよく使われています。
画像は2次元の広がりを持つデータなのでの2次元のカーネルを使いますが、音声は2次元と1次元の両方のカーネルが使えます。どちらがよいかは対象タスクによるのでとりあえず全部試した方がよいと思います。今回は
- 畳み込みニューラルネットワーク(2次元カーネル)
- 畳み込みニューラルネットワーク(1次元カーネル)
- CRNN (Convolutional Recurrent Neural Network)
- ResNet(1次元カーネル)
の4種類を検討しました。CRNNは畳み込みの結果のあとにLSTMを使うモデルです。
上記の特徴量とアーキテクチャの組み合わせで複数のモデルをたくさん作り、最後にそれらの分類結果をアンサンブルします。多分、コンペの王道です。
その他の細かい工夫
データ標準化
メルスペクトログラムやMFCCの値を音声ファイルごとに平均0、標準偏差1になるように標準化しました。本来は、訓練データ全体で平均0、標準偏差1になるようにするのが好ましいかもしれませんが、音声ファイル単位で処理しています。また、メルスペクトログラムやMFCCの各次元ごとに標準化せずに全体で標準化しています。
Cyclic Learning Rateを導入
fast.aiでも推奨されてましたが、局所最適解に陥りにくくなるそうです。学習曲線がギザギザしているのはそのためです。
データ拡張
音声波形のランダムクロップのみです。雑音付与、音の高さ変更、音の長さ変更などいろいろ考えられますが試しませんでした。
Test Time Augmentation (TTA)
テスト時にデータ拡張してそれらの結果を平均化する手法です。今回はランダムクロップのみですが、導入することでLBスコアで2〜3ポイントほど改善しました。
学習曲線
ギザギザしているのはCyclic Learning Rateを導入したせいです。
valid/accが高い順に並べると、
- ResNet (mfcc)
- CNN2d (mfcc)
- CNN1d (mfcc)
- CNN2d (mel spectrogram)
- CRNN (mfcc)
- CNN1d (mel spectrogram)
- CRNN (mel spectrogram)
となりました。ResNetはvalid/accが高いのですが、train/accが100%に達しており、過学習してる感じです。全体的にメルスペクトログラムよりMFCCの方が精度が高いという結果になっています。
スコア
学習時はTop-1の精度で求めていますが、Kaggle上ではTop-3のMean Average Precision (MAP@3) でスコアがつきます。
手法 | val_acc | Public LB | 収束エポック | TTA LB |
---|---|---|---|---|
cnn2d_mfcc | 0.782 | 0.881 | epoch 103 | 0.887 |
cnn1d_mfcc | 0.744 | 0.849 | epoch 121 | 0.853 |
cnn2d_melgram | 0.731 | 0.826 | epoch 118 | 0.843 |
cnn1d_melgram | 0.665 | 0.784 | epoch 141 | 0.815 |
crnn_mfcc | 0.725 | 0.823 | epoch 140 | 0.843 |
crnn_melgram | 0.639 | 0.746 | epoch 099 | 0.749 |
resnet_mfcc | 0.788 | 0.871 | epoch 105 | 0.888 |
上の全てのモデルの出力確率の幾何平均をとるアンサンブルを導入することで最終的なPublic LBのスコアは 0.937430 で38位/558チームでした。
最終的にPrivate LBのスコアは 0.892994 となり、61位/558チームまで下落してました(^_^;) おそらく、Validation Setに過学習してたのかなと思います。
一応、ブロンズメダルの範囲には入ってるみたいですが、このコンペはメダル出ないタイプでした。また何か面白そうなのは挑戦してみたいと思います!
その他のアイデア
他にも下のようなこと考えてましたが未達でした。
- タグ付けの信頼度データを活用する
- OpenSMILE の特徴量を活用する
- Bi-directional RNNのようなより高度なアーキテクチャを使用する
- さまざまなデータ拡張を導入する
あまり整理できてませんが、コードはこのリポジトリにあります。