web-dev-qa-db-ja.com

他のプログラミング言語でMATLABでトレーニングされたニューラルネットワークをエクスポートします

MATLAB Neural Network Toolboxを使用して、特にコマンドnprtoolを使用してニューラルネットワークをトレーニングしました。このコマンドは、ツールボックスの機能を使用し、情報を含むnetオブジェクトをエクスポートするためのシンプルなGUIを提供します。生成されたNNについて。

このようにして、分類器として使用できる実用的なニューラルネットワークを作成しました。それを表す図は次のとおりです。

Diagram representing the Neural Network

200個の入力、最初の隠れ層に20個のニューロン、最後の層に2個のニューロンがあり、2次元出力を提供します。

私がやりたいのは、他のプログラミング言語(C#、Javaなど)でネットワークを使用することです。

この問題を解決するために、MATLABで次のコードを使用しようとしています。

_y1 = tansig(net.IW{1} * input + net.b{1});
Results = tansig(net.LW{2} * y1 + net.b{2});
_

inputが200要素の単次元配列であるとすると、_net.IW{1}_が20x200マトリックス(20ニューロン、200重み)の場合、前のコードは機能します。

問題は、size(net.IW{1})が予期しない値を返すことに気づいたことです。

_>> size(net.IW{1})

    ans =

    20   199
_

入力が10000のネットワークでも同じ問題が発生しました。この場合、結果は20x10000ではなく、20x9384のようなものでした(正確な値は覚えていません)。

したがって、問題は、各ニューロンの重みをどのように取得できるかということです。その後、誰かがそれらを使用してMATLABの同じ出力を生成する方法を説明できますか?

15
Vito Gentile

上記の問題を解決したので、学んだことを共有することは有益だと思います。

施設

まず、いくつかの定義が必要です。 [1]から取った次の画像を考えてみましょう。

A scheme of Neural Network

上の図では、[〜#〜] iw [〜#〜]初期の重みを表します。これらはニューロンの重みを表します。次の画像が示すように、レイヤー1で、それぞれが各入力に接続されています[1]:

All neurons are connected with all inputs

他のすべての重みは、レイヤー重み[〜#〜] lw [〜#〜]最初の図では)と呼ばれ、これらも接続されています前のレイヤーの各出力で。この調査のケースでは、2層のみのネットワークを使用しているため、問題を解決するために1つのLWアレイのみを使用します。

問題の解決

上記の紹介の後、問題を2つのステップに分割して進めることができます。

  • 初期の重みの数を入力配列の長さと一致させる
  • 重みを使用して、他のプログラミング言語でトレーニングされたばかりのニューラルネットワークを実装および使用します

A-初期の重みの数を入力配列の長さと一致させる

nprtoolを使用して、ネットワークをトレーニングできます。プロセスの最後に、トレーニングプロセス全体に関する情報をワークスペースにエクスポートすることもできます。特に、以下をエクスポートする必要があります。

  • 作成されたニューラルネットワークを表すMATLABネットワークオブジェクト
  • ネットワークのトレーニングに使用される入力配列
  • ネットワークのトレーニングに使用されるターゲットアレイ

また、ニューラルネットワークを作成するためにMATLABが使用するコードを含むMファイルを生成する必要があります。これは、ニューラルネットワークを変更し、いくつかのトレーニングオプションを変更する必要があるためです。

次の画像は、これらの操作を実行する方法を示しています。

The nprtool GUI to export data and generate the M-code

生成されるMコードは次のようになります。

_function net = create_pr_net(inputs,targets)
%CREATE_PR_NET Creates and trains a pattern recognition neural network.
%
%  NET = CREATE_PR_NET(INPUTS,TARGETS) takes these arguments:
%    INPUTS - RxQ matrix of Q R-element input samples
%    TARGETS - SxQ matrix of Q S-element associated target samples, where
%      each column contains a single 1, with all other elements set to 0.
%  and returns these results:
%    NET - The trained neural network
%
%  For example, to solve the Iris dataset problem with this function:
%
%    load iris_dataset
%    net = create_pr_net(irisInputs,irisTargets);
%    irisOutputs = sim(net,irisInputs);
%
%  To reproduce the results you obtained in NPRTOOL:
%
%    net = create_pr_net(trainingSetInput,trainingSetOutput);

% Create Network
numHiddenNeurons = 20;  % Adjust as desired
net = newpr(inputs,targets,numHiddenNeurons);
net.divideParam.trainRatio = 75/100;  % Adjust as desired
net.divideParam.valRatio = 15/100;  % Adjust as desired
net.divideParam.testRatio = 10/100;  % Adjust as desired

% Train and Apply Network
[net,tr] = train(net,inputs,targets);
outputs = sim(net,inputs);

% Plot
plotperf(tr)
plotconfusion(targets,outputs)
_

トレーニングプロセスを開始する前に、MATLABが入力と出力で実行するすべての前処理および後処理関数を削除する必要があります。これは、_% Train and Apply Network_行の直前に次の行を追加して実行できます。

_net.inputs{1}.processFcns = {};
net.outputs{2}.processFcns = {};
_

create_pr_net()関数にこれらの変更を加えた後、それを使用して最終的なニューラルネットワークを作成できます。

_net = create_pr_net(input, target);
_

ここで、inputtargetは、nprtoolを介してエクスポートした値です。

このようにして、重みの数が入力配列の長さに等しいことを確認します。また、このプロセスは、他のプログラミング言語への移植を簡素化するために役立ちます。

B-他のプログラミング言語でトレーニングされたばかりのニューラルネットワークを実装して使用する

これらの変更により、次のような関数を定義できます。

_function [ Results ] = classify( net, input )
    y1 = tansig(net.IW{1} * input + net.b{1});

    Results = tansig(net.LW{2} * y1 + net.b{2});
end
_

このコードでは、上記のIW配列とLW配列だけでなく、nprtoolによってネットワークスキーマで使用されるbiasesbも使用します。このコンテキストでは、 バイアスの役割 ;は気にしません。単純に、nprtoolが使用するため、これらを使用する必要があります。

これで、次の例に示すように、上記で定義したclassify()関数またはsim()関数を同等に使用して、同じ結果を得ることができます。

_>> sim(net, input(:, 1))

ans =

    0.9759
   -0.1867
   -0.1891

>> classify(net, input(:, 1))

ans =

   0.9759   
  -0.1867
  -0.1891
_

明らかに、classify()関数は擬似コードとして解釈でき、MATLAB tansig()関数[2]と配列間の基本操作を定義できるすべてのプログラミング言語で実装できます。 。

参照

[1] Howard Demuth、Mark Beale、Martin Hagan:Neural Network Toolbox 6-ユーザーガイド、MATLAB

[2] Mathworks、tansig-双曲線正接シグモイド伝達関数、MATLABドキュメントセンター

追記

詳細については、 ロボットの回答 および サングンチの回答 をご覧ください。

14
Vito Gentile

VitoShadowとrobottの回答のおかげで、Matlabニューラルネットワークの値を他のアプリケーションにエクスポートできます。

私は彼らに本当に感謝していますが、彼らのコードにいくつかの些細な誤りを見つけ、それらを修正したいと思います。

1)VitoShadowコードでは、

Results = tansig(net.LW{2} * y1 + net.b{2});
-> Results = net.LW{2} * y1 + net.b{2};

2)ロボットの前処理コードでは、xmaxとxminを計算するよりも、正味変数から抽出する方が簡単です。

xmax = net.inputs{1}.processSettings{1}.xmax
xmin = net.inputs{1}.processSettings{1}.xmin

3)ロボット後処理コードでは、

