0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初心者向け】FastAPIで画像分類AIモデルをAPI化する:実践ガイド

Posted at

1. はじめに

以前、車の色を判定するAIモデルを構築しましたが、これをAPIとして提供する方法を模索していました。そこで、Pythonで簡単に実装できるFastAPIの技術を用いて、Web APIとして実装を試みました。本記事では、その際に得られた知見やコード、そして遭遇したエラーとその解決策について詳しく解説します。


2. 対象読者

本稿は、以下のような方々を対象としています。

  • PythonだけでAPIを構築したいが、具体的な方法がわからない方。
  • AIモデルの実装は行ったものの、それをどうAPI化すればよいかわからない方。
  • APIを自力で実装し、その詳細な解説も求めている方。

3. 設計概要

今回のプロジェクトは、AIモデルをWeb APIとして公開することを目的としており、以下の3つの主要なファイルで構成されています。

  • `predict.py`: AI予測モデルの実装を行います。事前に学習させたパラメータを`car_color_model.pth`から読み込み、予測処理を実行します。学習元データも`dataset_split`に格納します。
  • `main.py`: FastAPIを用いてAPIのGetとPostのエンドポイントを実装します。基本的には画像ファイルを受け取り、予測結果をJSON形式で返却するAPIです。
  • `client.py`: APIを呼び出すクライアント側のPythonコードを実装します。`test(2).jpg`という画像ファイルをAPIに送信し、その結果を受け取ります。

4. フォルダ構成

今回のプロジェクトは、以下の階層的なフォルダ構成で管理されています。これにより、コード、データ、モデルファイルが整理され、管理がしやすくなります。


app/
├─ dataset_split/
│  ├─ train/       # 学習用画像フォルダ
│  └─ test/        # テスト用画像フォルダ
├─ car_color_model.pth  # 学習済みモデル
├─ predict.py           # 画像予測処理の関数
├─ main.py              # FastAPIアプリ本体
├─ client.py            # テスト用クライアント
└─ test(2).jpg          # テスト用画像

5. 各プログラムコードの解説

ここでは、各Pythonコードの役割と具体的な内容について説明します。

5-1. predict.py

説明:車の画像から、その車がどんな色なのかを判定するAIモデルです。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import transforms , datasets
from torchvision.datasets import ImageFolder
import timm
from PIL import Image

def predict_image(file):
    # 画像の前処理を行うTransformを定義
    transform = transforms.Compose([
        # 256×256に画像サイズを変更
        transforms.Resize((256,256)),
        # 50%の確率で画像を左右反転
        transforms.RandomHorizontalFlip(p=0.5),
        # 画像をテンソル形式に変換
        transforms.ToTensor(),
        # 画像の正規化
        transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
    ])
    
    # 画像フォルダからデータセットを作成し、クラス名を取得
    # 注:このパスは実行環境に合わせて修正してください
    dataset = datasets.ImageFolder("C:/Users/yaku2/OneDrive/デスクトップ/car_color/dataset_split/train",transform=transform)
    class_names = dataset.classes
    num_classes = len(dataset.classes)
    
    # timmライブラリを利用して、ResNet18モデルを作成
    model = timm.create_model("resnet18",pretrained=False,num_classes=num_classes)
    # 事前学習済みパラメータ「car_color_model.pth」を読み込み、CPUで起動
    model.load_state_dict(torch.load(f"car_color_model.pth" ,map_location=torch.device('cpu')))
    
    # 予測対象の画像を開き、前処理とバッチ次元を追加
    image = Image.open(file).convert("RGB")
    input_tensor = transform(image).unsqueeze(0)
    
    # モデルの勾配計算をオフにして、推論モードに
    with torch.no_grad():
        # 入力テンソルをモデルに渡し、クラスごとのスコアを算出
        outputs = model(input_tensor)
        # スコアの中から最も高いもののインデックスを取得
        preds = outputs.max(1)[1]
        # テンソルから整数へ変換
        predicted_class_index = preds.item()
        # クラス名リストから予測結果を返却
        predicted_class_name = class_names[predicted_class_index]
    return predicted_class_name

5-2. main.py

説明:車の画像を受け取り、予測結果をJSON形式で返すAPIの実装。

ワンポイント解説:

  • FastAPIとは?
    PythonでAPIを開発するためのWebフレームワーク。APIドキュメントの自動生成や、高速なパフォーマンスがメリットです。
  • Getリクエストとは?
    情報を読み取り専用で取得したいときに利用するHTTPメソッド。
  • Postリクエストとは?
    情報をもとに何かしらの処理(更新・追加など)を行った上で、情報を返却したいときに利用するHTTPメソッド。
from fastapi import FastAPI, UploadFile, File
from predict import predict_image

app = FastAPI()

# ルートエンドポイント(確認用)
@app.get("/") # HTTP GET リクエストで "/" にアクセスされたときに呼ばれる
def read_root():
# 単純にJSONでメッセージを返す
    return {"message": "Car Color Prediction API"}

