機械学習
記事内に商品プロモーションを含む場合があります

BirdCLEF 2024に挑戦| Kaggleチャレンジ記録

tadanori

Kaggleのコンテスト「BirdCLEF 2024」へチャレンジした記録です。

今回はLBでは24位でしたが、最終は486/991位とかなりシェークダウンする結果となりました。どうも、LBに過剰適合していたようです。

BirdCLEF 2024の概要

BirdCLEFコンペは、2021年/2022年/2023年/2024年と継続して実施されている、鳥の音声データを用いて鳥の種を特定することを目的とした機械学習コンペティションです。

毎年、少しづつ異なるテーマになっており、今回は、4分間のでデータを5秒間隔で区切り、各区間で鳴いているのがどの鳥なのかを当てるコンテストです。出力がクラスラベルではなく、全部で182種類の鳥それぞれについて数値を返す形となっています。

BirdCLEF2021に参加したことがあり、今回のコンペで2回目の参加となります。前回と異なる部分はいくつかありますが、基本的には鳥の鳴き声から種類を識別し、結果を返すという点では同じです。

このコンペの難しい部分は、自然環境での録音には風や他の動物の声、川のせせらぎなどのノイズが含まれており、これらの音が含まれるデータから鳥の種別を判定する必要がある点です。

また、今回は、「CPUで2時間以内に推論」という制約も設けられているため、大きなモデルを使うことができない点もポイントです(昨年も同様だったようです)。

この記事では、BirdCLEF2024の取り組み内容について解説します。

取り組み内容

今回も参加が遅くて、ゴールデンウィークの途中から取り掛かりました。開始が4月3日なので1ヶ月してからの参加になります。参加していたコンペが4月中旬に終了して、しばらくまったりしていたので参加が遅れました。

とりあえず、公開コードとディスカッションを眺めてみる

とりあえず、公開コードとディスカッションを眺めてみます。鳥コンペは2021年に参加していましたが、ルールは若干違いました。今回の提出は、5秒毎にどの鳥が鳴いているかを182種類の鳥全ての確らしさ(Probability)を出力する形式でした。

とりあえず、公開コードを眺めるとPytorch Lightningを用いて書かれていた推論訓練コードがありましたので、それを参考にすることにしました。

参加した時の公開コードの一番高いスコアはLB0.64でした。参考にしたコードはLB0.61と決して高いスコアではありませんでしたが、Lightningが使われていたことと、変更しやすそうなことからこれを参考にすることにしました。今回は、最後まで、このコードを改変したコードを利用して学習・推論をしています。

学習・推論コードを作成

参考にした公開コードを書き換えて、オリジナルの学習・推論コードを作成しました。

まずは、公開コードで気になった部分を変更していきました。変更点は以下になります。

  • スペクトログラムからメルスペクトログラムへ変更
    公開コードはスペクトログラムを利用していましたが、2021年に参加した時にメルスペクトログラムの方が良かった記憶があるので、torchaudioのメルスペクトログラムを使う様に変更しました。
  • モデルの修正
    ベースにしたモデルは、timmのfeatureだけ利用してhead部を自作していました。また、入力が3chでした。これを、入力を1chにし、出力もtimmのパラメータ設定で182クラス出力にするように変更しました。timmの機能を使って、なるべく追加コードを書かない形に修正したイメージです。
  • ロス関数
    CrossEntropyLossをFocalLossBCEへ変更しました。こちらは、他のコードを参考にしました。これも2021年のコードで使っていたので合わせた形です。
  • スケジューラの変更
    CosineAnnealingWamRestartsから、GradualWarmupSchedulerにスケジューラーを変更しました。こちらも、GradualWarmupSchedulerを修正したバージョンが他のコードにあったのでそちらを参考に修正しました。
  • ログ記録
    Weight&Bias(wandb)へ結果とモデルを保存する様にコードを追加しました。
  • 学習コードからデータセット作成コードを分離
    データセット作成が重いので、ここを切り離して「データセット作成用のノートブック」を作成しました。データセット作成部分はGPUを使わずにCPUだけで処理することで、GPU時間を節約するのが狙いです。

とりあえず、上記の変更を行なったコードを作成して、以降の検討を行いました。

合わせて読みたい
メルスペクトログラムに変換する方法
Mel Spectrogram|音声データを画像用のDNNに入力する方法
Mel Spectrogram|音声データを画像用のDNNに入力する方法
音声データの学習に関する記事
音/音声データを画像用DNN(CNN)で学習・推論する方法
音/音声データを画像用DNN(CNN)で学習・推論する方法

初サブ(LB0.61)

ベースコードは、efficientnet_b0を使ったコードでしたが、少しオリジナリティを出したいと思ってefficinetvit_v0を使うように変更して学習・推論を行ない、動作確認を含めてサブしてみました。

初サブのLBは0.61と、参考にしたベースラインと同程度の結果でした。

まずは、きちんと動作したことが確認でき、スコアも可もなく不可もなくといったところでした。

