Module-LLMのNPU用モデルへ変換する(YOLO11編)

# 目的

Module-LLM(ax630c)のNPUでYOLO11を動かすために、
YOLO11のオブジェクト認識、ポーズ認識、セグメンテーションのモデルをaxmodelへの変換を行います。

# 注意点
・UltralyticsのYOLO11は、デフォルトでonnx変換を行うとopset19になるのですが、pulsar2がopset19に対応していないので、onnxへ変換するときにはopsetを18以下のバージョンへ指定します。YOLO11のモデルはopset19を含んでいると、Pulsar2でaxモデルへ変換するときに、Splitのオペランドでエラーが発生します。<br>
<img src="https://github.com/user-attachments/assets/cb086375-6049-4f68-83bf-02ea5c56dd6f" width="500"><br>


・Pulsar2はver 3.2-patch1-temp-vlm以降のバージョンにします。ver 3.2でYOLO11の変換を行うと、Shapeの形状不一致のエラーが発生します。<br>
<img src="https://github.com/user-attachments/assets/a6c9e084-b394-4731-ab3a-cf86cb3d5554" width="500"><br>


## pulsar2のインストール

@qqc -sanが管理するGoogleDriveからax_pulsar2_3.2_patch1_temp_vlm.tar.gzをダウンロードしてきます。
<https://drive.google.com/drive/folders/10rfQIAm5ktjJ1bRMsHbUanbAplIn3ium>

dockerをインストールし、以下のコマンドでdockerを読み込みます。

```
sudo docker load -i ax_pulsar2_3.2_patch1_temp_vlm.tar.gz
```
Dockerイメージを確認します。
```
$ sudo docker image ls
REPOSITORY                  TAG             IMAGE ID       CREATED         SIZE
pulsar2                     3.2             9a6b9d26f6a1   2 months ago    2.58GB
pulsar2                     temp-58aa62e4   c6ccb211d0bc   4 weeks ago     2.58GB
```

Dockerを起動します。

```
$ sudo docker run -it --net host --rm -v $PWD:/data pulsar2:temp-58aa62e4
 ```

Dockerを終了します。
```
$ exit
 ```

## quick_start_exampleのダウンロード

モデルのコンパイルとシミュレーション実行に必要なオリジナルモデル、データ、画像、シミュレーションツールを、次のリンクからダウンロードできるファイルの中に、quick_start_exampleフォルダ内に用意しています。
サンプルファイルをダウンロードをクリックし、ダウンロードしたファイルを解凍してdockerの/dataパスにコピーしてください。

 https://github.com/xiguadong/assets/releases/download/v0.1/quick_start_example.zip
```
root@xxx:~/data# ls
config  dataset  model  output  pulsar2-run-helper

# model: オリジナルのONNXモデルを格納します（事前にonnxsimを使用して最適化済み）
# dataset: オフライン量子化キャリブレーション（PTQ Calibration）に必要なデータセットの圧縮ファイルを格納します（tar、tar.gz、gzなどの一般的な圧縮形式に対応）
# config: 実行に必要な設定ファイルconfig.jsonを格納します
# output: 結果出力を格納します
# pulsar2-run-helper: X86環境でのaxmodelのシミュレーション実行をサポートする
```
 

## Ultralyticsのインストール

Pytorchをインストールした後に、Ultralytics をインストールします。
ここでは、CPU環境のPytorchをインストールしています。

```
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
pip install ultralytics
```



## axモデルへの変換1

このリポジトリをダウンロードし、pythonスクリプトを実行します。

```
$ git clone https://github.com/nnn112358/ax_model_convert_YOLO11
$ cd ax_model_convert_YOLO11
```

### オブジェクト認識モデル
オブジェクト認識モデルをUltralyticsからダウンロードして、モデルの最終段のカットを行います。<br>
YOLO11のm/s/nサイズをダウンロードします。

```
$ python yolo11_download.py
```
yolo11_download.py
``` yolo11_download.py
from ultralytics import YOLO
import os
os.chdir('./model')

# Load a model,Export to onnx with simplify
model = YOLO("yolo11n.pt")
model.info()
model.export(format='onnx', simplify=True,opset=17)
```

 yolo11_cut-onnx.py
```
$ python yolo11_cut-onnx.py
```

```yolo11_cut-onnx.py
import onnx
import os
def extract_onnx_model(input_path, output_path):
   input_names = ["images"]
   output_names = [
       "/model.23/Concat_output_0",
       "/model.23/Concat_1_output_0", 
       "/model.23/Concat_2_output_0"
   ]
   onnx.utils.extract_model(input_path, output_path, input_names, output_names)

# Usage
os.chdir('./model')
extract_onnx_model("yolo11n.onnx", "yolo11n-cut.onnx")

```

<img src="https://github.com/user-attachments/assets/2fda3d7e-709c-4f9b-94c1-8caa53b6ae37" width="300"><br>

### セグメンテーション認識モデル
セグメンテーション認識モデルをUltralyticsからダウンロードして、モデルの最終段のカットを行います。<br>
YOLO11-segのm/s/nサイズをダウンロードします。