# 画像予測エンドポイント
@app.post("/predict")  # HTTP POST リクエストで "/predict" にアクセスされたときに呼ばれる
async def predict(file: UploadFile = File(...)):
    """
    file: クライアントが送信した画像ファイル
    """
    prediction = predict_image(file.file)
    return {"prediction": prediction}

5-3. client.py

説明:APIを呼び出すためのPythonクライアントコード。

ワンポイント解説:

  • requestsとは?
    HTTPリクエストを簡単に送信できるPythonライブラリ。今回はAPIを構築したローカルホストにアクセスします。
import requests

url = "http://127.0.0.1:8001/predict/"

#APIへ画像ファイルのリクエストを送信
with open("test (2).jpg","rb") as f:
    files = {"file":("test(2).jpg",f,"image/jpeg")}
    res = requests.post(url , files=files)

print(res.status_code)
print(res.text)

6. APIの呼び出し

ここでは、構築したAPIを実際に呼び出す手順を解説します。

  1. 2つのターミナルを開き、どちらもアプリケーションのフォルダに移動します。
  2. 1つ目のターミナルで、以下のコマンドを実行してFastAPIサーバーを起動します。
uvicorn main:app --host 0.0.0.0 --port 8001

解説:

  • `uvicorn`: FastAPIを起動するためのライブラリです。
  • `main:app`: `main.py`ファイル内の`app`インスタンスを起動する指示です。
  • `--host 0.0.0.0 --port 8001`: すべてのネットワークインターフェイスからポート8001でアクセス可能にします。

実行すると、以下のような結果が出力され、サーバーが起動します。

INFO:      Waiting for application startup.
INFO:      Application startup complete.
INFO:      Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)

3. 2つ目のターミナルで、以下のコマンドを実行して`client.py`を動かします。

python client.py

実行後、APIへの接続情報と画像の判定結果がJSON形式で返却されます。

200
{"prediction":"rio red"}

7. 発生したエラーとその対処法

本プロジェクトの構築中に遭遇した、代表的なエラーとその解決策をまとめました。

1. ファイルパスが見つからないエラー

症状:

  • `FileNotFoundError: [WinError 3] 指定されたパスが見つかりません。`というエラーが発生する。

原因:

  • `predict.py`内のデータセットのパスが正しくない。
  • パスの記述方法がWindows環境とPythonの仕様に合っていない(例: `\`ではなく`/`を使用)。
  • Dockerを使用している場合に、ローカルのファイルがコンテナに正しくマウントされていない。

対処法:

  • `ImageFolder`のパスを環境に合わせた正しい絶対パス、または相対パスに修正する。
  • Dockerを使用する場合は、`docker-compose.yml`の`volumes`セクションに`- ./dataset_split:/app/dataset_split`を追加し、ファイルがコンテナ内にアクセス可能になるようにする。

2. ポート使用中によるバインドエラー

症状:

  • `ERROR: [Errno 10048] error while attempting to bind on address ('0.0.0.0', 8000)` のように、指定したポートがすでに使用中であるというエラーが発生し、サーバーが起動できない。

原因:

  • すでに`8000`番ポートが他のプロセスで使用されている。

対処法:

  • `netstat -ano | findstr :8000`のようなコマンドで、ポートを使用しているプロセスID(PID)を特定し、`taskkill /PID [PID] /F`で終了させる。
  • または、`uvicorn main:app --host 0.0.0.0 --port 8001`のように別のポートを指定して起動する。

3. `WatchFiles`による自動リロードの挙動

症状:

  • `uvicorn --reload`コマンド実行後、`WARNING: WatchFiles detected changes...`や`INFO: Waiting for application startup.`のログが表示されたまま止まったように見える。

原因:

  • `--reload`機能がファイルを監視しているため、一見止まっているように見える。特にWindowsのOneDrive環境では、ファイルの同期が原因でリロードがループすることがある。

対処法:

  • サーバーは実際には起動しているため、ブラウザや`curl`でアクセスして動作を確認する。
  • 開発環境を安定させたい場合は、`--reload`オプションをつけずに`uvicorn main:app --host 0.0.0.0 --port 8000`で起動する。

4. 相対インポートによるモジュール読み込みエラー

症状:

  • `ERROR: Error loading ASGI app. Could not import module "main".`のように、モジュールのインポートに失敗する。

原因:

  • `from .predict import predict_image`のような相対インポートは、直接`python main.py`で実行すると失敗することがある。

対処法:

  • 絶対インポートに変更する。`main.py`と`predict.py`が同じディレクトリにあることを確認した上で、`from predict import predict_image`と記述する。

まとめ

本稿では、AIモデルをFastAPIでAPI化する一連の流れと、その際に発生しうる主要なエラーについて解説しました。

  • FastAPIは`uvicorn`を使ってCLIで起動する必要があります。
  • `volumes`と`ports`の設定を適切に行うことで、ローカルでの開発とブラウザからのアクセスが両立できます。
  • エラー発生時は、ログメッセージをよく読み、ファイルパスやポート番号、Pythonのバージョンなどの環境設定を確認することが重要です。

これらのポイントを押さえることで、DockerとFastAPIを使ったAIモデルのAPI化を効率的に進めることができるでしょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?