【🛰MScup】超解像コンペで11位だったけど,やったことまとめまくる

【🛰MScup】超解像コンペで11位だったけど,やったことまとめまくる

※諸注意

cf. @solafune(https://solafune.com) コンテストの参加以外を目的とした利用及び商用利用は禁止されています。商用利用・その他当コンテスト以外で利用したい場合はお問い合わせください。(https://solafune.com)

SolafuneというプラットフォームでのMScupで低解像度を超解像化する珍しいコンペが出てきて,楽しそうだと(実際楽しかったけど)2ヶ月従事したものの11位で終わってしまいました😭

でも,学べることがたくさんあったし,画像系コンペに出てこなかったので,画像系でも活かせそうな知見が得られて楽しいコンペでした.

実験してみて効いたこと効かなかったことや学んだことをまとめます.

結果報告

先に結果を報告してしまいます.

タイトルですでに出オチですが,11位フィニッシュでした🎉

PublicもPrivateも共に11位だったので,Trust CV,Trust LBなコンペだったと思います.そのためとても取り組みやすかったです.

また,ライセンス表記に対してシビアではありましたが,運営さんの方から使っていいライセンスが詳しく話されていたので,これに関しても取り組みやすかったと思っています.

結果で見れば悔しい結果ですが,総評してとても楽しかったです🤗

コンペ概要

概要は以下です.

衛星画像を活用する際の課題の一つに「解像度」の問題があります。人工衛星は広域を撮影できる一方で、解像度の低さがボトルネックになります。近年、解像度が高い衛星画像の提供・販売が始まっていますが、利用可能な高解像データは世界で最も解像度が高い画像でも30cm程度です。また、高解像度のデータは値段が高く、利用規約にも様々な制限が課せられています。さらに、現時点では高解像度の画像データは法律で利用を禁止している国もあります。 そのため、今回のコンテストでは高解像度のデータを安く利用するための手段として、超解像を扱います。将来的に高解像度の衛星画像を扱えるようになることを想定して、本コンテストでは25 cm解像度の画像データを扱います。「超解像」という技術を通して、衛星データや航空写真などの地理空間データの社会実装が加速することを期待しています。

与えられて低解像度の画像に対して,高解像化をかけるモデル,アルゴリズムを作成し,SSIMが高くすることが課題でした.

SSIMは以下で表され,これを最大化することを目的とします.

SSIM(x,y)=(2μxμy+c1)(2σxy+c2)(μx2+μy2+c1)(σx2+σy2+c2)SSIM(x, y) = \frac{(2\mu_x\mu_y + c_1)(2\sigma_{xy} + c_2)}{(\mu_x^2 + \mu_y^2 + c_1)(\sigma_x^2 + \sigma_y^2 + c_2)}

My Solution

僕のSolutionは大したことができませんでした.というのも,たくさんの実験を試したのですが,ほとんどうまくいかず悪化し,結局Seed Averagingなどの手抜きアイデアが最終サブになってしまったからです.

とは言いつつも,おそらく僕独自の手法も入っているのではないかと思うので参考になればと思います.

モデル概要

訓練時は以下のようなモデル設計になっています.

訓練時モデル

推論時はSeed Averagingと**TTA(Test Time Augmentation)**を利用して,テストデータでの推論時にロバストに推論ができるようにしました.Seed Averagingは5つくらいのモデルの平均をとったはずです.

Seed AveragingやTTAは正直つまらない解法だと思っていますが,Sobel Filterを利用したLoss関数CutMixやCutoutなどのData Augmentationが個人的な解法のウリではあります.

また,僕がコンペを進める中での一番力を入れたことは,Loss関数による精度向上です.モデルも比較しましたが,結局SOTAモデルのSwinIRが圧巻で,Augmentationも思ったほど効かなかったので,Loss関数で制御することが最もの試みでした.これは独自の取り組みだと思っています(思いたい)

次のセクションで,なぜその実装をしたかと結果を話したいと思います.

実験中の課題感と対処法

実験している中での課題をまずは簡単に.

  1. データセットが少ない
    • 画像が大きいので,クロップすればデータはたくさんあるのですが,多様性という面では多いとは言えなかったかもしれない.
  2. 回転のAugmentationを加えるとノイズになる
    • エッジの再現性が重要となる超解像タスクでは,回転後のギザギザがノイズになってしまう.
    • 初めて知ったけど, 超解像タスクでは90度単位での回転が基本動作.画像見比べてなるほどってなった.
  3. 超解像時にエイリアシングが起きたとき,エッジの向きに再現性がない
    • データを見ていてこれが問題と感じて,Sobel Filterを施そうと...
    • 細くは改めて話します.
  4. Augmentationなんでもいいと思いきや,CutBlurやMixupはノイズだった
    • 実装の苦労の割に大幅悪化で辛かった...

それでは上記について,細かく述べていこうと思います.

データセットが少ない

訓練データは60シーンあり,クロップすればデータは多く感じるものの,やはり多様性という時点では少ないなぁという印象でした.

上位者の公開Solutionでは外部データの利用が鍵になったという指摘もあり,ここの調べを怠ったのが大敗につながってしまったのかなとも思っています.

そうは言っても全く対処しなかったわけではなく,「Rethinking Data Augmentation for Image Super-resolution: A Comprehensive Analysis and a New Strategy」で提案されたData Augmentationの手法をPytorchパイプラインに合うように再実装し,実験はしました.

効き目は以下のような感じでした.

手法 説明 効き目
CutBlur 低解像度に高解像度の画像を部分的に差し込む(またはその逆) ×
Cutout 一定確率でピクセル値を0にする
CutMix 異なる画像を部分的に差し込む
Mixup beta分布に従って異なる画像を混合する ×
CutMixup CutMixとCutupを同時に行う ×
Blend 一様分布に従ってサンプリングされた色を混合する
RGBPermutation RGBの順番を変える

実際に効いているかはそこまで詳しく見ることはできていませんが,スコアや生成された高解像度画像を見ているとこんな感じだったと思います.

効き目の詳細は改めて話します.

回転のAugmentationを加えるとノイズになる

これは最初気づかなくて,エッジがめちゃくちゃ鈍った画像が生成されたので分析した結果わかったことです.

画像ドメインを持っている人ならば当たり前に感じてしまうかもしれませんが,画像にドメインのない人にとっては最後まで気づかなくてもおかしくなかったと思っています.

ライブラリの仕様でも,回転を加える関数には,interpolationなどのデータ補間のための引数があるはずです.

例えばtorchvisionですが,ランダムでの回転は以下で実装されます.

import torchvision.transforms as T

# ニアレストネイバーでの補間
transforms = T.RandomRotation(degrees=180, interpolation=T.InterpolationMode.NEAREST)
# バイリニアでの補間
transforms = T.RandomRotation(degrees=180, interpolation=T.InterpolationMode.BILINEAR)

この実装を見る限りでも,エッジの部分が補間されることで元の入力画像との再現が取れなくなってしまうことは想像できると思います.

これは学びでしたが,超解像タスクにおいては,回転のAugmentationは90度単位というのが主流のようでした.

超解像時にエイリアシングが起きたとき,エッジの向きに再現性がない

本当は画像を見せた方が早いのですが,今回のコンペで使用された画像は公開できないので,手書きで頑張って表現しようと思います.

田んぼみたいに,ある程度イネを植えている方向に規則的な向きがあるような感覚を想像してください.

実験中下のような画像が生成されてしまいました.

エッジが再現できない生成画像

SSIMを最適化するのですが,korniaで実装されていたSSIM Lossでは,入力画像をGaussian Filterで平滑化してからSSIMの計算をかけていたので,ぼやけた状態でうまくreconstructできるかを学習してしまっていたのだと思います.

そこで僕は追加でSmooth L1 Lossをさらに追加しました.こちらはピクセルごとでロスが計算されるので細かいところまで再現できるかなと思ったのですが,MSEなどのピクセルを見るLoss関数は画像全体がぼやけてしまうらしいです.

結果行き着いた発想が,「エッジに対してLossを計算してしまえばいいじゃない??」でした.

これは結果的にスコア微増に起因したのですが,前に示した図のようなエイリアシングが取れていることが多く,実装と結果が直感的だったので最後まで採用しました.数学的に正しい感じは全くありませんが,なんかうまくいったので(正しいかは知りません.こいつほんまに微分可能なんか?ってお気持ちのままやってました笑)

実装上は,Sobel Filterをかけて,エッジ抽出された画像に対してSmooth L1 Lossを計算するように計算しました.

当時の実装を下記に載っけておきます.

class MeanSobelError(nn.Module):
   """
   Sobel Gradient Loss Function
   """
   def __init__(self,
      normalized: bool = True,
      eps: float = 1e-6,
      reduction: str = 'mean',
   ):
      super(MeanSobelError, self).__init__()
      self.filter = kornia.filters.Sobel(
         normalized=normalized,
         eps=eps,
      )
      self.lossfn = nn.SmoothL1Loss(reduction=reduction)

   def forward(self,
      y_pred: torch.Tensor,
      y_true: torch.Tensor,
   ):
      filtered_pred = self.filter(y_pred)
      filtered_true = self.filter(y_true)

      loss = self.lossfn(filtered_pred, filtered_true)
      return loss

Augmentationなんでもいいと思いきや,CutBlurやMixupはノイズだった

SISRタスクにおいて提案されていたAugmentationは全部効くだろう!というスタンスで,コンペ終盤に時間をめちゃくちゃかけて実装し,全部ごちゃ混ぜでやってみたのですが,スコアが激減....

幾つかパターンを試しましたが,CutBlurやMixup系はスコアの悪化の原因だったっぽいです.

  • Mixup
    • 画像を割合で混合するので,エッジが鈍りがちになってしまいました.
    • 車や車線など,エッジがはっきりした画像生成が必要だったため,ノイズになってしまったのかもしれません.
  • CutBlur
    • こちらは単純にモデル作成が下手くそなのかも知れないです.
    • SwinIRに入力する前に,ダウンサンプリング層を用意する必要がありました.
    • ↑これがうまく学習できず,生成される画像の色がおかしかったです.
      • 一応ダウンサンプリング層のみのwarmupなども試したが,うまく適合できず,この実装はボツに...(めっちゃ時間かけたのに...)

こんな感じで効くAugmentationと効かないAugmentationがあったので,早い段階でこの実装と実験ができていれば,もっと豊かな実験ができたかなと反省しています.

Augmentationはいずれコンペ中は確実に試すことになると思うので,早めに効くかどうか先にやっておくといいのかも(?)

まとめ

最後に今回のコンペで得た知見をまとめたいと思います.

  • Augmentationはタスク依存.課題感を意識したり,早めに取り組んだ方がいいかも(?)
  • データセットのやりくりはサボらない
    • 外部データの調査とかEDAとか
  • スコアだけでなく,予測値も確認してみる
    • 今回はラベル分布とかそういうのではないので,生成画像もしっかり確認するなど
    • それを割としっかりやったのでSobel Filterに行き着いたと思っています.
  • 論文とにかく読んでアイデアを得よう
    • めっちゃ読んで,実装して効かなかったアイデアが8割くらいだったけど,楽しかったです.
    • 今,大学で取り組んでいるNLPの研究にアイデアが入ったりしているので,やり切るって大事!
  • 楽しむ!!!
    • コンペは序盤と終盤がとても辛いです.
      • 序盤:バグが取れない.公開のやつに勝てない.
      • 終盤:スコアが伸び悩む.
    • でも,やり切れば結果楽しい(MScupが楽しかった説に一票ですが😂)
    • CVやLBをおかずに飯が食えるぞ?

僕が今回のMScupを通して学んだことはこんなところでしょうか.次もしSolafuneのコンペに参加することがあれば,しっかり賞金圏に入りたいと思います!

最後に今回のコンペで使ったライブラリや実装したコードを載せて終わりにしたいと思います.

つらつらと思うところを書きましたが,最後まで読んでいただきありがとうございます.意見やご指摘などがありましたら,Twitterなどでご連絡ください.

よく使ったライブラリなど

今回のコンペでよく使えたライブラリなどを載っけておきます.

  • kornia
    • ssim lossやsobel filterなど,画像処理で手前実装が大変でかゆいところに手が届きました.今後も自分は使うことがありそうだと感じました.
  • SwinIR
    • SISRにおけるSOTAモデル.事前学習済みモデルが公開されており,重宝しました.一番強かった...
    • paper
  • image-super-resolution
    • さまざまなSuper Resolutionタスクでのモデルの再現実装をされています.
    • 今回のコンペでは精度が微妙だったのでサブには使いませんでしたが,SISRタスクで遊びたい時に簡単に扱えると思います.
  • Pytorch Lightning
    • 単純にファン.もう楽
  • Pytorch
  • cv2
    • RGBの順番気をつけましょう.これで2週間くらい無駄にしました笑笑

実装したコード

SRAugmentationにて公開しています.

初めてGitHubでスターをもらって喜んでいました(小並感

CutBlurなど,Single Image Super ResolutionにおけるData Augmentationの手法を改めてPytorchのパイプラインに組み込めるように再実装したものです.

こちらの論文で提案されています.Rethinking Data Augmentation for Image Super-resolution: A Comprehensive Analysis and a New Strategy

タメになったらSHARE!!!