0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VIA VAB-5000上にTensorFlow Lite for C++の開発環境を構築するために、x86-64 Ubuntu22.04上でTensorFlow Lite for C/C++のソースコードをaarch64向けにクロスコンパイルする

Last updated at Posted at 2025-02-28

TensorFlow Lite for C/C++ on VAB-5000

前回の記事で産業用Raspberry Piに代わるAPU搭載のシングルボードコンピュータ「VAB-5000」について紹介しました。このVAB-5000の APU(AI Processing Unit)であるMDLA(MediaTek Deep Learning Accelerator) を最大限に活用できるAIアプリケーションを開発するためには、TensorFlow Lite for C/C++を利用して Delegate と呼ばれる、アクセラレーターを呼び出すプログラムを書く必要があります。今回はその前段階となる TensorFlow Lite for C/C++ (Delegate無し) をビルドしてサンプルソースコードをコンパイルする手順をご紹介します。

DSC01556.JPG

SoCベンダーのMediaTekが推奨するDelegateの記述例

C/C++で書かないとDelegateにアクセスできません...今回はそのための準備です!

本記事の目的

今回は以上の開発を行うべく、VIA VAB-5000環境上にTensorFlow Lite for C/C++の開発環境を構築する手順についてご紹介いたします。VAB-5000上でTensorFlow Lite for C++を直接ビルドすることはできないため、 本手順では、x86-64 Ubuntu22.04上でTensorFlow Lite for C/C++のソースコードをaarch64向けにクロスコンパイル します。

参考:x86-64向けのTensorFlow Lite for C/C++をビルドする手順

x86-64版のTensorFlow Lite for C/C++を利用したい方はこちらをご覧ください。

開発環境を準備をする

まず、 OSのインストールや開発に便利なツールなどのインストールを行い、 TensorFlow Lite for C/C++をインストールするための土台を組み上げていきます。

  • Core i5-14600K (14cores, 20threads)
  • RAM 64GB
  • On-Board Graphics (CPU)
  • Ubuntu 22.04 LTS
    • Yocto Linux等、併設する他の開発環境も考慮しました

Ubuntu 22.04 LTSをインストールする

まず、PCに「Ubuntu22.04 LTS」をインストールします。以下のインストーラーを利用し、インストールしました。今回はGPUを搭載せず、マザーボード上のオンボードのHDMIポートを利用しました。

ubuntu2204

TensorFlow for C/C++をビルドする準備をする

それでは TensorFlow Lite for C/C++ のビルドをはじめていきましょう。ビルドには bazelclangその他いくつかのパッケージ が必要になるため、事前にインストールします。

ビルドに必要なパッケージをインストールする

apt コマンドで以下のパッケージをインストールしましょう。

$ sudo apt update
$ sudo apt install -y vim 
$ sudo apt install -y linux-headers-$(uname -r) build-essential wget git cmake clang lldb lld
$ sudo apt install -y python3-dev python3-pip python3.10-venv
$ python3 -m pip install -U --user pip
$ sudo apt install -y libopencv-dev ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxml2 libstdc++6

ビルドツール「bazel」をインストールする

続いて、TensorFlow Liteをビルドするための bazel をインストールしましょう。

$ sudo apt install -y apt-transport-https curl gnupg
$ curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor >bazel-archive-keyring.gpg
$ sudo mv bazel-archive-keyring.gpg /usr/share/keyrings
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/bazel-archive-keyring.gpg] https://storage.googleapis.com/bazel-apt stable jdk1.8" | sudo tee /etc/apt/sources.list.d/bazel.list
$ sudo apt update && sudo apt install bazel

ビルドツール「clang」をインストールする

加えて、TensorFlow Liteをコンパイルするための clang をインストールします。

