next up previous contents
Next: To do list Up: Introduction Previous: Extended XOR program -

Object oriented back-propagation program

After understanding the back-propagation programs I created it's time to make an Object Oriented version. This page shows my first results, a working Object Oriented back-propagation network, consisting of 3 files. The main file creates a network, the network class file for the network options and a neuron class for all the neurons in the network.

With this program code it's possible to create a back-propagation network with x inputs, y hidden units and z ouput units.

The main file back3.cpp:

#include <iostream.h>
#include <math.h>
#include "network.h"

#define ITER 10000 // The number of iterations


/*
 * The main function
 */
void main() {
    // Input values
    float inputArray1[3]  = {0.0,0.0,0.0};
    float inputArray2[3]  = {0.0,0.0,1.0};
    float inputArray3[3]  = {0.0,1.0,0.0};
    float inputArray4[3]  = {0.0,1.0,1.0};    
    float inputArray5[3]  = {1.0,0.0,0.0};    
    float inputArray6[3]  = {1.0,0.0,1.0};    
    float inputArray7[3]  = {1.0,1.0,0.0};    
    float inputArray8[3]  = {1.0,1.0,1.0};    
    
    // Output values
    float outputArray1[1] = {1.0};
    float outputArray2[1] = {0.0};

    // Network 3 input units, 3 hidden layer units, 1 ouput unit
    Network network(3,3,1); 
   
    // Train
    for(int i=0; i<ITER; i++){
        network.Train(inputArray1, outputArray1);
        network.Train(inputArray2, outputArray2);
        network.Train(inputArray3, outputArray1);
        network.Train(inputArray4, outputArray2);
        network.Train(inputArray5, outputArray1);
        network.Train(inputArray6, outputArray2);
        network.Train(inputArray7, outputArray1);
        network.Train(inputArray8, outputArray2);
    }
    
    // Run
    network.Run(inputArray1);
    network.Run(inputArray2);
    network.Run(inputArray3);
    network.Run(inputArray4);
    network.Run(inputArray5);
    network.Run(inputArray6);
    network.Run(inputArray7);
    network.Run(inputArray8);
}

The network.h file:

#include <iostream.h>
#include <math.h>
#include <stdlib.h>     
#include <time.h>       
#include "neuron.h"

#define LEARNRATE 0.5   // The learnrate for the network

class Network {
    private:
        int inputNr;
        int hiddenNr;
        int outputNr;
        
        Neuron * inputLayer;
        Neuron * hiddenLayer;
        Neuron * outputLayer;
        
        // Functions
        float Network::Sigmoid(float num);
        	
    public:
        // Constructor                
        Network(int NumberOfInput, int NumberOfHidden, int NumberOfOutput);
                
        // Destructor
        ~Network(){};
        
        // Functions
        void Network::Train(float * input, float * desired);
        void Network::Run(float * input);
};


Network::Network(int NumberOfInput, int NumberOfHidden, int NumberOfOutput){
    // Store the network size
    inputNr  = NumberOfInput;
    hiddenNr = NumberOfHidden;
    outputNr = NumberOfOutput;
    
    // Change results of rand() every time    
	srand( time( NULL ) ); 
	
	inputLayer  = new Neuron[NumberOfInput ](NumberOfHidden); 
	hiddenLayer = new Neuron[NumberOfHidden](NumberOfOutput);
	outputLayer = new Neuron[NumberOfOutput](0); // The output layer doesn't have connections to another layer
}


/*
 * Name:   Train
 * Input:  Two arrays with input values and desired result values
 * Pre:    The two arrays contain floats and the nuber of floats doesn't 
 *         exceed the number of input units and output units.
 * Post:   The network weights and outputs have been altered
 */
