Project Adaptive Systems:

Study of a Back-Propagating Network for Binary Operators

 

Kristof Van Laerhoven

 

3th year Computer Science

 

   

Description.

The network has 2 input nodes, 2 hidden nodes and 1 output node. The hidden nodes and output nodes receive also input from a bias node. This way, the network can perform the binary operations OR, AND, NOR, NAND, and even XOR. The training is done with back-propagation.


 

 

Source Code:

 

/*             a simple ANN for binary operators,

//                              trained with backpropagation

//                by Kristof Van Laerhoven, 3th year computer science

*/

 

#include <stdio.h>

#include <stdlib.h>

#include <math.h>

#include <conio.h>

 

class BinOp {

                public:

                                BinOp( float newbias=-1, float newlr=0.7, float newmr=0.9,

                                       int newiter=300, char* newfname="xor.dat",

                                       float newlowlim=0, float newupplim=1, float newsteep=0.5 ):

                                    bias(newbias), lr(newlr), mr(newmr), iter(newiter),

                                    fname(newfname), lowlim(newlowlim), upplim(newupplim),

                                    steep(newsteep) {};

 

                                void  set_bias( int value ) { bias = value; };

                                void  set_lr( float value ) { lr = value; };

                                void  set_mr( float value ) { mr = value; };

                                void  set_steep( float value ) { steep = value; };

                                void  set_iter( int value ) { iter = value; };

                                void  set_fname( char* value ) { fname = value; };

                                void  set_lowlim( float value ) { lowlim = value; };

                                void  set_upplim( float value ) { upplim = value; };

                                void  set_weights(float w1, float w2, float w3, float w4, float w5,

                                                                  float w6, float w7, float w8, float w9);

 

                                float get_bias() { return bias; };

                                float get_lr() { return lr; };

                                float get_mr() { return mr; };

                                int   get_iter() { return iter; };

 

                                void  printschema();

                                void  printinfo();

                                void  printtest();

 

                                float train_bp();

 

                                float test( float i1, float i2 );

                                char  stoptest();

 

                protected:

                                void  initweights();

                                void  printweights();

                                char  readfile();

                                void  fillnodes(int i1, int i2, int o);

                                void  adjusterrors();

 

                                float rand_ab( float a, float b );

                                float activationf( float weight1, float O1,

                                                                   float weight2, float O2,

                                                                   float weight3, float O3 );

                                float errorf1( float target, float output );

                                float adjustweight1( float pweight, float delta, float Oi);

                                float errorf2( float Om, float weight, float err );

                                float adjustweight2( float mtoi, float pweight, float I );

 

                private:

                                int   iter; // # of iterations

                                float bias;

                                float lr; // learning rate

                                float mr; // momentum rate

                                float steep; // steepness

 

                                float weight[10]; // the weights

                                float pweight[10];// the previous weights

                                float IA, IB, MA, MB, O; // the node values

                                float target;     // the actual target

 

                                float lowlim, upplim; // lower & upper limit of randomness

 

                                float errorO;                            // output -> middle error

                                float errorMA, errorMB; // middle -> input errors

 

                                FILE  *fileptr;                         // filepointer

                                int   in[4][2], out[4]; // for file-reading

                                char  *fname;

};

 

/********************************************************************/

void  BinOp::printinfo() {

 

                printf("settings for the network:\n learning rate: %f\n", lr);

                printf(" momentum rate: %f\n bias: %f\n", mr, bias);

                printf(" iterations: %d\n filename: %s\n\n", iter, fname );

}

 

