<メモ>Human Protein Atlas Image Classification上位解法を読んだのでまとめる

1. まえがき

2018年11月〜2019年1月にHuman Protein Atlas Image Classificationに挑戦しました。
www.kaggle.com
結果は122位/2172と、銀メダルに届きませんでした。。悔しい。
f:id:kurupical:20190210220746p:plain

自分がやった取り組みは余力があれば書くとして、今回は上位の皆様の解法を読んでまとめていきます。

2. コンペ

灰色文字は私の考察、それ以外は記事の記載内容と書き分けています。
訳しきれなかったところは、注釈で原文を入れています。

2-1. 概要

コンペの概要については、以下を参考にしてください。
tawara.hatenablog.com

2-2. 上位Solution

2-2-1. 1st place solution

www.kaggle.com

全体像は以下の通り。 ※ディスカッションより転載
f:id:kurupical:20190210162255p:plain

  • CVはMultilabel Stratification*2で切っている。
  • Augmentation
    • Rotate 90
    • flip
    • randomly crop 512x512 patches from 768x768 images(or crop 1024x1024 patches from 1536x1536 images)
  • 学習
    • 学習率のスケジュール:
lr = 30e-5
if epoch > 25:
    lr = 15e-5
if epoch > 30:
    lr = 7.5e-5
if epoch > 35:
    lr = 3e-5