xmax = net.outputs{2}.processSettings{1}.xmax
xmin = net.outputs{2}.processSettings{1}.xmin

Results = (ymax-ymin)*(Results-xmin)/(xmax-xmin) + ymin;
-> Results = (Results-ymin)*(xmax-xmin)/(ymax-ymin) + xmin;

次のように、手動で値を確認して確認できます。

p2 = mapminmax('apply', net(:, 1), net.inputs{1}.processSettings{1})

->前処理されたデータ

y1 = purelin ( net.LW{2} * tansig(net.iw{1}* p2 + net.b{1}) + net.b{2})

->ニューラルネットワークで処理されたデータ

y2 = mapminmax( 'reverse' , y1, net.outputs{2}.processSettings{1})

->後処理されたデータ

参照: http://www.mathworks.com/matlabcentral/answers/14517-processing-of-i-p-data

3
Sangeun Chi

これは、素晴らしいVitoGentileの答えに対する小さな改善です。

前処理と後処理を使用する 'mapminmax'関数を使用する場合は、Matlabの 'mapminmax'に注意する必要があります。列ではなく行で正規化します!

これは、一貫した前処理/後処理を維持するために、上位の「分類」関数に追加する必要があるものです。

[m n] = size(input);
ymax = 1;
ymin = -1;
for i=1:m
   xmax = max(input(i,:));
   xmin = min(input(i,:));
   for j=1:n
     input(i,j) = (ymax-ymin)*(input(i,j)-xmin)/(xmax-xmin) + ymin;
   end