void  BinOp::printschema() {

 

                printf("                        ------------------\n");

                printf("                        ³ Output: %+2.3f ³------ w7: %+2.3f\n",O, weight[7] );

                printf("                        ------------------\n");

                printf("                         /               \\\n");

                printf("                    w5: %+2.3f            w6: %+2.3f\n",weight[5], weight[6]);

                printf("                        /                   \\\n");

                printf("             ----------------         ----------------\n");

                printf(" w8:%+2.3f --³ Ma: %+2.3f   ³         ³  Mb: %+2.3f  ³----- w9:%+2.3f\n",weight[8],MA,MB,weight[9]);

                printf("             ----------------         ----------------\n");

                printf("                     ³      \\        /        ³\n");

                printf("                     ³  w3:%+2.3f  w2:%+2.3f  ³\n", weight[3], weight[2]);

                printf("                     ³           \\/           ³\n");

                printf("                 w1: %+2.3f     / \\     w4: %+2.3f  \n", weight[1], weight[4]);

                printf("                     ³        /     \\         ³\n");

                printf("              ----------------       ----------------\n");

                printf("              ³  IA: %+2.3f  ³       ³ IB: %+2.3f   ³\n",IA,IB);

                printf("              ----------------       ----------------\n");

}

 

void  BinOp::printtest() {

 

                for (float i=0; i<2; i++)

                 for (float j=0; j<2; j++) {

                  printf( " %2g %2g = %+2.4g", i, j, test( i, j ));

                  if ( test( i, j ) < 0.2 )  printf(" => 0   \n");

                  else if ( test( i, j ) > 0.8 ) printf(" => 1   \n");

                  else printf("\n");

                 }

}

 

char  BinOp::stoptest(){

 

                return ( ( test(0,0) < 0.1 || test(0,0) > 0.9 ) &&

                                 ( test(1,1) < 0.1 || test(1,1) > 0.9 ) &&

                                 ( test(1,0) < 0.1 || test(1,0) > 0.9 ) &&

                                 ( test(0,1) < 0.1 || test(0,1) > 0.9 ) );

}

 

float BinOp::train_bp() {

 

                if ( readfile() < 0 ) printf("file error\n");

                else printf("file loaded. Press a key.\n");

                getch();

 

                initweights();

 

                int j=0;

                float maxerror;

 

                while (j<iter) {

                                gotoxy(1,2);printf(" iteration #: %4d\n", j);

                                j++;

                                maxerror=0;

                                for (int i=0; i<4; i++) {  // for each input pattern :

                                                fillnodes( in[i][0], in[i][1], out[i] );

                                                gotoxy(1,3);

                                                printschema();

                                                adjusterrors();

                                                printf("error = %+2.6f\n", errorO );

                                                if ( errorO > abs(maxerror) ) maxerror=errorO;

                                }

                                printf("%2.6f\n",maxerror);

                }

                return maxerror;

}

 

float BinOp::test( float i1, float i2 ) {

 

                IA = i1;

                IB = i2;

                MB = activationf( weight[2], IA, weight[4], IB, weight[9], bias);

                MA = activationf( weight[1], IA, weight[3], IB, weight[8], bias);

 

                return 1/(1+exp(-((MA*weight[5])+(MB*weight[6])+(bias*weight[7]))));

}

 

void  BinOp::initweights() {

 

                for (char i=0; i<10; i++) {

                                  weight[i]=rand_ab(lowlim,upplim);

                                  pweight[i]=0;

                                }

}

 

void  BinOp::fillnodes(int i1, int i2, int o) {

 

                IA = i1;

                IB = i2;

                target = o;

                MB = activationf( weight[2], IA, weight[4], IB, weight[9], bias);

                MA = activationf( weight[1], IA, weight[3], IB, weight[8], bias);

                O  = activationf( weight[5], MA, weight[6], MB, weight[7], bias);

}

 

void  BinOp::adjusterrors() {

 

                errorO = errorf1(target, O); // error at output

                pweight[5] = adjustweight1( pweight[5], errorO, MA);

                pweight[6] = adjustweight1( pweight[6], errorO, MB);

                pweight[7] = adjustweight1( pweight[7], errorO, bias);

                for( char k=5; k<8; k++) weight[k] = weight[k] + pweight[k];

 

                errorMA = errorf2( MA, weight[5], errorO); // error at middle nodes

                errorMB = errorf2( MB, weight[6], errorO);

                pweight[1] = adjustweight2( errorMA, pweight[1], IA);

                pweight[2] = adjustweight2( errorMB, pweight[2], IA);

                pweight[3] = adjustweight2( errorMA, pweight[3], IB);

                pweight[4] = adjustweight2( errorMB, pweight[4], IB);

                pweight[8] = adjustweight2( errorMA, pweight[8], bias);

                pweight[9] = adjustweight2( errorMB, pweight[9], bias);

                for( k=1; k<5; k++)                weight[k] = weight[k] + pweight[k];

                for( k=8; k<10; k++)              weight[k] = weight[k] + pweight[k];

}

 

