GCP AutoMLで解くリンゴとトマトの画像分類

Kaggleにリンゴとトマトの画像を分類するという課題があります。
GCP Vertex AIを用いてこの課題を解いてみます。
また作成したモデルを使って、画像の自動分類器を作成します。

https://www.kaggle.com/datasets/samuelcortinhas/apples-or-tomatoes-image-classification

# データセットをダウンロードする

上記のURLからデータセット(リンゴとトマトの画像)をダウンロードできます。(要ログイン)
データセット中にはモデルの訓練用の `train` と 検証用の `test` が含まれています。

# データセットをアップロードする

画像ファイルをCloud Storageにアップロードします。
あとでVertex AIにとりこむ際に、以下2点が必要です。

1. 画像ファイルそのもの
1. 画像ファイルのパスと、ラベルの対応を示したCSVファイル

CSVファイルの書式は以下のとおりで、1列目がCloud Storage上のパス、2列目がラベルです。

https://cloud.google.com/vertex-ai/docs/image-data/classification/prepare-data#csv

(例)
```csv
gs://hogehoge/train0.jpg,apple
gs://hogehoge/train1.jpg,apple
gs://hogehoge/train2.jpg,tomato
gs://hogehoge/train3.jpg,tomato
```

# モデルを作成する

- Vertex AIにアクセスします。
- データセットを作成します。
    - ウィザードでデータの種別と目的を選択します。
    - 今回は画像分類かつラベル1つのため `Image classification (Single-label)` とします。

![vertexai-datatype.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/230e98ca-8784-6e01-7a1f-17b20d7d6535.png)

- 画像をインポートする。
    - 先に用意しておいたCSVファイルを指定します。
![data-import.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/53e484f1-6aa6-e2d3-f841-b891694047da.png)

- インポートが完了すると、画面上でも結果を確認できます。
    - リンゴの画像を156点、トマトの画像を128点、インポートできました。

![imported-images.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/3e040b75-8dab-bcce-823f-93d7971eae14.png)

- モデルの訓練を行います。
    - 途中、Budgetの指定を求められます。この記事では最低の8を指定しました。

![train.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/ff6999ff-9e51-6932-7629-d685062402e1.png)

![budget.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/9fcef548-42bb-2e37-c496-0d7dc7d794e7.png)

- 機械学習の完了を待ちます。
    - 筆者の環境では学習完了まで約2時間を要しました。

- 学習を完了すると、モデルの精度を確認できます。
    - 混合行列の形でも確認できます。

![train-result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/aa2fd135-0ad0-ba42-29a9-cc6cd53869e8.png)

![confusion-matrix.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/c58addcf-c579-f1fd-62fa-293f34537090.png)

# エンドポイントを作成する

- Cloud Functionsから作成したモデルを利用できるように、エンドポイントを作成します。

![deploy-model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/31744126-feb8-395b-4a95-630af5ee51dc.png)

- 同じ画面上で、画像をアップロードし、モデルをテストできます。
    - この画像の場合は、確度100%でリンゴと判定しています。

![test-model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/9e835f63-c686-ef8c-9eaa-cce41f39cd2d.png)


- 同じ画面上で、エンドポイントの呼び出し方を確認できます。

![sample-request.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/2302bf2a-a592-d8d0-4d7b-8f55e5b1fd3a.png)

- 以下の画像でREST APIを試してみます。
    - こちらは99.9%の確度でトマトと判定しています。

![img_p1_9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1950110/69f18007-6418-9de2-002f-1d688ab1ac3c.jpeg)

```json
{
  "predictions": [
    {
      "ids": [
        "7964256950458253312"
      ],
      "confidences": [
        0.999813378
      ],
      "displayNames": [
        "tomato"
      ]
    }
  ],
  "deployedModelId": "1595853166786641920",
  "model": "projects/579808288126/locations/us-central1/models/200711449603997696",
  "modelDisplayName": "untitled_1675029982159",
  "modelVersionId": "1"
}
```

# Cloud Functionsを作成する

先に作成したエンドポイントを呼び出す、Cloud Functionsを作成します。

## 動作概要

1. `src` バケットへの画像アップロードをトリガーにCloud Functionsを実行する。
1. 先に作成したエンドポイントを呼び出し、リンゴかトマトか判別する。
1. リンゴの場合は `apple` バケットへ トマトの場合は `tomato` バケットへ画像を移動する。

## 認証

Vertex AIエンドポイントの呼び出しにはアクセストークンが必要です。
Cloud Functionsでアクセストークンを得るには、メタデータサーバーにアクセスします。

https://cloud.google.com/functions/docs/securing/function-identity#access_tokens

## 動作確認

- `src` バケットに画像をアップロードすると、 `apple` / `tomato` バケットに自動で振り分けられます。
- ダウンロードしたデータセット中の `test` ディレクトリ内の画像をテストに使えます。


# ソースコード全体

CDK for Terraformによる環境構築コードも含みます。

https://github.com/hsmtkk/qiita-apple-tomato-classify

# 料金

Cloud Storage, Cloud Functionsの料金は微々たるものなので、Vertex AIの料金が大半を占めます。

[Vertex AIの価格表](https://cloud.google.com/vertex-ai/pricing?hl=ja)によれば、以下のとおりです。
- トレーニング 3.465ドル/ノード・時間
- デプロイとオンライン予測 1.375ドル/ノード・時間

トレーニングで先に設定した8ノード・時間の予約枠を使いきったと仮定し、
1ノード・時間程度、デプロイしておいたとすると、
利用料金は約25ドルになります。

# 後始末

デプロイ状態だと課金対象になるため、エンドポイントを削除します。
プロジェクトごと削除するのが確実です。