end

そしてこれは関数の終わりにあります:

ymax = 1;
ymin = 0;
xmax = 1;
xmin = -1;
Results = (ymax-ymin)*(Results-xmin)/(xmax-xmin) + ymin;

これはMatlabコードですが、疑似コードとして簡単に読み取ることができます。これがお役に立てば幸いです。

3
Gabrer

OpenCVを使用してC++で単純な2層NNを実装しようとし、重みをAndroidにエクスポートしました。これは静かに機能しました。学習した重みを使用してヘッダーファイルを生成する小さなスクリプトを作成し、これは、次のコードで使用されています。

// Map Minimum and Maximum Input Processing Function
Mat mapminmax_apply(Mat x, Mat settings_gain, Mat settings_xoffset, double settings_ymin){

    Mat y;

    subtract(x, settings_xoffset, y);
    multiply(y, settings_gain, y);
    add(y, settings_ymin, y);

    return y;


    /* MATLAB CODE
     y = x - settings_xoffset;
     y = y .* settings_gain;
     y = y + settings_ymin;
     */
}




// Sigmoid Symmetric Transfer Function
Mat transig_apply(Mat n){
    Mat tempexp;
    exp(-2*n, tempexp);
    Mat transig_apply_result = 2 /(1 + tempexp) - 1;
    return transig_apply_result;
}


// Map Minimum and Maximum Output Reverse-Processing Function
Mat mapminmax_reverse(Mat y, Mat settings_gain, Mat settings_xoffset, double settings_ymin){

    Mat x;

    subtract(y, settings_ymin, x);
    divide(x, settings_gain, x);
    add(x, settings_xoffset, x);

    return x;


/* MATLAB CODE
function x = mapminmax_reverse(y,settings_gain,settings_xoffset,settings_ymin)
x = y - settings_ymin;
x = x ./ settings_gain;
x = x + settings_xoffset;
end
*/

}


Mat getNNParameter (Mat x1)
{

    // convert double array to MAT

    // input 1
    Mat x1_step1_xoffsetM = Mat(1, 48, CV_64FC1, x1_step1_xoffset).t();
    Mat x1_step1_gainM = Mat(1, 48, CV_64FC1, x1_step1_gain).t();
    double x1_step1_ymin = -1;

    // Layer 1
    Mat b1M = Mat(1, 25, CV_64FC1, b1).t();
    Mat IW1_1M = Mat(48, 25, CV_64FC1, IW1_1).t();

    // Layer 2
    Mat b2M = Mat(1, 48, CV_64FC1, b2).t();
    Mat LW2_1M = Mat(25, 48, CV_64FC1, LW2_1).t();

    // input 1
    Mat y1_step1_gainM = Mat(1, 48, CV_64FC1, y1_step1_gain).t();
    Mat y1_step1_xoffsetM = Mat(1, 48, CV_64FC1, y1_step1_xoffset).t();
    double y1_step1_ymin = -1;



    // ===== SIMULATION ========


    // Input 1
    Mat xp1 = mapminmax_apply(x1, x1_step1_gainM, x1_step1_xoffsetM, x1_step1_ymin);

    Mat  temp = b1M + IW1_1M*xp1;

    // Layer 1
    Mat a1M = transig_apply(temp);

    // Layer 2
    Mat a2M = b2M + LW2_1M*a1M;

    // Output 1
    Mat y1M = mapminmax_reverse(a2M, y1_step1_gainM, y1_step1_xoffsetM, y1_step1_ymin);

    return y1M;
}

