CVATにカスタムYOLOモデルをデプロイする - Qiita (2024)

@hbk24
  • AI
  • 画像認識
  • annotation
  • アノテーションツール
  • CVAT

Last updated at Posted at 2024-06-08

今回はCVATでYOLOのカスタムモデルをデプロイする方法を書いていきます。CVATは無料で使えるアノテーションツールですが、AIモデルを載せることで画像の分析基盤としても活用することができます。

今回のこの記事の内容は以下の内容を参考にしています。

前提

この記事はCVATの設定が終わっていて、CVATが用意しているAIモデルでの自動アノテーションが使える状態になった後を想定しています。ここまでの準備については以下の記事を参照してください。

まずは稼働中のCVATのDockerコンテナを停止させます。

cd ~/cvatdocker compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml down

サーバーレス関数の実装

CVATではNuclioを使ってAIのサーバーレス関数を構築し、CVATからこれを呼び出すことでAIによる処理を行っています。この項では、Nuclioへのサーバーレス関数の実装を解説します。

また、今回はUltralyticsのYOLOv9をデプロイしていきます。

Ultralyticsを使えば重みファイルをわざわざダウンロードしなくても大丈夫ですが、カスタムしたモデルをデプロイする場合は重みファイルを指定しなくてはなりません。そこで、デフォルトモデルの重みファイルをダウンロードして使用し、モデルのカスタムが終わったら重みファイルを差し替える想定で進めます。

まず、デフォルトの重みファイル(yolov9c.py)をダウンロードします。
https://github.com/ultralytics/assets/releases/download/v8.2.0/yolov9c.pt

次にCVATフォルダ内にあるserverlessフォルダ内へ今回デプロイする用のフォルダを作ります。

ちなみに、フォルダの階層やフォルダ名は自由に変更してもらっても問題ありません。単純に、フレームワークを区別するために、このような階層構造にしています。

次にこのフォルダ内に以下のファイルを置きます。

  • yolov9c.pt (カスタムした場合はその重みファイルを置く)
  • main.py
  • function.yaml (AIをCPUで処理する場合)
  • function-gpu.yaml (AIをCPUで処理する場合)

各フォルダには以下のコードを書いていきます。

YAML

YAMLファイルではNuclioのサーバーレス関数を実行するための設定を記述しています。DockerFileのようなイメージです。コードの詳細は以下のコードに付けているコメントを参照してください。

ちなみに、下で解説している通り、NuclioはGUIのダッシュボードが用意されいて、そこで設定をすることも可能です。ダッシュボードを覗いてみるとコードの中身が理解しやすくなると思います。

function-gpu.yaml

