AutoMLで画像識別をするreactアプリの作り方と高額請求への注意喚起！

この記事では、Google Cloud Platform (GCP) のAutoMLを使って画像識別機能を持ったReactアプリケーションの開発方法について解説します。AutoMLでの画像認識モデルの構築からReactで画像識別を行うapiの構築までを紹介します。

## 実際に作成したアプリ

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/ccc17379-f230-4108-a05a-76b29ed1d0af.png" width=50%>


## AutoMLとは

[Vertex AI AutoML](https://cloud.google.com/vertex-ai/docs/start/automl-users?hl=ja)は、専門的な機械学習の知識がなくても高精度なAIモデルを作成できるGCPのサービスです。コードを書かずに画像認識、自然言語処理、表形式データの分析などのモデルを簡単に構築・デプロイできます。

## 1. 学習データの準備とアップロード

### Cloud Storageバケットの作成

1. [Google Cloudコンソール](https://console.cloud.google.com/)にアクセスし、[Cloud Storage](https://console.cloud.google.com/storage)に移動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/1922b5dc-29b7-479f-82e1-f986cf51d45e.png)
2. 「バケットの作成」ボタンをクリック
![スクリーンショット 2025-04-12 3.29.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/fbf449ab-e710-4591-87b9-ef886ca2cb9f.png)
3. 適切なバケット名を指定してバケットを作成(ここら辺の設定はよしなに行ってください。私の場合はコストがかからないように最低限の設定にしています)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/b55887df-a3fe-44a8-8536-495bcf89b729.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/0f1873c4-58f0-4091-b607-00ccfb6b0dc8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/ca968a95-50ca-47e9-8b7b-cd4b3f1bddd4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/b4269098-9ae3-4f46-9eaa-39100dd1f5d7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/aaffc964-0dbd-4cf8-9f17-24c083866505.png)


詳細は[Cloud Storageのバケット作成ドキュメント](https://cloud.google.com/storage/docs/creating-buckets?hl=ja)を参照してください。

### 画像データのアップロード

ひとつづつ画像をアップロードすることもできるのですが、後々のラベル付きデータセットの作成が面倒になるので、ローカル環境でラベル別にディレクトリを分けて画像を保存しておき、これを使います。
**ディレクトリ名がそのままラベル名**になります。

![ディレクトリ構造](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/e160707a-2116-4be7-bee8-4e3d2dc5196a.png)

[gsutil](https://cloud.google.com/storage/docs/gsutil?hl=ja)コマンドでまとめてアップロード：

```bash
gsutil -m cp -r ./ gs://artie_dataset/
```

※ `artie_dataset`はご自身のバケット名に置き換えてください。

gsutilコマンドが使えない場合は、以下の方法で環境を構築できます：
1. Google Cloud SDKをインストール
2. インストール後、以下のコマンドでgcloud CLIを初期化： gcloud init
4. プロジェクトとアカウントの認証を行う
5. 認証完了後、gsutilコマンドが使用可能になります
詳しい手順は以下の記事も参考にしてください：
https://cloud.google.com/storage/docs/gsutil_install?hl=ja#install
https://blog.g-gen.co.jp/entry/basics-for-gcloud-storage
https://cloud.google.com/storage/docs/gsutil?hl=ja


アップロード完了後、GCPコンソールで確認できます：

![アップロード完了確認](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/d8174bf1-2cc5-4697-8706-41e195c280ae.png)

## 2. AutoML用データセットの作成

1. [Vertex AI](https://console.cloud.google.com/vertex-ai) → トレーニングにアクセス
2. 「データセットの作成」をクリック
3. データセット名を入力し、「単一ラベル分類」を選択して作成へ進む
4. 「Cloud Storageからインポート」を選択(一番めんどくさくない方法。)

![スクリーンショット 2025-04-12 4.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/21f6f147-c983-48ad-86b3-5fd6e360fc75.png)

この方法では、Cloud strageの保存先とラベルのペアを表す以下のようなcsvファイルを使って一括でラベル付きデータセットの構築ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/1a6cf992-bddd-4311-9343-d1d5d43fe5f9.png)

### CSVマニフェストファイルの作成

先ほども説明したCloud Storageの画像と対応するラベルを記述したCSVファイルが必要ですが、これも適当なpythonスクリプトで生成させました。base_dirで先ほどデータアップロードの際に作ったディレクトリを指定します。gcs_base_uriは自身のバケット名にしてください。
実行すると、ディレクトリ名がラベルとしてCSVマニフェストファイルが生成されます。

```python
import os
import csv

# スクリプトファイルがあるディレクトリをベースディレクトリとする
base_dir = os.path.dirname(os.path.abspath(__file__))

# Cloud Storage のベース URI（事前に gsutil でアップロードした先）
gcs_base_uri = "gs://artie_dataset"  # ご自身のバケット名に変更

# 出力する CSV ファイル名
output_csv = "manifest.csv"

# CSV を UTF-8 エンコードで書き込み
with open(output_csv, "w", newline="", encoding="utf-8") as csvfile:
    writer = csv.writer(csvfile)
    
    # base_dir 以下を再帰的に走査
    for root, dirs, files in os.walk(base_dir):
        for file in files:
            # 画像ファイルのみを対象
            if file.lower().endswith((".jpg", ".jpeg", ".png")):
                # ファイルのフルパス
                file_full_path = os.path.join(root, file)
                # base_dir からの相対パス
                rel_path = os.path.relpath(file_full_path, base_dir)
                # OS依存のパス区切りをスラッシュに統一
                rel_path_unix = rel_path.replace(os.sep, "/")
                # Cloud Storage 上のファイル URI を組み立て
                gcs_uri = f"{gcs_base_uri}/{rel_path_unix}"
                # ラベルとして、相対パスの最上位ディレクトリ名を利用
                label = rel_path.split(os.sep)[0]
                # ラベルが20文字を超えないように切り詰め
                if len(label) > 20:
                    label = label[:20]
                # CSV に1行追加（URI, ラベル）
                writer.writerow([gcs_uri, label])

print(f"CSV manifest has been created: {output_csv}")
```

CSVファイルとバケット名を入力して、データセット作成を完了する：

![スクリーンショット 2025-04-12 4.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/21f6f147-c983-48ad-86b3-5fd6e360fc75.png)

データセット作成が完了すると、ラベル付けされたデータセットが表示されます：

![スクリーンショット 2025-04-12 4.43.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/a940d401-85d3-42ea-a314-d3d6fb221785.png)



**注意**: 学習に使うデータセットはひとつのラベルにつき最低20個以上必要です。今回は20個も用意するのが面倒だったので適当に色味や回転を加えてデータを盛りました。しかし、おそらくこのような処理でデータを増やすのはautoMLの学習の際に行なってそうなのであまりお勧めしないです。)

## 3. モデルのトレーニング

1. Vertex AI → トレーニングへアクセス
![スクリーンショット 2025-04-09 0.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/726f3e01-2858-43f8-af46-01c20f8c0fbc.png)
2. 「新しいモデルのトレーニング」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/06a612b3-ad72-4d3a-bc58-fbf65b27513b.png)
3. 適切な設定を行う(よしなに設定してください。今回は一番コストがかからない設定にしました)
![スクリーンショット 2025-04-12 4.50.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/74dbd224-14b9-411e-bafa-2e05b4434222.png)
![トレーニング設定2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/6c2a06e4-a024-4b5a-a7d4-e429ba8205c8.png)
![トレーニング設定3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/38ff1f91-37c1-4402-b4ea-153ca01dba29.png)
![トレーニング設定4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/1f9ffa11-db3b-4547-a215-d4bce3610a49.png)
![トレーニング設定5](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/ce14c7de-aa6b-4011-a9b4-736003a1d042.png)
4. 「トレーニング開始」をクリックして学習を開始
5. 数時間後、トレーニングが完了

![スクリーンショット 2025-04-12 4.51.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/36e7849c-0559-4477-bd6b-b256089a885e.png)

トレーニングの詳細については[Vertex AI AutoMLのドキュメント](https://cloud.google.com/vertex-ai/docs/training/automl-train-image?hl=ja)を参照してください。

## 4. オンライン予測用エンドポイントの作成

1. Vertex AI → オンライン予測へアクセス
![スクリーンショット 2025-04-09 0.20.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/726f3e01-2858-43f8-af46-01c20f8c0fbc.png)
2. 「エンドポイント作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/a911d150-81f8-4ca3-96d8-84101966a250.png)
3. よしなに設定
![スクリーンショット 2025-04-09 0.20.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/f770f79d-9f2e-4199-973e-743527b8de09.png)
4. 先ほど作ったモデルを選択
![スクリーンショット 2025-04-12 4.52.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/5303c544-85f5-4c08-9795-e82425d30966.png)
5. 設定を完了するとエンドポイントが作成される
![スクリーンショット 2025-04-12 4.49.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/b61a996d-d8ac-42ef-8d17-482f6246c0d5.png)

詳細は[オンライン予測のドキュメント](https://cloud.google.com/vertex-ai/docs/predictions/online-predictions?hl=ja)を参照してください。

## 5. サービスアカウントの設定

**注意**: 一番手軽にできるサービスアカウントを使った方法を使いますが、適切な方法とは言えないので、実際に事業とかで使う場合はもっとセキュアな認証をしてください。

1. IAMと管理 → サービスアカウントへアクセス
![スクリーンショット 2025-04-09 0.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/3ce86694-3b3e-4493-915d-e566990cfb53.png)
2. 「サービスアカウントを作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/d0d27124-404f-41b8-b5f6-be53a52acb89.png)
3. よしなに設定してアカウント作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/ebd8550c-a8ee-4d9b-bd57-29a98b4c2098.png)
4. 作成したアカウントを選択し、「鍵」タブから「新しい鍵を作成」を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/4c8ba252-a7a4-40fb-b1a6-0f037a6d8bfe.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/f93d6efd-8b99-4b9a-b215-91a4d5aec40e.png)
5. JSONファイルがダウンロードされるので保存

