2022年3月6日日曜日

meta-riscvでTensorFlow Lite(Yocto)

目的


meta-tensorflowliteをv2.8対応+公式サンプルとベンチマークツールのレシピを追加した。
これを使ってmeta-riscvでbitbakeする。qemuriscv64でTensorFlow Liteのサンプル、ベンチマークツールを動作させてみる。


meta-tensorflowliteの対応


meta-tensorflowliteのリポジトリは以下。

今回、TensorFlow Lite v2.8の対応を含めてレシピの整理・追加を行った。
  • ブランチをYoctoバージョンと合わせて管理。
    • 今後はTensorFlow Liteのバージョンもブランチで固定。
  • Python3 interpreterのレシピのv2.8対応。
    • RISC-Vのビルドは以前から対応済み。
  • C++ API shared library(libtensorflowflow-lite)のレシピのv2.8対応。
    • レシピ名をcpp-tensorflow-liteからlibtensorflow-liteに変更。
    • インストール時、ヘッダファイルの不足があったので修正。
  • TensorFlowのリポジトリにあるPython、C++のサンプルをレシピ化&簡単なドキュメントを整理。
    (リンクはmeta-tensorflow-liteのドキュメントのリンク)
  • 同じように、ベンチマークツール(benchmark_model)もレシピ化。
  • GitHub Actionsを導入して、コードの変更&週一でのビルドのCIを導入。


リポジトリのClone


さて、ここからは実際にビルドしてみる。まずは必要なリポジトリをclone。
  • Yoctoのバージョンはhonisterとする。
  • meta-tensorflow-liteはcommit idを指定(このあとkirkstoneに移行予定)。
$ git clone -b 1.52 https://github.com/openembedded/bitbake.git
$ git clone -b honister https://github.com/openembedded/openembedded-core.git
$ git clone -b honister https://github.com/openembedded/meta-openembedded.git
$ git clone -b honister https://github.com/riscv/meta-riscv.git
$ git clone https://github.com/NobuoTsukamoto/meta-tensorflow-lite.git
$ cd meta-tensorflow-lite
$ git checkout eae1cb93a12fcb1ade71197b99111c2f02f515ef
$ cd ..


ビルド環境の設定


ここではmeta-riscvのsetup.shを実行する。
$ . ./meta-riscv/setup.sh

その後、meta-tensorflow-liteを追加。
$ bitbake-layers add-layer ../meta-tensorflow-lite/

conf/auto.confを編集。2行追加する。
  • USER_CLASSES:remove = "image-prelink" を指定しないとbitbakeでエラーとなる。
    これは、setup.shで追加されちゃうので...
  • IMAGE_INSTALL:appendでtensorflow-liteのレシピを追加。
USER_CLASSES:remove = "image-prelink"
IMAGE_INSTALL:append = " python3-tensorflow-lite \
 libtensorflow-lite \
 python3-tensorflow-lite-example \
 tensorflow-lite-label-image \
 tensorflow-lite-minimal \
 tensorflow-lite-benchmark"

bitbake


bitbake!!!!!!
$ MACHINE=qemuriscv64 bitbake core-image-full-cmdline

このときの結果はこんな感じ。
$ MACHINE=qemuriscv64 bitbake core-image-full-cmdline
Loading cache: 100% |                                                                                                                                                                                                        | ETA:  --:--:--
Loaded 0 entries from dependency cache.
Parsing recipes: 100% |#######################################################################################################################################################################################################| Time: 0:00:36
Parsing of 2428 .bb files complete (0 cached, 2428 parsed). 3767 targets, 165 skipped, 0 masked, 0 errors.
NOTE: Resolving any missing task queue dependencies

Build Configuration:
BB_VERSION           = "1.52.0"
BUILD_SYS            = "x86_64-linux"
NATIVELSBSTRING      = "fedora-35"
TARGET_SYS           = "riscv64-oe-linux"
MACHINE              = "qemuriscv64"
DISTRO               = "nodistro"
DISTRO_VERSION       = "nodistro.0"
TUNE_FEATURES        = "riscv64"
meta                 = "honister:eeae63c343c8ebd418679915ee20aa8c02fa0fdc"
meta-oe              
meta-python          
meta-multimedia      
meta-networking      = "honister:0fb490a08ce30b47a5ccd2fdc3448b08e0d9e4e9"
meta-riscv           = "honister:9561639c61663a10d8c9c23d26173db499f4c39b"
meta-tensorflow-lite = "main:eae1cb93a12fcb1ade71197b99111c2f02f515ef"