metadata: name: gpu-ultralytics-yolov9 # Nuclio内での表示名 namespace: cvat # Nuclioのプロジェクト名 annotations: name: GPU YOLOv9 by Ultralytics # CVATで表示されるモデル名 type: detector framework: pytorch # モデルが識別するラベル名のリスト(カスタムモデルの場合、それに合わせる) spec: | [ { "id": 0, "name": "person", "type": "rectangle" }, { "id": 1, "name": "bicycle", "type": "rectangle" }, { "id": 2, "name": "car", "type": "rectangle" }, { "id": 3, "name": "motorbike", "type": "rectangle" }, { "id": 4, "name": "aeroplane", "type": "rectangle" }, { "id": 5, "name": "bus", "type": "rectangle" }, { "id": 6, "name": "train", "type": "rectangle" }, { "id": 7, "name": "truck", "type": "rectangle" }, { "id": 8, "name": "boat", "type": "rectangle" }, { "id": 9, "name": "traffic light", "type": "rectangle" }, { "id": 10, "name": "fire hydrant", "type": "rectangle" }, { "id": 11, "name": "stop sign", "type": "rectangle" }, { "id": 12, "name": "parking meter", "type": "rectangle" }, { "id": 13, "name": "bench", "type": "rectangle" }, { "id": 14, "name": "bird", "type": "rectangle" }, { "id": 15, "name": "cat", "type": "rectangle" }, { "id": 16, "name": "dog", "type": "rectangle" }, { "id": 17, "name": "horse", "type": "rectangle" }, { "id": 18, "name": "sheep", "type": "rectangle" }, { "id": 19, "name": "cow", "type": "rectangle" }, { "id": 20, "name": "elephant", "type": "rectangle" }, { "id": 21, "name": "bear", "type": "rectangle" }, { "id": 22, "name": "zebra", "type": "rectangle" }, { "id": 23, "name": "giraffe", "type": "rectangle" }, { "id": 24, "name": "backpack", "type": "rectangle" }, { "id": 25, "name": "umbrella", "type": "rectangle" }, { "id": 26, "name": "handbag", "type": "rectangle" }, { "id": 27, "name": "tie", "type": "rectangle" }, { "id": 28, "name": "suitcase", "type": "rectangle" }, { "id": 29, "name": "frisbee", "type": "rectangle" }, { "id": 30, "name": "skis", "type": "rectangle" }, { "id": 31, "name": "snowboard", "type": "rectangle" }, { "id": 32, "name": "sports ball", "type": "rectangle" }, { "id": 33, "name": "kite", "type": "rectangle" }, { "id": 34, "name": "baseball bat", "type": "rectangle" }, { "id": 35, "name": "baseball glove", "type": "rectangle" }, { "id": 36, "name": "skateboard", "type": "rectangle" }, { "id": 37, "name": "surfboard", "type": "rectangle" }, { "id": 38, "name": "tennis racket", "type": "rectangle" }, { "id": 39, "name": "bottle", "type": "rectangle" }, { "id": 40, "name": "wine glass", "type": "rectangle" }, { "id": 41, "name": "cup", "type": "rectangle" }, { "id": 42, "name": "fork", "type": "rectangle" }, { "id": 43, "name": "knife", "type": "rectangle" }, { "id": 44, "name": "spoon", "type": "rectangle" }, { "id": 45, "name": "bowl", "type": "rectangle" }, { "id": 46, "name": "banana", "type": "rectangle" }, { "id": 47, "name": "apple", "type": "rectangle" }, { "id": 48, "name": "sandwich", "type": "rectangle" }, { "id": 49, "name": "orange", "type": "rectangle" }, { "id": 50, "name": "broccoli", "type": "rectangle" }, { "id": 51, "name": "carrot", "type": "rectangle" }, { "id": 52, "name": "hot dog", "type": "rectangle" }, { "id": 53, "name": "pizza", "type": "rectangle" }, { "id": 54, "name": "donut", "type": "rectangle" }, { "id": 55, "name": "cake", "type": "rectangle" }, { "id": 56, "name": "chair", "type": "rectangle" }, { "id": 57, "name": "sofa", "type": "rectangle" }, { "id": 58, "name": "pottedplant", "type": "rectangle" }, { "id": 59, "name": "bed", "type": "rectangle" }, { "id": 60, "name": "diningtable", "type": "rectangle" }, { "id": 61, "name": "toilet", "type": "rectangle" }, { "id": 62, "name": "tvmonitor", "type": "rectangle" }, { "id": 63, "name": "laptop", "type": "rectangle" }, { "id": 64, "name": "mouse", "type": "rectangle" }, { "id": 65, "name": "remote", "type": "rectangle" }, { "id": 66, "name": "keyboard", "type": "rectangle" }, { "id": 67, "name": "cell phone", "type": "rectangle" }, { "id": 68, "name": "microwave", "type": "rectangle" }, { "id": 69, "name": "oven", "type": "rectangle" }, { "id": 70, "name": "toaster", "type": "rectangle" }, { "id": 71, "name": "sink", "type": "rectangle" }, { "id": 72, "name": "refrigerator", "type": "rectangle" }, { "id": 73, "name": "book", "type": "rectangle" }, { "id": 74, "name": "clock", "type": "rectangle" }, { "id": 75, "name": "vase", "type": "rectangle" }, { "id": 76, "name": "scissors", "type": "rectangle" }, { "id": 77, "name": "teddy bear", "type": "rectangle" }, { "id": 78, "name": "hair drier", "type": "rectangle" }, { "id": 79, "name": "toothbrush", "type": "rectangle" } ]spec: description: GPU YOLOv9 by Ultralytics runtime: 'python:3.9' # サーバーレスファンクションを実行するランタイムを指定(Nuclio公式は3.9以上を推奨) handler: main:handler # トリガーを引かれた時に実行するプログラムファイル名(ここでは"main.py"を想定) eventTimeout: 30s build: image: cvat.yolov9.gpu # 構成するDockerイメージ名 baseImage: ultralytics/ultralytics # Dockerのベース # 以下DockerFileのような部分 directives: preCopy: - kind: USER value: root # NVIDIAコンテナのセッティング - kind: ENV value: NVIDIA_VISIBLE_DEVICES=all - kind: ENV value: NVIDIA_DRIVER_CAPABILITIES=compute,utility - kind: ENV value: RUN_ON_GPU="true" #cpuの場合”false” # Pythonの設定 - kind: RUN value: export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y python-is-python3 - kind: RUN value: pip install --no-cache-dir opencv-python-headless pillow pyyaml - kind: RUN value: pip uninstall -y torch torchvision torchaudio - kind: RUN value: pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 - kind: WORKDIR value: /opt/nuclio # トリガーの設定 triggers: myHttpTrigger: maxWorkers: 1 kind: 'http' workerAvailabilityTimeoutMilliseconds: 10000 attributes: maxRequestBodySize: 33554432 # 32MB # GPUの設定(最大何個使用するか)CPUの場合はここを消す resources: limits: nvidia.com/gpu: 1 # サーバーレスファンクションの追加設定 platform: attributes: restartPolicy: name: always maximumRetryCount: 3 mountMode: volume