$ wget https://github.com/llvm/llvm-project/releases/download/llvmorg-17.0.2/clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04.tar.xz
$ tar -xvf clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04.tar.xz
$ sudo cp -r clang+llvm-17.0.2-x86_64-linux-gnu-ubuntu-22.04/* /usr
$ clang --version
# clang version 17.0.2 (https://github.com/llvm/llvm-project b2417f51dbbd7435eb3aaf203de24de6754da50e)
# Target: x86_64-unknown-linux-gnu
# Thread model: posix
# InstalledDir: /usr/bin

以上で、クロスビルドするための準備は完了となります。

TensorFlow Lite for C/C++をクロスコンパイルする

それでは、TensorFlow Lite for C/C++をaarch64向けにビルドしてみましょう。

GithubからTensorFlowの【v2.16.1】を入手する。

まず、tensorflow のリポジトリから git コマンドを使ってソースコード一式を入手し、git checkout コマンドで v2.16.1 のソースコードへと切り替えます。 私が試したところ、他のバージョンを利用するとエラーが発生してビルドの通らないことが頻発したため、下記の記事で実績のある v2.16.1 を選択しました。

### 作業用ディレクトリを作成する
$ mkdir cross_tensorflow
$ cd cross_tensorflow/

### tensorflowのソースコードを取得する
$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow

### 今回はv2.16.1をビルドする
$ git checkout v2.16.1

TensorFlow Lite for C/C++をビルドする

ビルドする前に ./configure を実行してビルドの構成を決定 します。今回はGPUアクセラレーションを利用しない、以下の構成としました。 ./configure を行った後、 bazel build コマンドでTensorFlow Lite for C/C++をビルド します。なお、本手順には bazel-6.5.0 が必要だったため、apt コマンドでバージョンを変更しました。

### ビルドの設定を行う
$ ./configure
# You have bazel 6.5.0 installed.
# Please specify the location of python. [Default is /home/shino/anaconda3/bin/python3]: 
# Found possible Python library paths:
#   /home/shino/anaconda3/lib/python3.12/site-packages
# Please input the desired Python library path to use.  Default is [/home/shino/anaconda3/lib/python3.12/site-packages]
# Do you wish to build TensorFlow with ROCm support? [y/N]: N
# No ROCm support will be enabled for TensorFlow.
# Do you wish to build TensorFlow with CUDA support? [y/N]: N
# No CUDA support will be enabled for TensorFlow.
# Do you want to use Clang to build TensorFlow? [Y/n]: Y
# Clang will be used to compile TensorFlow.
# Please specify the path to clang executable. [Default is /usr/bin/clang]: 
# You have Clang 17.0.2 installed.
# Please specify optimization flags to use during compilation when bazel option "--config=opt" is specified [Default is -Wno-sign-compare]: 
# Would you like to interactively configure ./WORKSPACE for Android builds? [y/N]: N
# Not configuring the WORKSPACE for Android builds.
# 
# Preconfigured Bazel build configs. You can use any of the below by adding "--config=<>" to your build command. See .bazelrc for more details.
# 	--config=mkl         	# Build with MKL support.
# 	--config=mkl_aarch64 	# Build with oneDNN and Compute Library for the Arm Architecture (ACL).
# 	--config=monolithic  	# Config for mostly static monolithic build.
# 	--config=numa        	# Build with NUMA support.
# 	--config=dynamic_kernels	# (Experimental) Build kernels into separate shared objects.
# 	--config=v1          	# Build with TensorFlow 1 API instead of TF 2 API.
# Preconfigured Bazel build configs to DISABLE default on features:
# 	--config=nogcp       	# Disable GCP support.
# 	--config=nonccl      	# Disable NVIDIA NCCL support.
# Configuration finished

### ビルドができるか試してみる
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite/c:libtensorflowlite_c.so
# ERROR: The project you're trying to build requires Bazel 6.5.0 (specified in /home/shino/tensorflow/.bazelversion), but it wasn't found in /usr/bin.
# 
# You can install the required Bazel version via apt:
#   sudo apt update && sudo apt install bazel-6.5.0
# ...

### bazelのバージョンを指定してインストールしなおす
$ sudo apt update && sudo apt install bazel-6.5.0

### 再度ビルドする
# C++ライブラリをクロスビルドする
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite/c:libtensorflowlite_c.so

# Cライブラリをクロスビルドする
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite:libtensorflowlite.so

# Delegateを呼び出す際に利用するライブラリもクロスビルドする
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite/experimental/acceleration/compatibility:android_info
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite/delegates/utils/experimental/stable_delegate:tflite_settings_json_parser
$ bazel build --config=elinux_aarch64 -c opt //tensorflow/lite/delegates/utils/experimental/stable_delegate:delegate_loader

ビルド成果物を確認する

以下がビルドの成果物となります。 正しく aarch64 向けにビルドされているかfile コマンドを使って確認しておきましょう。

### C++ライブラリがArm用にビルドされていることを確認する
$ file bazel-bin/tensorflow/lite/libtensorflowlite.so
# bazel-bin/tensorflow/lite/libtensorflowlite.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[md5/uuid]=6dc411bc5ff781a5d77bec37cf4d46b3, stripped

### CライブラリがArm用にビルドされていることを確認する
$ file bazel-bin/tensorflow/lite/c/libtensorflowlite_c.so
# bazel-bin/tensorflow/lite/c/libtensorflowlite_c.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[md5/uuid]=1ffb549a913b1a1d161a8ab9ed3cc11f, stripped

ビルド成果物をtensorflowのディレクトリ内にコピーする

本手順によるビルドでは、ビルドの成果物は tensorflow のディレクトリ内ではなく、~/.cache/bazel 以下に格納されます。ディレクトリ名はランダムで決まりますので、ビルド後の tensorflow ディレクトリで ls -al bazel-tensorflow をすると確認することができます。確認後、対象のディレクトリを tensorflow ディレクトリ以下へコピーし、シンボリックリンクを貼り直し tensorflow ディレクトリのみで完結するフォルダ構成にします。

### ビルド成果物を tensorflow ディレクトリ内にコピーする
### -->> ソースコードとまとめてVAB-5000にコピーするため
$ cp ~/.cache/bazel/_bazel_shino/6bb7e8b4bffe091da9c41eb8a1440c54/execroot/org_tensorflow ~/cross_tensorflow/tensorflow/org_tensorflow -r

### bazel-bin ディレクトリのシンボリックリンクを貼り直す
$ rm bazel-bin
$ ln -s org_tensorflow/bazel-out/aarch64-opt/bin/ ./bazel-bin
$ ls bazel-bin
# bin   external  tensorflow

### bazel-out ディレクトリのシンボリックリンクを貼り直す
$ rm bazel-out
$ ln -s org_tensorflow/bazel-out ./bazel-out
$ ls bazel-out
# _actions  aarch64-opt           stable-status.txt
# _tmp      k8-opt-exec-50AE0418  volatile-status.txt

### bazel-tensorflow ディレクトリのシンボリックリンクを貼り直す
$ rm bazel-tensorflow
$ ln -s org_tensorflow ./bazel-tensorflow
$ ls bazel-tensorflow
# bazel-out  external  tensorflow  third_party

### bazel-testlogsディレクトリのシンボリックリンクを貼り直す
$ rm bazel-testlogs
$ ln -s org_tensorflow/bazel-out/aarch64-opt/testlogs ./bazel-testlogs
$ ls bazel-testlogs
# empty

ビルド成果物をtarに固めてUSBフラッシュメモリにコピーする

以上でaarch64(VAB-5000のアーキテクチャ)向けのTensorFlow Lite for C/C++の準備が整いましたので tensorflow ディレクトリを tar でまとめて、USBフラッシュメモリへ書き出しましょう。

### クロスコンパイルした成果物をtarに固めて、USBフラッシュへコピーする
$ tar -cvf tensorflow.tar ./tensorflow
$ ls ./tensorflow.tar
# tensorflow.tar
### USBフラッシュが "/media/shino/23D8-AF6D/" の場合の例
$ cp tensorflow.tar /media/shino/23D8-AF6D/
### 書き込み後umoundする
$ sudo umount /media/shino/23D8-AF6D

VAB-5000上にTensorFlowを利用したアプリ開発によく利用されるパッケージをインストールする (VAB-5000上での操作)

VAB-5000上にTensorFlow Lite for C++をインストールする前に、TensorFlowを使って推論するプログラムによく利用されるパッケージを apt でインストールします。 特に、最後の libabsl-dev はTensorFlow Lite for C/C++でDelegateを利用する際に必要とされるパッケージです。

### 一般的な開発ツールをインストールする
$ sudo apt update
$ sudo apt install -y vim 
$ sudo apt install -y build-essential wget git curl cmake clang lldb lld
$ sudo apt install -y python3-dev python3-pip python3-venv
$ sudo apt install -y libopencv-dev ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxml2 libstdc++6
$ sudo apt install -y libabsl-dev
$ sudo find / -type f | grep cleanup.h
# /usr/include/absl/cleanup/cleanup.h

USBフラッシュメモリからクロスコンパイルしたTensorFlow Liteをホームディレクトリへコピーする (VAB-5000上での操作)

クロスコンパイルの結果得られたTensorFlow Lite for C/C++をVAB-5000のホームディレクトリ(/home/debian)以下にUSBフラッシュメモリからコピーし tar で展開します。

### クロスコンパイルしたTensorFlowをVAB-5000上に展開する
$ cp /media/shino/23D8-AF6D/tensorflow.tar ~/
$ tar -xvf tensorflow.tar

TensorFlow Lite for C++が利用するflatbuffersをソースコードからコンパイルする (VAB-5000上での操作)

TensorFlow Lite for C/C++でTFLite形式のモデルを読み込む際に flatbuffers というライブラリが必要となります。git コマンドにより、FlatBuffersのソースコードを入手し、 git checkout コマンドでTensorFlowの要求するFlatBuffersのバージョンにソースコードを切り替えます。その後 cmake を利用してFlatBuffersをビルドします。

### FlatBuffers 23.5.26を入手する
$ cd ~
$ git clone https://github.com/google/flatbuffers.git
$ cd flatbuffers
$ git checkout v23.5.26

### FlatBuffersをビルドする
$ mkdir build
$ cd build/
$ cmake ..
$ make

TensorFlow Lite for C/C++を利用する (VAB-5000上での操作)

それでは実際に、ビルドしたTensorFlow Lite for C/C++を利用してみましょう。TensorFlow Lite for C/C++を呼び出すためのコードは、以下のサイトで非常にわかりやすく書かれていました。とても参考になりました、ありがとうございます。

以上のサイトを参考に収集したソースコード

以下のリポジトリが、以降の手順をまとめたソースコードとなります。 CMakeLists.txtに含まれているディレクトリのパスは、実行環境に合わせて調整してください。

ビルドしたTensorFlow Lite for C++を参照するCMakeLists.txt (VAB-5000上での操作)

まず CMakeLists.txt を記述します。 CMakeLists.txtcmake コマンドを実行した際に呼び出され、Makefile を自動的に生成するためのファイルです。 ファイルパスに上記でビルドしたTensorFlow Lite for C/C++のフォルダ位置を記述してください。 私の手順ではVAB-5000上の /home/debian/tensorflow にTensorFlow for C++を配置したため、以下のような記述となりました。

cmake_minimum_required(VERSION 2.8)
project(NumberDetector)

# Create Main project
add_executable(NumberDetector
	main.cpp
)

# For OpenCV
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
	target_include_directories(NumberDetector PUBLIC ${OpenCV_INCLUDE_DIRS})
	target_link_libraries(NumberDetector ${OpenCV_LIBS})
endif()

# For Tensorflow Lite
target_link_libraries(NumberDetector /home/debian/tensorflow/bazel-bin/tensorflow/lite/libtensorflowlite.so)
target_include_directories(NumberDetector PUBLIC /home/debian/tensorflow)
target_include_directories(NumberDetector PUBLIC /home/debian/tensorflow/tensorflow)
target_include_directories(NumberDetector PUBLIC /home/debian/tensorflow/tensorflow/lite)
target_include_directories(NumberDetector PUBLIC /home/debian/flatbuffers/include)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -std=c++17 -lstdc++")

# Copy resouce
file(COPY ${CMAKE_SOURCE_DIR}/resource/ DESTINATION ${PROJECT_BINARY_DIR}/resource/)
add_definitions(-DRESOURCE_DIR="${PROJECT_BINARY_DIR}/resource/")

TensorFlow for C++を呼び出すソースコード (VAB-5000上での操作)

TensorFlowを呼び出すソースコードは、以下の記事よりお借りしました。実行には、入力となる画像ファイル(ここでは 4.jpgtflite形式のAIモデル が必要になります。本ソースコードは、白地に黒で書かれた数字の画像が、0~9のうちいずれであるかを推論するプログラムです。実行には 画像ファイルとtflite形式のファイルが必要になります。

入力画像 4.jpg とtflite形式のモデル conv_mnist.tflite は以下に格納してあります。

#include <stdio.h>
#include <opencv2/opencv.hpp>
#include "tensorflow/lite/interpreter.h"
#include "tensorflow/lite/kernels/register.h"
#include "tensorflow/lite/model.h"
#include "tensorflow/lite/optional_debug_tools.h"

#define MODEL_FILENAME RESOURCE_DIR"conv_mnist.tflite"

#define TFLITE_MINIMAL_CHECK(x)                              \
    if (!(x)) {                                                \
        fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); \
        exit(1);                                                 \
    }

int main()
{
    /* 入力となる画像データを読み込む "4.jpg" */
    printf("input image path : %s\n", RESOURCE_DIR"4.jpg");
    cv::Mat image = cv::imread(RESOURCE_DIR"4.jpg");
    /* ディスプレイに出力する */
    cv::imshow("InputImage", image);
    /* 画面表示が閉じられるまで待機 */
    cv::waitKey(0);
    
    /* 入力画像をgrayscaleへと変換する */
    cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
    /* 28px x 28pxにリサイズする */
    cv::resize(image, image, cv::Size(28, 28));
    /* 背景が黒、文字色が白にする */
    image = ~image;
    /* ディスプレイに出力する */
    cv::imshow("InputImage for CNN", image);
    /* Normalize: 0.0 ~ 1.0 する */
    image.convertTo(image, CV_32FC1, 1.0 / 255);
    /* 画面表示が閉じられるまで待機 */
    cv::waitKey(0);

    /* tfliteモデルのパス */
    printf("model file name : %s\n", MODEL_FILENAME);
    /* tfliteのモデルをFlatBufferに読み込む */
    std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(MODEL_FILENAME);
    /* 開けたかチェック */
    TFLITE_MINIMAL_CHECK(model != nullptr);
    
	/* インタープリタを生成する */
	tflite::ops::builtin::BuiltinOpResolver resolver;
	tflite::InterpreterBuilder builder(*model, resolver);
	std::unique_ptr<tflite::Interpreter> interpreter;
	builder(&interpreter);
    /* 生成できたかチェック */
	TFLITE_MINIMAL_CHECK(interpreter != nullptr);

	/* 入出力のバッファを確保する */
	TFLITE_MINIMAL_CHECK(interpreter->AllocateTensors() == kTfLiteOk);
	printf("=== Pre-invoke Interpreter State ===\n");
	tflite::PrintInterpreterState(interpreter.get());

	/* 入力テンソルに読み込んだ画像を格納する */
	float* input = interpreter->typed_input_tensor<float>(0);
	memcpy(input, image.reshape(0, 1).data, sizeof(float) * 1 * 28 * 28 * 1);

	/* 推論を実行 */
	TFLITE_MINIMAL_CHECK(interpreter->Invoke() == kTfLiteOk);
	printf("\n\n=== Post-invoke Interpreter State ===\n");
	tflite::PrintInterpreterState(interpreter.get());

	/* 出力テンソルから結果を取得して表示 */
	float* probs = interpreter->typed_output_tensor<float>(0);
	for (int i = 0; i < 10; i++) {
		printf("prob of %d: %.3f\n", i, probs[i]);
	}

    /* 終了 */
	return 0;
}

