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?

More than 1 year has passed since last update.

ncnnでよくある問題とその対処

Last updated at Posted at 2023-12-14

概要

ncnnのWikiに記載されている、よくある問題とその対処を翻訳して紹介します。

ncnnはなぜ開発されたできたのか?

ディープラーニングアルゴリズムを携帯電話に実装する必要があり、caffeは依存関係が多すぎるし、携帯電話にはcudaがないので、高速で小さなフォワードネットワークの実装が必要でした。

ncnnの名前の由来は?

cnnはconvolutional neural network(畳み込みニューラルネットワーク)の略称で、冒頭のnはワンライナーのようなものです。 例えば、new/next(新しい実装)、naive(ncnnは素朴な実装)、neon(ncnnはもともと携帯電話向けに最適化された)。

対応プラットフォームは?

クロスプラットフォーム、android/ios/linux/windows/macosに対応、ベアメタルにも対応している。

A 計算精度はどうですか?
armv7 neon floatはieee754標準に準拠していないため、高速な実装(exp sinなど)を使用しているものもあり、高速ですが精度は十分高いです。

ncnn の作者は?

nihui氏です。

ソースコードのダウンロード方法は_

git clone --recursive https://github.com/Tencent/ncnn/

クロスコンパイルの方法と、cmakeツールチェインの設定方法は?

The submodules were not downloaded! Please update submodules with "git submodule update --init" and try again とでた場合は?

上記のように、完全なソースコードをGitからrecursiveでダウンロードします。 または、プロンプトに従ってください

git submodule update --init

Could NOT find CUDA (missing: CUDA_TOOLKIT_ROOT_DIR CUDA_INCLUDE_DIRS CUDA_CUDART_LIBRARY)と表示される

Could not find a package configuration file provided by "OpenCV" with any of the following names: OpenCVConfig.cmake opencv-config.cmakeと表示される

sudo apt-get install libopencv-dev

または自分でコンパイルしてインストールする。

CMakeList.txt
set(OpenCV_DIR {OpenCVConfig.cmakeがあるディレクトリ})

Could not find a package configuration file provided by "ncnn" with any of the following names: ncnnConfig.cmake ncnn-config.cmakeと表示される


set(ncnn_DIR {ncnnConfig.cmakeがあるディレクトリ})

Vulkanが見つからない。

cmakeのバージョンは3.10以上です。そうでない場合はFindVulkan.cmakeが付属しません。
android-api >= 24
macosの場合、最初にインストールスクリプトを実行する必要があります。

undefined reference to typeinfo for ncnn::Layerと表示される

opencv rtti -> opencv-mobileへ変更する

undefined reference to __cpu_modelと表示される

コンパイラ( libgcc_s libgcc)のアップグレードが必要

unrecognized command line option "-mavx2"と表示される

gccのアップグレードが必要

私がコンパイルしたncnnアンドロイド・ライブラリが特に大きいのはなぜですか?

ncnnoptimizeとカスタムレイヤー

ncnnoptimizeがカスタムレイヤーの保存を処理できなくなるのを防ぐため、カスタムレイヤーを追加する前にncnnoptimizeを実行してください。

rtti/EXCEPTIONの設定

ncnnはデフォルトでONになっているので、ncnnを再コンパイルする際に以下の2つのパラメーターを追加する

OFF:-DNCNN_DISABLE_RTTI=OFF -DNCNN_DISABLE_EXCEPTION=OFF
ON:-DNCNN_DISABLE_RTTI=ON -DNCNN_DISABLE_EXCEPTION=ON

error: undefined symbol: ncnn::Extractor::extract(char const*, ncnn::Mat&)と表示される

Android StudioのNDKバージョンをアップグレードしてみる

CMake 3.14.0 or higher is required. You are running version 2.8.12.2と表示される

