statsuのblog

愛知のデータサイエンティスト。自分の活動記録。主に機械学習やその周辺に技術について学んだことを記録していく予定。

Kaggle Bengali Classificationコンペに参加した記録

KaggleのBengali.AI Handwritten Grapheme Classificationコンペに参加して24位(2059チーム中)でソロ銀メダルを取れました。ついでにkaggle expertになりました。
以下ではその記録についてまとめます。

本記事の概要

  • コンペ概要
  • 私の取り組み
  • 興味深かった上位ソリューション
  • 感想

コンペ概要

f:id:st1990:20200318161815p:plain
Bengali.AI Handwritten Grapheme Classification
Bengali.AI Handwritten Grapheme Classification | Kaggle

  • タスク概要

    • 手書きのベンガル語1文字の画像が与えられ、どの文字が書かれているか分類するタスク。ただし、ベンガル語は書式素(Grapheme root)、母音(vowel diacritics)、子音(consonant diacritics)がくっついて1文字になるので、書式素、母音、子音がそれぞれ何かを分類する。
    • 書式素は169クラス、母音は11クラス、子音は7クラスある。
  • データ

  • 予測性能の評価方法

    • 書式素、母音、子音それぞれのマクロリコールを0.5、0.25、0.25で重みづけ平均したものが評価値。
  • スタンダードな解法

    • 解法(1):CNN系統の画像分類器で画像から書式素(169クラス)、母音(11クラス)、子音(7クラス)それぞれのラベルを予測する。
    • 解法(2):CNN系統の画像分類器で画像から文字のラベル(1295クラス)を予測する。学習データ中には書式素、母音、子音の組合せが1295パターンあったため1295クラス。ただし、実際のベンガル語では当然他の組合せも存在する(169×11×7パターン?)。
  • 大規模なshake

    • このコンペでは大規模なshakeが発生し、一部のトップ層を除いて、Public LBとPrivate LBの順位が激しく入れ替わった。原因は、学習データに含まれていない書式素、母音、子音の組合せがテストデータに多く含まれていたことである。上記の解法(2)のような学習データ中に存在している組合せに適合する方法を採用していた人は、Public LBではいいスコアであったが、Private LBでは大幅に下落した。トップ層は学習データに含まれている組合せに適合させつつ、見えていない組合せもケアする工夫をしていたので、Public LBでもPrivate LBでもいいスコアであった。ほんますごい。
    • 僕はこの恩恵にあずかってshakeupし、銀メダルになった。Public LBではメダル圏外で完全にあきらめていたので心底びっくりした。
      https://www.kaggle.com/c/bengaliai-cv19/leaderboard

私の解法

私の解法は以下のとおりです。基本は上記の解法(1)で、工夫した点は評価値であるマクロリコールに最適化するために実数値遺伝的アルゴリズムを使ったところです。

コード

GitHub - statsu1990/kaggle_bengali_classification: kaggle bengali classification 24th silver solution

概要

Discussionにもsolutionをあげましたが、ここでは日本語をのせておきます。(下手な英語だから理解してもらえているのかかなり不安。。笑)
https://www.kaggle.com/c/bengaliai-cv19/discussion/136064

私のソリューションでは、単純なアンサンブルモデルと、マクロリコールを最大化するための後処理を使いました。後処理では、モデルが出力したlogitにバイアスを足します。後処理が私のスコアの多くを占めており、後処理なしではメダル圏外でした。

Inference Label

まず、DL modelでlogitを出力します。後処理として、logitにバイアスを加えます。それをargmaxしたものを推定ラベルとします。
f:id:st1990:20200318172917p:plain

Model

モデルは5つのモデルをアンサンブルしたものです。学習にはトレーニングデータをすべて使いました。私の時間と計算資源が限られていたので、クロスバリデーションをほとんどできていません。

  • model 1, 2, 3, 4

    • Seresnext50_32x4d (pretrained model using imagenet)
    • image size 128x128
    • Three linear head (output is grapheme root logit(168), Vowel diacritic logit(168), Consonant diacritic logit(168))
    • Mish activation, Drop block
    • Cross Entropy Loss
    • AdaBound
    • 以下のパラメータをモデル毎に変える
      • Manifold mixup (alpha, layer), ShiftScaleRotate, Cutout
  • model 5

    • Seresnext50_32x4d (pretrained model using imagenet)
    • image size 137x236
    • Three linear head (output is grapheme root logit(168), Vowel diacritic logit(168), Consonant diacritic logit(168))
    • Mish activation, Drop block
    • Cross Entropy Loss
    • AdaBound
    • Manifold mixup (alpha, layer), ShiftScaleRotate, Cutout
後処理のバイアスの計算

モデルはCross Entropy Lossで学習されているので、一般的にマクロリコールを最大化しません。そこで、私はlogitに加えることでマクロリコールを最適化するようなバイアスを使うことを考えました。最適なバイアスを以下の手順で計算しました。
f:id:st1990:20200318172935p:plain

  • 学習データ全てに対してlogitを計算する。
  • 実数値遺伝的アルゴリズムでマクロリコールを最大化するバイアスを計算する。
  • データ拡張した学習データ全てに対してlogitを計算する。
  • 実数値遺伝的アルゴリズムでマクロリコールを最大化するバイアスを計算する。
  • 上記のバイアスを平均し、最終的なバイアスとする。

テストデータの推定では、学習データで算出したバイアスを使います。
f:id:st1990:20200318172959p:plain

実数値遺伝的アルゴリズムについてはこちらのページを参考にしてください。
st1990.hatenablog.com

Score (Public LB / Private LB)
  • model1(wo bias) : 0.9689 / 0.9285
  • model2(wo bias) : 0.9680 / 0.9270
  • model3(wo bias) : 0.9691 / 0.9317
  • model4(wo bias) : 0.9681 / 0.9243
  • model5(wo bias) : 0.9705 / 0.9290
  • ensemble1~5(wo bias) : 0.9712 / 0.9309
  • ensemble1~5(with bias) : 0.9744 / 0.9435

興味深かった上位のソリューション

上位はかなり工夫しててソリューションみるのも楽しかった。
まとめ
https://www.kaggle.com/c/bengaliai-cv19/discussion/136030

感想

  • パラメータチューニングやモデルの構造など細かいところに時間をとってしまい、問題の特性を踏まえた大局的な取り組みができなかったのが反省。評価値について考察し、それに最適化させる方法を考えられたのは良かった。
  • 今回のコンペの上位ソリューションは発想が素晴らしかった。問題についてよく考えて人と違う発想をしないと勝てないってことがよくわかった。