CircleCIのインバウンドWebhookを使ったMLOpsの最適化(2)

本ブログは、[CircleCI Advent Calendar 2023](https://qiita.com/advent-calendar/2023/circleci) の8日目の記事です。

# はじめに
DevOps の実装において、ビルドやテスト、リリース、デプロイといったステップを自動化するには、設定ファイルにワークフローを記述します。ワークフローが実行されるのは、リポジトリにコードがコミット、プッシュされるタイミングです。
機械学習を自動化する MLOps に関しても同様に、ロジックを実装したコードが変更されるタイミングでワークフローが実行されます。
ただし、MLOps の場合、コードに変更がない場合であっても、トレーニングデータが更新され、モデルを作成し直したいといったような場合があります。モデルを作成し直したら、これまでのテストデータでテストし、結果を見たいと考えるでしょう。テストデータが追加されたので、既存のモデルでテストしてみたいということもあるでしょう。
本記事では、CircleCI を MLOps の「ハブ」として使い、[Roboflow](https://roboflow.com/) のデータをもとにモデルを作成したり(ここまでが前記事)、作成したモデルが [Hugging Face](https://huggingface.co/) に登録されたタイミングでテストを実行する方法(本記事)をご紹介します。
- [CircleCIのインバウンドWebhookを使ったMLOpsの最適化(1)](https://qiita.com/mfunaki/items/dec90dcc3158e1bbaf41)

# MLOps ワークフローの流れ
ここでは、Hugging Face 上のモデルが更新されたら、その更新されたモデルをもとに、テストセットを実行します。
なお、プロジェクトのリポジトリは、GitHub にあります。
- https://github.com/rockqueue/cci-test-yolov8-mask-model

このプロジェクト(cci-test-yolov8-mask-model)をご自身のGitHubプロジェクトとして fork します。

ご自身で動作を CircleCI 上で実際に試してみたい場合、CircleCI のアカウントを用意する必要があります。この際、(2023年9月以前の) GitHub や Bitbucket の認証で CircleCI にログインするのではなく、CircleCI のアカウント(メールアドレス、パスワード)を作成し、そのアカウントでログインしたうえで、GitHub 上のプロジェクトを CircleCI のプロジェクトとして登録する必要があります。具体的な方法は、私が Qiita に書いた次の2つの記事をご参照ください。

- [【2023年版】CircleCIにサインアップ(Sign up)](https://qiita.com/mfunaki/items/14080547b352be9d265c)
- [【2023年版】CircleCI で GitHub プロジェクトをビルド＆テストする](https://qiita.com/mfunaki/items/7a68fd137e936798b391)

## Hugging Face から CircleCI の呼び出し設定(Webhook)
次に、Hugging Face 上のモデルが更新されたら、CircleCI のワークフローを呼びだすための設定を行います。

前述の GitHub プロジェクトを CircleCI のプロジェクトとして登録しておきます。つぎに、CircleCI の Project Setting 画面の左側にある Triggers をクリックし、Add Trigger ボタンをクリックして、新規トリガーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/65ced82d-0ea2-70d1-894f-cfb15793dc8b.png)

トリガー追加画面が出たら、接続先に Inbound Webhook を指定して、Next ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/dd094e5f-6a62-68b5-f94a-0f4321fb95ca.png)

次に、トリガー設定場面が出るので、Trigger Source に HuggingFace_ModelUpdate といった Webhook の発信元がわかるような名前を入力し、Save ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/ad8ae92e-5dd9-d497-a910-f8641b6d5691.png)

すると、最終的に Webhook の呼び出し先が表示されます。Webhook URL、および、<YOUR SECRET HERE> を置き換えるのに必要な Secret (一部マスクされた状態で表示されています) をコピーして、ご自身で保持しておくようにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/2c506e00-adbc-83c7-644e-88ce67a6e69c.png)

さて、呼び出し元の Hugging Face の設定に移ります。Hugging Face のユーザ設定(Settings)画面の左から Webhooks をクリックし、Add a new webhook をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/78d895c4-e8de-119e-459b-0b7fbee6f4b9.png)

