衛星画像分析コンテスト

# 1. はじめに

## 1.1. 記事の概要
今までテーブルデータをメインとして分析をしていたが非構造化データにも触れておきたかったため、非構造化データのなかでもライブラリや参考文献も豊富である画像解析に取り組んだ。
SIGNATEのSOTAコンペである[産業技術総合研究所 衛星画像分析コンテスト](https://signate.jp/competitions/263)に参加したため、本記事ではその際に使用した分析手法や参加結果の概要を記載する。
なお、本分析にて使用したコードは[Google Colaboratry](https://colab.research.google.com/drive/1V1hA_Zcwz-zL2TJcqQqN7fu3ssV45eGH?usp=sharing)にて公開している。

## 1.2. 画像認識とは
1. 概要
    - 画像から特徴を抽出して対象物を識別するタスク
    - 何が映っているか -> 「分類」<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/d4d5d728-c478-ca7d-28fa-e01f19efd40e.png' width='400'>

    - 何がどこに映っているか -> 「検出」<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/74d9a8c8-63b7-e8ef-a89b-1c5ab88a2dad.png' width='400'>

1. 活用事例
    - [画像診断による異常検知](https://www.tryeting.jp/column/4752/ '異常検知の事例を紹介！AIを活用した異常検知の効果とは？')
    - [AI-OCRによる手書き文字の読み取り](https://digi-mado.jp/article/53666/ 'AI-OCRの導入事例10選｜導入前の課題や導入後の効果を紹介')

## 1.3. コンペ概要
1. **コンペ名**: [産業技術総合研究所 衛星画像分析コンテスト](https://signate.jp/competitions/263)<br><br>

1. **データ**<br>
    - エリア: 日本国内全域および海外7エリア
    - 大きさ: 32px x 32px x 7チャネル([Landsat8](https://www.usgs.gov/landsat-missions/landsat-8 'Landsat 8 usgs')に搭載されているセンサーにより観測)
    - 分解能: 1pxあたり30m<br><br>※通常の画像データ（RGBの3チャネル）と比べてチャネル数が多いのが特徴<br>
<img src='https://static.signate.jp/competitions/61/GKr8PdRf7T6USf65zwpu3Q9eP4Q1QcO7zmbjqxkC.png' width='400'><br><br>

1. **タスク**: 画像における総面積の5%以上をゴルフ場が占めるかどうかを識別する2値分類タスク<br><br>

1. **ラベル**
    - 正例: ゴルフ場の画像に占める割合が5%以上<br>
    - 負例: ゴルフ場が画像に映っていない<br><br>※下図は正例画像の例<br>
<img src='https://static.signate.jp/competitions/61/uHqmSH4V5y0lBagDxYF62Tk9pAmhPQCcWrWI48R9.png' img='400'><br><br>

1. **評価指標**<br>
数式: $IoU = \frac{TP}{FN+FP+TP}$<br>
負例が多い不均衡データを扱うためAccuracyを評価指標にするとすべて負例と予測しても精度が高くなってしまうためIoUを用いる

    |ラベル/予測値|ゴルフ場|ゴルフ場以外|
    |:--:|:--:|:--:|
    |ゴルフ場|TP(True Positive)<br>ゴルフ場を正しくゴルフ場と予測した数|FN(False Negative)<br>ゴルフ場を誤ってゴルフ場以外と予測した数|
    |ゴルフ場以外|FP(False Negative)<br>ゴルフ場以外を誤ってゴルフ場と予測した数|TN(True Negative)<br>ゴルフ場以外を正しくゴルフ場以外と予測した数|


## 1.4. 環境・使用ツール
- Google Colaboratory
- Pytorch

# 2. [EDA](https://colab.research.google.com/drive/1UQLbFdwXeMPtK84fKLDAhkM-r3yQwk_9?usp=sharing)

## 2.1. 配布データ
学習データとテストデータの圧縮ファイル計7ファイルに加え学習データの概要が記載されたマスタファイル

|ファイル名|説明|
|:--:|:--:|
|train_1.zip|学習データ1|
|train_2.zip|学習データ2|
|train_3.zip|学習データ3|
|test_1.zip|テストデータ1|
|test_2.zip|テストデータ2|
|test_3.zip|テストデータ3|
|test_4.zip|テストデータ4|
|train_master.tsv|それぞれの学習データとラベルの対応表|

## 2.2. データ探索
1. **ファイル形式**
    画像データはすべて[tif形式](https://jp.easeus.com/data-recovery-solution/what-is-a-tiff-file.html)となっている
    tiffファイルの特徴として、主要なOSすべてに対応しており高解像度の画像を保存するための形式として適していることがあげられる<br>
    <br>

1. **基本情報の確認**
    ```python
    pos_data = train_master.loc[train_master['flag']==1]
    neg_data = train_master.loc[train_master['flag']==0]
    print(f'データセット件数: {len(train_master)}')
    print(f'正例件数: {len(pos_data)}')
    print(f'負例件数: {len(neg_data)}')
    print(f'正例割合: {round(len(pos_data)/len(train_master)*100, 1)}%')

    ### 出力
    # データセット件数: 296182
    # 正例件数: 12442
    # 負例件数: 283740
    # 正例割合: 4.2%
    ```
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/fe51599c-07e9-4206-ecc8-dc8338d6befe.png' width='500'><br>

    学習データは約30万枚あり、正例画像は約4%(約12,000枚)とかなりの不均衡データとなっている<br>
    <br>

1. **画像サンプル**<br>
    各ラベルのサンプルデータを3枚ずつ表示
    7チャネルを横に並べてそれぞれ表示しているが、正例サンプルにはゴルフコースらしきものが見える
    ※[こちら](https://colab.research.google.com/drive/1UQLbFdwXeMPtK84fKLDAhkM-r3yQwk_9?usp=sharing '01_AIST_EDA')にRGB(可視光)に絞った表示や[植生](https://sorabatake.jp/32461/)の表示など様々な可視化を実装しているので参考までに<br>
    **正例サンプル**<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/67da49fd-147c-0bdd-5a17-a9e4a79a4a18.png' width='600'><br>

    **負例サンプル**<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/7e5409fc-b2e1-9f18-af70-1eba737102cf.png' width='600'><br><br>


1. **統計量**
    チャネルごとのピクセル統計量を出して正例と負例を比較
    平均値や各パーセンタイル値に大きな違いは見られないが負例データではすべてのチャネルで最小値が0となっている<br>
    **正例統計値**<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/f2751a81-3d48-0f84-6252-29a4b080523f.png' width='600'><br>
    **負例統計値**<br><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/f7e15afb-cde1-16d0-b5f0-a48caeb5277b.png' width='600'><br>

# 3. データセット作成

## 3.1. [前処理](https://colab.research.google.com/drive/1-TNYKiVkgvf6sVdWTIPQQXgYS6je6Bc4?usp=sharing)
1. **目的**
AIが認識しやすい形状に画像データを加工する
機械学習プロジェクトにおいては入力データの質が非常に重要となるため本項目と次項目のデータ拡張がカギとなる
データ拡張とは異なり学習データとテストデータの双方に適用する(後述)<br><br>

1. **例**
画像のサイズがばらばらであるため統一する
対象物の色が偏っているためグレースケールに変更する<br><br>

1. **今回の実装**
可視光(RGB)のみに絞り込む(※学習の高速化のため)
上記の統計量から最小値5000、最大値25000にクリッピング後に正規化
※マルチバンドに対応している画像処理ライブラリ[Albumentation](https://albumentations.ai/)を使用<br>

## 3.2. [データ拡張](https://colab.research.google.com/drive/1-TNYKiVkgvf6sVdWTIPQQXgYS6je6Bc4?usp=sharing)
1. **目的**
データ数が少ない場合や偏りがある場合により頑健なモデル構築を行うために実装する
基本的に学習データのみに適用してテストデータには適用しない<br><br>

1. **例**
    サンプルが少ないためサイズ変換や回転などをランダムで行い水増しをする
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/a8292312-0f6d-dde1-397a-55b6c2a4e6c8.png' height='300'>

    対象物の向きがすべて同じであるため一定の確率で左右を反転させる<br>
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/9f5a4346-bd02-624d-45d2-36982eb7ee85.png' widtn='400'><br><br>


1. **今回の実装**
- 拡大や縮小をしてしまうとゴルフ場の大きさが変わりラベル自体が変わってしまう可能性があるため適用しない
- 最終的に実装したパイプラインは以下
    ⓵. 画像のふちをパディング->32x32サイズをランダムでくりぬき
    ※正例が少ないため水増しに使用
    ※パディングのサイズが大きすぎるとターゲット(ゴルフ場)が隠れてしまう可能性があるため注意
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/db4ef27a-2a3d-dd88-bb8c-f715541a848d.png' width='500'>
    <br><br>

    ⓶. 90°区切りでランダムに回転
    ※1と同様に正例が少ないため水増しに使用
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/7c38d1ea-22aa-f3e0-c418-81c86a551bfe.png' width='500'><br><br>

    ③. 指定の範囲でコントラスト変換->明度変換
    ※衛星画像は天候によって明暗が変わることや、テストデータには黒要素が多い画像が散見されたため適用
    ※マルチバンドに対応している画像処理ライブラリ[Albumentation](https://albumentations.ai/)を使用<br>
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/03e3d977-7a82-a43d-ee18-45a10423ca2e.png' width='500'><br><br>

## 3.3. [データセットクラスの作成](https://colab.research.google.com/drive/1qfgEreEWyEvj19XHYZBeumzxa5B5wRAk?usp=sharing)
PyTorchのDatasetモジュールを用いて作成する

1. **分割**
学習データ約30万枚を以下のように訓練用、検証用、テスト用に分割(分割割合は7:2:1)
訓練用と検証用でモデル構築・改善を行いテスト用で精度を確認する
<img src='https://carbgem.com/plus/wp-content/uploads/2022/08/%E5%9B%B31%EF%BC%9A%E3%83%86%E3%82%99%E3%83%BC%E3%82%BF%E3%82%BB%E3%83%83%E3%83%88%E3%81%AE%E5%88%86%E5%89%B2-1024x545.png' width='500'><br><br>

1. [**オーバー(アンダー)サンプリング**](https://colab.research.google.com/drive/1TJiFZP7bmrCkLQKlCi_QqiJmn4b62cSj?usp=sharing)
正例データが全体の5%未満しかない不均衡データであるため、正例データの特徴を正確にとらえたモデル構築ができるように訓練データは正例比率が20%となるよう水増しする
訓練フェーズで繰り返し同じ正例画像を読み込み、上述のデータ拡張を適用することで同じ元画像でも異なる正例画像とする
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/5d36b75a-3c75-69da-c279-57267d02cb02.png' width='500'><br>

# 4. [分類モデル作成・提出](https://colab.research.google.com/drive/1aNsb1-XPeVAJ5CjhuW3HdeGznx0x63wq?usp=sharing)
## 4.1. アルゴリズム
- [ResNext](https://arxiv.org/abs/1611.05431 'Aggregated Residual Transformations for Deep Neural Networks')や[ViT](https://arxiv.org/abs/2010.11929 'An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale')など複数試行したが、学習時間の早さや精度の点から最終的に[EfficientNet](https://arxiv.org/abs/2104.00298 'EfficientNetV2: Smaller Models and Faster Training')を採用

1. **アーキテクチャ概要**<br>
    - 従来のモデルよりパラメータが少ないため、計算コストを抑えながら精度を向上させることができる<br>
    - また、モデルを大きくすると精度は上がるがメモリに収まらなくなるという問題も解消される<br>
    - 層の数、チャネル数、解像度を同時に定数倍して増やすことで効率的なモデルのスケールアップを実現<br>
    - 以下図はモデル構造と精度比較([原論文](https://arxiv.org/abs/2104.00298 'EfficientNetV2: Smaller Models and Faster Training')より)
    <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/c3174639-21d8-b3f9-87c5-559a2efc0433.png' height='300' name='精度比較'><img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/bca8dadf-76fb-dd58-6aaa-9b8b12c44710.png' height='300' name='モデル構造'><br>

## 4.2. ハイパーパラメータ

1. **ハイパーパラメータとは**
AIモデルの学習により自動的に決まるパラメータとは対照的に人間が手動で決める必要がある設定値
適切に設定しておかなければ未学習や過学習の原因となる
計算資源が限られる環境においてはグリッドサーチ等で最適なハイパーパラメータの値を探し出すことは現実的ではないため、各ハイパーパラメータの概要を理解しておくことが重要となる<br><br>

1. **主なハイパーパラメータ**
以下のハイパーパラメータのうち今回はエポック数、バッチサイズ、学習率、最適化アルゴリズム、パラメータ荷重減衰をチューニング対象とする<br>

    |ハイパーパラメータ|概要|補足|
    |:--:|:--:|:--:|
    |エポック数|学習を何回繰り返すか|大きくしすぎると学習時間の増加や過学習につながる|
    |バッチサイズ|パラメータ更新に用いるデータ数|データセットの大きさに応じて調整|
    |損失関数|目的変数と出力の差|Appendix参照|
    |最適化アルゴリズム|損失関数を0に近づける手法|Appendix参照|
    |学習率|パラメータの更新幅|1回の学習におけるパラメータの更新幅<br>大きすぎると収束せず、小さすぎると最適解にたどり着くまでに時間がかかりすぎる|
    |パラメータ初期値|パラメータの初期値|学習済みのものを使用|
    |ドロップアウト|一定の割合でノードを不活性化させる(過学習の抑制)|既存モデルのものをそのまま使用|
    |パラメータ荷重減衰|パラメータが大きくなりすぎることを防ぐ(正則化)|損失関数に罰則項を与える|
    |バッチ正規化|バッチごとに特徴量を正規化する|既存モデルのものをそのまま使用|<br>

# 5. 結果総括

## 5.1. 学習結果
ハイパーパラメータを変えながらモデル構築を行った結果を以下に示す
手元のデータでもSIGNATEへの提出結果でもモデル⓸が最も高い結果となった
バッチサイズを小さくし、学習率を低くすると汎化性能が高くなる傾向にあった

|ハイパーパラメータ/モデル|⓵EfficientNet M|⓶EfficientNet L|③EfficienNet L|⓸EfficientNet L|⑤EfficientNet L|
|:--:|:--:|:--:|:--:|:--:|:--:|
|エポック数|13|15|13|13|13|
|バッチサイズ|256|256|256|128|1024|
|最適化アルゴリズム|SGD|SGD|Adam|SGD|SGD|
|学習率|0.005|0.01|0.001|0.0025|0.0025|
|パラメータ荷重減衰|0.001|0.01|0.001|0.001|0.001|
|オーバーサンプリング|正例比率20%|正例比率20%|正例比率20%|正例比率20%|正例比率20%|
|結果||||||
|検証用データIoU|0.68|0.72|0.65|<span style="color: red">**0.76**</span>|0.72|
|評価用データIoU|0.68|0.73|0.60|<span style="color: red">**0.75**</span>|0.71|
|SIGNATE提出結果|0.52|0.62|0.52|<span style="color: red">**0.67**</span>|0.64|

## 5.2. SIGNATE提出結果
Bronzeラインを超えることができた(2023年6月時点)
ハイパーパラメータの調整やデータセットの作成に苦心したおかげか過学習が抑制され汎化性能が高くなったと感じた<br>
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2712784/7f671656-6f2e-7352-9eef-4b7e0a44e625.png' height='300'>


## 5.3. まとめ

1. 成果
    - 初めて画像解析に取り組んだが、SIGNATEコンペで一定の成果を出すことができた
    - モデルの選定やハイパーパラメータのチューニングを通じて深層学習への理解が深まった
    - PyTorchを用いた画像解析の基礎を習得できた

1. 課題
    - 画像解析プロジェクトではデータの収集や加工に大半の工程を割くことになると思うが、今回はコンペで配布されているデータを用いたこともありあまり時間がかからなかった<br>今後はデータの収集やラベリングを自ら行う必要があるテーマに取り組みたい
    - データ拡張の手法は今回実装したもの以外にも様々あるが、試し切れていない<br>精度向上や過学習抑制につながるものもあると思うのでさらに深堀していきたい

# Appendix

## appx 1. 損失関数

1. 損失とは
    - 目的関数と出力値(予測値)の誤差を示す
    - 深層学習では個の誤差が最も小さくなるようにパラメータを決定する
    - 損失関数はこの誤差を算出するための関数
    - PyTorchには様々な損失関数が搭載されているほか自作も可能
2. 代表的な損失関数(y:正解, t:予測値, k:データ数)
    - 2乗誤差(回帰): $\sum_k{(y_k-t_k)}^2$
    - 交差エントロピー誤差(分類): $-\sum_k{(t_k\log y_k)}^2$

## appx 2. 最適化アルゴリズム

1. **最急降下法**
    - パラメータ更新式: $w_{t+1} = w_t - \alpha \delta_w L(w)$ &emsp;&emsp; $w_t$:更新t回目のパラメータ,$\delta_w$:微分,$\alpha$:学習率,$L(w)$:損失関数
    - 更新手順
        - パラメータ初期値をランダムに決定
        - すべてのデータを用いて予測値を算出し、損失を計算
        - 損失をパラメータで微分→パラメータ更新
        - 上記を繰り返す(損失最小付近では微分値がほぼ0となるためパラメータが更新されなくなる→収束)<br><br>

1. **確率的勾配降下法(SGD)**
    - 最急降下法はデータをすべて用いているため計算量が多いことや局所会に陥りやすいことがあげられる
    - SGDではパラメータ更新時にデータをランダムに使用することで上述の問題に対処する<br><br>
1. **モーメンタム**
    - パラメータ更新式: $w_{t} = w_{t-1} - \alpha v_t$<br>&emsp;&emsp;&emsp;&emsp; &emsp;&emsp; &emsp;&emsp;$v_t = \beta v_{t-1} + (1-\beta)\delta_w L(w)$<br>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;  $w_t$:更新t回目のパラメータ,$\delta_w$:微分,$\alpha$:学習率,$L(w)$:損失関数, $v_{t-1}$:前回の勾配,$\beta$:ハイパーパラメータ(0~1)
    - 概要
        - 1銅の更新が大きすぎると最小値にたどり着くのが遅くなるため、より早く効率的に最適解にたどり着くように勾配を調整する(更新式$v_t$部分)
        - 移動平均($\beta$)を用いることで勾配に急激な変化が生じないようにする<br><br>

1. **RMSProp**
    - パラメータ更新式: $w_{t} = w_{t-1} - \frac{\alpha}{v_t+\epsilon}\delta_w L(w)$<br>&emsp;&emsp;&emsp;&emsp; &emsp;&emsp; &emsp;&emsp;$v_t = \beta v_{t-1} + (1-\beta)(\delta_w L(w))^2$<br>&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;  $w_t$:更新t回目のパラメータ,$\delta_w$:微分,$\alpha$:学習率,$L(w)$:損失関数, $v_{t-1}$:前回の勾配,$\beta$:ハイパーパラメータ(0~1),$\epsilon$:ゼロ除算を避けるために使用
    - 概要
        - 更新幅が大きいときの学習率を低下させることで振動を抑える
        - 勾配を2乗することで正負に関わらず振動が大きい際に学習率を抑える<br><br>

1. **Adam**
    - モメンタムとRMSPropの組み合わせ

# 参考
1. [【決定版】スーパーわかりやすい最適化アルゴリズム -損失関数からAdamとニュートン法-](https://qiita.com/omiita/items/1735c1d048fe5f611f80)
1. [EfficientNet V2](https://arxiv.org/abs/2104.00298 'EfficientNetV2: Smaller Models and Faster Training')
1. [DeepLearning (上級編)](https://nextgem.jp/deeplearning-%E4%B8%8A%E7%B4%9A%E7%B7%A8/)