🦜

Kaggle過去コンペ解説 BirdCLEF 2023 1st Solution

2024/03/20に公開

今回はKaggleで過去に開催されたコンペ BirdCLEF 2023の、1st Solutionを解説していきます。

概要

1 目標

コンペの目標は与えられた連続音声データを処理し、東アフリカの鳥類を鳴き声で識別することです。

2 評価指標

評価指標はcmAP(class mean Average Precision)です。
cmAPは各クラスの予測精度平均の平均で、それぞれのクラスでどれだけ正確に予測できたかを評価します。

※実際にはcmAPを拡張したpaddng cmAPが評価関数となっています。

評価関数の実装
import pandas as pd
import sklearn.metrics

def padded_cmap(solution, submission, padding_factor=5):
    solution = solution.drop(['row_id'], axis=1, errors='ignore')
    submission = submission.drop(['row_id'], axis=1, errors='ignore')
    new_rows = []
    for i in range(padding_factor):
        new_rows.append([1 for i in range(len(solution.columns))])
    new_rows = pd.DataFrame(new_rows)
    new_rows.columns = solution.columns
    padded_solution = pd.concat([solution, new_rows]).reset_index(drop=True).copy()
    padded_submission = pd.concat([submission, new_rows]).reset_index(drop=True).copy()
    score = sklearn.metrics.average_precision_score(
        padded_solution.values,
        padded_submission.values,
        average='macro',
    )
    return score