void  BinOp::printweights() {

 

                printf("%3.3g %3.3g %1.3g %1.3g %1.3g %1.3g %1.3g %1.3g %1.3g\n",

                                weight[1], weight[2], weight[3], weight[4], weight[5],

                                weight[6], weight[7], weight[8], weight[9] );

}

 

char  BinOp::readfile() {

 

                fileptr = fopen(fname, "r");

                if (fileptr==0) {

                                return -1; // The file could not be found

                }

                for (char i=0; i< 4; i++) {

                                fscanf(fileptr,"%i",&in[i][0]);  // inputs

                                fscanf(fileptr,"%i",&in[i][1]);

                                fscanf(fileptr,"%i",&out[i]);     // target

                }

                fclose(fileptr);                // close file

                return 0;

}

 

float BinOp::rand_ab(float a, float b) {

                return ( a + (b - a) * rand() / RAND_MAX );

}

 

float BinOp::activationf(        float weight1,float O1,

                                                                float weight2,float O2,

                                                                float weight3, float O3) {

 

                float t[3];                 // temporary array

                t[0]=(weight1*O1);

                t[1]=(weight2*O2);

                t[2]=(weight3*O3);

                return 1/(1+exp(-2*steep*(t[0]+t[1]+t[2])));         // returns sigmoid

}

 

float BinOp::errorf1( float target, float output ) {

 

                return ( (target-output)*2*steep*(output)*(1-output) );

}

 

float BinOp::adjustweight1( float pweight, float delta, float Oi) {

 

                return ( (lr*delta*Oi)+(mr*pweight) );

}

 

float BinOp::errorf2( float Om, float weight, float err ) {

 

                return ( 2*steep*Om*(1-Om)*(err*weight) );

}

 

float BinOp::adjustweight2( float mtoi, float pweight, float I) {

 

                return ( (lr*mtoi*I)+(mr*pweight) );

}

 

void  BinOp::set_weights(float w1, float w2, float w3, float w4, float w5,

                                                 float w6, float w7, float w8, float w9) {

                weight[1]=w1;        weight[2]=w2;   weight[3]=w3;

                weight[4]=w4;   weight[5]=w5;   weight[6]=w6;

                weight[7]=w7;   weight[8]=w8;   weight[9]=w9;

}

 

/********************************************************************/

 

void main(void) {

 

    BinOp binop;

    clrscr();

    //randomize();

 

    for (int i=0; i<1; i++) {

 

      binop.set_iter(300);

      binop.set_mr(0.99);

      binop.set_lr(0.3);

      binop.set_bias(1);

      binop.set_fname("xor.dat");

 

      binop.train_bp();

      binop.printtest();

    }

}

  

 

Example Output:

                        ------------------

                        │ Output: +0.004 │------ w7: -8.367

                        ------------------

                         /               \

                    w5: -12.044            w6: +14.698

                        /                   \

             ----------------         ----------------

 w8:+4.334 --│ Ma: +0.987   │         │  Mb: +1.000  │----- w9:+14.675

             ----------------         ----------------

                     │      \        /        │

                     │  w3:-11.748  w2:-6.582 │

                     │           \/           │

                 w1: -11.902     / \     w4: -10.411 

                     │        /     \         │

              ----------------       ----------------

              │  IA: +0.000  │       │ IB: +0.000   │

              ----------------       ----------------

error = -0.000015

0.000005

  0  0 = +0.003845 => 0  

  0  1 = +0.9978 => 1  

  1  0 = +0.9982 => 1  

  1  1 = +0.0008667 => 0

 

 

 

Evaluation plots: