I want to compile my MATLAB application that uses neural networks to a stand-alone application, but as you know MATLAB can't compile training neural network as stand-alone and can only compile already trained neural networks.
The core of my application consists of training a neural network on an imported data. How can I do that? Is there an alternative way to do this? My MATLAB version is R2014a.
I tried using deploytool
for compiling, but according to the MATLAB Compiler documentation:
THIS CAN BE COMPILED
* Pre-trained network
* command line functions
THIS CANNOT BE COMPILED
* All other command line functionality
* All GUIs provided with toolbox
* Simulink blocks
* gensim
So we get error after compiling the app if we have functions like newff
or patternnet
or other training functions in our code.
I know this is a limitation of the MATLAB Compiler and I searched for solutions for months but I didn't find any workarounds or alternative ways.
Apparently there is a function added to newer versions of MATLAB for using trained neural networks in MATLAB compiler: Deploy Neural Network Functions.
The bottom line is that MATLAB Compiler only supports deploying pre-trained neural networks.
Neural Network Toolbox
Can be compiled:
- Pre-trained network command line functions
Cannot be compiled:
- All other command line functionality
- Apps and UIs
- Simulink blocks
gensim
This means you cannot mcc
-compile functions with training functionality (anything containing TRAIN, ADAPT, etc...), you can only deploy functions that evaluate/simulate an already trained network object (SIM function and the like).
For the supported scenario (deploying a pre-trained network), there are a couple of ways to go about it:
In a normal MATLAB session, load the training data you have, then create and train a neural network using desired settings (keep tuning the network parameters until you are satisfied with the result). Finally save the network object to disk (export as variable in a MAT-file).
% sample regression dataset
[x,y] = simplefit_dataset();
% feed-forward neural network (one hidden layer with 4 neurons)
net = fitnet(4);
net = configure(net, x, y); % configure net to match data
net.trainParam.showWindow = false; % dont show training GUI
net.trainParam.showCommandLine = true; % display output in command line
net.trainParam.show = 1; % display output every iteration
% train networks (data is divided into train/validation/test sets)
net = init(net); % initialize network weights
[net,tr] = train(net, x, y);
% save pre-trained network to MAT-file
save('pretrained_network.mat', 'net')
Next create a deployable function that loads the saved network, and uses it to predict the output given some test data (note the use of %#function
pragma line):
function y_hat = simulateSavedNet(x)
% this is a special pragma for MATLAB Compiler
% used to declare "network" class as dependency in deployed mode
%#function network
% load pre-trained network
S = load('pretrained_network.mat', 'net');
net = S.net;
% predict outcome given input data
%y_hat = net(x);
y_hat = sim(net, x);
end
You can generate a standalone MATLAB function from a pre-trained network object using genFunction
, which can then be used to simulate network output. This functionality was introduced in MATLAB R2013b.
It will basically hard-code network settings, structure, and weights all in one M-function. The generated function is fully compatible with MATLAB Compiler mcc
(compiled into one of the supported targets) as well as MATLAB Coder codegen
(converted to standalone C/C++ code).
% generate standalone M-function from the trained net
genFunction(net, 'simulateStandaloneNet.m', 'MatrixOnly','yes', 'ShowLinks','no')
Here is the code for the generated function:
For simple static neural networks (feed-forward and the like), it is relatively easy to evaluate a pre-trained network and simulate its output (the hard part is training them!).
I've showed how to do this in previous answers. You basically extract the learned weights from the network, then plug those numbers into the transfer functions, feed it the input, and compute the propagated outputs (one layer at-a-time). You will have to take care of applying any pre-/post- processing on the data and use the same transfer functions in each layer.
In fact, this is basically what genFunction
does in the previous approach, only it is automated and handles all cases (works for all kinds of neural networks, not just feed-forward ANNs).
Here is an example from the network trained above:
function y_hat = simulateManualNet(x)
% pre-trained feed-forward neural network
% contains one hidden layer with 4 neurons, 1D input, 1D output
% We assume the default transfer functions, preprocessing, etc..
% The following hardcoded values were obtained
% from net.IW, net.LW, net.b properties using MAT2STR
% hidden layer weights/biases
b1 = [6.0358701949521; 2.72569392497815; 0.584267717191459; -5.1615078566383];
W1 = [-14.0019194910639; 4.90641117353245; -15.2282807645331; -5.26420794868803];
% output layer weights/biases
b2 = -0.756207251486408;
W2 = [0.548462643231606 -0.435802343861239 -0.085111261420613 -1.13679228253379];
% scale input
in = mapFcn(x);
% hidden layer
hid = hiddenLayerTransferFcn(bsxfun(@plus, W1*in, b1));
% output layer
out = outputLayerTransferFcn(W2*hid + b2);
% inverse scale output
y_hat = mapInverseFcn(out);
end
function xx = mapFcn(x)
% linear mapping from [mn,mx] to [-1,1]
mn = 0; mx = 9.97628374728129;
xx = (x - mn)*2 / (mx - mn) - 1;
end
function x = mapInverseFcn(xx)
% inverse linear mapping from [-1,1] to [mn,mx]
mn = 0; mx = 10;
x = (xx + 1) * (mx - mn)/2 + mn;
end
function out = hiddenLayerTransferFcn(in)
% Hyperbolic tangent sigmoid transfer function
out = tanh(in);
end
function out = outputLayerTransferFcn(in)
% Linear transfer function
out = in;
end
The idea here is to generate a Simulink block from the pre-trained network using gensim
, then convert the generated block into a standalone C/C++ application using Simulink Coder (formerly known as Real-Time Workshop). Compiling neural network to Simulink blocks was introduced in R2010b.
I'm not a Simulink expert, so I'll leave it to you to explore this approach:
gensim(net)
In each approach above (first three anyway), the idea is to compile the simulate
functions into one of the supported targets by MATLAB Compiler (standalone executable, shared library, Java package, .NET assembly) then deploy the generated component.
(Actually approach #2 and #3 can also be converted into C/C++ source code using MATLAB Coder codegen
).
Here is how to compile each into a shared library using mcc
command (you could use deploytool
if you like):
% 1) saved network
mcc -v -W cpplib:libANN -T link:lib -N -p nnet simulateSavedNet.m -a pretrained_network.mat
% 2) standalone simulation function (genFunction)
mcc -v -W cpplib:libANN -T link:lib -N simulateStandaloneNet
% 3) standalone simulation function (manual)
mcc -v -W cpplib:libANN -T link:lib -N simulateManualNet
To check the resulting DLLs, below is a C++ test program that links against the produced shared library:
% 1)
mbuild -output test_savedNet -DSIMFCN=simulateSavedNet -I. test_net.cpp libANN.lib
% 2)
mbuild -output test_standaloneNet -DSIMFCN=simulateStandaloneNet -I. test_net.cpp libANN.lib
% 3)
mbuild -output test_manualNet -DSIMFCN=simulateManualNet -I. test_net.cpp libANN.lib
The code for the test program:
#include <cstdlib>
#include <iostream>
#include "libANN.h"
// choose one!
//#define SIMFCN simulateSavedeNet
//#define SIMFCN simulateStandaloneNet
//#define SIMFCN simulateManualNet
int main()
{
// initialize MCR and lib
if (!mclInitializeApplication(NULL,0)) {
std::cerr << "could not initialize the application" << std::endl;
return EXIT_FAILURE;
}
if(!libANNInitialize()) {
std::cerr << "Could not initialize the library" << std::endl;
return EXIT_FAILURE;
}
try {
// create input data (1x5 vector)
double x[] = {1.0, 3.0, 5.0, 7.0, 9.0};
mwArray in(1, 5, mxDOUBLE_CLASS, mxREAL);
in.SetData(x, 5);
// predict network output by simulating network
mwArray out;
SIMFCN(1, out, in);
double y[5];
out.GetData(y, 5);
// show result
std::cout << "y = net(x)" << std::endl;
std::cout << "y = \n" << out << std::endl;
} catch (const mwException& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
} catch (...) {
std::cerr << "Unexpected error thrown" << std::endl;
return EXIT_FAILURE;
}
// cleanup
libANNTerminate();
mclTerminateApplication();
return EXIT_SUCCESS;
}
Here is the output of the resulting programs, compared against the original network object and the source M-functions:
>> net([1 3 5 7 9])
ans =
9.5620 7.7851 7.2716 6.1647 2.4073
>> simulateSavedNet([1 3 5 7 9])
ans =
9.5620 7.7851 7.2716 6.1647 2.4073
>> simulateStandaloneNet([1 3 5 7 9])
ans =
9.5620 7.7851 7.2716 6.1647 2.4073
>> simulateManualNet([1 3 5 7 9])
ans =
9.5620 7.7851 7.2716 6.1647 2.4073
>> !test_savedNet.exe
y = net(x)
y =
9.5620 7.7851 7.2716 6.1647 2.4073
>> !test_standaloneNet.exe
y = net(x)
y =
9.5620 7.7851 7.2716 6.1647 2.4073
>> !test_manualNet.exe
y = net(x)
y =
9.5620 7.7851 7.2716 6.1647 2.4073
This ended up being a long post, but I wanted to cover all possible cases for this question and future ones :) HTH