機械学習エンジニアの備忘録

主に自分が勉強したことのメモ

Kaggleの網膜コンペで銅メダルをとったので振り返る

Kaggle で開催されたAPTOS 2019 Blindness Detection(網膜コンペ)にソロで参加したのでその振り返りです。

www.kaggle.com

結果は174th/2943 (TOP6%)で銅メダルでした。このコンペで2枚目の銅メダルを獲得し、Kaggle Expertになることができました。

f:id:rikeiin:20190917104024p:plain

コンペ概要

網膜の画像から糖尿病網膜症の重症度を予測します。
ラベルは0〜4の5段階で数字が大きいほど重症を表しています。

糖尿病網膜症については以下のスライドが分かりやすいです。

評価指標はquadratic weighted kappaです。
またkernel only コンペだったので学習は手元でOKですが推論はkernel上で完結させる必要があり、以下にkernelの制限時間内に推論できるかもこのコンペのポイントでした。

自分の手法

Validation Strategy

2015年にも同様のコンペ (APTOS2015)が行われており、そのデータと現コンペ (APTOS2019)のデータを単純に結合して学習データとしました。discussionでは2015のデータでpretrainして2019のデータでfine-tuningする手法が出ていましたが、めんどくさかったので単純に結合して使いました。
検証データは訓練データと同じラベルの分布になるように分割。最初は5-foldでCVしていましたが、時間がかかるので中盤からはsigle fold で学習させました。

あとの祭りですが、external dataとしてAPTOS2015だけでなくIDRIDやMessidorのような他のデータもあったようでそちらも使うべきだったなあと反省…。

preprosessing

前回コンペの優勝者が使用していた前処理がkernelで公開されていたのでそちらのコードを丸コピして使わせてもらいました。
処理内容は黒の背景部分を自動でcropし、ガウシアンフィルタをかけるというものです。

ガウシアンフィルタの強さを3段階くらい試してその中で最もlocal CVのスコアが高いものを使用しました。

画像サイズは256, 340, 512でいろいろ試したが画像サイズを増やすとoverfitする傾向があったので256 or 340の小さめのサイズでやっていました。

Augmentation

以下を使用しました。

  • Flip
  • Rotate
  • ShiftScaleRotate
  • RandomBrightness
  • RandomContrast
  • RandomGamma

後で上位の解法を見ると相当heavyな拡張をしていたのでもっといろいろ試しても良かったかも。

Models

CNNのモデルはResnet34, Resnet50, SE-ResNext50, Efficient-Netなどいろいろ試しました。
Efficient-Netは最近GoogleがNeural Architecture Searchで発見したモデルの構造で既存モデルより少ないパラメータで精度が良いようです。
実際、上位の解法の多くがEfficient-Netを使用していました。

ai.googleblog.com

Efficient-Netはパラメータの数によってB0~B7の7段階あり、自分は学習済みモデルが公開されていたB0~B5まで試してローカルのスコアが最も高かったB3を使用しました。

Training

discussionではロスをMSEとして回帰問題として解いているものが多かったですが、自分は最初あえてCrossEntropyLossを使った分類問題として解いていました。最終的には回帰として解く方法も試しました。
optimizerはAdam+CosineAnnealingを使用。
また、discussionで同じ画像でも異なるラベルがついていることがわかったりとラベル付けが適当な感じあったのでミスラベリング&汎化性能向上のためにLabelSmoothingを試してみました。
Label Smoothing: An ingredient of higher model accuracy

実際LabelSmoothingによってスコアが0.05ほど上がりました。

inference

最終的には以下の4つのモデルのアンサンブルした結果で提出しました。

  • Classificationとして解いたEfficient-NetB3で画像サイズを3種類で学習したモデル(size256, 340, 512)
  • Regressionとして解いたEfficient-NetB3で画像サイズ320(しきい値はローカルスコアで最適化)

自分のkernelの単純なコードだと制限時間で4回推論するのが限界だったんですが他の解法を見るとさらに多くのアンサンブルをしているものもあったのでここは工夫してもっと短時間で大量に推論してアンサンブルできるようにするべきだったかもしれません。