ここから、改善検討を行いました。

検討内容

以下、今回検討したことを簡単に紹介していきます。

検討したこと

スケジューラ調整

学習データセットを生成するコードを修正

学習データセットを生成するこコードを修正して、オーバーサンプリング、メルスペクトログラムのパラメータ調整、nocall(鳴いていない)データ追加など行いました。

label smooting

学習時にはラベルスムージングを行いました。

mixup/cutmix

mixup/cutmixを入れて学習させました

あわせて読みたい
データ拡張(data augmentation)手法のmixupを解説|Pytorch 【初級 深層学習講座】
データ拡張(data augmentation)手法のmixupを解説|Pytorch 【初級 深層学習講座】
あわせて読みたい
timmを使ってMixup/CutMixを手軽に実装する方法
timmを使ってMixup/CutMixを手軽に実装する方法

データ拡張

ホワイトノイズ追加、ピンクノイズ追加などの音声データに対するデータ拡張を行いました。

あわせて読みたい
Audiomentations|音声データ向けデータ拡張ライブラリを解説
Audiomentations|音声データ向けデータ拡張ライブラリを解説

色々なモデルをチェック

efficientvit_b1, efficientnet_b0/b1, resnet18d, eca_nfnet_l0などのいくつかのモデルを試しました。timmを使っていたのでここは簡単でした

アンサンブル

複数の学習したモデルのアンサンブルを行いました。efficientvit_b1が思いのほか高速だったので結構な数のアンサンブルが可能でした。

OpenVINOによる高速化

実行時間が2時間+CPUでの推論という制約があるのでOpenVINOを使って高速化しました。体感2倍くらい高速になります。

バックグラウンドデータ作成

BirdCLEF 2021のtrainデータからnocallを切り出してサウンドデータを作成し、バックグランドデータ(データ拡張)に利用しました。

n_melsを 128から256に変更

スペクトログラムの解像度が256×256にしていたので、メルスペクトログラムのフィルタバンクサイズを 256に変更。

予測結果の時間平滑化

予測結果を最終的に、±10秒の範囲で平滑化する処理を後処理として加えました。コンペ後に公開モデルに対しても適用してみましたが、これは効果があった様です。

検討しなかったこと

プレトレーニング

プレトレーニングは今回検討しませんでした。

最終的なモデル

最終的なモデルは

  • efficientvit_b0を3モデル
  • efficientvit_b1を3モデル
  • mobilenetv3_large_100を3モデル
  • resnet18dを1モデル

のアンサンブルです。OpenVINOを使うと、このモデルで2時間の制限にギリギリ間に合う形でした。

結果

目標にしていたLB0.70に、締切1日前になんとか到達できました。若干オーバフィッティングが不安ですが、とりあえず目標を達成できたことは大きいです。

最終的な結果は、PB0.59で486位と、LB24位から-462位と大幅シェークダウンしました。かなりオーバーフィッティングしてしまっていた様です。

感想など

前回、birdCLEF2021に参加した時は、音声に対する推論を全く知らない状態でした。今回は、音声データの取り扱いを知っている状態からスタートできました。また、3年前はPyTorchも初心者で「??」な部分が多かったのですが、今回は最初から自分でモデルを設計できる程度には知識もあり、自身のスキルレベルが前回よりは大幅に進歩していました。

おかげで、いろいろやり方を考えることができました。

ただ、オーバーフィッティングしてしまって成績自身はパッとしなかったです。

CVが全然当てにならなかったので、得られる情報からオーバフィッティングはしょうがないかなと少し思っています(LBでメダル圏内だった人のたくさんの人がオーバフィッティングして順位を落としていました。順位を維持できたのは20組くらい?)

After Contest

After contestとして、34位にエントリーしていた公開ノートブックに、検討していた後処理(時間方向の平滑か)を加えただけのやつがPB0.662と20位以内になりました。後処理は間違っていなかった様です。

また、上位解法に音声信号へのデータ拡張は行わずに、画像に変換したあとの部分だけデータ拡張を行うものがありました。とりあえず、オーバサンプリングその他の工夫は全部やめて、mixupと画像に対するデータ拡張で行うだけのものに変更してサブしてみました。efficientvit_b1のモデルを使って実験してみましたが、スコアはLB0.625, PB0.571と特に変化はありませんでした。ということで、オーバサンプリング、オーディオ信号へのデータ拡張がスコア悪化に繋がったわけではなさそうです。

以上、After Contestで実験してみた結果です。

結局、モデルの作りがシンプルすぎたのもの問題かもしれません。Head部分も工夫すればよかったかもしれません。

まとめ

ICRに引き続き大幅なシェークダウンを経験しました。LBに公開しているデータが全体のデータと特性が異なる場合にオーバフィッティングしやすいと思うのですが、今回はそこまでオーバーフィッティングしていないと思っていたのに予想外でした。上位数人の方はそこまで落ちていないのを考えると、うまく学習させる方法があった様です。

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました