こんにちは。
ディープラーニングお兄さんの”はやぶさ”@Cpp_Learningです。
前回、Sony製の深層学習フレームワーク“Neural Network Libraries(NNabla)”のPython APIと学習済みモデル(MyChain.nnp)による推論を実践するチュートリアル記事を書きました。
今回は、C++ APIと学習済みモデル(MyChain.nnp)による推論を実践します。
最初にPythonではなくC++で深層学習をする理由について説明します。
Contents
組み込みシステムと深層学習
組み込みシステムの多くはリアルタイム処理・小型化・低消費電力を求められ、さらに低コスト化の要求もあるため、高性能プロセッサや大容量メモリの使用不可という制約があります。
また、組み込みシステムのソフトウェアはC言語あるいはC++で実装することも多いです。
そのため、組み込みシステムに深層学習を適用する場合、機械学習ライブラリが充実しているPythonではなく、C/C++によるソフトウェア開発を求められるケースがあります。
その他、様々な理由でC/C++で深層学習を実現したいという要求があります。
深層学習とC++
深層学習のサンプルソースを調査するとPythonで書かれたものが多く、C/C++のサンプルソースを探すのに苦労します。
深層学習フレームワークの中には、PythonとC++の両方をサポートしているものもありますが、Pythonで使用できる関数がC++では使えない!なんてこともあり、C++よりもPythonのサポートが手厚いと感じています。
(C++のサポートも充実してきているので、時間が解決してくれる気もしますが…)
C++自体の難しさもあり、C++で深層学習をするのはハードルが高いと感じています。
Neural Network Libraries(NNabla)の特徴
Sony製の深層学習フレームワーク“Neural Network Libraries”のコア部分はC++で実装されており、Python APIだけでなくC++ APIも用意してあります。
そのため、C++で深層学習の学習フェーズも推論フェーズも実現できます。
また「学習フェーズをPython」⇒「推論フェーズをC++」という一連の処理もシームレスに実現できます。
【学習フェーズ(Python API)】
- Python APIでニューラルネットワークを設計
- 学習(ニューラルネットワークの”重み”を調整)
- 学習済みモデルをNNPファイルに保存
【推論フェーズ(C++ API)】
- C++ APIで学習済みモデル(NNPファイル)を読込む
- 学習済みモデルへの入力値(センサ値など)を取得
- 推論(入力値に対する推論値を取得)
- 推論値を使って”ごにょごにょ”する(制御とか)
冒頭で少し説明した通り、組み込みシステムはプロセッサやメモリに制約があるため、学習を実行するのは辛い…
また、組み込みシステムは特定の処理に特化したシステムが多く、推論のみを実行できれば良いケースも多いです。
そのため…
以降から、Python APIで生成した学習済みモデル(MyChain.nnp)を使い、C++ APIによる推論を実践します。
NNabla C++ APIのインストール方法など
最初に各種ライブラリをインストールして環境構築を行います。C++ APIを使うための環境構築については、以下の記事で説明しています。
また、学習済みモデルの生成など”NNabal”の使い方に関する”Tips集”を作成・公開していますので、ご参考までに
以降から学習済みモデル(MyChain.nnp)が手元にある前提で説明します。まだ、手元にnnpファイルがない人は”Tips集”を参考に作成してみて下さい(*・ω・)ノ♪
【実践】学習済みモデル(NNPファイル)とC++による推論
NNableのC++ APIと学習済みモデル(NNPファイル)を使って推論を行います。
先に作成するファイルについて説明しておきます。
ファイル | 内容 |
MyChain.nnp | 学習済みモデル |
makefile | ビルド/実行用 |
nnabla_cpp.cpp | 推論ソフト(C++バージョン) |
nnabla_python.py | 推論ソフト(Pythonバージョン) |
ディレクトリツリーは以下の通りです。
1 2 3 4 |
workspace -- NNabla_cpp -- MyChain.nnp | -- makefile | -- nnabla_cpp.cpp | -- nnabla_python.py |
※workspaceとNNabla_cppはディレクトリ
なお、今回使用する学習済みモデル”MyChain.nnp”は、1入力1出力(SISO系)の非線形システムの同定モデルです。
【深層学習】推論ソフト(C++バージョン)
C++と学習済みモデル”MyChain.nnp”で推論するソースコード”nnabla_cpp.cpp”が以下です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <nbla_utils/nnp.hpp> #include <iostream> #include <string> #include <cmath> int main(int argc, char *argv[]) { nbla::CgVariablePtr x, y; float xt; const float *y_data; // Create a context (the following setting is recommended.) nbla::Context cpu_ctx{{"cpu:float"}, "CpuCachedArray", "0"}; nbla::Context ctx = cpu_ctx; // Create a Nnp object nbla::utils::nnp::Nnp nnp(ctx); // Set nnp file to Nnp object nnp.add("MyChain.nnp"); // Get an executor instance. auto executor = nnp.get_executor("runtime"); executor->set_batch_size(1); // Use batch_size = 1 // Get input data as a CPU array. x = executor->get_data_variables().at(0).variable; float *data = x->variable()->cast_data_and_get_pointer<float>(ctx); // Predict x = 0~9 for(int i = 0; i < 10; i++){ // set input data xt = i; *data = xt; // execute executor->execute(); y = executor->get_output_variables().at(0).variable; y_data = y->variable()->get_data_pointer<float>(ctx); // print predict result std::cout << "y = MyChaine(x): " << "x = " << xt << ", y = " << y_data[0] << std::endl; } return 0; } |
このソースコードでx = 0~9整数を”MyChain(x)”に入力し、出力値yを推論します。
makefile
ビルドおよび実行コマンドを手打ちするのは大変なので、以下のmakefileを作成します。
1 2 3 4 5 6 7 8 9 10 11 |
LDFLAGS = -L/usr/local/lib -lnnabla -lnnabla_utils all: nnabla_cpp.cpp $(CXX) -std=c++11 -O -o nnabla_cpp nnabla_cpp.cpp -lnnabla -lnnabla_utils run: LD_LIBRARY_PATH=/usr/local/lib ./nnabla_cpp clean: rm -f nnabla_cpp |
ビルド&実行
以下のコマンドで”NNabla_cpp”に移動
cd NNabla_cpp
以下のコマンドでビルド
make
nnabla_cppという実行ファイルが生成できたら成功です。
以下のコマンドで実行
make run
一連の流れを実施したときのターミナル画面は以下の通りです。
C++で推論できました!
【エラーレポート】ライブラリ(soファイル)のリンク
makeでビルドが成功し、実行ファイル”nnabla_cpp”を生成できたら、以下のコマンドで実行できるはずでした!
./nnabla_cpp
しかし、以下のエラーメッセージが表示されました。
./nnabla_cpp: error while loading shared libraries: libnnabla.so: cannot open shared object file: No such file or directory
これは、nnabla_cppで使用するNNablaライブラリ(SOファイル)”libnnabla.so”が見つからない!と怒られています。
以下のコマンドでnnabla_cpp(実行ファイル)のリンクを確認することができます。
ldd nnabla_cpp
このコマンドで”libnnabla.so”が表示されればリンク済み、未表示の場合は上記したエラーが表示されます。
今回の場合は、以下のNNablaライブラリ(soファイル)をリンクする必要がありました。
- libnnabla.so
- libnnabla_utils.so
そのため、makefileで”make run”コマンドを作成し、/usr/local/libに保存されたライブラリ(soファイルなど)をリンクして実行できるようにしました。
LD_LIBRARY_PATH=/usr/local/lib ./nnabla_cpp
ライブラリ(SOファイル)をリンクするシンプルな方法を説明しました。NNablaに限らず汎用的に使えるテクニックなので、ご参考までに
【深層学習】推論ソフト(Pythonバージョン)
比較のためにPythonバージョンの推論ソースコード”nnabla_python.py”も作成して実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import nnabla as nn import nnabla.functions as F import nnabla.parametric_functions as PF from nnabla.utils.nnp_graph import NnpLoader import numpy as np import time batch_size = 1 x = nn.Variable((batch_size, 1)) y = nn.Variable((batch_size, 1)) # Read a .nnp file. nnp = NnpLoader("MyChain.nnp") # Assume a graph `graph_a` is in the nnp file. net = nnp.get_network("MyChain", batch_size) start = time.time() # Predict x = 0~9 for i in range(10): x.d = i # `x` is an input of the graph. x = net.inputs['x'] # 'y' is an outputs of the graph. y = net.outputs['y'] y.forward() # print predict result print('y = MyChaine(x): x = %d, y = %f' %(x.d.copy(), y.d.copy())) predict_time = time.time() - start # print ("predict_time:{0}".format(predict_time) + "[sec]") print ("predict_time:%f" %(predict_time * 1000) + "[msec]") |
実行結果は以下の通りでした。
推論結果yはPythonでもC++でも同じでした。同じ学習済みモデルを使っているので、当然ですね(*・ω・)ノ♪
また、推論にかかった時間を計測した結果は以下の通りでした。
ソースコード | 処理時間 |
C++ | 約8msec |
Python | 約17msec |
※第8世代のインテルCoreプロセッサ(Core i5-8250U)で動作確認しました
使用するPCに依存すると思いますが、C++の方が高速ですね!
※C++の処理時間計測にはchronoというライブラリを使って計測しました。
まとめ
Sony製の深層学習フレームワーク“Neural Network Libraries”のC++ APIと学習済みモデル(NNP)を使った推論を実践しました。
なお、学習済みモデルはPython APIで生成したものを使っています。
そのため、「学習フェーズをPython」⇒「推論フェーズをC++」という一連の処理もシームレスに実現できました。
“Neural Network Libraries(NNabla)”すごい!
本記事を読んで…
という人が現れたら、ディープラーニングお兄さんはすごく嬉しい!
【おまけ】Spresenseの紹介
Neural Network Librariesで学習したモデルは、Sony製のスマートセンシングプロセッサ搭載ボード”Spresense”で実行できるそうです。
Spresenseメインボード
Arduino IDE や専用のSDKを使って簡単にIoTシステムを実現できるようです
Spresense拡張ボード
Arduino UNO 互換のピンソケット
Spresenseカメラボード
Sony製CMOSイメージセンサー