1
6

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 5 years have passed since last update.

ONNX Chainer + OpenCV メモ

Last updated at Posted at 2019-05-01

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は色々な最適化法を実装しているので,もっと勉強しよう.
学習過程は,以下のようになったので,最適化はうまくいっているみたい.

errorFunction.png
学習過程の誤差関数

で,学習結果をプロットした.ユニットの数や最適化手法,層数をいじると
もっと複雑な回帰ができると思う.

prediction.png
学習結果

まぁ,近似できていることが確認できた.

[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モデルを読み込んで予測ができている.ちょっと感動.

opencvresult.png
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

1
6
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
1
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?