6. IAMと管理 → IAMへアクセス
![スクリーンショット 2025-04-09 0.55.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/06c1b179-361b-4ef9-9307-b9676312a3f7.png)
7. 作成したサービスアカウントに適切なロールを付与（Vertex AI User や Vertex AI Prediction User など）
![ロール設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/f63c7b71-c27e-4da4-8850-83cfc3c70170.png)
サービスアカウントの詳細は[GCPのIAMドキュメント](https://cloud.google.com/iam/docs/service-accounts?hl=ja)を参照してください。

## 6. Reactアプリからの接続設定

### アクセストークン取得関数の作成

```typescript
// src/lib/getAccessToken.ts
import { GoogleAuth } from "google-auth-library";

export const getAccessToken = async () => {
  const auth = new GoogleAuth({
    credentials: JSON.parse(
      process.env.GOOGLE_APPLICATION_CREDENTIALS_JSON || "{}",
    ),
    scopes: ["https://www.googleapis.com/auth/cloud-platform"],
  });

  const client = await auth.getClient();
  const accessToken = await client.getAccessToken();
  return accessToken.token;
};
```

### APIエンドポイントの作成（Next.js使用例）

```typescript
// src/app/api/scan/route.ts
import { getAccessToken } from "@/lib/getAccessToken";
import { NextResponse } from "next/server";

const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT_ID;
const LOCATION = process.env.GOOGLE_CLOUD_LOCATION || "us-central1";
const ENDPOINT_ID = process.env.GOOGLE_CLOUD_MODEL_ID;

const ENDPOINT = `https://${LOCATION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION}/endpoints/${ENDPOINT_ID}:predict`;

export async function POST(req: Request) {
  try {
    const body = await req.json();
    const { imageBase64 } = body;

    if (!imageBase64) {
      return NextResponse.json(
        { error: "画像データが提供されていません。" },
        { status: 400 }
      );
    }

    const accessToken = await getAccessToken();

    const response = await fetch(ENDPOINT, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${accessToken}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        instances: [
          {
            content: imageBase64,
          },
        ],
      }),
    });

    if (!response.ok) {
      return NextResponse.json(
        { error: "予測リクエストが失敗しました。" },
        { status: response.status }
      );
    }

    const data = await response.json();
    const predictions = data.predictions?.[0];
    console.log(predictions);

    if (!predictions || !predictions.confidences || !predictions.ids) {
      return NextResponse.json(
        { error: "作品を認識できませんでした。再度近づいて撮影してください。" },
        { status: 200 }
      );
    }

    const maxConfidenceIndex = predictions.confidences.indexOf(
      Math.max(...predictions.confidences)
    );

    const maxConfidence = predictions.confidences[maxConfidenceIndex];
    const maxId = predictions.ids[maxConfidenceIndex];

    if (maxConfidence < 0.8) {
      return NextResponse.json(
        { error: "作品を認識できませんでした。再度近づいて撮影してください。" },
        { status: 200 }
      );
    }

    return NextResponse.json({ id: maxId }, { status: 200 });
  } catch (error) {
    console.error("API エラー:", error);
    return NextResponse.json(
      {
        error: "予期せぬエラーが発生しました。再度時間をおいて試してください。",
      },
      { status: 500 }
    );
  }
}
```

Vertex AI APIの詳細については[Vertex AI APIリファレンス](https://cloud.google.com/vertex-ai/docs/reference/rest?hl=ja)を参照してください。

## 7. 環境変数の設定

`.env.local`ファイルなどに以下の環境変数を設定します：

```
GOOGLE_CLOUD_PROJECT_ID=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1
GOOGLE_CLOUD_MODEL_ID=your-endpoint-id
GOOGLE_APPLICATION_CREDENTIALS_JSON={"type":"service_account",...} # ダウンロードしたJSONキーの内容
```

以上で簡単ではありますが、automlで画像識別をするreactアプリを作る方法になります。

しかし！ここからが本番です

## 重要: コスト管理について

AutoMLは非常に便利ですが、コストがかかるサービスです。特に注意すべき点：

1. オンライン予測用のエンドポイントは常時稼働しているとコストが発生します（最低設定でも1日約4,000円程度）
2. 不要になったらエンドポイントは必ず停止・削除してください
3. 学習自体にもコストがかかります

![コスト例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3287608/4158b088-1ad0-47da-b5c6-2aa233de1260.png)

今回の場合はオンライン予測用のエンドポイントを6時間ほど動かしたけか、学習にかかった金額(5000円弱)と合わせて上記が請求金額でした。

エンドポイントの停止を忘れると平気で数万の請求がくると思います。マジで気をつけてください。

## 参考リンク

- [Vertex AI AutoML 公式ドキュメント](https://cloud.google.com/vertex-ai/docs/start/automl-users?hl=ja)
- [Vertex AI オンライン予測](https://cloud.google.com/vertex-ai/docs/predictions/online-predictions?hl=ja)
- [Cloud Storage ドキュメント](https://cloud.google.com/storage/docs?hl=ja)
- [Google Cloud 認証](https://cloud.google.com/docs/authentication?hl=ja)
- [Next.js API Routes](https://nextjs.org/docs/api-routes/introduction)