KV260向けにVitisプラットフォームを作成してDPUを動かす その2 (Vitis 2022.1 + Vitis-AI v2.5)

[KV260向けにVitisプラットフォームを作成してDPUを動かす その1 (Vitis 2022.1 + Vitis-AI v2.5)](https://qiita.com/lp6m/items/df1b87b11f8275ee6210)の続きです。

今回は物体検出モデルYOLOv4（正確にはYOLOv4-leaky）を動作させようと思います。
YOLOv4はXilinxのAI-model-zooで`tf_yolov4_coco_416_416_60.3G_2.5`として公開されているので簡単にDPUで動作させることができます。

https://github.com/Xilinx/Vitis-AI/blob/v2.5/model_zoo/model-list/tf_yolov4_coco_416_416_60.3G_2.5/model.yaml

評価アプリケーションはmodel-zooには含まれないので簡単な描画アプリケーションも作成します。


# xmodelの生成

Model-zooのファイル内の`README.md`の手順に従って作業します。

## floatモデルとprototxtのダウンロード
`float&quantized`とxmodelのダウンロードURLを[modelzoo](https://github.com/Xilinx/Vitis-AI/blob/v2.5/model_zoo/model-list/tf_yolov4_coco_416_416_60.3G_2.5/model.yaml)から取得してファイルをダウンロードします。
model-zooで公開されているxmodelは前回記事で作成したDPUコンフィグレーション向けのものではないため動作しません。動作しないのでxmodel自体は不要なのですが、xmodelと同梱されているprototxtファイルが必要なのでダウンロードします。

Vitis-AI dockerイメージ内で使用するのでVitis-AIリポジトリより下の階層にダウンロードすることをおすすめします。
```sh
# float&quantized
wget https://www.xilinx.com/bin/public/openDownload?filename=tf_yolov4_coco_416_416_60.3G_2.5.zip -O tf_yolov4_coco_416_416_60.3G_2.5.zip
unzip tf_yolov4_coco_416_416_60.3G_2.5.zip
# xmodel
wget https://www.xilinx.com/bin/public/openDownload?filename=yolov4_leaky_416_tf-zcu102_zcu104_kv260-r2.5.0.tar.gz -O yolov4_leaky_416_tf-zcu102_zcu104_kv260-r2.5.0.tar.gz
tar xvf yolov4_leaky_416_tf-zcu102_zcu104_kv260-r2.5.0.tar.gz
```

## Vitis-AI dockerコンテナ起動
```sh
cd <VITIS_AI_REPO_DIR>
./docker_run.sh xilinx/vitis-ai-gpu:2.5
```
docker_run.shを実行したディレクトリが`/workspace`としてマウントされます。私はVSCodeからコンテナにアタッチして作業しています。以降はdockerコンテナ内で作業を行います。

## 環境とデータセットの準備
`tf_yolov4_coco_416_416_60.3G_2.5/README.md`に従って作業を行います。
環境を用意して、量子化および評価に使用するCOCOデータセットをダウンロードします。
```sh
cd tf_yolov4_coco_416_416_60.3G_2.5
conda create -n yolov4-tf1 python=3.6
source activate yolov4-tf1
pip install cython
pip install -r code/requirements.txt
cd code
bash download_data.sh
bash convert_data.sh
```
YOLOv4のオリジナルリポジトリ[YOLOv4-model-zoo](https://github.com/AlexeyAB/darknet/wiki/YOLOv4-model-zoo#coco-pre-trained-files-416x416)から`yolov4-leaky-416.cfg`,`yolov4-leaky-416.weights`をダウンロードして`code/`以下に配置します。

## Tensorflowモデルの生成
darknetのcfgとweightsからkerasを経由してtensorflow形式(.pb)のモデルを生成します。
```sh
python tools/model_converter/convert.py --yolo4_reorder --fixed_input_shape yolov4-leaky-416.cfg yolov4-leaky-416.weights yolov4-leaky-416.h5
python tools/model_converter/keras_to_tensorflow.py --input_model yolov4-leaky-416.h5 --output_model yolov4-leaky-416.pb
```

`keras_to_tensorflow`で`AttributeError: 'str' object has no attribute 'decode'`が出る場合h5pyのバージョンを下げれば動作しました。
```sh
pip install  install h5py==2.10.0
```
参考: [【Keras】エラー 'str' object has no attribute 'decode'について](https://qiita.com/Hiroki-Fujimoto/items/b078bfb680fb710c38c1)

## 量子化
Tensorflowモデルができたので、ここからは量子化→コンパイルの流れです。

```sh
conda activate vitis-ai-tensorflow
cd quantize
bash quantize_416.sh
```
データが多いので量子化はRTX 2070 GPUで40分くらいかかりました。（データサイズを減らしても良いかもしれません）
`quantized/quantize_results_416/quantize_eval_model.pb`が量子化されたモデルになります。

## コンパイル
Vitisで生成した`arch.json`を使い勝手の良いところにコピーしておきます。ここでは`kv260_myarch.json`としています。
```sh
cd ..
vai_c_tensorflow -f ./quantized/quantize_results_416/quantize_eval_model.pb -a /workspace/kv260_myarch.json -o ./compiled -n yolov4_leaky_416_tf
```

- コンパイルログ
```
**************************************************
* VITIS_AI Compilation - Xilinx Inc.
**************************************************
[INFO] Namespace(batchsize=1, inputs_shape=None, layout='NHWC', model_files=['./quantized/quantize_results_416/quantize_eval_model.pb'], model_type='tensorflow', named_inputs_shape=None, out_filename='/tmp/yolov4_leaky_416_tf_0x101000017010406_org.xmodel', proto=None)
[INFO] tensorflow model: /workspace/tf_yolov4_coco_416_416_60.3G_2.5/quantized/quantize_results_416/quantize_eval_model.pb
[INFO] parse raw model     :100%|███████████████████████████████████████████| 532/532 [00:00<00:00, 19153.06it/s]             
[INFO] infer shape (NHWC)  :100%|███████████████████████████████████████████| 670/670 [00:00<00:00, 1647.00it/s]              
[INFO] perform level-0 opt :100%|███████████████████████████████████████████| 3/3 [00:00<00:00, 115.79it/s]                   
[INFO] perform level-1 opt :100%|███████████████████████████████████████████| 7/7 [00:00<00:00, 137.90it/s]                   
[INFO] generate xmodel     :100%|███████████████████████████████████████████| 421/421 [00:00<00:00, 910.45it/s]               
[INFO] dump xmodel: /tmp/yolov4_leaky_416_tf_0x101000017010406_org.xmodel
[UNILOG][INFO] Compile mode: dpu
[UNILOG][INFO] Debug mode: function
[UNILOG][INFO] Target architecture: DPUCZDX8G_ISA1_B3136_0101000017010406
[UNILOG][INFO] Graph name: quantize_eval_model, with op num: 861
[UNILOG][INFO] Begin to compile...
[UNILOG][INFO] Total device subgraph number 5, DPU subgraph number 1
[UNILOG][INFO] Compile done.
[UNILOG][INFO] The meta json is saved to "/workspace/tf_yolov4_coco_416_416_60.3G_2.5/./compiled/meta.json"
[UNILOG][INFO] The compiled xmodel is saved to "/workspace/tf_yolov4_coco_416_416_60.3G_2.5/./compiled/yolov4_leaky_416_tf.xmodel"
[UNILOG][INFO] The compiled xmodel's md5sum is 56787860a835fa3ae22b248293156a3a, and has been saved to "/workspace/tf_yolov4_coco_416_416_60.3G_2.5/./compiled/md5sum.txt"
```
`DPU subgraph number 1`となっておりDPUで実行されるサブグラフは1つなので理想的にグラフがDPUにオフロードされていることが確認できました。
`compiled/yolov4_leaky_416_tf.xmodel`がコンパイルされたモデルです。netronでモデルの構成を見ることができます。
![Screenshot from 2022-09-21 02-51-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/ada5d343-f0dc-0031-adca-4b5cfc835e19.png)


## prototxtの用意

Vitis-AI-Libraryを使用するにあたって、prototxtを用意しておく必要があります。
prototxtにはモデルの種類や前後処理のパラメータを記述します。前後処理の実装はVitis-AI-Libraryに
含まれています。yolov3/yolov4向けの実装は[Vitis-AI-Library/yolov3/src](https://github.com/Xilinx/Vitis-AI/tree/v2.5/src/Vitis-AI-Library/yolov3/src)にあります。

ダウンロードした`yolov4_leaky_416_tf-zcu102_zcu104_kv260-r2.5.0.tar.gz`に含まれる`yolov4_leaky_416_tf/yolov4_leaky_416_tf.prototxt`を元に作成します。

```
cp ../yolov4_leaky_416_tf/yolov4_leaky_416_tf.prototxt ./compiled/yolov4_leaky_416_tf.prototxt
```

元のprototxtに含まれる
```
model {
}
```
の階層は不要なので削除します。
name属性は不要かもしれませんがVitis-AI v1.4などでは使用していたので記述しておきました。

```protobuf
name: "yolov4"
kernel {
    mean: 0.0
    mean: 0.0
    mean: 0.0
    scale: 0.00390625
    scale: 0.00390625
    scale: 0.00390625
}
model_type : YOLOv3
yolo_v3_param {
  num_classes: 80
  anchorCnt: 3
  layer_name: "109"
  layer_name: "101"
  layer_name: "93"
  conf_threshold: 0.3
  nms_threshold: 0.6
  biases: 10
  biases: 13
  biases: 16
  biases: 30
  biases: 33
  biases: 23
  biases: 30
  biases: 61
  biases: 62
  biases: 45
  biases: 59
  biases: 119
  biases: 116
  biases: 90
  biases: 156
  biases: 198
  biases: 373
  biases: 326
  test_mAP: false
}
```

prototxtとxmodelの入った`compiled`ディレクトリをKV260に転送します。
```sh
scp -r compiled petalinux@192.168.xxx.xxx:~/
```



# アプリケーションの作成
画像を読み込んで物体検出をさせたいところですが、残念ながらmodelzooにはアプリケーションは含まれていません。
[examples/Vitis-AI-Library/samples/yolov3](https://github.com/Xilinx/Vitis-AI/tree/v2.5/examples/Vitis-AI-Library/samples/yolov3)に評価用アプリケーションが含まれているので、それを元に検出結果を描画するアプリケーションを作成しました。: https://github.com/lp6m/vitis_ai_dpu_yolo

手抜きなのでラベルなどは描画していません。

# ビルド用ツールのインストール
今回はクロスコンパイルは行わずKV260上でコンパイルするのでビルドツールをインストールします。
petalinuxビルド時に含めておけばよかったかもしれません。

```
sudo dnf install -y cmake g++ g++-symlinks gcc-symlinks binutils pkgconfig git
```

# アプリケーションのビルド・実行
[VitisでのDPUプロジェクト作成後のloadapp動作確認](https://qiita.com/lp6m/items/df1b87b11f8275ee6210#dpu%E3%82%92%E5%90%AB%E3%82%93%E3%81%A0%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%81%AE%E3%83%AD%E3%83%BC%E3%83%89%E7%A2%BA%E8%AA%8D)が済んでいることを前提としています。
```
sudo xmutil unloadapp
sudo xmutil loadapp dpuprj
```

アプリケーションを動作させるには`/run/media/mmcblk0p1/`以下にも`dpu.xclbin`が存在している必要があるのでコピーします。
```
sudo mkdir -p /run/media/mmcblk0p1/
sudo cp dpu.xclbin /run/media/mmcblk0p1/
```

```
git clone https://github.com/lp6m/vitis_ai_dpu_yolo
cd vitis_ai_dpu_yolo
sh build.sh
./demo_yolov4 ~/compiled/yolov4_leaky_416_tf.prototxt ~/compiled/yolov4_leaky_416_tf.xmodel dog.jpg image
```


実行結果
```sh
xilinx-kv260-starterkit-20221:~/vitis_ai_dpu_yolo
$ ./demo_yolov4 ~/compiled/yolov4_leaky_416_tf.prototxt ~/compiled/yolov4_leaky_416_tf.xmodel dog.jpg image
/home/petalinux/compiled/yolov4_leaky_416_tf.prototxt /home/petalinux/compiled/yolov4_leaky_416_tf.xmodel dog.jpgModel Initialize Done
bicycle 0.951898 132.472 550.138 140.873 414.652
car 0.763319 458.46 700.775 71.8596 174.588
truck 0.558414 472.915 687.069 72.4816 169.532
dog 0.988311 133.963 300.746 210 548.74
```

result.jpgに結果が保存されます。イイ感じ！

![result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243354/7aecccf4-bf20-6b0b-ebe2-25c967661965.jpeg)

# おわりに
というわけでKV260のデザインフローに入門してみました。Ultra96v2のときとはデザインフローが異なりかなり戸惑いましたが、なんとかDPUを動かすところまでこぎ着けました。次回はUltra96V2で動かしたことのあるVexRiscvをKV260で動作させてみたいと思います。