サポートベクター回顧

プログラミングとか機械学習とか画像処理などに興味がある人のブログというかメモというか。アウトプット苦手なので頑張ります。

Kaggle振り返り🥈:BirdCLEF 2023

Kaggleコンペ、鳥コンペ2023こと「BirdCLEF 2023」に参加し20thで銀メダルを獲得しました!

www.kaggle.com

そして今回のコンペでKaggle Masterに昇格することができました!

また、今回はKaggleで初のチームを組ませていただきました。チームメイトの皆さんには非常にお世話になりました。

参加体験記を書いていこうと思います。 KaggleのDiscussionの方にも解法は載せたので、この記事は感想・考察・反省点を散りばめながら書いていきます。

www.kaggle.com

コンペの概要

この競技の目的は、鳥は高い移動性と多様な生息地要件を持ち、生物多様性の変化の優れた指標であるため、復元プロジェクトの成功または失敗を示すことができるが、伝統的な観察者による調査は大規模な地域に対して実施することが困難であるため、機械学習に基づくパッシブ音響モニタリングを使用して、より高い時間分解能度で大規模な空間スケールをサンプリングし、復元干渉と生物多様性の関係を探索することができる。この競技では、東アフリカの鳥の種を音で特定する機械学習スキルを使用し、連続した音声データを処理し、鳥の鳴き声で種を認識する計算ソリューションを開発する。優秀なエントリーは、限られた訓練データで信頼性の高い分類器を訓練できるようになる。成功すれば、NATURAL STATEなどのケニア保全団体によるアフリカの鳥類の生物多様性を保護する取り組みを進めることができます。(ChatGPT)

→要は従来手法では鳥の調査が大変なので機械学習を使って調査を効率化したいということ。

Soundscape(環境音)より、どんな鳥がその環境に居るのかを予測します。 264種の鳥の存在の予測確率を出すマルチラベルタスクのコンペでした。

昨年までの鳥コンペとタスク設計はほぼ似ていますが、以下の点が従来と大きく異なる点です。

  • 出力が2値分類 (0/1) ではなく確率。閾値が必要ない。
  • 推論はCPU限定。GPU使えない。
  • Submissionの時間制限が2時間まで。多くのアンサンブルが実行できない、推論高速化の工夫が必要。

評価指標はcmAPというものです。 これはaverage precisionのマクロ平均であるmAPの派生系です。 少数ラベルがあるとmAPの数値が不安定になるため、その補完として5つの正例ラベルを加えて安定化させるという評価指標です。 正直あまり直観的には理解しづらい評価指標ですが、安定化(補強?)させたPR-AUCのマクロ平均みたいなものだと思っています。

解法

概要

最終subに選択したのは、以下の5モデルのアンサンブルです。

  • eca_nfnet_l0 (SED)
  • eca_nfnet_l0 (Simple CNN)
  • tf_efficientnetv2_b0 (SED)
  • tf_efficientnet_b0.ns (Simple CNN)
  • tf_mobilenetv3_large_100 (Simple CNN)

推論高速化のため、これらのモデルはonnx形式に変換されており、onnxruntimeで推論しました。 主に推論高速化の部分はチームメイトの@yokuyamaさんの担当です。私自身、推論高速化の部分についてあまり詳しくなく非常にありがたかったです。

モデル

  • SEDモデルとシンプルなCNNモデルの2つを主に使用しました。
    • 過去の鳥コンペでもよく使われていたmixup + CNNのモデルではなく、本当にシンプルなCNNモデルを使いました。

モデルのパラメータ

  • drop_rate: 0.5
  • drop_path_rate: 0.2
  • criterion: BCEWithLogitsLoss (label smoothing: 0.0025)
  • optimizer: AdamW (lr: 1.0e-3, weight_decay: 1.0e-2)
  • scheduler: OneCycleLR (pct_start: 0.1, div_factor: 1.0e+3, max_lr: 1.0e-3)
  • epoch: 30
  • batch_size: 32

音声について

Torchaudio を使いました。

  • 音声波形
    • duration: 5s
    • sample rate: 32000
    • secondary label の使用 (0.5でソフトラベルに)
    • 音声が5秒未満の場合は、5秒を超えるまで音声を繰り返し連結
    • 検証時は音声の最初の5秒を使用
    • audiomentations.Normalizeを使用し正規化
  • メルスペクトログラム
    • n_fft: 2048
    • win_length: 2048
    • hop_length: 320
    • f_min: 50
    • f_max: 14000
    • n_mels: 128
    • top_db: 80
    • ImageNetの平均と標準偏差を使用して標準化