結果

Public LB: 0.808829, Private LB 0.918672で無事銅メダル圏内に残ることができました。
QWKで0.9超えるってどういう問題なんだ…

上位ソリューションのまとめ

1st place solution

1st place solution summary | Kaggle

  • 2015+2019のデータを学習データとして使用。Public LBをvalidationとして信頼した。
  • 特別な前処理なく単純なresizeのみ ← !!
  • 以下8つのアンサンブル
    • 2 x inception_resnet_v2, input size 512
    • 2 x inception_v4, input size 512
    • 2 x seresnext50, input size 512
    • 2 x seresnext101, input size 384
  • nn.SmoothL1Loss()
  • 最後のプーリング層をGeneralized mean poolingに変更
  • pseudo labeing

2nd place solution

4th place solution (2nd after LB cleaning) | Kaggle

  • APTOS2015でpretrained
  • 以下3つのモデル
    • Efficient-Net B3 image size 300
    • Efficient-Net B4 image size 460
    • Efficient-Net B5 image size 456
  • 黒背景をクロップしてサイズを変えるだけの単純な前処理
  • a lot of augmentation
  • pseudo labeling
  • Test Time Augmentation (TTA)

4th place solution

4th place solution | Kaggle

  • 黒背景除去&学習データの画像サイズごと3種類に分け、それぞれに対応した前処理
  • Effcient-Net B2~B7
  • heavy augmentation
  • APTOS2015で事前学習、2019で5-fold
  • 5 model x 8 TTA
    • 前処理を1回しかしないことで推論時間を短縮

5th place solution

5th place solution | Kaggle

  • APTOS2015でpretrained、2019でfine-tuning
  • 分類と回帰のマルチタスク学習 ← !!
  • Efficient-Net B0~B3
  • 2 TTA
  • pseudo labeling

7th place solution

10th place solution w/ code [Catalyst, Albumentations] | Kaggle

コードも公開されていました。
GitHub - BloodAxe/Kaggle-2019-Blindness-Detection

  • APTOS2015でpretrained、2019でfine-tuning. 外部データとしてidrid&Messidor使用
  • SeResNext50, SeResNext101, InceptionV4
  • 特殊な回帰として解く
    • 最終層でsigmoidを通した4要素のベクトルを予想し合計をとる
    • MSE
  • Loss function
    • focal kappa > soft CE(label smoothing) > plain CE
    • Regression: WingLoss
    • Ordinal Regression: Huber loss (aka smooth L1 loss) -> Cauchy Loss (?)
  • mixtured precision FP16 (Apex)
  • pseudo labeling
  • RAdam
  • 4 model ensumble

9th place solution

12th place solution | Kaggle
9th place solution | Kaggle
APTOS 2019 Blindness Detection - ALOHA

  • APTOS2015で事前学習、2019で4-fold
  • 前処理はBen's croppingをベースにアスペクト比を維持するようなresize
  • Efficientnet-B7 and SE-Resnext 50
  • 4 TTA
  • duplicate imageのラベルを平均するような処理?
  • blending
    • 12個の異なるモデルの4-hold x 4 TTA(計192)
    • どうやったらカーネルの制限時間で192回も推論できるんだろう…

10th place solution

13th place solution | Kaggle

日本人の方がブログで手法を公開してくれています。
nmaviv.hatenablog.com

反省

上位のソリューションを見るとほとんどがpseudo labeling を使用していました。このコンペはtrainとtestの分布がかなり違ったのでpseudo labelingでtestの情報を与えることが重要だったのだと思います。
まだ自分はコンペでpseudo labelingをしたことがないので次回はしっかり習得したい使ってみたい。

また、Grad-CAMのような手法をつかってデータセットや前処理の手法を分析をしているカーネルがあったりしてとても勉強になりました。今後は自分もそういう分析ができるようになりたいですね。

何はともあれKaggle Expertに上がれてよかったです。
ymicky | Kaggle

次はNIPSコンペ、鉄鋼コンペでやろうと思います。

参考

28thの方も手法を公開してくれています。
speakerdeck.com