function.yaml

metadata: name: cpu-ultralytics-yolov9 # Nuclio内での表示名 namespace: cvat # Nuclioのプロジェクト名 annotations: name: CPU YOLOv9 by Ultralytics # CVATで表示されるモデル名 type: detector framework: pytorch # モデルが識別するラベル名のリスト(カスタムモデルの場合、それに合わせる) spec: | [ { "id": 0, "name": "person", "type": "rectangle" }, { "id": 1, "name": "bicycle", "type": "rectangle" }, { "id": 2, "name": "car", "type": "rectangle" }, { "id": 3, "name": "motorbike", "type": "rectangle" }, { "id": 4, "name": "aeroplane", "type": "rectangle" }, { "id": 5, "name": "bus", "type": "rectangle" }, { "id": 6, "name": "train", "type": "rectangle" }, { "id": 7, "name": "truck", "type": "rectangle" }, { "id": 8, "name": "boat", "type": "rectangle" }, { "id": 9, "name": "traffic light", "type": "rectangle" }, { "id": 10, "name": "fire hydrant", "type": "rectangle" }, { "id": 11, "name": "stop sign", "type": "rectangle" }, { "id": 12, "name": "parking meter", "type": "rectangle" }, { "id": 13, "name": "bench", "type": "rectangle" }, { "id": 14, "name": "bird", "type": "rectangle" }, { "id": 15, "name": "cat", "type": "rectangle" }, { "id": 16, "name": "dog", "type": "rectangle" }, { "id": 17, "name": "horse", "type": "rectangle" }, { "id": 18, "name": "sheep", "type": "rectangle" }, { "id": 19, "name": "cow", "type": "rectangle" }, { "id": 20, "name": "elephant", "type": "rectangle" }, { "id": 21, "name": "bear", "type": "rectangle" }, { "id": 22, "name": "zebra", "type": "rectangle" }, { "id": 23, "name": "giraffe", "type": "rectangle" }, { "id": 24, "name": "backpack", "type": "rectangle" }, { "id": 25, "name": "umbrella", "type": "rectangle" }, { "id": 26, "name": "handbag", "type": "rectangle" }, { "id": 27, "name": "tie", "type": "rectangle" }, { "id": 28, "name": "suitcase", "type": "rectangle" }, { "id": 29, "name": "frisbee", "type": "rectangle" }, { "id": 30, "name": "skis", "type": "rectangle" }, { "id": 31, "name": "snowboard", "type": "rectangle" }, { "id": 32, "name": "sports ball", "type": "rectangle" }, { "id": 33, "name": "kite", "type": "rectangle" }, { "id": 34, "name": "baseball bat", "type": "rectangle" }, { "id": 35, "name": "baseball glove", "type": "rectangle" }, { "id": 36, "name": "skateboard", "type": "rectangle" }, { "id": 37, "name": "surfboard", "type": "rectangle" }, { "id": 38, "name": "tennis racket", "type": "rectangle" }, { "id": 39, "name": "bottle", "type": "rectangle" }, { "id": 40, "name": "wine glass", "type": "rectangle" }, { "id": 41, "name": "cup", "type": "rectangle" }, { "id": 42, "name": "fork", "type": "rectangle" }, { "id": 43, "name": "knife", "type": "rectangle" }, { "id": 44, "name": "spoon", "type": "rectangle" }, { "id": 45, "name": "bowl", "type": "rectangle" }, { "id": 46, "name": "banana", "type": "rectangle" }, { "id": 47, "name": "apple", "type": "rectangle" }, { "id": 48, "name": "sandwich", "type": "rectangle" }, { "id": 49, "name": "orange", "type": "rectangle" }, { "id": 50, "name": "broccoli", "type": "rectangle" }, { "id": 51, "name": "carrot", "type": "rectangle" }, { "id": 52, "name": "hot dog", "type": "rectangle" }, { "id": 53, "name": "pizza", "type": "rectangle" }, { "id": 54, "name": "donut", "type": "rectangle" }, { "id": 55, "name": "cake", "type": "rectangle" }, { "id": 56, "name": "chair", "type": "rectangle" }, { "id": 57, "name": "sofa", "type": "rectangle" }, { "id": 58, "name": "pottedplant", "type": "rectangle" }, { "id": 59, "name": "bed", "type": "rectangle" }, { "id": 60, "name": "diningtable", "type": "rectangle" }, { "id": 61, "name": "toilet", "type": "rectangle" }, { "id": 62, "name": "tvmonitor", "type": "rectangle" }, { "id": 63, "name": "laptop", "type": "rectangle" }, { "id": 64, "name": "mouse", "type": "rectangle" }, { "id": 65, "name": "remote", "type": "rectangle" }, { "id": 66, "name": "keyboard", "type": "rectangle" }, { "id": 67, "name": "cell phone", "type": "rectangle" }, { "id": 68, "name": "microwave", "type": "rectangle" }, { "id": 69, "name": "oven", "type": "rectangle" }, { "id": 70, "name": "toaster", "type": "rectangle" }, { "id": 71, "name": "sink", "type": "rectangle" }, { "id": 72, "name": "refrigerator", "type": "rectangle" }, { "id": 73, "name": "book", "type": "rectangle" }, { "id": 74, "name": "clock", "type": "rectangle" }, { "id": 75, "name": "vase", "type": "rectangle" }, { "id": 76, "name": "scissors", "type": "rectangle" }, { "id": 77, "name": "teddy bear", "type": "rectangle" }, { "id": 78, "name": "hair drier", "type": "rectangle" }, { "id": 79, "name": "toothbrush", "type": "rectangle" } ]spec: description: CPU YOLOv9 by Ultralytics runtime: 'python:3.9' # サーバーレスファンクションを実行するランタイムを指定(Nuclio公式は3.9以上を推奨) handler: main:handler # トリガーを引かれた時に実行するプログラムファイル名(ここでは"main.py"を想定) eventTimeout: 30s build: image: cvat.yolov9.cpu # 構成するDockerイメージ名 baseImage: ultralytics/ultralytics # Dockerのベース # 以下DockerFileのような部分 directives: preCopy: - kind: USER value: root # NVIDIAコンテナのセッティング - kind: ENV value: NVIDIA_VISIBLE_DEVICES=all - kind: ENV value: NVIDIA_DRIVER_CAPABILITIES=compute,utility - kind: ENV value: RUN_ON_GPU="false" # Pythonの設定 - kind: RUN value: export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -y python-is-python3 - kind: RUN value: pip install --no-cache-dir opencv-python-headless pillow pyyaml - kind: RUN value: pip uninstall -y torch torchvision torchaudio - kind: RUN value: pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 - kind: WORKDIR value: /opt/nuclio # トリガーの設定 triggers: myHttpTrigger: maxWorkers: 1 kind: 'http' workerAvailabilityTimeoutMilliseconds: 10000 attributes: maxRequestBodySize: 33554432 # 32MB # サーバーレスファンクションの追加設定 platform: attributes: restartPolicy: name: always maximumRetryCount: 3 mountMode: volume

