🌏

[solafune] Sentinel-2 を活用した太陽光パネル検出 - 2nd Solution -

2024/03/01に公開

はじめに

はじめまして、2024年2月より松尾研究所で働いているshigeです.
前職ではSIerでデータサイエンティスト兼コンサルタントとして働いており、多様な業界でのデータ分析とソリューション実装を経験しました.

松尾研究所では、AI技術の社会実装を推進するというミッションに深く共感し、企業様と協力しながらAIソリューションの開発に携わるプロジェクトマネージャーとして活動しています.

また私は、データサイエンスのスキルを磨くためにKaggleをはじめとしたデータ分析コンペティションに継続的に参加しています.
今回は衛星データ解析コンテストプラットフォームであるSolafuneが主催していた太陽光パネル検出コンペに参加し2位入賞することが出来ましたので取り組みについて紹介します.

コンペの概要

今回参加したコンペは比較的解像度が低いSentinel-2の光学衛星画像から、太陽光パネルが存在するピクセルをセグメンテーションするタスクでした.

Sentinel-2の画像には12バンドの情報が含まれており、それぞれのバンドでは空間分解能や波長が異なっているデータが提供されております.
また、正解mask画像は太陽光パネルが存在するピクセルを1, それ以外は0となっている2値をとります.
各バンドでの空間分解能と波長の詳細は下記テーブルにまとめています.

Band 空間分解能 中心波長 観測項目
B1 60m 444nm エアロゾル
B2 10m 493nm 可視光(Blue)
B3 10m 560nm 可視光(Green)
B4 10m 665nm 可視光(Red)
B5 20m 704nm 可視近赤外(VNIR)
B6 20m 740nm 可視近赤外(VNIR)
B7 20m 783nm 可視近赤外(VNIR)
B8 10m 833nm 近赤外(NIR)
B8A 20m 865nm 近赤外(NIR)
B9 60m 945nm 近赤外(NIR)
B11 20m 1610nm 短波長赤外線(SWIR)
B12 20m 2190nm 短波長赤外線(SWIR)

各バンドと正解画像は下記のような見え方です.
画像

  • 学習データと評価データはそれぞれ2,066枚あり、LBは20%のデータでの評価と記載ありましたのでおよそ400枚のデータでの順位がコンペ期間中は表示されていました.

コンペページ

取り組んだこと

  • Solution Pipeline
    2nd_solution_pipeline
    それぞれやったことを詳細に説明します.
    ※コンペの解法紹介のため、以下専門用語を多用しております.ご留意ください.

前処理

Normalize

  • 衛星画像ということで各バンドの値域は0~255ではなく、0~14,000まで広がるロングテールな分布でした.
  • ロングテールということもあり、まずは対数変換(log2)を実施しています.
  • その後、ノイズを除去していくために mean ± 3σ の値でクリッピングをしています.
  • 学習済みのセグメンテーションモデルを使用することを想定していたため、MinMaxScalingによりどの画像でも値域が0~1の範囲に収まるような前処理をしています.
  • 上記処理は、すべてバンドごとに実施しています.

Padding

  • 提供されている画像は画像ごとに幅と高さが異なっていることが特徴的でした.
    • width: 22~26 height: 22~26
  • 学習済みモデルを想定すると224, 512へリサイズする際に画像ごとに拡大係数が異なることが気になっていたため、画像の右と下を0でpaddingして26x26のサイズに揃えるような処理を実施しています.
    zero_padding

Augmentation

  • 12chということもあり、また解像度も低かったため空間的なAugmentationのみにしております.
    • Holizontal Flip
    • Vertical Flip
    • Rotate
    • Transpose

1st Stage

  • segmentation_models_pytorchに搭載されているArchitectureとBackboneを採用しています.
    • Architecture: Unet++
    • Backbone: maxvit_small_tf_224
  • ピクセルごとの0, 1の2値分類でしたので、LossはBCEwithLogitsLossFocal Lossを使用.
  • 90度回転 x 水平反転の8パターンでのTest Time Augmentationを実施.
  • BCEwighLogitsLossとFocal Loss、それぞれで学習したモデルをNelder-Meadで重みを計算しEnsemble.
  • この時点でCV: 0.6524, LB: 0.6472で上位が見えてきていました.

2nd Stage

  • 1st Stageでの予測結果と実際の正解画像を見比べたところエッジ周辺での取りこぼしが多く、1st StageでPadding(26, 26)->Resize(224, 224)->Resize(26, 26)->Triming(23,24)と繰り返し画像サイズを変えていることに起因しているのでは?と考察しました.
  • そこで1st Stageの予測結果を入力画像の13ch目に入れる形で実際のデータと合わせて調整をかけるような3層のUnetを実装.
  • CV: 0.6588, LB: 0.6447でLBはちょっと下がりましたが、Trust CVの精神で最終Subとして選出しました.
    pred_vs_gt

Not Work

  • 他に試したけど上手くハマらなかったことについても記載します.
    • 太陽光パネルは目で見ても分かりやすかったので、可視光のRGBのみで学習.
    • どのchが重要か学ばせるためにInputに近いところにchの重みを学習させる層追加.
    • アノテーションが間違っていそうな画像を除去.
    • パネルがない画像はLossのペナルティを強めるようなLoss関数を自作.
    • パネルが入っているか入っていないかの2値分類をAux Headとして追加.
    • 画像のリサイズを384, 512など高解像度化.
      • Backboneもbase, largeと大きくしましたが効きませんでした.

上位解法

  • 上位解法で個人的に気になった部分について紹介します.

1st Solution

  • 12ch総当たりで(band A - band B)/(band A + band B)を計算し、スコアが上がったものを採用.
    • band2, band3, band4の6バンド
    • band11, band12の2バンド を追加し、トータル20chで学習をしていたようです.
  • 後から調べたところ、衛星データ分野ではよく使われる手法のようで、例えば近赤外と赤色でNDVI(正規か植生指標)を検出などに用いられているようです.

3rd Solution

  • 画像を8倍、16倍へアップサンプリングしたのちに224x224, 416x416へPaddingし同サイズへ揃えていました.
  • Pseudo-Labeling
    • 外部データとしてSentinel-2 L2Aのデータを用いて24,000以上の予測ラベルを用いて学習をされていたようです.

所感

Kaggleの雲検出コンペをはじめ、Solafuneの前回の雲領域検出と衛星画像を扱うコンペと立て続けに参加し上位解法を都度再現してきたことが今回の入賞に繋がったと思います.
ドメイン知識の活用や、外部データ活用などまだまだコンペ期間中にやれることが多かったこともあるので、引き続きコンペに参加しつつスキルを磨いていきます.

またKaggleなどのデータ分析コンペに興味のあるメンバーも募集してますので、興味持っていただけた方は松尾研究所についても下記リンクからさらに知っていただけると幸いです.

松尾研究所テックブログ

Discussion