次に、Webhook の呼び出し方法の設定に移ります。Target repositories には Hugging Face 上で(更新を)監視するリポジトリを指定します。ここでは、mfunaki/cci-gpu-yolov8-maskdetection を指定していますが、ご自身のモデル格納先を指定します。また、Webhook URL には、先ほどの CircleCI のトリガー設定画面で表示されていた Webhook URL (<YOUR SECRET HERE>を Secret の値で置換したもの)を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/3d62ebaa-94e4-54a6-aaf7-d3004424e383.png)
最後に、Triggers には Repo update (リポジトリの更新) をチェックしておけば、モデル更新時に CircleCI が呼び出されます。Create webhook ボタンをクリックしておきましょう。

## 動作環境の設定
トレーニングは CircleCI の Linux GPU 環境上で実行しましたが、テスト(画像認識)はトレーニングほどは負荷が高くないので、Linux CPU 環境上(コンテナ上)で実行します。

```config.yml
orbs:
  python: circleci/python@2.1.1

jobs:
  test_model:
    executor: python/default
```

## ジョブの定義
ここでは test_model というジョブを定義して、その中で、Hugging Face CLI のインストール、Hugging Face 上で更新されたモデルの取得、テストデータを使ったマスク着用／不着用の検出、検出結果(アーティファクト)のアップロードを行います。

### Hugging Face CLI のインストール
前回もご紹介しましたが、Hugging Face CLI は pip パッケージが用意されているので、簡単にインストール可能です。
```config.yml
    steps:
      - checkout
      - run:
          name: Set up Hugging Face CLI
          command:  pip3 install -U "huggingface_hub[cli]"
```

### Hugging Face 上のモデルをダウンロード
次に Hugging Face 上のモデルをダウンロードします。前回は huggingface-cli upload でアップロードしましたが、今回は huggingface-cli download でダウンロードします。前回と同様、CircleCI プロジェクトの環境変数として、HUGGINGFACE_TOKEN に API キーを設定しておきます。
```config.yml
      - run:
          name: Set up a model from Hugging Face
          command: |
            cd ~
            mkdir datasets; cd datasets
            huggingface-cli login --token $HUGGINGFACE_TOKEN
            huggingface-cli download mfunaki/cci-gpu-yolov8-maskdetection \
              mask_best.pt \
              --local-dir ~/.pyenv/runs/detect/train/weights
```

### モデルをテスト(マスク着用の有無を検出)
次に、YOLOv8を使ってモデルをテストします。YOLOv8 のインストールは前回の「モデルの構築」の際と同様です。
モデルの構築の際は、yolo task=detect mode=train でトレーニングを行いましたが、今回のモデルのテストでは、yolo task=detect mode=predict で検出を行います。また、ここでは、モデルがプロジェクトの image ディレクトリに格納してあるものとします。
```config.yml
      - run:
          name: Test the model
          command: |
            pip3 install ultralytics
            yolo task=detect mode=predict \
              model=~/.pyenv/runs/detect/train/weights/mask_best.pt \
              source=~/project/images
```

### アーティファクトの保存
検出結果を後で参照、取得できるようにアーティファクトとして保存しておきます。
```config.yml
      - store_artifacts:
          path: ~/.pyenv/runs/detect/predict
          destination: predict
```
アーティファクトは CircleCI のウェブ画面から参照することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/50458bb8-8ad5-b8a0-3285-63a0adf8ed01.png)

個々のファイルを開くことで、元の画像からマスク着用(mask)、または非着用(no-mask)の検出結果を参照することが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/0d996f3a-0546-6a62-902c-a09b6575a3b0.png)

# おわりに
今回は、モデルの更新時に CircleCI がモデルテスト用のワークフローを自動実行する設定を行いましたが、教師データの追加、更新時にモデルを再更新する、テストデータが追加されたら追加されたデータに対してテストを行うなど、さまざまな用途が考えられます。
機械学習は PoC よりも、実際に利用されるデータをもとに学習を積み重ねていき、テストを実行していくことで、精度を担保していくことが重要です。更新したモデルが(現行のモデルと比較して)有意の精度が得られたら、実環境にデプロイするといったことも MLOps に組み込む必要があるでしょう。
CircleCI のインバウンド Webhook を活用することで、みなさんの MLOps が最適化されれば幸いです。