アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

Vitis-AIを使ってultra96v2上で学習済みモデルを動かすまで【hardware, FPGA, AI】

今回はultra96v2上で学習済みモデルを動かしてみる。

この作業がすんなりできれば、論理回路の高位合成とか組み込み部分を除けば、学習済みモデルを作れればドローン制御、自動運転の制御とかいろんなことの礎になる。

本家サイト「Ultra96V2向けVitis AI(2019.2)の組み立て方」通りに進めたけど情報が古かったのでかなり苦労した。


f:id:trafalbad:20200429092854p:plain


目次
1.動作環境「vitis AI」の構築
2.Deep Learning Processor Unit (DPU) IP の作成
3.学習済みデータと画像の用意
4.pbファイルの量子化(quantization)
5.量子化したファイルからFPGA用アプリケーションの作成
6.Ultra96v2ボードでの動作確認




1.動作環境「vitis AI」の構築

dockerのインストール



dockerが必要なのでdockerをインストール。

$ sudo apt-get update && sudo apt-get upgrade
$ sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
>>>
OK

$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Uninstall Old Versions of Docker & Install Docker
$ sudo apt-get remove docker docker-engine docker.io && sudo apt install docker.io
# Start and Automate Docker
$ sudo systemctl start docker && sudo systemctl enable docker
# コンテナ確認
$ sudo docker ps
>>>
CONTAINER ID        IMAGE               COMMAND   CREATED             STATUS              PORTS     NAMES


sudo なしで実行できるようにする。

# dockerグループ作成 & 現行ユーザをdockerグループに所属させる
$ sudo groupadd docker && sudo gpasswd -a $USER docker
# dockerデーモンを再起動する (CentOS7の場合)
$ sudo systemctl restart docker
# exitして再ログインすると反映される。
$ sudo reboot
# コンテナ確認
$ docker ps
>>>
CONTAINER ID        IMAGE               COMMAND   CREATED             STATUS              PORTS     NAMES

参考:Ubuntu 18.04にDockerをインストールする(+docker-composeも)


vitis-Aiのインストール


「tesnsorflow_q_val」コマンドがvirtualbocxだとCPUの関係で使えないので、EC2インスタンスで作業した。

次にvitis AIのインストール。
XilinxのVitis-AIがupgradeされてた。ブランチのv1.1をgit clone。

$ git clone -b v1.1 https://github.com/Xilinx/Vitis-AI
# Dockerの設定
$ cd Vitis-AI/docker
$ ./docker_build_cpu.sh
$ cd Vitis-AI
$ ./docker_run.sh xilinx/vitis-ai-cpu:latest


今回はCPU環境で試してみて、動いた!
f:id:trafalbad:20200429092932p:plain

# 抜ける
$ exit
# GPUの時はこちら
$ sudo ./docker_build_gpu.sh
$ sudo ./docker_run.sh xilinx/vitis-ai-gpu:latest


BitStreamのためにメモリの増設



VirtualBoxで作業してるので、DPUは死ぬほどメモリを食うので


プロセッサーでCPUを6枚

・メインメモリを11800くらいに変更

して計算リソースを増やした。

f:id:trafalbad:20200808225619p:plain

f:id:trafalbad:20200808225623p:plain


計算リソースが足りないとBitStream時に「強制終了エラー」が出る。




2.Deep Learning Processor Unit (DPU) IP の作成

DPU IPは「DPU for Convolutional Neural Network v1.1」によるとFPGAでCNNとかを動かすリソース(レジスタ設定、データ コントローラー、たたみ込み演算の各モジュールとか)が組み込まれてる。

DPU IPはvivadoで作成しなきゃならないけど、ここは本家サイトからresnet50用のDPUをdownloadした。


DPU(AI)IP作成パート
f:id:trafalbad:20200429092954p:plain

download後にultra96v2ボードにetcherでコピー。


コピー後のボード内ファイル一覧

$ ls ultra96_oob
BOOT.BIN        image.ub        system.dtb
README.txt        init.sh            ultra96v2_oob.hwh
dpu.xclbin        platform_desc.tx

これはSDCARDのboot領域(/media/user/${SDCARD}/boot)にコピーされる



3.学習済みネットワークと画像の用意

学習済みネットワークと画像の用意のパート
f:id:trafalbad:20200429093029p:plain



1.学習済みネットワークを用意



まず、作業環境のDocker起動(これ以降はほとんどdocker上で動かす)

$ cd Vitis-AI
$ sudo ./docker_run.sh xilinx/vitis-ai-cpu:latest
# 作業ディレクトリ作成
$ mkdir workspace
$ cd workspace


“vitis-ai-tensorflow”(または”vitis-ai-caffe”)をcondaで実行。

$ conda activate vitis-ai-caffe
# tensorflowの場合
$ conda activate vitis-ai-tensorflow


今回はtensorflowのunetを自分で学習して、kerasの重みファイル(hdf5, ckptファイル)からultra96v2で動くアプリケーションを作る。



