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:
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?
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]:
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]:
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:
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:
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:
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);
où 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
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).
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
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!
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.
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.
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