Hailo8(Raspberry Pi AI HAT+)でYOLOX Nanoがうまく動かない謎

# 1.この記事の内容

[Raspberry Pi AI HAT+（26TOPS版）](https://www.switch-science.com/products/9999)の理解を深めるために，[hailo_model_zoo](https://github.com/hailo-ai/hailo_model_zoo)を参考に，自前学習のモデルとしてYOLOX Nanoの実行を試みました．

結果として，ソフトウェアとしてはエラーなく動作する状態に持っていくことができましたが，期待する検知結果が出力されない状態となり，謎を残したままいったん区切りとしました．

本記事は，ここまでの内容を書き留めたものとなります．

## 1-1.使用環境

- ホストPC
    - Windows 11 Home 23H2
    - Ubuntu 22.04.4 LTS (GNU/Linux 5.15.90.1-microsoft-standard-WSL2+ x86_64)
    - Intel(R) Core(TM) i7-10700F CPU @ 2.90GHz   2.90 GHz
    - RAM 64GB
    - NVIDIA GeForce RTX4070 Ti
- 実行環境
    - YOLOモデルの学習とHailo8向けのコンパイル → [ryoma-jp/machine_learning](https://github.com/ryoma-jp/machine_learning)
        - YOLOモデルの学習 → [Training YOLO](https://github.com/ryoma-jp/machine_learning#training-yolo)
        - Hailo8向けのコンパイル → [Compile YOLOX for Hailo8](https://github.com/ryoma-jp/machine_learning#compile-yolox-for-hailo8)
    - Hailo8上での実行 → [python/gui_app](https://github.com/ryoma-jp/samples/tree/master/python/gui_app)

# 2.内容

Raspberry Pi AI HAT+でYOLOXを動かすための大まかな流れは下記の通りです．

- [YOLOモデルの学習](#2-1YOLOモデルの学習)
- [学習済みモデルをHailo8向けにコンパイル](#2-2学習済みモデルをHailo8向けにコンパイル)
- [Hailoデバイスへのデプロイと実行](#2-3Hailoデバイスへのデプロイと実行)

これらを実行可能なDocker環境を[GitHub](https://github.com/ryoma-jp/machine_learning)で公開しています．

学習データとして[COCO Dataset](https://cocodataset.org/)を使用します．  
デフォルトの`docker-compose.yaml`ではホストPCの`$HOME/work/dataset/coco2017/`にデータセットを格納した状態であることを前提としています．

```bash
ls $HOME/work/dataset/coco2017/
annotations  train2017  val2017
```

また，Hailoデバイス向けの実行ファイルを生成するコンパイル用のコンテナのみ，[Hailo Developer Zone](https://hailo.ai/developer-zone/software-downloads/)からダウンロードする必要があります．

`machine_learning/compiler/hailo/docker/whl`に
- Dataflow Compiler (hailo_dataflow_compiler-3.29.0-py3-none-linux_x86_64.whl)
- Hailo Model Zoo (hailo_model_zoo-2.13.0-py3-none-any.whl)
を置いた状態で，下記を実行してコンテナのビルドと起動を行います．

```bash
cd /path/to/machine_learning
./scripts/make_env.sh
./scripts/compose_up.sh
```

## 2-1.YOLOモデルの学習

YOLOモデルの学習は，オフィシャルのリポジトリ[Megvii-BaseDetection/YOLOX](https://github.com/Megvii-BaseDetection/YOLOX)(commit: d872c71bf63e1906ef7b7bb5a9d7a529c7a59e6a)を流用(clone)して実行しました．

YOLOの学習が可能なDocker環境は[筆者のGitHub](https://github.com/ryoma-jp/machine_learning/tree/688e15e5cb4d352e29d10f65a7ebcc94f6b27c25/external/yolox/docker)で公開しています．

手順は[README](https://github.com/ryoma-jp/machine_learning/blob/688e15e5cb4d352e29d10f65a7ebcc94f6b27c25/README.md)に記載していますが，学習部分だけまとめると下記のとおりです．  
Hailo8へのコンパイルではONNX形式へ変換しておく必要がありますので，`tools/export_onnx.py`でPyTorch形式からONNX形式へ変換します．

```bash
docker-compose exec yolox bash
cd external/yolox
git clone https://github.com/Megvii-BaseDetection/YOLOX.git
cd YOLOX
mkdir weights
wget -P ./weights https://github.com/Megvii-BaseDetection/YOLOX/releases/download/0.1.0/yolox_tiny.pth
python -m yolox.tools.train -f exps/default/yolox_tiny.py -c ./weights/yolox_tiny.pth -d 1 -b 8 --fp16 -o
python tools/export_onnx.py --output-name weights/yolox_tiny.onnx -n yolox-tiny -c weights/yolox_tiny.pth
```

## 2-2.学習済みモデルをHailo8向けにコンパイル

ONNX形式の学習済みモデルをHailoデバイスで実行可能なhefファイルへ変換します．

コンパイルで使用するコンフィグファイルには，ネットワーク定義ファイル(networks)，アロケータファイル(alls)，後処理定義ファイル(nms)の3種があります．

コンパイルコマンド`hailomz compile`の引数に指定するコンフィグファイルはネットワーク定義ファイルとアロケータファイルで，後処理定義ファイルはアロケータファイル内から参照する作りのようです．

```bash
docker-compose exec hailo_compiler bash
cd compiler/hailo
ln -s /workspace/external/yolox/YOLOX/YOLOX_outputs/yolox_tiny/yolox_tiny.onnx weights/yolox_tiny.onnx
hailomz compile --ckpt weights/yolox_tiny.onnx --calib-path /dataset/coco2017/val2017/ --yaml cfg/networks/yolox_tiny.yaml --model-script cfg/alls/yolox_tiny.alls
```

## 2-3.Hailoデバイスへのデプロイと実行

コンパイルした実行ファイルをRaspberryPiに転送します．

Raspberry Pi AI HAT+で推論する環境は[こちら](https://github.com/ryoma-jp/samples/tree/10f6759e2201977fe4247d3bc90459c9434e6856/python/gui_app)に用意しています．

`./02_inference-camera-image/run.sh`でターゲットとする実行ファイル(hefファイル)を指定できるようにしており，コンパイルした実行ファイルは`models`ディレクトリに置いてください．

YOLOX Tinyを実行する際は，`run.sh`の`MODELS`変数に`"yolox_tiny.hef"`を指定し，（正しく動作しませんが）YOLOX Nanoを実行する際は，`"yolox_nano.hef"`を指定してください．

```bash
git clone https://github.com/ryoma-jp/samples.git
cd samples/python/gui_app
./build-image.sh
./enter-docker.sh
./02_inference-camera-image/run.sh
```

YOLOX TinyとYOLOX Nanoを実行した結果はそれぞれ下図の通りです．  
※Raspberry Pi CameraでRaspberry Piのデスクトップを撮影した検知結果です．

### YOLOX Tiny

![YOLOX-Tiny.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/34286315-cedd-7824-a18c-0685b46dad5a.png)

### YOLOX Nano

YOLOX TinyとYOLOX Nanoの差は，各畳み込みチャネル数が異なる点と，ヘッド部分にDepthwise Separable Convolutionがあるかどうかくらいですが，正しい検知結果が出力されない結果となりました．  
※YOLOX Tinyも船の上の鳥を人間違えていますが・・・

前処理の誤り有無を中心にデバッグを進めたものの，原因は分からず，いったん深追いはしないこととしました．

![YOLOX-Nano.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/0b902e99-0041-cc41-e8ab-cd6ceb3dac8c.png)


# 3.さいごに

汎用的なアプリケーションを開発する場合は，Hailo Model Zooなどで公開されているhefファイルをそのまま使用するだけで十分だと思いますが，ニッチなモデルを自前で学習する場合などは，ONNX化とコンパイルが必要です．

これ以外の開発も一つのPC上で実行するためにホストPCのシステムを可能な限り汚さないように，すべてDockerコンテナ上で実行できるように作りましたが，なかなか上手くいかず多くの試行錯誤を繰り返すこととなりました．

このような使い方をする人は少ないかもしれませんが，参考になれば幸いです．

# 4.関連リンク

- [Raspberry Pi AI HAT+（26TOPS版）](https://www.switch-science.com/products/9999)
- [hailo-ai/hailo_model_zoo](https://github.com/hailo-ai/hailo_model_zoo)
- [Megvii-BaseDetection/YOLOX](https://github.com/Megvii-BaseDetection/YOLOX)
- [ryoma-jp/machine_learning](https://github.com/ryoma-jp/machine_learning)
- [ryoma-jp/samples](https://github.com/ryoma-jp/samples)
- [COCO Dataset](https://cocodataset.org/)
- [Hailo Developer Zone](https://hailo.ai/developer-zone/)