C/C++ Neural Networks
Introduction
This project was created in order to enable FeedForward Neural Network API for C programmers.
It enables training, saving, loading and activation of FeedForward neural networks.
The training is performed using Backpropagation algorithm.
The currently supported activation functions are Sigmoid, Tanh, Linear (sum of [inputs * weights]), Step Function (used mainly in Perceptron).
For perceptrons, it's recommended to use the Perceptron API
Special Features
- Starting from the version from 2015-10-29, it is enabled to accelerate the training process using the function "FeedForwardNetwork_train_fast()". In contrary to the usual training function "FeedForwardNetwork_train()", the new fast function also takes a pointer to a user function. The user's function gets 2 double arrays: an array of the expected results and an array of the actual network output. The user's function should return 1 in case that the actual output is satisfying and 0 otherwise. This process saves a lot of training time, skipping the back-propagation and weights updating for samples which are considered satisfying
This feature can be very useful is some cases:
- When you train your network to return a binary result. E.g. if you decide that a result greater than 0.5 means that a sample is classified as "true", you can implement your function to return 1 whenever the expected result is 1 and the output is greater than 0.5 (or when the expected result is 0 and the output is less than 0.5)
- When you have many output neurons, each representing a class, and you take the output with the maximun values as the "winning" class. You can implement your function to return 1 if the "winning class" in the output is the same as in the expected result
API & Usage Example
Usage Example
#include <stdio.h>
#include <stdlib.h>
#include "freeNN/feedforwardnetwork.h"
// Network constants
static const unsigned NUM_INPUTS = 2;
static const unsigned NUM_OUTPUTS = 4;
static const unsigned NUM_HIDDEN_LEVELS = 1;
static const unsigned NUM_NEURONS_IN_HIDDEN_LEVELS[] = {6};
static const int WITH_BIAS = 1;
static const double TRAINING_RATE = 0.08;
static const double OUTPUT_TRAINING_RATE = 0.01;
static const NeuronActivationFunction HIDDEN_LAYER_TYPE = TANH;
static const NeuronActivationFunction OUTPUT_LAYER_TYPE = LINEAR;
static const unsigned NUM_EPOCHS = 10000;
int main() {
// Defining network custom settings
FeedForwardNetworkSettings *settings = FeedForwardNetworkSettings_new_default();
settings->withBias_ = WITH_BIAS;
settings->trainingRate_ = TRAINING_RATE;
settings->outputLayerTrainingRate_ = OUTPUT_TRAINING_RATE;
settings->hiddenLayerType_ = HIDDEN_LAYER_TYPE;
settings->outputLayerType_ = OUTPUT_LAYER_TYPE;
// Creating instance of the network
FeedForwardNetwork *ffn = FeedForwardNetwork_new(NUM_INPUTS, NUM_OUTPUTS, NUM_HIDDEN_LEVELS, NUM_NEURONS_IN_HIDDEN_LEVELS, settings);
double inputs[NUM_INPUTS];
double expectedOutputs[NUM_OUTPUTS];
double *results;
int i;
// Training 1st output neuron to function as AND
// 2nd output neuron to function as OR
// 3rd output neuron to function as XOR
// 4th output neuron to a custon function (0,0 -> 9.128 , 0,1 -> -4.27 , 1,0 -> 3.927 , 1,1 -> -8.532)
for (i = 0 ; i < NUM_EPOCHS ; i++) {
inputs[0] = 0;
inputs[1] = 0;
expectedOutputs[0] = 0;
expectedOutputs[1] = 0;
expectedOutputs[2] = 0;
expectedOutputs[3] = 9.128;
FeedForwardNetwork_train(ffn, inputs, expectedOutputs);
inputs[0] = 0;
inputs[1] = 1;
expectedOutputs[0] = 0;
expectedOutputs[1] = 1;
expectedOutputs[2] = 1;
expectedOutputs[3] = -4.27;
FeedForwardNetwork_train(ffn, inputs, expectedOutputs);
inputs[0] = 1;
inputs[1] = 0;
expectedOutputs[0] = 0;
expectedOutputs[1] = 1;
expectedOutputs[2] = 1;
expectedOutputs[3] = 3.927;
FeedForwardNetwork_train(ffn, inputs, expectedOutputs);
inputs[0] = 1;
inputs[1] = 1;
expectedOutputs[0] = 1;
expectedOutputs[1] = 1;
expectedOutputs[2] = 0;
expectedOutputs[3] = -8.532;
FeedForwardNetwork_train(ffn, inputs, expectedOutputs);
}
// Printing results
printf("--------------------------------------------------------------------\n");
printf("| Input | AND Neuron | OR Neuron | XOR Neuron | Custom Neuron |\n");
printf("--------------------------------------------------------------------\n");
inputs[0] = 0;
inputs[1] = 0;
results = FeedForwardNetwork_activate(ffn, inputs);
printf("| (0,0) | %f | %f | %f | %f |\n", results[0], results[1], results[2], results[3]);
printf("--------------------------------------------------------------------\n");
free(results);
inputs[0] = 0;
inputs[1] = 1;
results = FeedForwardNetwork_activate(ffn, inputs);
printf("| (0,1) | %f | %f | %f | %f |\n", results[0], results[1], results[2], results[3]);
printf("--------------------------------------------------------------------\n");
free(results);
inputs[0] = 1;
inputs[1] = 0;
results = FeedForwardNetwork_activate(ffn, inputs);
printf("| (1,0) | %f | %f | %f | %f |\n", results[0], results[1], results[2], results[3]);
printf("--------------------------------------------------------------------\n");
free(results);
inputs[0] = 1;
inputs[1] = 1;
results = FeedForwardNetwork_activate(ffn, inputs);
printf("| (1,1) | %f | %f | %f | %f |\n", results[0], results[1], results[2], results[3]);
printf("--------------------------------------------------------------------\n");
free(results);
// Calling the destructor for the network
FeedForwardNetwork_destroy(ffn);
return 0;
}
Example Outputs
* Your results might appear a little different, as the first initialization of the network is random
--------------------------------------------------------------------
| Input | AND Neuron | OR Neuron | XOR Neuron | Custom Neuron |
--------------------------------------------------------------------
| (0,0) | 0.000000 | -0.000000 | 0.000000 | 9.128000 |
--------------------------------------------------------------------
| (0,1) | -0.000000 | 1.000000 | 1.000000 | -4.270000 |
--------------------------------------------------------------------
| (1,0) | -0.000000 | 1.000000 | 1.000000 | 3.927000 |
--------------------------------------------------------------------
| (1,1) | 1.000000 | 1.000000 | 0.000000 | -8.532000 |
--------------------------------------------------------------------
Perceptron Usage Example
#include <stdio.h>
#include "freeNN/perceptron.h"
int main() {
// Constants
const int TRAINING_ITERATIONS = 160;
const int NUM_OF_INPUTS = 2;
const double TRAINING_RATE = 0.2;
const double ZERO_ZERO[] = {0, 0};
const double ZERO_ONE[] = {0, 1};
const double ONE_ZERO[] = {1, 0};
const double ONE_ONE[] = {1, 1};
int i;
// Creating Perceptron instances
Perceptron *pAND = Perceptron_new(NUM_OF_INPUTS, TRAINING_RATE);
Perceptron *pOR = Perceptron_new(NUM_OF_INPUTS, TRAINING_RATE);
// Printing the results of the randomly generated perceptrons (BEFORE TRAINING)
printf("Results for 'OR' perceptron, BEFORE training:\n");
printf("Input: (0,0). Result: %f\n", Perceptron_getResult(pOR, ZERO_ZERO));
printf("Input: (0,1). Result: %f\n", Perceptron_getResult(pOR, ZERO_ONE));
printf("Input: (1,0). Result: %f\n", Perceptron_getResult(pOR, ONE_ZERO));
printf("Input: (1,1). Result: %f\n", Perceptron_getResult(pOR, ONE_ONE));
printf("Results for 'AND' perceptron, BEFORE training:\n");
printf("Input: (0,0). Result: %f\n", Perceptron_getResult(pAND, ZERO_ZERO));
printf("Input: (0,1). Result: %f\n", Perceptron_getResult(pAND, ZERO_ONE));
printf("Input: (1,0). Result: %f\n", Perceptron_getResult(pAND, ONE_ZERO));
printf("Input: (1,1). Result: %f\n", Perceptron_getResult(pAND, ONE_ONE));
// Training each of the perceptrons
for (i = 0 ; i < TRAINING_ITERATIONS ; i++) {
Perceptron_train(pAND, ZERO_ZERO, 0);
Perceptron_train(pAND, ZERO_ONE, 0);
Perceptron_train(pAND, ONE_ZERO, 0);
Perceptron_train(pAND, ONE_ONE, 1);
Perceptron_train(pOR, ZERO_ZERO, 0);
Perceptron_train(pOR, ZERO_ONE, 1);
Perceptron_train(pOR, ONE_ZERO, 1);
Perceptron_train(pOR, ONE_ONE, 1);
}
// Printing the results of the trained perceptrons (AFTER TRAINING)
printf("Results for 'OR' perceptron, AFTER training:\n");
printf("Input: (0,0). Result: %f\n", Perceptron_getResult(pOR, ZERO_ZERO));
printf("Input: (0,1). Result: %f\n", Perceptron_getResult(pOR, ZERO_ONE));
printf("Input: (1,0). Result: %f\n", Perceptron_getResult(pOR, ONE_ZERO));
printf("Input: (1,1). Result: %f\n", Perceptron_getResult(pOR, ONE_ONE));
printf("Results for 'AND' perceptron, AFTER training:\n");
printf("Input: (0,0). Result: %f\n", Perceptron_getResult(pAND, ZERO_ZERO));
printf("Input: (0,1). Result: %f\n", Perceptron_getResult(pAND, ZERO_ONE));
printf("Input: (1,0). Result: %f\n", Perceptron_getResult(pAND, ONE_ZERO));
printf("Input: (1,1). Result: %f\n", Perceptron_getResult(pAND, ONE_ONE));
return 0;
}
Perceptron Example Results
Results for 'OR' perceptron, BEFORE training:
Input: (0,0). Result: 0.000000
Input: (0,1). Result: 1.000000
Input: (1,0). Result: 1.000000
Input: (1,1). Result: 1.000000
Results for 'AND' perceptron, BEFORE training:
Input: (0,0). Result: 0.000000
Input: (0,1). Result: 0.000000
Input: (1,0). Result: 0.000000
Input: (1,1). Result: 0.000000
Results for 'OR' perceptron, AFTER training:
Input: (0,0). Result: 0.000000
Input: (0,1). Result: 1.000000
Input: (1,0). Result: 1.000000
Input: (1,1). Result: 1.000000
Results for 'AND' perceptron, AFTER training:
Input: (0,0). Result: 0.000000
Input: (0,1). Result: 0.000000
Input: (1,0). Result: 0.000000
Input: (1,1). Result: 1.000000
FeedForward Network API
/*
Constructor (Use settings=NULL for default settings)
*/
FeedForwardNetwork *FeedForwardNetwork_new(unsigned numInputs, unsigned numOutputs, unsigned numHiddenLevels, const unsigned *numNeuronsInHiddenLevels, FeedForwardNetworkSettings *settings);
/*
Constructor from string
(An instance of FeedForward Network can be saved into a string -> This string can be used to construct another identical network)
*/
FeedForwardNetwork *FeedForwardNetwork_new_from_string(const char *networkStr);
/*
Constructor from file
Similar to the "from String" constructor
*/
FeedForwardNetwork *FeedForwardNetwork_new_from_file(const char *filePath);
/*
Destructor
*/
void FeedForwardNetwork_destroy(FeedForwardNetwork *network);
/*
Train the network for given input vector and desired output vector
*/
void FeedForwardNetwork_train(FeedForwardNetwork *network, const double *inputs, const double *expectedOutputs);
/*
Train the network for given input vector, a desired output vector.
This function is faster than the regular "train" function, as it enables user to supply a function, which decides whether to update the network or not, according to the desired output and the actual output
*/
int FeedForwardNetwork_train_fast(FeedForwardNetwork *network, const double *inputs, const double *expectedOutputs, int (*binaryChecker)(const double*, const double*));
/*
Activate the network on a given input vector (I.e. get the values of the output level neurons)
*/
double *FeedForwardNetwork_activate(const FeedForwardNetwork *network, const double *inputs);
/*
Get a string which represents the network (Can be used to clone the network)
*/
char *FeedForwardNetwork_toString(const FeedForwardNetwork *network);
/*
Save the string which represents the network to a file
*/
int FeedForwardNetwork_saveToFile(const FeedForwardNetwork *network, const char *pathToFile);
/*
Get a new default instance of FeedForwardNetworkSettings
*/
FeedForwardNetworkSettings *FeedForwardNetworkSettings_new_default();
Perceptron API
/*
Constructor
*/
Perceptron *Perceptron_new(unsigned numOfInputs, double trainingRate);
/*
Constructor from a string (A perceptron object can be saved into a string -> can use string to create another identical object)
*/
Perceptron *Perceptron_new_from_string(const char *perceptronStr);
/*
Destructor
*/
void Perceptron_destroy(Perceptron *perceptron);
/*
Get the sum of [inputs * weights]
*/
double Perceptron_getValue(const Perceptron *perceptron, const double inputs[]);
/*
Change the training rate
*/
void Perceptron_setTrainingRate(Perceptron *perceptron, double trainingRate);
/*
Train the perceptron on a given input vector and a result
*/
void Perceptron_train(Perceptron *perceptron, const double inputs[], int expectedResult);
/*
Get the activation value result
*/
double Perceptron_getResult(const Perceptron *perceptron, const double inputs[]);
/*
Get the weight at the i'th index
*/
double Perceptron_getWeightAt(const Perceptron *perceptron, unsigned index);
/*
Get the weights vector
*/
const double *Perceptron_getWeights(const Perceptron *perceptron);
/*
Get the length of the input vector
*/
unsigned Perceptron_getNumOfInputs(const Perceptron *perceptron);
/*
get the Perceptron step-function threshold
*/
double Perceptron_getThreshold(const Perceptron *perceptron);
/*
Get the training rate
*/
double Perceptron_getTrainingRate(const Perceptron *perceptron);
/*
Change the weight of the i'th input
*/
void Perceptron_setWeightAt(Perceptron *perceptron, unsigned index, double weight);
/*
Override the weights vector with another one
*/
void Perceptron_setWeights(Perceptron *perceptron, const double *weights);
/*
Change the Perceptron step-function threshold
*/
void Perceptron_setThreshold(Perceptron *perceptron, double threshold);
/*
Convert a Perceptron instance into string (Useful for saving/loading Perceptrons and for deeply cloning them)
*/
char *Perceptron_toString(const Perceptron *perceptron);