Deep Percept

OFFICIAL BLOG
2020/06/09

リサーチ

KaggleのUnderstanding Clouds from Satellite Images(雲コンペ)に参加しました

あいさつ・自己紹介

はじめまして。Deep Percept開発チームの久保です。

私は以前、親会社であるSimplexで金融商品の商品価値計算、リスク指標計算エンジンの開発をしていました。 元々Pythonと機械学習に興味を持っていて、趣味でKaggleをやっていたこともあり、2019年にDeep Perceptで働き始めました。

この記事では、衛星画像から雲の形態を推定するKaggleのコンペティション "Understanding Clouds from Satellite Images"(以後、雲コンペ)に参加した経験を紹介します。

衛星画像の解析は⾦融領域におけるalternative dataの一つとして重要性が⾼まっている分野で、具体的には石油タンク画像から貯蔵量を算出し先物取引へ活用、店舗の駐車場を撮影した衛星画像から売上高の予測を行い投資判断に活用といった事例があります。 このコンペティションも、衛星画像を使った投資判断のシステムを構築する上でとても参考になる題材の1つです。

Kaggle紹介

Kaggleとは幅広い分野の機械学習コンペティションが開催されているプラットフォームで、様々なデータ形式(テーブルデータ、時系列、テキスト、画像、動画等)、様々なタスク(回帰、分類、時系列予測、異常検知、物体検出、画像セグメンテーション等)の大会が開催されています。

このコンペで上位の成績を取ると賞金をもらえたりメダルを取得することができます。メダルをいくつか集めると、その取得数に応じてNovice→Contributor→Expert→Master→Grandmasterと階級が上がっていきます。まるでゲームのようですが、機械学習エンジニアとしてのスキルを具体的に示せる指標でもあるので、モチベーション高く取り組むことができます。

また、世界中の機械学習エンジニア達が生のソースコードを"Kaggle Notebook"形式で共有してくれているので、機械学習のノウハウを広く深く得るのに最適な場です。

ちなみに私は今まで1つの金メダル(チーム)と4つの銀メダル(ソロ)を取っていて、現在Kaggle Masterです。https://www.kaggle.com/yukikubo123

コンペティション紹介

Understanding Clouds from Satellite ImagesはMax Planck Institute for Meteorologyによって開催されました。

このコンペでは衛星画像に写っている雲の形態を検出するのが目的で、うまくいけば雲が気候形成に及ぼす影響の研究の一助になるそうです。

ここで言う雲の形態は、コンペ開催者によって「Sugar」,「Flower」,「Fish」,「Gravel」の4つとして定義されています。だいたいは名前の通りなのですが、見た目では違いがわかりにくい部分もあるので簡単にまとめておきます。

雲の形態 説明
Sugar かなり細かい雲で、粉っぽい見た目。
構造化されておらず、まばらに散らばっている。
Flower 一つ一つの独立した雲の塊が複数の花のように見える。
Fish 魚の骨のような見た目。
広範囲に骨のようなマクロ的ネットワーク構造が見える。
GravelSugar Sugarよりは大きく、Flowerよりは小さな雲の塊が
散らばっている様子。小石のような雲が大量に散らばっている。

提供されるデータセットとしては、訓練用の衛星画像5546枚と、各画像に写っている雲の形態の正解マスクラベルの座標情報、テスト用の3698枚の衛星画像です。例として実際に訓練用の衛星画像に正解マスクラベルを重ねて表示してみると、以下のようになります。

Sugarが黄色、Flowerが赤、Fishが青、Gravelが緑です。

機械学習タスクとしては、この4つの雲の形態に対するセグメンテーションを行うことになります。つまり、テスト用の3698枚の画像に対してどれだけ精度の高い予測マスクを生成できるかが問われています。

評価指標などの詳細が気になる方は、公式サイトでの確認をお願いします。https://www.kaggle.com/c/understanding_cloud_organization/overview/evaluation

私の解法

Framework

今回はPyTorchと、PyTorch frameworkのcatalystを使いました。PyTorchの訓練部分はepoch毎のループを自分で実装しないといけないのですが、catalystを使うと非常に簡素に書けます。他にも、煩雑になりがちなmetrics、optimizer、logging、callbackなどの処理を簡単に記述することができます。公式のexamplesも非常に充実していて、大いに参考になりました。

Network architecture

ネットワークアーキテクチャはU-NetFPNPSPNetLinkNetあたりを試しました。

私の事前の予想ではこの部分で大きく精度に差が出るのではないかと思っていたのですが、このコンペでは意外と精度に大きな差が出ず、U-Netを軸に実験を進めていくことにしました。これらのアーキテクチャは、segmentation_modelsライブラリで提供されています。