```
$ python yolo11-seg_download.py
$ python yolo11-seg_cut-onnx.py
```
<img src="https://github.com/user-attachments/assets/d5d8fe37-71bf-489e-a41a-56fedca66dda" width="300"><br>

### ポーズ認識モデル

ポーズ認識モデルをUltralyticsからダウンロードして、モデルの最終段のカットを行います。<br>
YOLO11-poseのm/s/nサイズをダウンロードします。

```
$ python yolo11-pose_download.py
$ python yolo11-pose_cut-onnx.py
```
<img src="https://github.com/user-attachments/assets/2f6199f8-1b7b-478f-be3a-c0657044e22e" width="300"><br>


### 補足

モデルの最終段のカットを行う目的は、モデルをNPUで実行するために量子化を行うと整数精度の処理になり精度が低下するのですが、
モデルから最終段を削除し、最終段をCPUで演算することで浮動小数点精度で処理し、不必要な精度低下を防ぐためです。


https://x.com/qqc1989/status/1859293298877399322

### 補足の補足

YOLO11モデルの最終段の出力は、[1, 84, 8400]の形をしています。

 * 1(バッチサイズ)：1回の推論で処理する画像の枚数
 * 84（チャンネル数）：
    前半 4 チャンネル：バウンディングボックスの座標 [x, y, w, h]（範囲：1～640）
    後半 80 チャンネル：各クラスの信頼度スコア[c1,c2,c3...]（範囲：0～1）
 * 8400（検出候補の数）：「8400」は、画像サイズが 640 の場合の YOLO11 の 3 つの検出ヘッドの出力セル数を示します (80×80+40×40+20×20=8400)。

INT8量子化では、浮動小数点数を8ビット整数に変換します。このとき、モデルの出力全体に対して同じスケール変換を適用する必要があります。
YOLO11の出力では、座標情報が0~640、信頼度スコアの最小値が0～１です。この座標情報のスケールで信頼度スコア（0～1）をINT8量子化変換すると、量子化後の信頼度スコアはすべて0扱いとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/fdc53eb1-1580-3b3f-e03c-32d59fc5ce71.png)


通常、物体検出の信頼度スコアの閾値は0以上に設定されるので、何も検出されない状態になってしまうのです。
この問題を解決するためには、モデルから最終段を削除し、最終段をCPUで演算することで浮動小数点精度で処理しをしています。



## axモデルへの変換2

Pulsar2がインストールされている、Dockerを起動します。

```
$ sudo docker run -it --net host --rm -v $PWD:/data pulsar2:temp-58aa62e4
```

Pulsar2のbuildコマンドで、onnxモデルをModule-LLM(ax630c)のNPUに対応するaxモデルに変換します。

```
# pulsar2 build --input model/yolo11n-cut.onnx --output_dir output --config config/yolo11-config.json --target_hardware AX620E
# cp output/compiled.axmodel model/yolo11n.axmodel

# pulsar2 build --input model/yolo11n-pose-cut.onnx --output_dir output --config config/yolo11-pose_config.json --target_hardware AX620E
# cp output/compiled.axmodel model/yolo11n-pose.axmodel

# pulsar2 build --input model/yolo11n-seg-cut.onnx --output_dir output --config config/yolo11-seg_config.json --target_hardware AX620E
# cp output/compiled.axmodel model/yolo11n-seg.axmodel
```


モデルが生成できていることを確認します。<br>

```
$ ls model/*.axmodel
model/yolo11n-pose.axmodel  model/yolo11n.axmodel       model/yolo11s-seg.axmodel
model/yolo11n-seg.axmodel   model/yolo11s-pose.axmodel  model/yolo11s.axmodel
```

## Module-LLMで実行

Module-LLMにNPU用モデルとCVサンプルの実行ファイルをコピーして実行します。

CVサンプルの実行ファイルは、Module-LLMのCVサンプルのビルド手順を参照してください。



```
root# ./ax_yolo11 -m yolo11n.axmodel -i ssd_horse.jpg
root# ./ax_yolo11_pose -m yolo11n-seg.axmodel -i ssd_horse.jpg
root# ./ax_yolo11_seg-m yolo11n-pose.axmodel -i ssd_horse.jpg
```

https://qiita.com/nnn112358/items/46a347592e28641edd4a


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">YOLO11のポーズも変換できたぞ。腕の長さは <a href="https://twitter.com/hashtag/%EF%BD%BD%EF%BE%80%EF%BD%AF%EF%BD%B8%EF%BD%BB%EF%BE%9D?src=hash&amp;ref_src=twsrc%5Etfw">#ｽﾀｯｸｻﾝ</a> の触手に騙されてないですね?? <a href="https://t.co/Frbj8LXmxO">pic.twitter.com/Frbj8LXmxO</a></p>&mdash; nnn (@nnn112358) <a href="https://twitter.com/nnn112358/status/1863736123698319435?ref_src=twsrc%5Etfw">December 3, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 



# 参考リンク
@nnn112358/M5_LLM_Module_Report
https://github.com/nnn112358/M5_LLM_Module_Report

pulsar2-docs
https://pulsar2-docs.readthedocs.io/en/latest/index.html
https://axera-pi-zero-docs-cn.readthedocs.io/zh-cn/latest/doc_guide_algorithm.html