web-dev-qa-db-fra.com

Exporter un réseau de neurones formé avec MATLAB dans d'autres langages de programmation

J'ai formé un réseau de neurones à l'aide de la boîte à outils MATLAB Neural Network, et en particulier à l'aide de la commande nprtool, qui fournit une interface graphique simple permettant d'utiliser les fonctions de la boîte à outils et d'exporter un objet net contenant les informations relatives au NN généré.

De cette manière, j'ai créé un réseau de neurones fonctionnel, que je peux utiliser comme classificateur, et un diagramme le représentant est le suivant:

Diagram representing the Neural Network

Il y a 200 entrées, 20 neurones dans la première couche cachée et 2 neurones dans la dernière couche qui fournissent une sortie bidimensionnelle.

Ce que je veux faire, c'est utiliser le réseau dans un autre langage de programmation (C #, Java, ...).

Afin de résoudre ce problème, j'essaie d'utiliser le code suivant dans MATLAB:

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

En supposant que input soit un tableau monodimensionnel de 200 éléments, le code précédent fonctionnerait si net.IW{1} est une matrice de 20x200 (20 neurones, 200 poids).

Le problème est que j'ai remarqué que size(net.IW{1}) renvoie des valeurs inattendues:

>> size(net.IW{1})

    ans =

    20   199

J'ai eu le même problème avec un réseau avec 10000 entrées. Dans ce cas, le résultat n'était pas 20x10000, mais quelque chose comme 20x9384 (je ne me souviens pas de la valeur exacte).

La question est donc: comment puis-je obtenir les poids de chaque neurone? Et après cela, quelqu'un peut-il m'expliquer comment puis-je les utiliser pour produire la même sortie de MATLAB?

15
Vito Gentile

J'ai résolu les problèmes décrits ci-dessus et je pense qu'il est utile de partager ce que j'ai appris.

Locaux

Tout d'abord, nous avons besoin de définitions. Considérons l'image suivante, tirée de [1]:

A scheme of Neural Network

Dans la figure ci-dessus,IWsignifie poids initiaux: ils représentent les poids des neurones de la couche 1 , chacun étant connecté à chaque entrée. , comme le montre l'image suivante [1]:

All neurons are connected with all inputs

Tous les autres poids s'appellent poids de calque (LWdans la première figure), qui sont également connectés à chaque sortie du calque précédent. Dans notre cas d'étude, nous utilisons un réseau avec seulement deux couches, nous n'utiliserons donc qu'un seul réseau LW pour résoudre nos problèmes.

Solution du problème

Après l'introduction ci-dessus, nous pouvons procéder en divisant le problème en deux étapes:

  • Forcer le nombre de poids initiaux à correspondre à la longueur du tableau en entrée
  • Utilisez les poids pour implémenter et utiliser le réseau de neurones qui vient d'être formé dans d'autres langages de programmation

A - Force le nombre de poids initiaux à correspondre à la longueur du tableau en entrée

En utilisant nprtool, nous pouvons former notre réseau et à la fin du processus, nous pouvons également exporter dans l'espace de travail des informations sur l'ensemble du processus de formation. En particulier, nous devons exporter:

  • un objet réseau MATLAB représentant le réseau de neurones créé
  • le tableau d'entrée utilisé pour former le réseau
  • le tableau cible utilisé pour former le réseau

En outre, nous devons générer un fichier M contenant le code utilisé par MATLAB pour créer le réseau de neurones, car nous devons le modifier et modifier certaines options de formation.

L'image suivante montre comment effectuer ces opérations:

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

Le code M généré sera similaire au suivant:

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)

Avant de commencer le processus de formation, nous devons supprimer toutes les fonctions de prétraitement et de post-traitement que MATLAB exécute sur les entrées et les sorties. Cela peut être fait en ajoutant les lignes suivantes juste avant les lignes % Train and Apply Network:

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

Après ces modifications de la fonction create_pr_net(), nous pouvons simplement l’utiliser pour créer notre réseau de neurones final:

net = create_pr_net(input, target);

input et target sont les valeurs que nous avons exportées via nprtool.