NOTE: Fetching uninative binary shim http://downloads.yoctoproject.org/releases/uninative/3.5/x86_64-nativesdk-libc-3.5.tar.xz;sha256sum=e8047a5748e6f266165da141eb6d08b23674f30e477b0e5505b6403d50fbc4b2 (will check PREMIRRORS first)
Initialising tasks: 100% |####################################################################################################################################################################################################| Time: 0:00:05
Sstate summary: Wanted 1403 Local 0 Network 0 Missed 1403 Current 0 (0% match, 0% complete)
NOTE: Executing Tasks
WARNING: mtools-native-4.0.35-r0 do_fetch: Failed to fetch URL https://ftp.gnu.org/gnu/mtools/mtools-4.0.35.tar.bz2, attempting MIRRORS if available
NOTE: Tasks Summary: Attempted 4090 tasks of which 0 didn't need to be rerun and all succeeded.
NOTE: Writing buildhistory
NOTE: Writing buildhistory took: 79 seconds
NOTE: Build completion summary:
NOTE:   do_populate_sysroot: 0.0% sstate reuse(0 setscene, 267 scratch)
NOTE:   do_package_qa: 0.0% sstate reuse(0 setscene, 192 scratch)
NOTE:   do_package: 0.0% sstate reuse(0 setscene, 192 scratch)
NOTE:   do_packagedata: 0.0% sstate reuse(0 setscene, 192 scratch)
NOTE:   do_package_write_ipk: 0.0% sstate reuse(0 setscene, 192 scratch)
NOTE:   do_populate_lic: 0.0% sstate reuse(0 setscene, 360 scratch)

Summary: There was 1 WARNING message shown.


QEMUの実行


qemuを実行。
$ MACHINE=qemuriscv64 runqemu nographic

qemuriscv64が動いた!
OpenSBI v0.9
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 4
Firmware Base             : 0x80000000
Firmware Size             : 124 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*,1*,2*,3*
Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
[    0.000000] Linux version 5.14.21-yocto-standard (oe-user@oe-host) (riscv64-oe-linux-gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37.20210721) #1 SMP PREEMPT Wed Mar 2 12:59:31 UTC 2022
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Machine model: riscv-virtio,qemu
[    0.000000] Memory limited to 256MB
[    0.000000] efi: UEFI not found.
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x000000008fffffff]
[    0.000000] SBI specification v0.2 detected
[    0.000000] SBI implementation ID=0x1 Version=0x9
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI v0.2 HSM extension detected
[    0.000000] riscv: ISA extensions acdfimsu
[    0.000000] riscv: ELF capabilities acdfim

rootでログイン。
OpenEmbedded nodistro.0 qemuriscv64 ttyS0

qemuriscv64 login: root
root@qemuriscv64:~# 


TensorFlow Liteのサンプル&ツールの実行


実際にビルドしたTensorFlow Liteのサンプル&ツールを実行する。


python3-tensorflow-lite-example


Pythonのサンプル(label_image.py)。
TensorFlow Liteで動作するようにimportするパッケージ名を修正している。
テスト用のモデル、画像も公式と同じもの(mobilenet_v1_1.0_224.tflite & grace_hopper.bmp)をインストールするようレシピ化している。
# cd /usr/share/tensorflow/lite/examples/python/
# python3 label_image.py \
   --image ./grace_hopper.bmp \
   --model_file ./mobilenet_v1_1.0_224.tflite \
   --label_file ./labels.txt

しばらくすると、結果が出力される。
0.919720: 653:military uniform
0.017762: 907:Windsor tie
0.007507: 668:mortarboard
0.005419: 466:bulletproof vest
0.003828: 458:bow tie, bow-tie, bowtie
time: 4925.118ms

Top1スコアは公式結果と同様の「military uniform」。
(スコアの値が違うのは...XNNPACKと何か違うんだっけ?)


tensorflow-lite-label-image


C++のサンプル(label_image)。
こちらはlibtensorflow-liteのレシピでビルドした共有ライブラリ(libtensorflow-lite.so)をリンクするように変更。
テスト用のモデル、画像も公式と同じものを用意。
# cd /usr/share/tensorflow/lite/examples/label_image/
# ./label_image \
   --tflite_model ./mobilenet_v1_1.0_224.tflite \
   --labels ./labels.txt \
   --image ./grace_hopper.bmp 