main.py

init_context: サーバーレス関数のコンテキストを初期化を行っています。YOLOモデルを読み込みや、GPUの使用が有効か確認を行っています。

handler(): 処理のメイン部分です・

  • 入力処理:画像データを含むHTTPリクエストによってトリガーされます。画像データを抽出してデコードし、オブジェクト検出の信頼度閾値を設定し、Pillowを使用して画像を読み込んでいます。
  • オブジェクト検出:読み込まれたモデルは、設定された閾値に基づいて画像からオブジェクトを検出します。context.user_dataに保存されているモデル()が検出を実行し、結果を返します。
  • 結果のフォーマット:検出されたオブジェクトは、CVATが理解できるJSON構造にフォーマットされます。スクリプトは、検出された各オブジェクトについて、信頼度スコア、クラスラベル、バウンディングボックスの座標を抽出しています。
  • HTTPレスポンス:関数は、JSON形式の検出結果を含むHTTPレスポンスを構築します。レスポンスにはヘッダーとコンテンツタイプの仕様が含まれ、ステータスコード200は処理が成功したことを示しています。

main.py

from ultralytics import YOLOimport json, base64, io, osfrom PIL import Imagedef init_context(context): context.logger.info("Init context... 0%") # main.pyと同じフォルダにあるモデルの重みファイル(’yolov9c.pt’)を読み込み model = YOLO('/opt/nuclio/yolov9c.pt') use_gpu = os.getenv("RUN_ON_GPU", 'False').lower() in ('true', '1') # Import the GPU env variable and covert to a boolean value print(f"CUDA-STATUS: {use_gpu}") if use_gpu: model.to('cuda') context.user_data.model = model context.logger.info("Init context...100%")def handler(context, event): context.logger.info("Run yolo-v9 model") data = event.body buf = io.BytesIO(base64.b64decode(data["image"])) threshold = float(data.get("threshold", 0.6)) context.user_data.model.conf = threshold image = Image.open(buf) yolo_results = context.user_data.model(image, conf=threshold) results = yolo_results[0] encoded_results = [] for idx, class_idx in enumerate(results.boxes.cls): confidence = results.boxes.conf[idx].item() label = results.names[int(class_idx.item())] points = results.boxes.xyxy[idx].tolist() encoded_results.append({ 'confidence': confidence, 'label': label, 'points': points, 'type': 'rectangle' }) return context.Response(body=json.dumps(encoded_results), headers={}, content_type='application/json', status_code=200)