De cette façon, nous sommes sûrs que le nombre de poids est égal à la longueur du tableau en entrée. Ce processus est également utile pour simplifier le portage vers d'autres langages de programmation.

B - Implémentation et utilisation du réseau de neurones récemment formé dans d'autres langages de programmation

Avec ces changements, nous pouvons définir une fonction comme celle-ci:

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

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

Dans ce code, nous utilisons les tableaux IW et LW mentionnés ci-dessus, mais également les polarisations b, utilisées dans le schéma de réseau par la variable nprtool. Dans ce contexte, nous ne nous soucions pas du rôle rôle des biais ; nous devons simplement les utiliser car nprtool le fait.

Maintenant, nous pouvons utiliser la fonction classify() définie ci-dessus ou la fonction sim() également, en obtenant les mêmes résultats, comme illustré dans l'exemple suivant:

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

ans =

    0.9759
   -0.1867
   -0.1891

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

ans =

   0.9759   
  -0.1867
  -0.1891

Il est évident que la fonction classify() peut être interprétée comme un pseudocode, puis implémentée dans tous les langages de programmation dans lesquels il est possible de définir la fonction MATLAB tansig() [2] et les opérations de base entre les tableaux.

Références

[1] Howard Demuth, Mark Beale et Martin Hagan: Boîte à outils Neural Network 6 - Guide de l'utilisateur}, MATLAB

[2] Mathworks, tansig - Fonction de transfert sigmoïde tangente hyperbolique}, Centre de documentation MATLAB

Notes complémentaires

Jetez un coup d'œil au réponse de robott et au réponse de Sangeun Chi pour plus de détails).

14
Vito Gentile

Grâce aux réponses VitoShadow et robott, je peux exporter les valeurs du réseau neuronal Matlab vers d'autres applications.

Je les apprécie vraiment, mais j’ai trouvé des erreurs triviales dans leurs codes et je veux les corriger.

1) Dans les codes VitoShadow,

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

2) Dans les codes de prétraitement robott, il serait plus facile d'extraire xmax et xmin de la variable net que de les calculer.

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

3) Dans les codes de post-traitement robott,

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;

Vous pouvez vérifier et confirmer manuellement les valeurs comme suit:

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

-> données prétraitées

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

-> données traitées par le réseau de neurones

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

-> données post-traitées

Référence: http://www.mathworks.com/matlabcentral/answers/14517-processing-of-i-p-data

3
Sangeun Chi

C'est une petite amélioration par rapport à la réponse du grand Vito Gentile.

Si vous voulez utiliser utilisez les fonctions ' mapminmax ', vous devez faire attention car 'mapminmax' dans Matlab se normalise par ROW et non par colonne!

Voici ce que vous devez ajouter à la fonction "classify" supérieure pour conserver un pré/post-traitement cohérent:

[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

Et ceci à la fin de la fonction:

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

C'est du code Matlab, mais on peut facilement le lire en pseudocode. J'espère que cela vous sera utile!

3
Gabrer

J'ai essayé d'implémenter un NN simplement à deux couches en C++ à l'aide d'OpenCV, puis j'ai exporté les poids vers Android, qui fonctionnaient bien en mode silencieux. J'ai écrit un petit script qui génère un fichier d'en-tête avec les poids appris et qui est utilisé dans le code suivant snippé.

// 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;
}

exemple pour un biais dans l'en-tête pourrait être ceci:

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}};

Maintenant, que Google a publié Tensorflow, cela est devenu obsolète.

0
beniroquai

La solution devient alors (après correction de toutes les parties)

Ici, je donne une solution dans Matlab, mais si vous avez la fonction tanh (), vous pouvez facilement la convertir en n’importe quel langage de programmation. C'est juste pour montrer les champs de l'objet réseau et les opérations dont vous avez besoin.

  • Supposons que vous souhaitiez exporter un ann (objet réseau) formé
  • Supposons que le nom de la personne formée est training_ann

Voici le script pour exporter et tester. Le script de test compare le résultat réseau initial avec le résultat 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')';

où vous avez besoin des deux fonctions suivantes

Premier 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

Deuxième 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