DatabricksにおけるYOLOv8を用いた物体検知(実践編)

こちらを翻訳したので、ノートブックも実行してみようと思ったのですがデータがありませんでした…。

https://qiita.com/taka_yayoi/items/098c895db7cce1623930

良さそうなサンプルがないかなと探したら、こちらに行き当たりました。

https://learnopencv.com/train-yolov8-on-custom-dataset/

こちらを参考にしながらノートブックを動かしてみます。こちらのGPUクラスターを使いました。
![Screenshot 2024-07-11 at 17.25.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b24cdc49-b47d-750f-acb4-f8bf94d490a8.png)


# データの準備

事前にボリュームを作成しておきます。ここでは、`/Volumes/users/takaaki_yayoi/yolo/`と言うボリュームにしています。

ファイルをダウンロードします。

```sh
!wget https://www.dropbox.com/s/qvglw8pqo16769f/pothole_dataset_v8.zip?dl=1 -O /Volumes/users/takaaki_yayoi/yolo/pothole_dataset_v8.zip
```

解凍します。

```sh
%sh
cd /Volumes/users/takaaki_yayoi/yolo
unzip pothole_dataset_v8.zip
```

このようになります。
![Screenshot 2024-07-11 at 17.24.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/ecdfb55a-3805-d975-38f2-3c319866e804.png)

# セットアップ

```py
%pip install ultralytics==8.2.53 opencv-python
%pip install ray[default]>=2.3.0
dbutils.library.restartPython()
```

```py
import os
os.environ["CUDA_LAUNCH_BLOCKING"] = "1"
```

```py
from ultralytics import YOLO

import mlflow
import torch.distributed as dist
from ultralytics import settings
from mlflow.types.schema import Schema, ColSpec
from mlflow.models.signature import ModelSignature

os.environ["OMP_NUM_THREADS"] = "4"  # 必要に応じて設定

input_schema = Schema(
    [
        ColSpec("string", "image_source"),
    ]
)
output_schema = Schema([ColSpec("string","class_name"),
                        ColSpec("integer","class_num"),
                        ColSpec("double","confidence")]
                       )

signature = ModelSignature(inputs=input_schema, 
                           outputs=output_schema)

settings.update({"mlflow":False})


##############################################################################
## モデルの結果と predict() メソッドをキャプチャするための YOLOC クラスの作成        ##
##############################################################################

class YOLOC(mlflow.pyfunc.PythonModel):
  def __init__(self, point_file):
    self.point_file=point_file

  def load_context(self, context):
    from ultralytics import YOLO
```

```py
# MLflowの設定
mlflow.autolog(disable=False)
mlflow.end_run()

# プロジェクト構成のディレクトリの設定
project_location = '/Volumes/users/takaaki_yayoi/yolo/pothole_dataset_v8/'
os.makedirs(f'{project_location}/training_runs/', exist_ok=True)
os.chdir(f'{project_location}/training_runs/')
```

# トレーニング

以下で指定するYAMLファイルを準備します。ノートブックと同じパスに保存します。

```yaml:pothole_v8.yaml
path: /Volumes/users/takaaki_yayoi/yolo/pothole_dataset_v8/
train: 'train/images'
val: 'valid/images'
 
# class names
names: 
  0: 'pothole'
```

動作確認目的なので、エポック数は10にしています。必要に応じて増やしてください。

```py
if not dist.is_initialized():
  dist.init_process_group("nccl")


# MLflowトレーニングプロセスの起動
# このエクスペリメントにおけるバッチ、エポック数などを指定

with mlflow.start_run():
  #model = YOLO("yolov8l-seg.pt")
  model = YOLO('yolov8n.pt')

  model.train(
          batch=8,
          device=[0],
          #data=f"{project_location}/training/data.yaml",
          data="/Workspace/Users/takaaki.yayoi@databricks.com/20240711_object_detection/pothole_v8.yaml",
          epochs=10,
          project='/tmp/taka_pothole/',
          exist_ok=True,
      )


  mlflow.log_params(vars(model.trainer.model.args))
  yolo_wrapper = YOLOC(model.trainer.best)
  mlflow.pyfunc.log_model(artifact_path = "model",
                          artifacts = {'model_path': str(model.trainer.save_dir), 
                                       "best_point": str(model.trainer.best)},
                          python_model = yolo_wrapper,
                          signature = signature
                          )
```

トレーニングが終わりました。
![Screenshot 2024-07-11 at 17.19.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/96e9e7c4-bdea-8d31-6054-732490c2ff73.png)

# 評価

```py
##########
# モデルと入力画像が与えられたら境界ボックスを予測する関数
##########

def predict_on_image(model, img):
  import pandas as pd
  import json, os

  # ロードされた画像に対して境界ボックスの予測を生成するためにモデルを使用
  results = {}
  result_list = []

  model_results = model.predict(img)
  label_names, boxes, confidence = model_results[0].names, model_results[0].boxes.cpu().numpy(), model_results[0].boxes.conf.tolist()
  label_result = [int(i) for i in model_results[0].boxes.cls.tolist()]
  named_results = [{'class_name': label_names[i], 
                    'class_num': i, 
                    'confidence': confidence[count],
                    'box': boxes[count].xyxy[0].astype(int).tolist()} for count, i in enumerate(label_result)
                  ]
  results['labels'] = named_results

  return results


##########
# 指定された信頼度の閾値に基づき、穴の周辺の境界ボックスをが描かれた画像を表示する関数
##########

def prepare_img_w_box(img_in, results, confidence_thresh):

  # ボックスのポイントに変換するヘルパー関数
  def _convert_box_yolo_cv2(boxpoints):
    xmin, ymin, xmax, ymax = boxpoints
    x, y, w, h = xmin, ymin, xmax-xmin, ymax-ymin
    return x,y,w,h
  
  # それぞれの結果に対してループを行い、信頼度の条件を満たしたらボックスを配置
  for solar_panel in results['labels']:

    if solar_panel['confidence'] > confidence_thresh:
      x,y,w,h = _convert_box_yolo_cv2(solar_panel['box'])
      cv2.rectangle(img_in, (x,y), (x+w,y+h), (0,255,0), 2)

  cv2_imshow(img_in)
```

```py
import cv2
from dbruntime.patches import cv2_imshow

# opencv2経由で画像をロード
img_loc = f'{project_location}/train/images/China_Drone_000205.jpg'
img = cv2.imread(img_loc)

cv2_imshow(img)
```
![download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/2be61a06-62e7-8570-d43e-a496fb03b1ef.png)

推論します。

```py
results = predict_on_image(model, img)
results
```
```
{'labels': [{'class_name': 'pothole',
   'class_num': 0,
   'confidence': 0.3940852880477905,
   'box': [136, 73, 181, 124]},
  {'class_name': 'pothole',
   'class_num': 0,
   'confidence': 0.2777845561504364,
   'box': [326, 0, 447, 76]}]}
```

`confidence`の値をベースに閾値を指定します。

```py
prepare_img_w_box(img, results, 0.2)
```

検知できました！
![download (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7a4c424f-168a-c0f6-813c-bec6ade423c6.png)

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)