Encoder

ResNet18、ResNet34、EfficientNet-B2~B5を試しました。

この部分は当初の想定通り、モデルのパラメータ数が多い大きなモデルほど精度が高くなりました。ただし、学習にかかる時間も長くなる傾向にあって、かつEfficientNet B4程度で精度が頭打ちになっていたので、私はEfficientNet-B2やB3を主に使用しました。これらのencoderも上述のsegmentation_modelsで学習済みモデルが公開されているので、それを転移学習して使っていました。

Data augmentation

albumentationsを使って実装しました。

精度の良し悪しがアーキテクチャやモデルの種類よりもaugmentationに大きく依存することを発見して以来、コンペの大半の時間をaugmentationの工夫に費やしました。かけるaugmentationの種類やパラメータ、組み合わせを実験して、最終的なaugmentationコードは以下のようなものになりました。

 



 elif AUG_PATTERN == AugmentationPattern.FINAL:
     blue = (0, 26, 67)
     train_transform = [
         albu.CoarseDropout(p=1, max_holes=16, min_holes=1, max_height=32, max_width=32,
                            min_height=6, min_width=6, fill_value=blue),
         RandomNoiseCoarseDropout(p=1, max_holes=16, min_holes=1, max_height=32,
                                  max_width=32, min_height=6, min_width=6),
         albu.VerticalFlip(p=0.5),
         albu.HorizontalFlip(p=0.5),
         albu.ShiftScaleRotate(scale_limit=0.3, rotate_limit=10, shift_limit=0.1, p=0.5,
                               border_mode=cv2.BORDER_REFLECT_101),
         albu.GridDistortion(p=0.4, distort_limit=0.2, border_mode=cv2.BORDER_REFLECT_101),
         albu.OpticalDistortion(p=0.4, distort_limit=1.0, shift_limit=0.5,
                                border_mode=cv2.BORDER_REFLECT_101),
         albu.RGBShift(p=0.3, r_shift_limit=5, g_shift_limit=5, b_shift_limit=10),
         albu.HueSaturationValue(p=0.3, hue_shift_limit=20, sat_shift_limit=30,
                                 val_shift_limit=20),
         albu.CLAHE(p=0.3, clip_limit=4.0, tile_grid_size=(8, 8)),
         albu.RandomBrightnessContrast(p=0.3, brightness_limit=0.2, contrast_limit=0.2),
     ]

 

実際にaugmentation前後の様子を示したのが以下の画像です。左の画像にこのコードを適用すると、右の画像のようになります(random seedの値に依存して、毎回異なる画像が生成されます)。人間目で見ても雲の形態の判別が困難になっているようにも思いますが、これこそが訓練画像データの拡張につながっていたのかもしれません。

Cross Validation

CVの切り方も少し工夫を入れてあります。

公開されているnotebook上で行われていたCVの切り方は主に、1枚の衛星画像に含まれるラベル数をCV間で一定にするStratified K-Foldでした。私も最初はこのやり方で進めていたのですが、fold間で各ラベルに含まれる面積にずれが生じることに気付いたので、1枚当たりの雲の総面積・雲タイプそれぞれの数・面積などのパラメータが、fold間でほぼ一定になるようにStratified 5-Foldを行うようにしました。

この工夫を入れて以降、CV間のスコアの分散が小さくなり、学習がより安定してくれました。

Ensemble

アンサンブルは、augmentationをかけた画像に対するアンサンブル(Test Time Augmentationと呼ばれます)と、モデルアンサンブルを行いました。

Test Time Augmentationは、推論したい画像に対して「推論精度が劣化しないであろう」augmentationをかけて画像を水増しし、それぞれの画像に対しての予測結果の平均を最終予測結果とする手法です。ここでは、元画像と上下、左右、上下左右を反転させた画像を用いました。推論速度は単純に4倍になってしまいますが、CVのスコアに1%~2%程度もの大きな改善が見られました。単純で簡単な実装の割に精度が高まる便利なテクニックなので、精度が求められる場面では試してみると良いと思います。

モデルアンサンブルに関しては、以下の3つのモデルの予測値(Test Time Augmentation込み)を重み付け平均することにしました。重み付け平均のパラメータと予測マスクを2値化する際の閾値は、Optunaを使って精度が最大になる組み合わせを探索しました。

  • U-Net、EfficientNet-B3
  • U-Net、EfficientNet-B3 (data augmentationを少し緩やかにしたもの)
  • FPN、EfficientNet-B3

学習環境

