不注意でよく物を壊す

アクアリウムとかマイクロマウスとかアリュージョニストとかそのへん

Recursion Cellular Image Classification振り返り(39th)

はじめに

まずは興味深く楽しいコンペティションを開催してくれたkaggleに感謝します. 今回のRecursion Cellular Image Classificationは自分にとって初めてメダルを獲得でき,またそれ以上に多くの学びを得られたコンペティションでした. 以下,時系列ごとにやったことをずらっと並べてみました(まとめだけ見たい人は下までスクロールしてください......).

試した手法一覧

ベースラインモデル作り

まずはsoftmax_cross_entropyを用いた単純な分類モデルを作成しました(LB:0.11).

ベースはDenseNet121で入力は3x256x256のRGBに変換された画像を用いていました.最適化にはCorrectedMomentumSGDを用いました.(このころの学習率の設定などは散逸していしまいました)
Data Augmentationはrandom_sized_croprandom_flipを用いており,特にデータの正規化などはしていませんでした.

このころdiscussionでは単純な分類モデルでLB:0.3くらいは出ると言われているのでloss関数を改良しはじめました.
またCross Validationはcell typeで3foldで分割していましたがCVとLBのスコアは乖離していました.

Focal Lossを実装し,スコアが少し改善されました.(CV0.27->LB0.124) この時期にAdaboundも試しましたが機能しませんでした.

次にRGBに変換されていない6chの画像を入力に取るようにしました. kernelを見ているとmixupしているものがあったのでmixupとcutmixを用いましたが精度向上しませんでした.

次に単純な方策として学習epoch数を70から90に増やしスコアを上げました(CV:0.32 -> LB:0.19).

2stage学習

某神discussionのやり方に乗っ取って実装しました.
stage2のみでのスコアはCV:0.52->LB:0.21でstage1との重み付きensemble(2:8)を行うとLBスコアは0.225に上昇しました.
このころ例のleakが発表されLBスコアが0.303に改善されました.

みんな大好きArcFace

この辺りとか神々のクジラ解法などを参考にしながらChainerで実装し直しました.
実際CVスコアは0.5あたり出ていたのですが,ソースコードにバグがあったようで結局正しいスコアは出ていません.(LB:0.06)

AdaCos

ArcFaceハイパラなんもわからんってなってる時にTwitterに流れてきて良さげだったのでこれもPyTorch実装を参考にして実装しました. stage1でLB:0.298(Leakなし)に改善されましたがstage2でNaNが発生し,適当にClipingして直しましたがstage1に比べてスコアは上昇しませんでした.これはstage1を90epoch回していたので過学習していたのではと思います.ここからはstage2の細胞別で学習を行なうことはしていません(学習時間が流石にかかりすぎだと感じたので).

AutoFocalLoss

FocalLossのハイパラなんもわからんだったのでハイパラ調整自動化してくれるのないかなーと思って探したら見つけました.
FocalLossのgammaをいい感じに適応的に動かすパラメータに置き換えるというもので,ざっくり見た感じGitHubに上がっている様子もなかったのでFocalLossを改造して論文読みながら実装しました.記録は残っていませんが0.01~0.02くらいのスコア改善がありました.

random_sized_cropからrandom_crop

細胞の大きさはtrainとtestで違ってこないだろう(顕微鏡写真だし)と考えて,scaleのバリエーションを増やすのは害にしかならないと考えrandom_cropで512x512から256x256を切り出すようにしました.これによりLBは0.428(Leak込み)に改善しました.

Label Smoothingとか色々

label smoothingしましたが特に精度向上はしませんでした.過学習が怖かったのでとりあえずお祈りがわりに最後まで使用しました.
細胞の種類を予測するヘッドを追加すると良いのではと考えましたが全てがバグって辛かったです(ものすごい勢いで過学習する).

Metric LearningとFocal Lossの学習比率チューニング

これまでは固定の割合で学習させていましたが学習曲線を眺めているとMetric Learningの比率が高い場合,収束が早く心なし精度の上限が低く,Focal Lossの比率が高い場合,収束が遅かったのではじめはMetric Learningの比率を高めにして(1.0:0.1)epoch数が進むごとにFocal Lossの比率を上げていく(最終的には0.1:1.0)のを実装したところ0.05ほどのスコア改善がありました.鯨7位解法でもMetric LearningしたものをFineTuningするとよかったという話はあったような気がするのでそんな感じです.

per-Image-Normalize

それでも上位陣のスコアに程遠く,データやdiscussionを見ているとtrainとtestで明るさがかなり違うことに気が付いたのでクジラコンペ3位解法で用いられている画像ごとにNormalizeする手法を使うことでCV:0.6 -> LB: 0.55 -> 0.65(Leak)に改善しました.このころからCVスコアがある程度信用できるようになりました.

Data Augmentationの再考

コントラストや明るさを使ったデータ増強を用いましたが精度が低下しました. また,試したのはこの時点ではないですがShiftScaleRotateなども試しましたが精度がよくありませんでした. 90, 180, 270, 360 rotateを追加し,多少のスコア向上を得ました.

Cell Typeの分布でFocal Lossを重み付け

機能しませんでした.

GlobalConcatPooling(GAP+GMP)の実装

機能しませんでした.おそらくパラメータ数を落としすぎたためな気がします.

EfficientNet・DenseNet169・SEResNext101

機能しませんでした.DenseNet169とSEResNext101は過学習していました.EfficientNetは学習率を変えたりしていましたがうまく学習させることができませんでした.

ベースモデルの学習率を落とす

学習時のCVの上限が明らかに低下したのでsubmissionを作成しませんでした.

random crop から 画像全体を見るためにresizeに

CVは向上しましたがLBは低下しました.

Pseudo Labeling

上記までのDenseNet121で推定した結果の確信度が高いものの上位をtrainデータの1/5のtestからtrainに加えました.CV:0.67 -> LB:0.6 -> 0.689(Leak)に改善されました.

線形割り当て

終了3日前、ダメもとでHungarian Algolismを用いた線形割り当てを試したところLB:0.822に改善することができました.これに気づかなかったら銅メダルで終わっていたと思います.

Test Time Augmentation

上記のrandom cropをTestの時も使用していたので常にinputが違う画像になっていました.そのためかTest Time Augmentationの効果が大きかったと考えられます.最終日はTTAを増やしてスコアを上げて遊んでいました.

最終的なモデルまとめ

  • input: 6x256x256
  • model: DenseNet121(Leak+Pseudo Labeling+線形割り当て)をPseudo Labelingに用いたSEResNext50(Leakと線形割り当て込み)
  • Data Augmentation:random_flip+random_crop(size=(256, 256))+90rotate
  • per-Image-Normalize
  • 4Fold
  • LR: 0.04(54epoch目、63epoch目で0.1倍)
  • 32TTA
    • trainと同じaugmentationを使用
    • 上記のrandom cropのおかげでTTAの効果が大きかった(と考えられる)
  • CV:0.72 -> LB0.869

ソースコード

GitHub上にあるのですが最終盤で崩壊しかけたので整理して後日上げます......

追記

ここにあげました.

github.com

最後に

とても色々な学びがあり,楽しいコンペティションでした.改めて全てのwinnerに賞賛を,何より開催してくれたkaggleチームの方々に感謝します.
次は雲か脳かをやると思いますがファッションコンペの復讐復習をしたいので雲に気持ちが傾いています.読んでいただいた方もリーダーボードで会えることを楽しみにしています.参加された皆さん、お疲れ様でした!!!