ビルドと実行結果 (VAB-5000上での操作)

以上を格納したディレクトリを作成し、これに対して cmakemake を使ってビルドを実施すると NumberDetector というバイナリが生成 されます。これを実行することにより、画像に書かれた文字を認識することができます。

実行結果(成功例)

### 作業用ディレクトリ(参考)
$ pwd
# /home/debian/sandbox/tensorflow_c/first

### ソースコードとCMakeLists.txtを準備する
$ ls
# CMakeLists.txt  main.cpp  resource

### resourceに画像とtfliteモデルを格納する
$ ls resource/
# 4.jpg  conv_mnist.tflite

### ビルドする
$ mkdir build
$ cd build
$ cmake ..
$ make 
# [ 50%] Building CXX object CMakeFiles/NumberDetector.dir/main.cpp.o
# [100%] Linking CXX executable NumberDetector
# [100%] Built target NumberDetector

### 実行する
$ ./NumberDetector 
# input image path : /home/debian/sandbox/first/tensorflow_c/build/resource/4.jpg
# model file name : /home/debian/sandbox/tensorflow_c/first/build/resource/conv_mnist.tflite
# ...
# prob of 0: 0.000
# prob of 1: 0.000
# prob of 2: 0.000
# prob of 3: 0.000
# prob of 4: 1.000
# prob of 5: 0.000
# prob of 6: 0.000
# prob of 7: 0.000
# prob of 8: 0.000
# prob of 9: 0.000

以上が VIA VAB-5000 向け (aarch64(64bit-Arm)向け) に
TensorFlow Lite for C/C++をx86_64上でビルドして
デバイス上から利用する手順です。やや複雑に感じられますが、
一度設定を行えば、以降は利用できますので、是非お試しください。

お疲れさまでした!


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?