ONNX Chainer + OpenCV メモ
Chainerで学習したモデルをVCで実行したいというのがきっかけ.
Chainerで自作NNモデルをONNX形式ではき出してOpenCV dnnで読み込んでみたので忘れないようにメモ.
概要
[0] 環境:Win10x64 + VisualStudio2015 + Opencv3.4.5+Anaconda3-2019(Python3.7.1 -- >3.6.7)
[1] 回帰モデルをNNで学習.
[2] ONXX形式で学習済みモデルを保存.
[3] OpenCV3.4.5(C++)で学習済みモデルを読み込み予測する.
[1] 回帰モデルの学習
(1) NNモデル
NNモデルは,入力1,中間層x2,出力2層モデルとした.
モデルは色々変えてみるとおもしろいかもしない.
class MyChain(Chain):
def __init__(self, n_units=10):
super(MyChain, self).__init__(
l1=L.Linear(1, n_units),
l2=L.Linear(n_units, n_units),
l3=L.Linear(n_units, 1))
print("n_units={}".format(n_units) )
# ONXXでは,__call__が呼ばれる
def __call__(self, x):
return self.predict(x)
# forward方向の計算
def predict(self, x):
h1 = F.sigmoid(self.l1(x))
h2 = F.relu(self.l2(h1))
h3 = self.l3(h2)
return h3
(2) サンプルデータ
サンプルデータを-π~πの範囲で,2.2sin(x)で作成.
振幅の2.2とか別に意味はない.
データ数は,256.
# データ数
N=256
# A sin( w*x )
A=2.2
w=1
# 標本データの生成
# 真の関数としてsin関数を
x, y = [], []
for i in np.linspace(-np.pi , np.pi , N ):
x.append([i])
y.append([A*math.sin(w*i)]) # 真の関数
(3) 学習
学習の最適化アルゴリズムにはAdamを使用.
他にもSGDやAdaGradなど,Chainerは色々な最適化法を実装しているので,もっと勉強しよう.
学習過程は,以下のようになったので,最適化はうまくいっているみたい.
学習過程の誤差関数 |
で,学習結果をプロットした.ユニットの数や最適化手法,層数をいじると
もっと複雑な回帰ができると思う.
学習結果 |
まぁ,近似できていることが確認できた.
[2] ONXX形式で学習済みモデルを保存
onnx形式での保存は,onnx_chainerをインポートしてexportするだけ.
import onnx_chainer
:
onnx_model = onnx_chainer.export(model, x, filename='MyChain.onnx')
【onnx-chainerインストメモ】
onnx-chainerのインストールは,onnxとonnx-chainerをインストする必要があるみたい.
githubからonnxをcloneして,python setup.py install でもコンパイル時にエラー
pip install onnxでもonnx-chainerとバージョンがあわないみたい...
色々やってみたが,20190430時点では,conda-forgeにあるbinaryしかだめみたい.
conda install -c conda-forge onnx
でonnxを入れないと,onnx-chainerは走らないらしい.
その後,
pip install onnx-chainer
とした.onnx-chainerでpythonが3.6までの対応のようだ.
The following packages will be DOWNGRADED:
_ipyw_jlab_nb_ext~ 0.1.0-py37_0 --> 0.1.0-py36_0
anaconda-navigator 1.9.7-py37_0 --> 1.9.7-py36_0
certifi 2019.3.9-py37_0 --> 2019.3.9-py36_0
conda 4.6.14-py37_0 --> 4.6.14-py36_0
navigator-updater 0.2.1-py37_0 --> 0.2.1-py36_0
numba 0.43.1-py37hf9181ef_0 --> 0.43.1-py36hf9181ef_0
protobuf 3.7.1-py37he025d50_0 --> 3.5.1-py36_vc14_3
py-lief 0.9.0-py37ha925a31_2 --> 0.9.0-py36ha925a31_2
pycurl 7.43.0.2-py37h7a1dbc1_0 --> 7.43.0.2-py36h7a1dbc1_0
pyqt 5.9.2-py37h6538335_2 --> 5.9.2-py36h6538335_2
scipy 1.2.1-py37h29ff71c_0 --> 1.2.1-py36h29ff71c_0
sphinxcontrib 1.0-py37_1 --> 1.0-py36_1
とか,Anaconda3(python3.7)を入れていてもダウングレードしてまう...けど,まぁいいか.
全コードはこんな感じ.
#-*- coding: utf-8 -*-
# 曲線回帰
# 数値計算関連
import math
import random
import numpy as np
import matplotlib.pyplot as plt
# chainer
from chainer import Chain, Variable
import chainer.functions as F
import chainer.links as L
from chainer import optimizers
# chainer onnx
import onnx_chainer
#################################################
# 3層ニューラルネットワークモデル
# 色々試してもおもしろい
class MyChain(Chain):
def __init__(self, n_units=10):
super(MyChain, self).__init__(
l1=L.Linear(1, n_units),
l2=L.Linear(n_units, n_units),
l3=L.Linear(n_units, 1))
print("n_units={}".format(n_units) )
# ONXXでは,__call__が呼ばれる
def __call__(self, x):
return self.predict(x)
# forward方向の計算
def predict(self, x):
h1 = F.sigmoid(self.l1(x))
h2 = F.relu(self.l2(h1))
h3 = self.l3(h2)
return h3
#################################################
# データ数
N=256
# A sin( w*x )
A=2.2
w=1
# 標本データの生成
# 真の関数としてsin関数を
x, y = [], []
for i in np.linspace(-np.pi , np.pi , N ):
x.append([i])
y.append([A*math.sin(w*i)]) # 真の関数
# chainerの変数として再度宣言
x = Variable(np.array(x, dtype=np.float32))
y = Variable(np.array(y, dtype=np.float32))
# NNモデルを宣言
model = MyChain( 128 )
# 損失関数の計算
# 損失関数には自乗誤差(MSE)を使用
def errorFunction(x, y, model):
t = model.predict(x)
loss = F.mean_squared_error(t, y)
return loss
# chainerのoptimizer
# 最適化のアルゴリズムには Adam を使用
optimizer = optimizers.Adam()
#optimizer = optimizers.SGD() #SGDの場合
# modelのパラメータをoptimizerに渡す
optimizer.setup(model)
# 学習回数
epoch=500
ef=np.zeros((epoch,2),dtype=np.float32)
# パラメータの学習
for i in range(0,epoch):
model.zerograds()
loss = errorFunction(x, y, model)
loss.backward()
ef[i,0]=i;
ef[i,1]=loss.data;
print("{}/{} loss={}".format( i, epoch,loss.data)) # 現状のMSEを表示
optimizer.update(errorFunction, x, y, model)
# プロット
plt.plot(ef[:,0], ef[:,1], color='blue', label='error function')
plt.legend()
plt.savefig('errorFunction.png')
plt.show()
t = model.predict(x)
plt.plot(x.data, y.data, color='blue', label='sample')
plt.plot(x.data, t.data, color='red' , label='prediction')
plt.grid(which='major',color='gray',linestyle='-')
plt.xlim(-np.pi, np.pi)
plt.legend()
plt.savefig('prediction.png')
plt.show()
# ONNX形式に学習モデルを出力
onnx_model = onnx_chainer.export(model, x, filename='MyChain.onnx')
[3] OpenCVで予測
OpenCV3.4.5(C++)でONXX形式の学習モデルを読み込むには,dnnモジュールを利用した.
サンプルデータの作成は,NNモデルを学習した際の教師データにあわせている.
結果は,以下.
青がサンプルデータ(答え)で,赤が予測結果.
onnxモデルを読み込んで予測ができている.ちょっと感動.
OpenCV予測結果 |
コードは,以下.
#pragma comment(lib,"C:\\OpenCV\\lib\\opencv_core345.lib")
#pragma comment(lib,"C:\\OpenCV\\lib\\opencv_dnn345.lib")
#define _USE_MATH_DEFINES
#include <math.h>
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
int main(int argc, char* argv[])
{
// ネットワークインスタンス
cv::dnn::Net net;
// ONNXのネットワーク読み込み
net = cv::dnn::readNetFromONNX ("MyChain.onnx");
net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU);
int N=256;
//サンプルデータ作成
//chainerに合わせて,floatで作成
cv::Mat matX(N,1,CV_32F);
cv::Mat matY(N,1,CV_32F);
for( int i=0; i<N; i++){
float x=2.0*M_PI/N * i - M_PI;
matX.ptr<float>(i)[0]=x;
matY.ptr<float>(i)[0]=2.2*sin(x);
}
net.setInput( matX );
cv::Mat matYpredict=net.forward();
for( int i=0; i<N; i++){
float x=matX.ptr<float>(i)[0];
printf(" %d\t%f\t%f\t%f\n", i,
matX.ptr<float>(i)[0] , //定義域
matYpredict.ptr<float>(i)[0] , //予測
matY.ptr<float>(i)[0] ); //答え
}
return 0;
}
参考にしたページ
以下のページを参考にしました.ありがとうございました.
1)Deep Learning 入門(2) - Chainerで自作の非線形回帰を試そう -
https://qiita.com/yoshizaki_kkgk/items/bfe559d1bdd434be03ed
2)OpenCV Manual(cv::dnn::Net Class Reference)
https://docs.opencv.org/3.4.5/db/d30/classcv_1_1dnn_1_1Net.html#a5e74adacffd6aa53d56046581de7fcbd
3)run Keras model on opencv
https://www.slideshare.net/takmin/run-keras-model-on-opencv