2.評価用画像の用意





評価用として学習用に使った画像をprepare_dataset.pyで100枚用意した。

量子化に使う「graph_input_fn.py」を少し書き換えた。

graph_input_fn.py

calib_batch_size = 10

def calib_input(iter):
  images = []
  line = open(calib_image_list).readlines()
  #print(line)
  for index in range(0, calib_batch_size):
      curline = line[iter*calib_batch_size + index]
      #print("iter= ", iter, "index= ", index, "sum= ", int(iter*calib_batch_size + index), "curline= ", curline)
      calib_image_name = curline.strip()

      image_path = os.path.join(calib_image_dir, calib_image_name)
      image2 = NormalizeImageArr(image_path)

      #image2 = image2.reshape((image2.shape[0], image2.shape[1], 3))
      images.append(image2)

  return {"input_1": images}


def main():
  calib_input()

if __name__ == "__main__":
    main()


4.pbファイルの量子化(quantization)


kerasの訓練済の重みファイルを変換して、freezeしたpbファイル(frozen_graph.pb)を量子化する。

quantize.shで量子化

#!/bin/sh
FREEZE_DIR=freeze_tfpb
FROZEN_GRAPH_FILENAME=frozen_graph.pb
QUANT_DIR=quantized_model
INPUT_NODE="input_1"
Q_OUTPUT_NODE="conv2d_23/Sigmoid" # output node of quantized CNN

vai_q_tensorflow quantize \
	 --input_frozen_graph  ${FREEZE_DIR}/${FROZEN_GRAPH_FILENAME} \
	 --input_nodes         ${INPUT_NODE} \
	 --input_shapes        ?,224,224,3 \
	 --output_nodes        ${Q_OUTPUT_NODE} \
	 --output_dir          ${QUANT_DIR}/ \
	 --method              1 \
	 --input_fn            graph_input_fn.calib_input \
	 --calib_iter          10 \
	 --gpu 0




4.量子化データから、FPGA用アプリケーションの作成

アプリケーション作成のパートf:id:trafalbad:20200429093057p:plain


1.dcfファイルの作成



次に、etcherでイメージをSDカードにコピーしたときできた、”ultra96v2_oob.hwh”のhwhファイル(ハードウェア情報ファイル)を使って、dcfファイルを作る。

$ dlet -f ultra96v2_oob.hwh
>>>
[DLet]Generate DPU DCF file dpu-11-18-2019-18-45.dcf successfully.
# rename
$ mv dpu-11-18-2019-18-45.dcf resnet50.dcf

dletコマンドは“vitis-ai-caffe”(“vitis-ai-tensorflow”)内でのみ使える。
dcfファイルは後で使う。




2.compileを実行



compile.sh

#! /bin/sh
CNN=unet
COMPILE_DIR=output_compile
QUANT_DIR=quantized_model
TARGET=custom
ARCH=${TARGET}.json
vai_c_tensorflow \
 	 --frozen_pb ${QUANT_DIR}/deploy_model.pb \
 	 --arch ${ARCH} \
 	 --output_dir ${COMPILE_DIR}/${CNN}2 \
	 --options    "{'mode':'normal'}" \
	 --net_name ${CNN}2

dcfを入れたcustom.jsonの中身。

$cat custom.json
>>>
{"target": "dpuv2", "dcf": "dpu-11-18-2019-18-45.dcf", "cpu_arch": "arm64"}


compileを実行。

$ ./compile.sh
>>>>

**************************************************
* VITIS_AI Compilation - Xilinx Inc.
**************************************************
[VAI_C][Warning] layer [conv2d_23_Sigmoid] (type: Sigmoid) is not supported in DPU, deploy it in CPU instead.

Kernel topology "unet2_kernel_graph.jpg" for network "unet2"
kernel list info for network "unet2"
                               Kernel ID : Name
                                       0 : unet2_0
                                       1 : unet2_1

                             Kernel Name : unet2_0
--------------------------------------------------------------------------------
                             Kernel Type : DPUKernel
                               Code Size : 1.16MB
                              Param Size : 29.63MB
                           Workload MACs : 83685.54MOPS
                         IO Memory Space : 17.04MB
                              Mean Value : 0, 0, 0, 
                      Total Tensor Count : 40
                Boundary Input Tensor(s)   (H*W*C)
                            input_1:0(0) : 224*224*3

               Boundary Output Tensor(s)   (H*W*C)
              conv2d_23_convolution:0(0) : 224*224*11

                        Total Node Count : 35
                           Input Node(s)   (H*W*C)
                 conv2d_1_convolution(0) : 224*224*3

                          Output Node(s)   (H*W*C)
                conv2d_23_convolution(0) : 224*224*11

                             Kernel Name : unet2_1