こちらもしばらくすると結果が表示される。
INFO: Loaded model ./mobilenet_v1_1.0_224.tflite
INFO: resolved reporter
INFO: invoked
INFO: average time: 2973.11 ms
INFO: 0.860174: 653 653:military uniform
INFO: 0.0481021: 907 907:Windsor tie
INFO: 0.00786705: 466 466:bulletproof vest
INFO: 0.00644936: 514 514:cornet, horn, trumpet, trump
INFO: 0.00608029: 543 543:drumstick

スコアは一応、公式と一致。


tensorflow-lite-minimal


こちらもC++のサンプル(minimal)。
tensorflow-lite-label-imageと同じ。
# cd /usr/share/tensorflow/lite/examples/minimal/
# ./minimal ./mobilenet_v1_1.0_224.tflite

結果の表示(出力が多いので一部割愛)。
=== Pre-invoke Interpreter State ===
Interpreter has 1 subgraphs.

-----------Subgraph-0 has 103 tensors and 31 nodes------------
1 Inputs: [87] -> 602112B (0.57MB)
1 Outputs: [86] -> 4004B (0.00MB)

Tensor  ID Name                      Type            AllocType          Size (Bytes/MB)    Shape      MemAddr-Offset  
Tensor   0 MobilenetV1/Conv2d_0/w... kTfLiteFloat32  kTfLiteMmapRo      3456     / 0.00 [32,3,3,3] [16842720, 16846176)
Tensor   1 MobilenetV1/Conv2d_10_... kTfLiteFloat32  kTfLiteMmapRo      18432    / 0.02 [1,3,3,512] [6423008, 6441440)
Tensor   2 MobilenetV1/Conv2d_10_... kTfLiteFloat32  kTfLiteMmapRo      1048576  / 1.00 [512,1,1,512] [10624920, 11673496)

...

Execution plan as the list of 31 nodes invoked in-order: [0-30]
--------------Subgraph-0 dump has completed--------------


tensorflow-lite-benchmark


ベンチマークツールのbenchmark_model。
このツールは必須ですね!!
libtensorflow-liteのレシピでビルドした共有ライブラリをリンクするようにしたかったけど、色々と無理があったので、単体でビルドするようにした。
# cd /usr/share/tensorflow/lite/tools/benchmark/
# ./benchmark_model \
   --graph=./mobilenet_v1_1.0_224.tflite \
   --num_threads=4 \
   --enable_op_profiling=true

こちらも結果は一部省略。
STARTING!
Log parameter values verbosely: [0]
Num threads: [4]
Graph: [./mobilenet_v1_1.0_224.tflite]
Enable op profiling: [1]
#threads used for CPU inference: [4]
Loaded model ./mobilenet_v1_1.0_224.tflite
The input model file size (MB): 16.9008
Initialized session in 83.064ms.
Running benchmark for at least 1 iterations and at least 0.5 seconds but terminate if exceeding 150 seconds.
count=1 curr=3470145

...

Number of nodes executed: 31
============================== Summary by node type ==============================
	             [Node type]	  [count]	  [avg ms]	    [avg %]	    [cdf %]	  [mem KB]	[times called]
	                 CONV_2D	       15	  2554.810	    89.801%	    89.801%	     0.000	       15
	       DEPTHWISE_CONV_2D	       13	   289.070	    10.161%	    99.962%	     0.000	       13
	         AVERAGE_POOL_2D	        1	     0.846	     0.030%	    99.991%	     0.000	        1
	                 SOFTMAX	        1	     0.226	     0.008%	    99.999%	     0.000	        1
	                 SQUEEZE	        1	     0.019	     0.001%	   100.000%	     0.000	        1

Timings (microseconds): count=50 first=2936299 curr=2869577 min=2657264 max=3069766 avg=2.84499e+06 std=82794
Memory (bytes): count=0
31 nodes observed

最後に


2022年4月にはYoctoの次のバージョン(kirkstone)がリリースされる。
ようやくmeta-tensorflow-liteのレシピの追加・整理も終わってkirkstoneへの準備ができる状態になった。

また、TensorFlow LiteはXNNPACKがRISC-Vアーキテクチャをサポートしたみたい。
次のv2.9のリリースでは取り込まれると思われる。
meta-tensorflow-liteもRISC-VでXNNPACKを有効にできるようにしたい。

RISC-V CPUでTensorFlow Liteが動くと楽しくなってくるね。