ディープラーニングフレームワークのncnnを試してみた

はじめに

こんにちは。イノベーションセンターテクノロジー部門の齋藤と申します。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。今回は、モバイル向けの推論フレームワークのncnnに触れてみたので、その結果について書いて行きます。

ncnnとは

ncnn1とは、モバイル向けの推論フレームワークでAndroidとiOSにどちらも対応しています。Pytorchの場合モデルは、pthの形式で1つのファイルで構成されています。ncnnの場合モデルは、param(モデル構造)とbin(重み)ファイルに分割されています。

自身のncnnを使用するモチベーションは、ncnnのデータフォーマットにあります。モバイルで使用するフレームワークにTensorFlow Lite2がありますが、他のフレームワークからモデルを変換するためにNCHW形式からNHWC形式に変換する必要があります。ONNX3からTensorFlow4に変換する際、 Conv layerが余計に追加される問題 があるなど、うまく変換されないケースもあります。

現在では、先程あげた例は上記リンクのように問題が解消されていると思います。しかし、モデルの変換をストレートにできた方が、変換されないケースに時間を使うことがないのではないかと考えています。

ncnnを使用することが推論速度やメモリの使用量の性能面で良さそうか考えた時に、MXNet5、ncnn、ONNX、および OpenVINO6の各フレームワークの性能を比較しているブログ がありました。このブログでは、Intel CPUを使用した場合ncnnよりもonnxを使用した方が推論時間やメモリ性能上、上回っていることを述べています。特にメモリの使用量に関して、ncnnがONNXと比較して5倍ほど消費してしまうことを問題視 していましたが、以下で解消されるようです。

i dont know is it to late to answer this issue,i met zhe same situation as you. it is beacuse that ncnn have some acceleration algorithm witch consum more momery,if you want to reduce the usage you can set opt.use_winograd_convolution as false. In my project,r101 just uses about 450mb

推論速度やメモリの使用量をみるとONNXの方が良さそうに思えます。ただ、このベンチマークが各フレームワークの条件を揃えるためにfp32を使用していると仮定をすれば、fp16にすることにより推論速度の高速化は可能かと思われます。これらの理由により、今回はncnnを使いたいと思います。次は、ONNX -> ncnnによるモデルの変換をしていきます。

  • 補足
    • N– バッチ サイズ
    • C– 特徴マップの数 (チャネル数)
    • H– 高さ
    • W– 幅

onnx2ncnn

今回はncnnのリポジトリ 内にあるONNXからncnnに変換するコードを実行しました。 onnx-simplifierのweb版 もありそこでも変換できるみたいです。

cd tools/onnx
onnx2ncnn input.onnx output.param output.bin

ONNXのモデルの最適化をしてみたのですが、中をNetron7で確認をしたところ構造があまり変わっていないように見えたので今回は省きます。

Androidの環境構築

今回使用するモバイル開発に必要なツール群は主に以下となります。

  • Android NDK8
  • ncnn

Android NDK(Native Development Kit)

Android NDKとは、AndroidでCやC++のコードを使用可能にするツールです。今回はC++をアプリで使っているため、利用しています。このURLからNDKをダウンロード可能です。

unzip android-ndk-r25b-linux.zip
cd android-ndk-r25b
export NDK_PATH=${PWD}

ncnn

実行方法には自分の手元でbuildする方法とbuild済みのものをダウンロードする方法がする方法があります。https://github.com/Tencent/ncnn/releases/tag/20220729。今回は自身の手元でbuildする方法を試したため以下のような手順を取りました。

git clone -b 20221128 https://github.com/Tencent/ncnn.git
cd ncnn
git submodule update --init
export NCNN_DIR=${PWD}
export ANDROID_ABI=arm64-v8a
# ANDROID_PLATFORMは、Android13の場合33、Android12の場合31
export ANDROID_PLATFORM=33
# Vulkan: https://developer.android.com/ndk/guides/graphics
export VULKAN_SDK=$(pwd)/1.2.189.0/x86_64
export LD_LIBRARY_PATH=${NCNN_DIR}/build/install/lib/:$LD_LIBRARY_PATH

mkdir -p build_${ANDROID_ABI}
cd build_${ANDROID_ABI}

# ANDROID_PLATFORM
# cmakeのversionは3.23で実行
cmake -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM=android-${ANDROID_PLATFORM} -DNCNN_VULKAN=ON -DNCNN_DISABLE_EXCEPTION=OFF -DNCNN_DISABLE_RTTI=OFF ..
make -j$(nproc) install

ここでビルドしたものもしくは、ダウンロードしたものをCMakeLists.txtにパスを記述しビルドをさせます。 今回はncnnのサンプルアプリ が出ていたので、それをベースに作成しています。

動作結果

実行環境は以下となります。

  • Pixel6 Pro
    • プロセッサは、Google Tensor(CPU: ARM_Coretex-X1 , Architecture: armv8.2 a)

今回はcoco test 20179画像の20枚使用し、推論速度の比較をしました。結果は以下の表のようになりました。モデルは、yolov5s10とfp16化したものを利用しております。

モデル  1枚あたりの平均推論時間 (ms)
  yolov5s 82.085
yolov5s fp16 81.488

20枚の画像を使用した結果ではfp16化によってほぼ速度は変わらないような結果となっております。単純に速度が2倍になると考えておりましたが、今回動かした環境ではその恩恵が得られていないように思えるので、調査をしたいです。

終わりに

今回はncnnの実行環境の構築とモデルの動作をさせました。実行環境によって推論時間が異なりますが、mmdeploynのベンチマーク のSnapDragon888の推論時間をみると近い値になっていることから、今回動かしたncnnの推論速度は違和感がないかなと思います。ONNXとncnnのモデルを比較していないため、ONNXと比較したncnnの良い部分を実感しにくいものとなってしまったため、今後調査してみたいです。

それでは、明日の投稿もお楽しみに。

© NTT Communications Corporation All Rights Reserved.