学習の環境としては、主にAWS EC2インスタンスのp2.xlargeをメインで使っていました。

ただし、大きなネットワークモデルを試したくなったり、今すぐ大量の実験をしたいときなど、よりスペックの高いマシン(p3.8xlarge等)で学習を回したりしました。特にコンペ終盤はずっとp3.8xlargeを使っていました。スコアが思ったように上がらなくて、でもやり残したことは大量に残っていて、その中から取捨選択して順次実験していく中、投稿されるKaggle Notebooksから自分のモデルに取り込めそうなアイデアを試して、、、みたいな状況でした。

また、AWSの起動テンプレートやparamikoを組み合わせて、コマンド1つでインスタンス作成から学習、結果ログとネットワークの重みのS3へのアップロードをやってくれるようなスクリプトを書いて効率化していました。

細かな工夫

他にも様々な工夫をしていました。

optimizerやschedularやloss functionを変えてみたり、画像に特定の雲タイプが含まれているかどうかを分類してみたり、RICAPを試してみたり、衛星写真の黒い帯(衛星が撮影できなかった範囲)を除く処理を除いてみたり、前処理の一部分をキャッシュしてみたり、後処理の方法を変えたり、本当にいろいろなことを試しました。これらは自分でなにか思いついて工夫したり、共有されているKaggle notebookを参考にしたりしたものです。特に自分オリジナルの工夫や考えがLBのスコア(と順位)を向上させたときの達成感や嬉しさはすごく大きくて、Kaggleの醍醐味の1つだと個人的に思っています。

結果

コンペ終了時の結果は以下のようになりました。

  スコア 順位
Local CV 0.65981 -
Public LB 0.66655 -
Private LB 0.66145 58位 / 1538チーム (銀メダル)

反省

Public LBとPrivate LBの差もほぼなく、overfitはほぼしていなかったと言えるんじゃないでしょうか。実際、順位大きく上がってくれたので嬉しかったです。CVの切り方を工夫したのが功を奏したのかもしれません。

自分で失敗したなと思っている点は、シングルモデルの調整に時間をかけすぎたこと、アンサンブルするモデル数が少なすぎたことです。アンサンブルしたモデルは結局、コンペ最終盤に急いで作った3つのモデルだけでした。特にdata augmentation辺りで大量の実験(augmentationパターンとネットワーク、エンコーダー等の組み合わせ)を行ったのですが、それらのネットワーク重みを全て保存しておいて、後のアンサンブルに使えばよかったです。スコアだけを見るとベストなモデルではないけれども他と比べてユニークな推論をするモデルが隠れていて、そのモデルをアンサンブルモデル群に組み込むと多様性の観点からスコアが上がる、ということもあり得たと思います。実際、コンペ上位勢の方々はアンサンブルするモデルの多様性を重視しているようです。

コンペ上位勢の方々の解法をまとめてくれているdiscussionがあるので、興味のある方はそちらも見てみてください。特に1位の方はソースコードも公開してくれています。

https://www.kaggle.com/c/understanding_cloud_organization/discussion/118089

感想

個人的に非常に多くのことを学べたコンペティションでした。

私はこのコンペに参加するまで、画像コンペの敷居は高いと思っていました。なぜならテーブルコンペと比べて、まず最初にGPU環境の構築をしないといけないし、最新のネットワークモデルや手法に熟知しないと順位は上がらなさそうだし、データサイズは大きいし、始めるのに時間と労力が必要とされると思っていたからです。しかしながら実際にコンペを始めてみると、簡単に扱えるクラウド環境、知見に溢れているKaggle notebooks、便利なライブラリがたくさんあって、挫折するポイントはほとんどなかったように思います。

日々の業務との両立にも苦労したのですが、試行錯誤の楽しみやLBを登っていく達成感もあって、非常に楽しく取り組めたことが良かったです。今後も未経験の種類のコンペに挑戦を続けていく予定ですが、今度はチームを作ってKaggleに挑戦することもやってみようと考えています。きっととても楽しいはずです!

というわけで、

Deep Perceptでは一緒に働く人を募集中です。また、個人的なKaggleチームのお誘いも大歓迎です。ご興味を持たれた方は是非こちらまで!

https://www.deep-percept.co.jp/recruit/

この記事をシェアする

この記事の投稿者

久保 祐貴(くぼ ゆうき)

エンジニアです。日々、機械学習と金融とエンジニアリングを学びつつ業務に取り組んでいます。
自分の育てたモデルやシステムが実際に動いているのを見るのが好きです。
現在Kaggle Masterです。
https://www.kaggle.com/yukikubo123

PAGE TOP