if epoch > 40:
    lr = 1e-5
  • 損失関数

    • FocalLoss+Lovasz.
      Lovasz lossはSemantic segmentationのものでは?と思ったのですが、Lovasz lossの考え方はPrecision/Recallの考え方に一部適用できるということだそうです(Lovasz lossよくわかっていないです…)
  • NN

    • DenseNet121
      DenseNetについては以下が分かりやすかったです。
      qiita.com
      分類レイヤーは以下の通り。
  (1): AdaptiveConcatPool2d(
    (ap): AdaptiveAvgPool2d(output_size=(1, 1))
    (mp): AdaptiveMaxPool2d(output_size=(1, 1))
  )
  (2): Flatten()
  (3): BatchNorm1d(2048, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (4): Dropout(p=0.5)
  (5): Linear(in_features=2048, out_features=1024, bias=True)
  (6): ReLU()
  (7): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  (8): Dropout(p=0.5)
  (9): Linear(in_features=1024, out_features=28, bias=True)

参考:DenseNetの元論文の構造*3
f:id:kurupical:20190210163224p:plain

原論文はAvgPoolingだけなのに対し、この解法はAdaptiveMaxPoolingも加えています。全体の情報に加えて、特徴的な情報も食わせることで、より「よしなに」やってくれる感じなんでしょうか。※AdaptiveXXXPool2d(output_size(1, 1))はGlobalXXXPooling2dと同義。

  • TTA

    • lossが一番低いepochのモデルを分類器とし、random cropで予想を4回作成→予測の最大値をpredictionとする?*4
  • Metric Learning ※筆者の解釈を多く含みます。間違っていたらご指摘ください。。
    」 ※(2/14追記:metric learningについては、以下がわかりやすいと情報いただきました。)
    techblog.zozo.com

    • Metric Learningとは、平たく言えば、testデータに対してtrainデータとの類似度を計算し、その類似度が一定以上であればtrainデータのラベルに書き換える処理?のようです 
      いくつか、デモがされていました。(ディスカッションより転載)

    f:id:kurupical:20190210194822p:plain f:id:kurupical:20190210195038p:plain f:id:kurupical:20190210195358p:plain

    一番左が「予想したいデータ」、一番左以外は、スコア(≒類似度)が高い画像から順に並べたものです。
    スコアのしきい値によって1000枚ほどの画像を書き換えたところ、LBのスコアが0.03以上向上したそうです。

    この「Metric Learning」ですが、スコアを向上する以外だけでなく、以下のように応用ができると述べられています。

    具体的にどのように画像間の距離を計算したのかが気になるところですが、「開催中の別コンペでも使っているのでまだ教えられない」ということでした。

  • 最後に
    if we compete only for win,we may loss,if we compete for learning and providing useful solution to the host,nothing to loss.

2-2.2. 3rd place solution

www.kaggle.com

  • CV
    • trainの10%を、アンサンブル検証用に除外(holdout)
    • 残り90%で5fold cv
    • 異なるfold間で類似の画像がないようにする。(ahashなどで画像をハッシュ化して調べた)
  • normalization.(officialデータとexternalデータで、画像の平均と分散が大きく異なったため)
  • Augmentation
    • AutoAugmentation*5を参考にした。最適なaugmentationの探索は、強化学習ではなくランダムサーチで行った。
  • 損失関数
    • FocalLoss(gamma=2)
  • Optimizer
    • Adam(lr=5e-4)
    • learning rate scheduling無し
  • 大きいモデルで大きい画像を扱うときは、32枚ごとに重みをupdateするようにした?※原文の意味が読み取れず…バッチサイズ32だとメモリ不足になるのでこの対応をしている?*6
  • 一番メジャーなクラスのF1スコアが減少したところで訓練を止めた(Early stopping)
  • 推論
    • 各訓練の最終10個のチェックポイントの重みの平均を使ったNN
    • 8 TTAs
    • weighted average ensemble
    • 分類のしきい値
      • validation setの予測割合とあうようにしきい値を選択
    • モデル(以下3つをアンサンブル)
      • 512*512
        • ResNet34(5fold ensemble)
      • 1024*1024
        • inceptionv3
        • se_resnext50
    • NN構造
      • 1024*1024だとglobal average poolingがworkしなかったので以下変更(よくわからなかったので原文)
        • remove global average pooling.
        • compute MxM logits using 1x1 convolution.
        • compute weight maps using 1x1 convolution followed by softmax.
        • using weight maps, compute weighted averaged logits.
          global average poolingの代わりに、セルの値×各セルの重みをsoftmaxで計算した感じなんでしょうか…。

2-2-3. 4th place solution

www.kaggle.com

  • GAPNet*7を使った
    • GAPNetはmultiscaleの能力がある点で優位。→multiscaleのニュアンスは、画像全体(マクロ)と細かい点(ミクロ)の両方から判断できる、ということかなと解釈しています。
    • GAPNetの発想をResNet18+SE-Blocksに適用した。
  • Dual Loss ResNet
    • 30分格闘してさらにいろいろ教えてもらったのですが消化できなかったので、後で書きます。現時点では、何が分かってなきゃいけないのか分かってないです。。 f:id:kurupical:20190211113402p:plain

2-2-4. 5th place solution

www.kaggle.com

  • Augmentation
    • Rotation
    • Flip
    • Shear
    • Random Cropの代わりにFiveCrop*8が有効だった
  • NN
    • ImageNetでpretrainした重みを使用
    • 最終的に採用したのは
      • Inception-v3
      • Inception-v4
      • Xception
    • モデルの変更点
      • 最後のpooling層をglobal poolingに変更した→Inception-v4, Xceptionはもともとglobal average poolingになっているが、どういうニュアンスなんでしょうか?1st place solutionみたいに、global (average + max) pooling?
      • pooling層とoutput層の間に、dense(128)を加えた
      • 2-stage制にした
        • まず512サイズで訓練
        • その後650サイズ、800サイズで訓練
  • 10-fold CV
  • 損失関数
    • MultiLabelSoftMarginLoss
  • オプティマイザ
    • SGD(lr=0.05)
    • StepLR(gamma=0.1, step=6)
  • 25epochs(650か800サイズは15epoch程度でearly stopping)
  • lossが一番低いものを選択(F1scoreではない)
  • sampling weightを設定。(レアなクラスほどよくサンプリングされるように)
  • workしなかったもの
    • DenseNet
    • SENet
    • ResNet
    • 3-channel input
    • サイズ1024以上での訓練
    • focal loss
    • C3D
    • TTA
    • 古典的な機械学習手法:決定木やランダムフォレスト、SVMなど

2-2-5. 7th place solution

www.kaggle.com

  • RGB入力
  • 最後のpooling層を、concat([attention weighted average pooling*9, global max pooling, global std pooling])に変更
  • augmentation
    • contrast(各チャネル独立に)
    • rotate
    • scale
    • shear
    • shift
  • 損失関数:BCE loss
  • NN
    • ResNet18(1024*1024)
    • ResNet34(10241024, 768768)
    • inceptionv3(768*768)
  • trick
    • テストデータ間で重複している思われるものは、重複しているデータ間で予測の平均を取るようにしたところ、スコアが0.04-0.05が向上した

2-2-6. 8th place solution

www.kaggle.com

  • NN
    • SE-ResNext50(256, 512, 768)
    • InceptionV4(512)
    • BN-Inception(512)
    • Xception(512)
  • CyclicLR(最初のサイクルは長くして、逓減させる)
  • Focal Loss, LSEP loss*10
  • Augmentation:
    • Brightness
    • D4 and wrap transforms
  • 32TTAの平均

3. 感想

  • bestfitting氏、single modelで優勝スコアだわ、、実務への適用まで考えたソリューションを提案しているわでカッコ良すぎでした。
  • 多くのsolutionは、最後のpooling層はいくつかのGlobalXXXPoolingをconcatしていて(論文では単純にGlobalAveragePoolingが多い)、これが主流なのかな〜と思いました。(確かに、MaxPoolingも与えてあげるとよさそう)
  • externalデータはラベルの質がよくなかったようです。また、OfficialデータとExternalデータにいくつか重複があったり、分布が違ったり…というところは上位陣はしっかりおさえていました。ネットワークの調整だけでなくデータの質もしっかり見ることが大事ですね。
  • 最後のほう、力尽きました。(ごめんなさい)
  • solutionのまとめ記事書くだけでかなり勉強になりました。こうでもしないと流し読みして何も頭に入らないですね。

*1:https://www.kaggle.com/allunia/protein-atlas-exploration-and-baseline

*2:https://www.kaggle.com/c/human-protein-atlas-image-classification/discussion/67819

*3:https://arxiv.org/abs/1608.06993

*4:原文:I predicted the test set by using best focal loss epoch with 4 seeds to random crop 512x512 patches from 768x768 images, and got the max probs from the predictions.

*5:https://arxiv.org/pdf/1805.09501.pdf

*6:For the large model with 1024x1024 images, I used gradient accumulation so that weights are updated every 32 examples.

*7:https://openreview.net/pdf?id=ryl5khRcKm

*8:torchvision.transforms.FiveCrop(size) Crop the given PIL Image into four corners and the central crop

*9:3rdと同じ発想のよう。github.com

*10:https://arxiv.org/pdf/1704.03135.pdf