void Network::Train(float * input, float * desired){
    float * outputHidden = new float[hiddenNr]; // Array contains the calculated output of the hidden units
    float * outputOuput  = new float[outputNr]; // Array contains the calculated output of the ouput  units
    
    // Calculate the product of the input to the hidden unit and 
    // the weight from the input to the hidden unit    
    for(int i=0; i<hiddenNr; i++){ 
        outputHidden[i] = 0;
        for(int j=0; j<inputNr ; j++){
            outputHidden[i] += input[j] * inputLayer[j].Weight(i); 
        }
        outputHidden[i] += hiddenLayer[i].Output();
        outputHidden[i] = Sigmoid(outputHidden[i]);
        //cout << "Hidden unit " << i << " : " << outputHidden[i] << endl;
    }

    // Calculate the product of the input to the ouput unit and 
    // the weight from the hidden to the ouput unit    
    for(int i=0; i<outputNr; i++){ 
        outputOuput[i] = 0;
        for(int j=0; j<hiddenNr ; j++){
            outputOuput[i] += outputHidden[j] * hiddenLayer[j].Weight(i); 
        }
        outputOuput[i] += outputLayer[i].Output();
        outputOuput[i] = Sigmoid(outputOuput[i]);
        //cout << "Ouput unit " << i << " : " << outputOuput[i] << endl;
    }    
    
    // The code above is also Run function    
    
    
    // Calculate the error
    float * outputError = new float[outputNr]; // Array contains the output units error
    float * hiddenError = new float[hiddenNr]; // Array contains the hidden units error      

    for(int i=0; i<outputNr; i++){ 
        outputError[i] = outputOuput[i] * (1-outputOuput[i]) * (desired[i] - outputOuput[i]);

        for(int j=0; j<hiddenNr; j++){ 
            hiddenError[j] = outputHidden[j] * (1-outputHidden[j]) * outputError[i] * hiddenLayer[j].Weight(i);
            //cout << "Hidden unit " << i << ", layer " << j << " error : " << hiddenError[j] << endl;
        }

        //cout << "Ouput unit " << i << " error : " << outputError[i] << endl;
    }


    // Adjust the weights of the input layer to the hidden layer
    for(int i=0; i<inputNr; i++){ 
        for(int j=0; j<hiddenNr; j++){
            inputLayer[i].SetWeight(j, (inputLayer[i].Weight(j) + LEARNRATE * hiddenError[j] * input[i]) );
            //cout << "Weight input unit " << i << " to hidden " << j << " is : " << inputLayer[i].Weight(j) << endl;
        }
    }
    
    // Adjust the weights of the hidden layer to the output layer
    for(int i=0; i<hiddenNr; i++){ 
        for(int j=0; j<outputNr; j++){
            hiddenLayer[i].SetWeight(j, (hiddenLayer[i].Weight(j) + LEARNRATE * outputError[j] * outputHidden[i]) );
            //cout << "Weight hidden unit " << i << " to output " << j << " is : " << hiddenLayer[i].Weight(j) << endl;
        }
    }    

    // Adjust the outputs of the hidden and the ouput units
    for(int i=0; i<hiddenNr; i++){ 
        hiddenLayer[i].SetOutput( hiddenLayer[i].Output() + LEARNRATE * hiddenError[i] );
        //cout << "Hidden layer unit " << i << " has new output: " << hiddenLayer[i].Output() << endl;
    }      

    for(int i=0; i<outputNr; i++){ 
        outputLayer[i].SetOutput( outputLayer[i].Output() + LEARNRATE * outputError[i] );
        //cout << "Output layer unit " << i << " has new output: " << outputLayer[i].Output() << endl;
    }      
}

/*
 * Name:   Run
 * Input:  Arrays with input values 
 * Pre:    The array contains floats and the nuber of floats doesn't 
 *         exceed the number of input units.
 * Post:   The output of the output unit(s) will be shown
 */