wget https://github.com/Kitware/CMake/releases/download/v3.18.2/cmake-3.18.2-Linux-x86_64.tar.gz
tar zxvf cmake-3.18.2-Linux-x86_64.tar.gz
mv cmake-3.18.2-Linux-x86_64 /opt/cmake-3.18.2
ln -sf /opt/cmake-3.18.2/bin/* /usr/bin/

プロジェクトにncnnライブラリを追加するには?

linux/windowsでは,make installを実行。
cmakeのncnn_DIR の設定で、インストールディレクトリの ncnnConfig.cmake を含むディレクトリを設定します。

学習モデリング変換の問題

  • caffe
./caffe2ncnn caffe.prototxt caffe.caffemodel ncnn.param ncnn.bin
  • mxnet
 ./mxnet2ncnn mxnet-symbol.json mxnet.params ncnn.param ncnn.bin
  • darknet
https://github.com/xiangweizeng/darknet2ncnn
  • pytorch - onnx

  • tensorflow 1.x/2.x - keras
https://github.com/MarsTechHAN/keras2ncnn @MarsTechHAN
  • tensorflow 2.x - mlir

  • MLIR経由でtensorflow2のモデルをncnnに変換する
    https://zhuanlan.zhihu.com/p/152535430

  • Shape not supported yet! Gather not supported yet! Cast not supported yet!
    onnx-simplifier でshapeを整える

  • convertmodel

Shape情報を固定してモデルを生成するには?

Input 0=w 1=h 2=c

Could NOT find Protobuf (missing: Protobuf_INCLUDE_DIR)と表示される

sudo apt-get install libprotobuf-dev protobuf-compiler

モデルを暗号化する方法は?

後処理を削除してonnxでエクスポートするには?

https://zhuanlan.zhihu.com/p/128974102 の記事の中でステップ3は、後処理を削除し、onnxをエクスポートすることができます。プロジェクト内でテストする際に、後続のステップを削除することになります。

pytorchはいくつかのレイヤーからonnxを導出することができません。

まずエクスポートできる、ONNX_ATEN_FALLBACK 完全にカスタマイズされたopで、(例:スライスの連結)に変更し、ncnnに移動した後にparamを変更する。
もしくは、PNNXで出力する。PNNXの大まかな説明は以下の記事を参照されたい。
https://zhuanlan.zhihu.com/p/431833958
https://zhuanlan.zhihu.com/p/427512763

Matlab が出力するcaffemodel が row-major になっていない

caffe2ncnnツールは、caffemodelがrow-major(pycaffe/c++のcaffe trainコマンドで生成)であることを前提としています。

例えば、カーネル3x3の重みは以下のように、格納されます。

a b c
d e f
g h i

しかし、matlab が出力するcaffeはcol-major caffemodelを生成します。

a d g
b e h
c f i

全てのカーネル重みを自分で転置するか、c++ caffe trainコマンドを使って再トレーニングする必要があります。また、 matcaffe model (column major)からpycaffe / c++ caffe (row major) modelへ変換するツールがあります。

input画像が RGBか BGRかを確認する

もしあなたのモデルがcaffemodelがc++ caffeとopencvを使ってトレーニングされたものであれば、入力画像はBGRオーダーになるはずです。もしあなたのモデルがmatlab caffeやpytorch、mxnet、tensorflowを使って学習されたものであれば、入力画像はおそらくRGB順になります。

チャンネル順序は、ncnnの処理によって、以下のように適切なタイプに変更できます。

// construct RGB blob from rgb image
ncnn::Mat in_rgb = ncnn::Mat::from_pixels(rgb_data, ncnn::Mat::PIXEL_RGB, w, h);

// construct BGR blob from bgr image
ncnn::Mat in_bgr = ncnn::Mat::from_pixels(bgr_data, ncnn::Mat::PIXEL_BGR, w, h);

// construct BGR blob from rgb image
ncnn::Mat in_bgr = ncnn::Mat::from_pixels(rgb_data, ncnn::Mat::PIXEL_RGB2BGR, w, h);

// construct RGB blob from bgr image
ncnn::Mat in_rgb = ncnn::Mat::from_pixels(bgr_data, ncnn::Mat::PIXEL_BGR2RGB, w, h);

imageデコード

JPEG(.jpg、.jpeg)は圧縮率が低いため、同じ位置の同じ画像でも画素値が異なる場合があります。代わりに.bmp画像をお勧めします。

補間/リサイズ

画像のリサイズにはいくつかの方法があり、同じ入力画像でも異なる結果を生成することがあります。
同じ補間方法を指定しても、フレームワーク/ライブラリやそのバージョンが異なれば、違いが生じる可能性があります。例えば、入力レイヤーが224x224のサイズを必要とする場合、224x244のbmp画像を読み込むようにします。

Mat::from_pixels/from_pixels_resizeは、連続したピクセルバッファを仮定

連続したピクセルバッファを from_pixels ファミリに渡す必要があります。
OpenCVで,画像 がroiから得られた切り取られた画像である場合は, clone() を呼び出して,連続したピクセルバッファ画像を取得します。

cv::Mat image;// the image
cv::Rect facerect;// the face rectangle

cv::Mat faceimage = image(facerect).clone();// get a continuous sub image

ncnn::Mat in = ncnn::Mat::from_pixels(faceimage.data, ncnn::Mat::PIXEL_BGR, faceimage.cols, faceimage.rows);

事前処理

トレーニングの設定に従って前処理を適用します。
モデルによってプリプロセスの設定は異なりますが、データレイヤーセクションに以下のような変換のパラメータ設定があります。

transform_param {
    mean_value: 103.94
    mean_value: 116.78
    mean_value: 123.68
    scale: 0.017
}

すると、ncnnの前処理に対応するコードは次のようになります。

const float mean_vals[3] = { 103.94f, 116.78f, 123.68f };
const float norm_vals[3] = { 0.017f, 0.017f, 0.017f };
in.substract_mean_normalize(mean_vals, norm_vals);

pytorchまたはmxnet-gluonの場合は以下のように、パラメータを設定します。

transforms.ToTensor(),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),

このときには、ncnnの前処理に対応するコードは、0~1の範囲のデータを0~255の範囲のデータになるように変換を行う必要があるため、次のようになります。

const float mean_vals[3] = {0.485f*255.f, 0.456f*255.f, 0.406f*255.f};
const float norm_vals[3] = {1/0.229f/255.f, 1/0.224f/255.f, 1/0.225f/255.f};
in.substract_mean_normalize(mean_vals, norm_vals);

// R' = (R / 255 - 0.485) / 0.229 = (R - 0.485 * 255) / 0.229 / 255
// G' = (G / 255 - 0.456) / 0.224 = (G - 0.456 * 255) / 0.224 / 255
// B' = (B / 255 - 0.406) / 0.225 = (B - 0.406 * 255) / 0.225 / 255

ncnn::Extractorのbolb名をモデルのbolb名とあわせる

入力と出力のblob名はモデルによって異なります。
例えば、squeezenet v1.1では入力ブロブとして "data"、出力ブロブとして "prob "を使用しますが、mobilenet-ssdでは入力ブロブとして "data"、出力ブロブとして "detection_out "を使用します。
モデルによっては、複数の入力が必要な場合や、複数の出力を生成する場合があります。

ncnn::Extractor ex = net.create_extractor();

ex.input("data", in);// change "data" to yours
ex.input("mask", mask);// change "mask" to yours

ex.extract("output1", out1);// change "output1" to yours
ex.extract("output2", out2);// change "output2" to yours

blob が channel gapを持つ場合

各チャンネルのポインタは,ncnn Mat 構造体内で 128bit ずつずらして保持しています。
(width x height) が 4 で正確に割り切れない場合,チャンネル間にギャップが生じる可能性があります。

画像データから入力 blob を作成するには, ncnn::Mat::from_pixels または、ncnn::Mat::from_pixels_resize を利用することを推奨します.

連続した blob バッファが必要な場合は,出力の形状を変更します

// out is the output blob extracted
ncnn::Mat flattened_out = out.reshape(out.w * out.h * out.c);

// plain array, C-H-W
const float* outptr = flattened_out;

画像ごとに新しい Extractor を作成する

ncnn::Extractor オブジェクトは処理状態を保持しています。異なる入力データに対して再利用すると、常に内部にキャッシュされた全く同じ結果が得られます。
Extractor がどのように動作するかを把握している場合を除き、画像をループで処理するには常に新しい Extractor を作成します。

for (int i=0; i<count; i++){
    // always create Extractor
    // it's cheap and almost instantly !
    ncnn::Extractor ex = net.create_extractor();

    // use
    ex.input(your_data[i]);
}

適切なLoad 関数を使う

メモリ上に配置したパラメータファイルバッファをロードしたい場合は、Net::load_param の代わりに Net::load_param_mem を使用しなければなりません。ncnn モデルのロード API については ncnn-load-model を参照してください。

ncnn-load-model

ncnn::Net net;

// param_buffer is the content buffe of XYZ.param file
net.load_param_mem(param_buffer);

fp16を無効にする

モデルによってはfp16がオーバーフローし、nanの結果になることがあります。
そこで、fp16の低精度最適化をオフにしてみると、精度がfp32に改善され、これに起因するオーバーフロー問題を調査・解決することができます。

ncnn::Net net;

net.opt.use_fp16_packed = false;
net.opt.use_fp16_storage = false;
net.opt.use_fp16_arithmetic = false;

参考

この記事は以下のページを参考に作成しました。

FAQ-ncnn-produce-wrong-result
[FAQ]https://github.com/Tencent/ncnn/wiki/faq)

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?