--------------------------------------------------------------------------------
                             Kernel Type : CPUKernel
                Boundary Input Tensor(s)   (H*W*C)
                  conv2d_23_Sigmoid:0(0) : 224*224*11

               Boundary Output Tensor(s)   (H*W*C)
                  conv2d_23_Sigmoid:0(0) : 224*224*11

                           Input Node(s)   (H*W*C)
                       conv2d_23_Sigmoid : 224*224*11

                          Output Node(s)   (H*W*C)
                       conv2d_23_Sigmoid : 224*224*11

終わったあとのファルダ構造。

$ tree 
>>>
compile
├── compile.sh
├── custom.json
├── dpu-11-18-2019-18-45.dcf
├── output_compile
│   └── unet2
│       └── unet2_kernel_graph.gv
|        └── dpu_unet2_0.elf
├── quantized_model
│   ├── deploy_model.pb
│   └── quantize_eval_model.pb
└── ultra96v2_oob.hwh


3.アプリケーションの作成(sigmoidを使う


まずdocker。Xilinx提供のVitis AI runtimeを実行。

$ ./docker_run.sh xilinx/vitis-ai:runtime-1.0.0-cpu

今回unetにsigmoidを使った。

sigmoidはDPUで対応してないため、DPUKernelとCPUKernelが分割されるので、dpu_unet2_0.elf(unet2_0)が作成される。


なのでアプリケーション作成時に使う
「src/fpc_main.cc」のコードで

#define KERNEL_CONV   "unet2"

から

#define KERNEL_CONV       "unet2_0"

に置き換えないとultra96v2上で動かない。








5.量子化したファイルからFPGA用アプリケーションの作成


1.開発環境構築・ライブラリをSDカードへコピー



Vitis AIをUltra96上で動かすには、xilinxのライブラリーが必要なので、runtime パッケージをSDカード(rootのsdcardフォルダ)にコピー

# パッケージのinstall(開発環境構築)
$ sudo cp -r /opt/vitis_ai/xilinx_vai_board_package Vitis-AI/workspace/
$ cd Vitis-AI/workspace/xilinx_vai_board_package/
$ sudo ./install.sh

# パッケージをSDカードにコピー
$ sudo cp -r /opt/vitis_ai/xilinx_vai_board_package /media/user/${SDCARD}/root/home/root/


2.FPGA用アプリケーションの作成




さっき作った「dpu_unet2_0.elf」をのmodelファルダの中に移動。
「$make」コマンドでアプリケーション作成実行。

コードはXilinxsegmentationチュートリアルのコードを少し改造して使った。

# アプリケーションの作成実行
$ sudo make 


アプリケーション完成後のディレクトリ構造

$ tree 
>>>
├── common
│   ├── dputils.cpp
│   ├── dputils.h
│   └── dputils.py
└── unet2
    ├── Makefile
    ├── build
    │   ├── dputils.o
    │   └── fps_main.o
    ├── model
    │   ├── dpu_unet2_0.elf
    │   └── libdpumodelunet2.so
    ├── src
    │   └── fps_main.cc
    └── unet2

src/fps_main.ccの一部を変更。

// constants for segmentation network
#define KERNEL_CONV       "unet2_0"
#define CONV_INPUT_NODE   "conv2d_1_convolution"
#define CONV_OUTPUT_NODE  "conv2d_23_convolution"


アプリケーションとして「unet2」と「libdpumodelunet2.so」ができるので、それをSDカード(rootのsdcardフォルダ)にコピー。

$ cp resnet50 /media/user/${SDCARD}/root/home/root/



6.Ultra96v2ボードでの動作確認

いつもみたいにultra96v2の実機を起動後に、gtktermで接続してログイン

$ gtkterm -p /dev/ttyUSB1 -s 115200
# ライブラリのinstall
$ cd xilinx_vai_board_package
$ ./install.sh
>>>
Begin to install Xilinx DNNDK ...
Requirement already satisfied: Edge-Vitis-AI==1.0 from file:///sdcard/pkgs/python/Edge_Vitis_AI-1.0-py2.py3-none-any.whl in /usr/lib/python3.5/site-packages (1.0)
Complete installation successfully.

判別用画像をroot/home/rootの「worksapace/dataset1/」の中に入れておく。

$ cd unet2/
$ sudo chmod 755 *
$ ./unet2

f:id:trafalbad:20200902200235p:plain


中身は適当なので出力結果は気にしないけど、とりあえず動いた。





ここでこの部分の作業が終わって、AIを動かす過程が一通り終了。
f:id:trafalbad:20200429093241p:plain




ここまででようやくultra96v2上で学習済みモデルを動かせた。

f:id:trafalbad:20200429094131j:plain


参考サイト



Ultra96V2向けVitis AI(2019.2)の組み立て方

Ubuntu 18.04にDockerをインストールする(+docker-composeも)

Vitis AI User Guide

ザイリンクス社「Vitis AI開発環境」を評価キット ZCU102 で動かしてみた

GitHub - Xilinx/Vitis-AI at v1.1

Vitis-AI 1.1 Flow for Avnet VITIS Platforms - Part 1