ヘッダーのバイアスの例は次のとおりです。

static double b2[1][48] = {
        {-0.19879, 0.78254, -0.87674, -0.5827, -0.017464, 0.13143, -0.74361, 0.4645, 0.25262, 0.54249, -0.22292, -0.35605, -0.42747, 0.044744, -0.14827, -0.27354, 0.77793, -0.4511, 0.059346, 0.29589, -0.65137, -0.51788, 0.38366, -0.030243, -0.57632, 0.76785, -0.36374, 0.19446, 0.10383, -0.57989, -0.82931, 0.15301, -0.89212, -0.17296, -0.16356, 0.18946, -1.0032, 0.48846, -0.78148, 0.66608, 0.14946, 0.1972, -0.93501, 0.42523, -0.37773, -0.068266, -0.27003, 0.1196}};

現在、GoogleがTensorflowを公開したため、これは廃止されました。

0
beniroquai

したがって、解決策は(すべての部分を修正した後)になります

ここではMatlabでソリューションを提供していますが、tanh()関数がある場合は、それを任意のプログラミング言語に簡単に変換できます。これは、ネットワークオブジェクトのフィールドと必要な操作を表示するためのものです。

  • エクスポートしたい訓練されたアン(ネットワークオブジェクト)があると仮定します
  • 訓練されたアンの名前がtrained_annであると仮定します

これは、エクスポートとテストのためのスクリプトです。テストスクリプトは、元のネットワーク結果をmy_ann_evaluation()結果と比較します

% Export IT
exported_ann_structure = my_ann_exporter(trained_ann);

% Run and Compare 
% Works only for single INPUT vector
% Please extend it to MATRIX version by yourself
input = [12 3 5 100];
res1 = trained_ann(input')';
res2 = my_ann_evaluation(exported_ann_structure, input')';

次の2つの機能が必要な場合

最初のmy_ann_exporter

function [ my_ann_structure ] = my_ann_exporter(trained_netw)
% Just for extracting as Structure object
my_ann_structure.input_ymax = trained_netw.inputs{1}.processSettings{1}.ymax;
my_ann_structure.input_ymin = trained_netw.inputs{1}.processSettings{1}.ymin;
my_ann_structure.input_xmax = trained_netw.inputs{1}.processSettings{1}.xmax;
my_ann_structure.input_xmin = trained_netw.inputs{1}.processSettings{1}.xmin;

my_ann_structure.IW = trained_netw.IW{1};
my_ann_structure.b1 = trained_netw.b{1};
my_ann_structure.LW = trained_netw.LW{2};
my_ann_structure.b2 = trained_netw.b{2};

my_ann_structure.output_ymax = trained_netw.outputs{2}.processSettings{1}.ymax;
my_ann_structure.output_ymin = trained_netw.outputs{2}.processSettings{1}.ymin;
my_ann_structure.output_xmax = trained_netw.outputs{2}.processSettings{1}.xmax;
my_ann_structure.output_xmin = trained_netw.outputs{2}.processSettings{1}.xmin;
end

2番目のmy_ann_evaluation:

function [ res ] = my_ann_evaluation(my_ann_structure, input)
% Works with only single INPUT vector
% Matrix version can be implemented

ymax = my_ann_structure.input_ymax;
ymin = my_ann_structure.input_ymin;
xmax = my_ann_structure.input_xmax;
xmin = my_ann_structure.input_xmin;
input_preprocessed = (ymax-ymin) * (input-xmin) ./ (xmax-xmin) + ymin;

% Pass it through the ANN matrix multiplication
y1 = tanh(my_ann_structure.IW * input_preprocessed + my_ann_structure.b1);

y2 = my_ann_structure.LW * y1 + my_ann_structure.b2;

ymax = my_ann_structure.output_ymax;
ymin = my_ann_structure.output_ymin;
xmax = my_ann_structure.output_xmax;
xmin = my_ann_structure.output_xmin;
res = (y2-ymin) .* (xmax-xmin) /(ymax-ymin) + xmin;
end
0
fermat4214