void Network::Run(float * input){
    float * outputHidden = new float[hiddenNr]; // Array contains the calculated output of the hidden units
    float * outputOuput  = new float[outputNr]; // Array contains the calculated output of the ouput  units
    
    for(int i=0; i<hiddenNr; i++){ 
        outputHidden[i] = 0;        
        for(int j=0; j<inputNr ; j++){
            // Calculate the product of the input to the hidden unit and 
            // the weight from the input to the hidden unit
            outputHidden[i] += input[j] * inputLayer[j].Weight(i); 
        }
        outputHidden[i] += hiddenLayer[i].Output();
        outputHidden[i] = Sigmoid(outputHidden[i]);
        //cout << "Hidden unit " << i << " : " << outputHidden[i] << endl;
    }
    
    for(int i=0; i<outputNr; i++){ 
        outputOuput[i] = 0;        
        for(int j=0; j<hiddenNr ; j++){
            // Calculate the product of the input to the ouput unit and 
            // the weight from the hidden to the ouput unit
            outputOuput[i] += outputHidden[j] * hiddenLayer[j].Weight(i); 
        }
        outputOuput[i] += outputLayer[i].Output();
        outputOuput[i] = Sigmoid(outputOuput[i]);
        cout << "Ouput unit " << i << " : " << outputOuput[i] << endl;
    }    
}


/*
 * Name:   Sigmoid
 * Input:  One input
 * Pre:    The input is a float (positive or negative)
 * Post:   Returns the result of the sigmoid function (also known as the logistic function)
 */
float Network::Sigmoid(float num){
    return (float)(1/(1+exp(-num)));
}

The neuron.h file:

#include <iostream.h>
#include <math.h>
#include <stdlib.h>  // rand() function

class Neuron{
    private:
        float output;
        float * weights;

        // Functions
        float Random();
            
    public:
        // Constructor
        Neuron(int NumberOfLowerLayer);
                
        // Destructor
        ~Neuron(){};
        
        // Functions
        float Output();
        void  SetOutput(float newOutput);
        float Weight(int weight);
        void  SetWeight(int weight, float newWeight);
};

Neuron::Neuron(int NumberOfLowerLayer) {
    weights  = new float[NumberOfLowerLayer];
    
    // Set random value for own output
    output = Random();
    
    // Set random values for weights to lower layer
    for(int i=0; i<NumberOfLowerLayer ; i++){
        weights[i] = Random();
    }
}


/*
 * Name:   Random
 * Input:  None
 * Pre:    None
 * Post:   Returns a random value between -1 and 1
 */
float Neuron::Random(){
    return (2.0 * (rand() / (float) RAND_MAX) -1.0); // random number between 1 and -1
}


/*
 * Name:   Output
 * Input:  None
 * Pre:    None
 * Post:   Returns the output value
 */
float Neuron::Output(){
    return output; 
}

/*
 * Name:   SetOutput
 * Input:  The new output value
 * Pre:    The new output value is a float
 * Post:   The ouput has changed to the new output
 */
void Neuron::SetOutput(float newOutput){
    output = newOutput; 
}

/*
 * Name:   Weight
 * Input:  The number of the weight (0 connects to next layer node 0, 1 to 1 etc etc..)
 * Pre:    The number is an integer and doesn't exceed the number of weights - 1 
 * Post:   Returns the weight
 */
float Neuron::Weight(int weight){
    return weights[weight]; 
}


/*
 * Name:   SetWeight
 * Input:  The number of the weight (0 connects to next layer node 0, 1 to 1 etc etc..), and the new weight
 * Pre:    The number is an integer and doesn't exceed the number of weights - 1 , the new weight is a float
 * Post:   Returns the pointer to the weight
 */
void  Neuron::SetWeight(int weight, float newWeight){
    weights[weight] = newWeight;
}

Download sources here: back3.cpp network.h neuron.h.

The results (10.000 iterations on a Pentium 166):

[rmorrien@escay backpropagation]$ time ./a.out
Ouput unit 0 : 0.994314
Ouput unit 0 : 0.00455939
Ouput unit 0 : 0.995344
Ouput unit 0 : 0.00506789
Ouput unit 0 : 0.995416
Ouput unit 0 : 0.00516222
Ouput unit 0 : 0.996269
Ouput unit 0 : 0.0057142

real    0m2.917s
user    0m2.800s
sys     0m0.120s
[rmorrien@escay backpropagation]$

Update on 11 april 2005
Download java version sources here: Neuron.java Network.java.

Robocode page that contains a robot that uses the java version to train the XOR problem



Copyright © 2001, R.M. Morriën