3 データ

  • train_audio/[bird_id]/*.oog
    学習データは市民ボランティアサイト(Xeno-Canto)によって録音された鳥の鳴き声で、テストデータの音声に合わせて32kHzにダウンサンプリングされ、ogg形式に変換されています
  • train_metadata.csv
    学習データのメタデータが記述されています。
    ・primary_label: 鳥種のコード。(idとは異なります。idは個別音声データのコード)
    ・latitude longitude: 記録が撮影された場所の座標。鳥の地域差による「方言」が存在する可能性があるため、多地域の同種データの活用を推奨。
    ・author: 録音を提供したユーザー。
    ・filename: 関連するオーディオファイルの名前。

1. 1st Solution 「Correct Data is All You Need」

今回はVolodymyrさんの1st Solution 「Correct Data is All You Need」を解説します。
https://www.kaggle.com/competitions/birdclef-2023/discussion/412808

1.1 データはどこ?

train_metadeta(学習用メタデータ)に含まれるデータ数を確認すると奇妙なことが分かります。

# 鳥のid  データ数
# barswa     500
# wlwwar     500
# thrnig1    500
# eaywag1    500
# comsan     500
#           ... 
# lotcor1      1
# whctur2      1
# whhsaw1      1
# afpkin1      1
# crefra2      1
# Name: primary_label, Length: 264, dtype: int64

なぜデータ数の最大値が500なのでしょうか?
最も有力な仮説はXC APIのバグです。前年のAPIを使用することで、データセットの量が非常に増加しました。

このSolutionは正しいデータを使用しています。

1.2 データ準備

1.2.1 評価をよりロバストにするために

・「データ数が1つしかない鳥種」の鳴き声サンプルを2つ(trainとval)に分割し、最低でも一度のcv検証を保証します。
・重複データを手動で除去します。
・'id','author',長さが同じデータをルールベースで除去します。

1.2.2 学習データの追加

2020~2023開催の同コンペのデータとXeno-Cantoのデータから、今年の学習データと'primary_label'が同じデータのみを取り出し、学習データに加えました。

また、追加で
・'author', 'primaly_label'が重複しているデータのフィルタリング
・追加したデータの中で、鳥種に対するデータ数が10以上のデータのみ使用する
という処理を行いました。

1.3 評価方法

評価には以下を使用しました。
・5FoldsのStratified CV
・全てのサンプルに渡って、時間の経過とともに各5秒のクリップから最大確率を取得
※注意!: 評価指標がpadding cmAPの場合、foldに関係なく平均を取ることが大切であり、Out Of Fold(Foldの評価に使用したデータを省くこと)の平均を取ってはいけません。

Stratified CVについては、過去に解説していますのでこちらもご覧下さい。

私は仮説を持って294回の実験を行いましたが、この手法によるCVスコアとLBスコアの相関は非常に良く、CVが向上すれば、LBも向上しました。

1.4 学習

philippsingerさんのnotebookを見るまで、私は過学習を繰り返していました。
その後は時間とGPUの節約のため、以下の手法を選択しました。

  • CVで仮説を検証し、データを5つに分割した5Foldの2~3Foldのみを学習に使用してファイルを提出します
  • ただし、アンサンブルの場合は全データで学習します

学習の詳細
・50 Epochs
・Adam (最適化関数)
10^{-4}から10^{-6}までのコサインアニーリング(学習率の調整)
・Focal loss(物体検出向けに提案された損失関数。幅広いタスクへ応用可能)
・64 Batch Size
・5 second chunk (データを5秒ごとに区切る)
・モデル再学習時の各種設定を、事前学習と同じにする
・超重要: 種族クラス毎にサンプリングを重み付けする

# 重み付け
sample_weights = (
    all_primary_labels.value_counts() / 
    all_primary_labels.value_counts().sum()
)  ** (-0.5)

1.5 モデル

1.5.1 推論最適化

本コンペでは計算量の制約から、精度を上げるために層を深くすることができませんでした。
そこで、推論最適化技術を試してみました。

  • ONNX: ONNXは異なる環境のAIモデルを統合するフォーマットです。これは非常にうまくいきました。推論時間がわずかに改善され、推論ノートブック内の依存関係の数を減らすことができました。
  • 量子化: 一週間以上かけて実験しましたが、うまくいきませんでした。
  • openvino: 私はこれを使用していません。2位の方が説明してくれていました。
1.5.2 使用モデル

さて、私の最終的なsubmissionは、以下のバックボーン(モデルの主要ネットワークアーキテクチャ)を持つ3つのSED(音声イベント検出)モデルのアンサンブルです。

  • eca_nfnet_l0 (2段階の学習。初期学習率は10^{-3})
  • convnext_small_fb_in22k_ft_in1k_384 (2段階の学習。初期学習率は10^{-4})
  • convnextv2_tiny_fcmae_ft_in22k_in1k_384 (1段階の学習。初期学習率は10^{-4})

様々なアーキテクチャに合わせて初期学習率を調整することは非常に重要でした。

1.6 データ拡張

データ拡張の選択には非常にこだわりました。

  • Mixup: 単純な割合0.5のMixup(2つの入力データを重み付けして混合する手法)
  • Zenodo nocallと呼ばれるデータセットを用いた背景ノイズの追加
  • RandomFiltering: ランダムなフィルタリングを適用して、データのバリエーションを増やす方法
  • Spec Aug: スペクトログラムへのマスク。
    ・周波数軸
    最大長さ10、最大行数(周波数帯域)3、確率30%で各データ毎にマスクを行う。10データあれば3つがマスクを受ける計算になる
    ・時間軸
    最大長さ20、最大行数(時間帯)3、確率30%で各データ毎にマスクを行う。最大で3つの時間帯で、20step(計60step)のマスクが行われる。

1.7 失敗した実験

以下は実験のうち上手くいかなかったもの、もしくは十分に試せていないため効果が出なかったものです。
・Xeno-Cantoの前データを使用した大規模事前トレーニング
・追加データから高品質データ(>=32kHz)のみを使用
・カラーノイズによる補強
・CQT(定数Q変換)、LEAF
・微調整:LRを小さくする、エポック数を少なくする、バックボーン(モデルの主要ネットワークアーキテクチャ)を再学習しない、バックボーンとヘッドでLRを変更する

見どころ

個人的に参考になった部分を挙げていきます。
・294の仮説と実験を行った
・時間とGPUの削減のため、CVで仮説を検証し、データを5つに分割した5Foldの2~3Foldのみを学習に使用してファイルを提出する
・学習時に、クラス毎に重み付けサンプリングを行う。(おそらくクラス間のサンプリング数を揃えるため)
・ONNXの利用
・3つの異なるモデルを利用したアンサンブルを行った。それらのアーキテクチャに合わせて初期学習率を調整することは非常に重要。
・データ拡張 Mixup, RandomFiltering, Spec Aug

まとめ

BirdCLEF 2023の1st Solutionを解説しました。
過去コンペのSolutionからは非常に多くのことを学ぶことができます。興味が湧いた方は是非実際のSolutionを確認してみて下さい。

今回は以上です。読んでいただきありがとうございました。

Discussion