Data Augmentation

  • 音声波形
    • 訓練時には「全音声から5秒をランダムクロップ」 or 「音声の最初の5秒」を使用。それぞれ50%ずつの割合で行った。
    • audiomentationsという音声用のaugmentationライブラリを使用
  • メルスペクトログラム
    • Torchaudioの関数を使用。
      • torchaudio.transforms.FrequencyMasking( freq_mask_param=mel_specgram.shape[1] // 5 )
      • torchaudio.transforms.TimeMasking( time_mask_param=mel_specgram.shape[2] // 5 )
    • 単純なmixup (alpha = 0.5) とcutmix (alpha = 0.5) も使用。

事前学習

  • Birdclef 2021と2022のデータを使用して事前学習。リークを防ぐため2023との重複データは除外
  • 事前学習は15秒でover samplingは無しで実行。ここでは秒数は長めの方が効いた。

不均衡データへの対応

  • over samplingして、各クラスに最低50件のデータがあるようにした。

Nocall Dataの追加

  • augmentationで使用したバックグラウンドノイズの音声をnocall dataとしても使用。nocall dataはすべてのラベルを0に設定。

後処理

  • SEDモデルの場合、出力はframewise予測とclipwise予測の平均をとった。

推論高速化

  • モデルをonnx化した上でonnxruntime上で実行。torchscriptも試したがそれよりも高速だった。
  • 音声データの前処理を統一化。同じ前処理をしたデータを複数のモデルに入力できるようにした。
    • 音声の前処理がボトルネックだった(と思う)のでこれは効いた。
  • データにはあらかじめ前処理を施しdictに格納。この前処理には並列処理を使用し高速化。

CV戦略

  • StratifiedKFold (n=5, stratify=primary_label)
  • すべての鳥種が訓練データに含まれまれるようにした。

開発環境

いつも通り定額制のクラウドGPUであるPaperspace Gradientを使用。最初の2か月はProプラン(A4000中心)、最後の1か月はGrowthプラン(A6000中心)。 コンペ終了間際に障害とかメンテナンスいっぱい発生して大変でした...

うまくいったこと

  • over sampling
  • 「訓練時には全音声から5秒をランダムクロップ or 最初の5秒を使用。それぞれ50%ずつの割合で行った。」←これ
  • Background noise, Gaussian noise , Mask melspectrogram
  • SEDモデル
  • label smoothing
  • Nocall Dataの追加
  • 事前学習で秒数長め
  • SEDモデルとシンプルCNNのアンサンブル
  • secondary label
  • data loaderのnumworkersを多くする → めっちゃ学習と推論速くなる。

うまくいかなかったこと

  • Pink noise, Random volume
    • 上位解法には普通に含まれているので単に誤差だと
  • Focal Loss
  • 2023のデータで本学習を行う際の長めの秒数 (15s, 30s, etc.)
  • 前後の予測を使う
    • 前後の5s使って計15sにする→notebook timeout
    • 前後5sの移動平均、移動加重平均
  • 大きいモデル
    • 例えばeffcientnetv2_sよりefficientnetv2_b0の方がCV良かったりした。
  • geometric mean
  • LSTM head
  • 強めのdropout
  • rank ensemble
  • ハンドラベリング
    • チームの方が試されていたがうまくいかなかった...逆に過学習に繋がった説

上位解法

  • 高速化手法
    • openvino, onnx, torchscript, 並列化などさまざま
    • 高速化の面で言うとopenvinoが正解だったっぽい?
  • backbone
    • eca_nfnet, efficientnet, efficientnetv2, resnet, seresnext, convnext, convnextv2, rexnet
    • ↑これらの系統が多かった
    • nfnetは精度は良いが推論が比較的遅い。nfnetを使わないsolutionも存在していた。
    • resnet系統をアンサンブルに混ぜるのとか効いているっぽかった。
  • ハンドラベリングはうまくいかない
  • もっと過剰なaugmentation
  • レーニングデータ
    • birdclef2020/2021/2022/2023のデータ
    • birdclef2020の追加データ
    • Zenodo
    • xeno-cantoからの追加のトレーニングデータ(2023の鳥種も含まれていた)
      • 私たちのチームはCC BY-NC-NDライセンスは使用不可だと思っていたが、使用可能だとDiscussionに記載されていた。見逃していた。
    • 上記のデータを事前学習や本学習で使用
  • 50 epochなど多めの学習回数
  • 重み付け損失関数
  • secondary labelをpseudo lableling
  • BirdNetの使用
    • 蒸留
    • 埋め込みの使用。埋め込み→全結合層
  • 蒸留
  • mixup + CNNモデル(birdclef2021 2nd solution)

反省点・やりたかったができなかったこと

  • dataloaderのnum_workers多くして実験回せば良かった
    • ずっと少ない数で頑張って実験していた。CPU律速のタスクの場合こういうのは意識したい。
  • 上位との差は主に以下の点にありそう。
    • 使用データの違い。2023の鳥種を含むxeno-cantoの追加データを使うか使わないかでだいぶ違いそう。Disucussionの見落としがきつかった...
    • openvinoの使用
    • mixup + CNNモデルの使用
    • ensembleの数・多様性の少なさ
  • CVを見てアンサンブルに組み込まなかったモデルがある
    • resnetなど。サブ数的にも勿体無かったり純粋に面倒だったりと、作ったのに放置してたモデルとかあった。
  • birdnetの使用
    • 埋め込みを学習に使うなど、アイデアとしては思いついていたが実装を後回しにしており結果的に試さなかった。
    • 今回のこの例に限らず、効きそうだけど実装重めのアイデアを後回しにしてしまう癖をなんとしてでも直したい。

感想

Kaggle Masterをかけて臨んだコンペだったので、無事に銀メダルを取り昇格することができて良かったです。 一方、あと少し詰めていれば金メダルも取れそうな順位・解法だったので悔しさも大きいです。

また、今までKaggleはソロ参加でしたが本コンペで初チームマージをしました。 実際チームを組んだ方が楽しかったですし、入ってくるアイデアや知識の量も多くなり勉強になりそうだという感じがしました。 今まではビビってチームを組めていませんでしたが、今後積極的にチームマージに挑戦していきたいと思います。

Kaggle Masterになったということで、今後はKaggle Grand Masterを目指して頑張ります。