デプロイ

GPUを使用する場合は

./serverless/deploy_gpu.sh ./serverless/ultralytics/yolov9/nuclio/

CPUの場合は

./serverless/deploy_gpu.sh ./serverless/ultralytics/yolov9/nuclio/

を実行し、しばらくすると

 NAMESPACE | NAME | PROJECT | STATE | REPLICAS | NODE PORT nuclio | gpu-ultralytics-yolov9 | cvat | ready | 1/1 | 33311 

とターミナルで返されるのでこれでデプロイ完了です。

CVATでカスタムYOLOモデルを使用

CVATをサーバーPCに入れている場合は

export CVAT_HOST=<IPアドレス>

を実行してから

docker compose -f docker-compose.yml -f components/serverless/docker-compose.serverless.yml up -d

でCVATのコンテナを起動させます。

アノテーション画面で「AI Tools」を開くとデプロイしたYOLOのモデルが使用できるようになっています。

CVATにカスタムYOLOモデルをデプロイする - Qiita (2)
(画像はCOCOのデータセットに含まれる画像です)

サーバーレス関数の削除

実装したサーバーレス関数を削除したい場合はCVATのコンテナが走っている状態で

http://IPアドレス/:8070

にアクセスするとNuclioのダッシュボードにアクセスできます。

CVATにカスタムYOLOモデルをデプロイする - Qiita (3)

ここから、実装したサーバーレス関数を削除することができます。

CVATにカスタムYOLOモデルをデプロイする - Qiita (4)

CVATのコンテナが走っていない状態では、以下のコマンドでNuclioのダッシュボードのコンテナを立ち上げるとダッシュボードにアクセスできます。

docker run --detach --publish 8070:8070 --volume /var/run/docker.sock:/var/run/docker.sock --name nuclio-dashboard quay.io/nuclio/dashboard:stable-amd64

List of users who liked

List of comments

comment0

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

Sign upLogin

CVATにカスタムYOLOモデルをデプロイする - Qiita (2024)

References

Top Articles
Latest Posts
Article information

Author: Roderick King

Last Updated:

Views: 6225

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Roderick King

Birthday: 1997-10-09

Address: 3782 Madge Knoll, East Dudley, MA 63913

Phone: +2521695290067

Job: Customer Sales Coordinator

Hobby: Gunsmithing, Embroidery, Parkour, Kitesurfing, Rock climbing, Sand art, Beekeeping

Introduction: My name is Roderick King, I am a cute, splendid, excited, perfect, gentle, funny, vivacious person who loves writing and wants to share my knowledge and understanding with you.