|
C++ Neural Networks and Fuzzy Logic
by Valluru B. Rao M&T Books, IDG Books Worldwide, Inc. ISBN: 1558515526 Pub Date: 06/01/95 |
| Previous | Table of Contents | Next |
Listing 13.2 Layer.cpp file updated to include noise and momentum
// layer.cpp V.Rao, H.Rao
// added momentum and noise
// compile for floating point hardware if available
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include "layer.h"
inline float squash(float input)
// squashing function
// use sigmoid can customize to something
// else if desired; can add a bias term too
//
{
if (input < -50)
return 0.0;
else if (input > 50)
return 1.0;
else return (float)(1/(1+exp(-(double)input)));
}
inline float randomweight(unsigned init)
{
int num;
// random number generator
// will return a floating point
// value between -1 and 1
if (init==1) // seed the generator
srand ((unsigned)time(NULL));
num=rand() % 100;
return 2*(float(num/100.00))-1;
}
// the next function is needed for Turbo C++
// and Borland C++ to link in the appropriate
// functions for fscanf floating point formats:
static void force_fpf()
{
float x, *y;
y=&x;
x=*y;
}
// ---------------------
// input layer
//---------------------
input_layer::input_layer(int i, int o)
{
num_inputs=i;
num_outputs=o;
outputs = new float[num_outputs];
orig_outputs = new float[num_outputs];
if ((outputs==0)||(orig_outputs==0))
{
cout << "not enough memory\n";
cout << "choose a smaller architecture\n";
exit(1);
}
noise_factor=0;
}
input_layer::~input_layer()
{
delete [num_outputs] outputs;
delete [num_outputs] orig_outputs;
}
void input_layer::calc_out()
{
//add noise to inputs
// randomweight returns a random number
// between -1 and 1
int i;
for (i=0; i<num_outputs; i++)
outputs[i] =orig_outputs[i]*
(1+noise_factor*randomweight(0));
}
void input_layer::set_NF(float noise_fact)
{
noise_factor=noise_fact;
}
// ---------------------
// output layer
//---------------------
output_layer::output_layer(int ins, int outs)
{
int i, j, k;
num_inputs=ins;
num_outputs=outs;
weights = new float[num_inputs*num_outputs];
output_errors = new float[num_outputs];
back_errors = new float[num_inputs];
outputs = new float[num_outputs];
expected_values = new float[num_outputs];
cum_deltas = new float[num_inputs*num_outputs];
past_deltas = new float[num_inputs*num_outputs];
if ((weights==0)||(output_errors==0)||(back_errors==0)
||(outputs==0)||(expected_values==0)
||(past_deltas==0)||(cum_deltas==0))
{
cout << "not enough memory\n";
cout << "choose a smaller architecture\n";
exit(1);
}
// zero cum_deltas and past_deltas matrix
for (i=0; i< num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
{
cum_deltas[k+j]=0;
past_deltas[k+j]=0;
}
}
}
output_layer::~output_layer()
{
// some compilers may require the array
// size in the delete statement; those
// conforming to Ansi C++ will not
delete [num_outputs*num_inputs] weights;
delete [num_outputs] output_errors;
delete [num_inputs] back_errors;
delete [num_outputs] outputs;
delete [num_outputs*num_inputs] past_deltas;
delete [num_outputs*num_inputs] cum_deltas;
}
void output_layer::calc_out()
{
int i,j,k;
float accumulator=0.0;
for (j=0; j<num_outputs; j++)
{
for (i=0; i<num_inputs; i++)
{
k=i*num_outputs;
if (weights[k+j]*weights[k+j] > 1000000.0)
{
cout << "weights are blowing up\n";
cout << "try a smaller learning constant\n";
cout << "e.g. beta=0.02 aborting...\n";
exit(1);
}
outputs[j]=weights[k+j]*(*(inputs+i));
accumulator+=outputs[j];
}
// use the sigmoid squash function
outputs[j]=squash(accumulator);
accumulator=0;
}
}
void output_layer::calc_error(float & error)
{
int i, j, k;
float accumulator=0;
float total_error=0;
for (j=0; j<num_outputs; j++)
{
output_errors[j] = expected_values[j]-outputs[j];
total_error+=output_errors[j];
}
error=total_error;
for (i=0; i<num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j<num_outputs; j++)
{
back_errors[i]=
weights[k+j]*output_errors[j];
accumulator+=back_errors[i];
}
back_errors[i]=accumulator;
accumulator=0;
// now multiply by derivative of
// sigmoid squashing function, which is
// just the input*(1-input)
back_errors[i]*=(*(inputs+i))*(1-(*(inputs+i)));
}
}
void output_layer::randomize_weights()
{
int i, j, k;
const unsigned first_time=1;
const unsigned not_first_time=0;
float discard;
discard=randomweight(first_time);
for (i=0; i< num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
weights[k+j]=randomweight(not_first_time);
}
}
void output_layer::update_weights(const float beta,
const float alpha)
{
int i, j, k;
float delta;
// learning law: weight_change =
// beta*output_error*input + alpha*past_delta
for (i=0; i< num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
{
delta=beta*output_errors[j]*(*(inputs+i))
+alpha*past_deltas[k+j];
weights[k+j] += delta;
cum_deltas[k+j]+=delta; // current cycle
}
}
}
void output_layer::update_momentum()
{
// This function is called when a
// new cycle begins; the past_deltas
// pointer is swapped with the
// cum_deltas pointer. Then the contents
// pointed to by the cum_deltas pointer
// is zeroed out.
int i, j, k;
float * temp;
// swap
temp = past_deltas;
past_deltas=cum_deltas;
cum_deltas=temp;
// zero cum_deltas matrix
// for new cycle
for (i=0; i< num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
cum_deltas[k+j]=0;
}
}
void output_layer::list_weights()
{
int i, j, k;
for (i=0; i< num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
cout << "weight["<<i<<","<<
j<<"] is: "<<weights[k+j];
}
}
void output_layer::list_errors()
{
int i, j;
for (i=0; i< num_inputs; i++)
cout << "backerror["<<i<<
"] is : "<<back_errors[i]<<"\n";
for (j=0; j< num_outputs; j++)
cout << "outputerrors["<<j<<
"] is: "<<output_errors[j]<<"\n";
}
void output_layer::write_weights(int layer_no,
FILE * weights_file_ptr)
{
int i, j, k;
// assume file is already open and ready for
// writing
// prepend the layer_no to all lines of data
// format:
// layer_no weight[0,0] weight[0,1] ...
// layer_no weight[1,0] weight[1,1] ...
// ...
for (i=0; i< num_inputs; i++)
{
fprintf(weights_file_ptr,"%i ",layer_no);
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
{
fprintf(weights_file_ptr,"%f ",
weights[k+j]);
}
fprintf(weights_file_ptr,"\n");
}
}
void output_layer::read_weights(int layer_no,
FILE * weights_file_ptr)
{
int i, j, k;
// assume file is already open and ready for
// reading
// look for the prepended layer_no
// format:
// layer_no weight[0,0] weight[0,1] ...
// layer_no weight[1,0] weight[1,1] ...
// ...
while (1)
{
fscanf(weights_file_ptr,"%i";,&j);
if ((j==layer_no)|| (feof(weights_file_ptr)))
break;
else
{
while (fgetc(weights_file_ptr) != `\n')
{;}// get rest of line
}
}
if (!(feof(weights_file_ptr)))
{
// continue getting first line
i=0;
for (j=0; j< num_outputs; j++)
{
fscanf(weights_file_ptr,"%f",
&weights[j]); // i*num_outputs
= 0
}
fscanf(weights_file_ptr,"\n");
// now get the other lines
for (i=1; i< num_inputs; i++)
{
fscanf(weights_file_ptr,
%i,&layer_no);
k=i*num_outputs;
for (j=0; j< num_outputs; j++)
{
fscanf(weights_file_ptr,%f,
&weights[k+j]);
}
}
fscanf(weights_file_ptr,\n);
}
else cout << end of file reached\n;
}
void output_layer::list_outputs()
{
int j;
for (j=0; j< num_outputs; j++)
{
cout << outputs[<<j
<<] is: <<outputs[j]<<\n;
}
}
// -
// middle layer
//
middle_layer::middle_layer(int i, int o):
output_layer(i,o)
{
}
middle_layer::~middle_layer()
{
delete [num_outputs*num_inputs] weights;
delete [num_outputs] output_errors;
delete [num_inputs] back_errors;
delete [num_outputs] outputs;
}
void middle_layer::calc_error()
{
int i, j, k;
float accumulator=0;
for (i=0; i<num_inputs; i++)
{
k=i*num_outputs;
for (j=0; j<num_outputs; j++)
{
back_errors[i]=
weights[k+j]*(*(output_errors+j));
accumulator+=back_errors[i];
}
back_errors[i]=accumulator;
accumulator=0;
// now multiply by derivative of
// sigmoid squashing function, which is
// just the input*(1-input)
back_errors[i]*=(*(inputs+i))*(1-(*(inputs+i)));
}
}
network::network()
{
position=0L;
}
network::~network()
{
int i,j,k;
i=layer_ptr[0]->num_outputs;// inputs
j=layer_ptr[number_of_layers-1]->num_outputs; //outputs
k=MAX_VECTORS;
delete [(i+j)*k]buffer;
}
void network::set_training(const unsigned & value)
{
training=value;
}
unsigned network::get_training_value()
{
return training;
}
void network::get_layer_info()
{
int i;
//
//
// Get layer sizes for the network
//
// -
cout << Please enter in the number of layers for your net work.\n;
cout << You can have a minimum of 3 to a maximum of 5. \n;
cout << 3 implies 1 hidden layer; 5 implies 3 hidden layers : \n\n;
cin >> number_of_layers;
cout << Enter in the layer sizes separated by spaces.\n;
cout << For a network with 3 neurons in the input layer,\n;
cout << 2 neurons in a hidden layer, and 4 neurons in the\n;
cout << output layer, you would enter: 3 2 4 .\n;
cout << You can have up to 3 hidden layers,for five maximum entries
:\n\n;
for (i=0; i<number_of_layers; i++)
{
cin >> layer_size[i];
}
//
// size of layers:
// input_layer layer_size[0]
// output_layer layer_size[number_of_layers-1]
// middle_layers layer_size[1]
// optional: layer_size[number_of_layers-3]
// optional: layer_size[number_of_layers-2]
//-
}
void network::set_up_network()
{
int i,j,k;
//-
// Construct the layers
//
//-
layer_ptr[0] = new input_layer(0,layer_size[0]);
for (i=0;i<(number_of_layers-1);i++)
{
layer_ptr[i+1] =
new middle_layer(layer_size[i],layer_size[i+1]);
}
layer_ptr[number_of_layers-1] = new
output_layer(layer_size[number_of_layers-2], layer_size[number_of_
layers-1]);
for (i=0;i<(number_of_layers-1);i++)
{
if (layer_ptr[i] == 0)
{
cout << insufficient memory\n;
cout << use a smaller architecture\n;
exit(1);
}
}
//-
// Connect the layers
//
//-
// set inputs to previous layer outputs for all layers,
// except the input layer
for (i=1; i< number_of_layers; i++)
layer_ptr[i]->inputs = layer_ptr[i-1]->outputs;
// for back_propagation, set output_errors to next layer
// back_errors for all layers except the output
// layer and input layer
for (i=1; i< number_of_layers -1; i++)
((output_layer *)layer_ptr[i])->output_errors =
((output_layer *)layer_ptr[i+1])->back_errors;
// define the IObuffer that caches data from
// the datafile
i=layer_ptr[0]->num_outputs;// inputs
j=layer_ptr[number_of_layers-1]->num_outputs; //outputs
k=MAX_VECTORS;
buffer=new
float[(i+j)*k];
if (buffer==0)
{
cout << insufficient memory for buffer\n;
exit(1);
}
}
void network::randomize_weights()
{
int i;
for (i=1; i<number_of_layers; i++)
((output_layer *)layer_ptr[i])
->randomize_weights();
}
void network::update_weights(const float beta, const float alpha)
{
int i;
for (i=1; i<number_of_layers; i++)
((output_layer *)layer_ptr[i])
->update_weights(beta,alpha);
}
void network::update_momentum()
{
int i;
for (i=1; i<number_of_layers; i++)
((output_layer *)layer_ptr[i])
->update_momentum();
}
void network::write_weights(FILE * weights_file_ptr)
{
int i;
for (i=1; i<number_of_layers; i++)
((output_layer *)layer_ptr[i])
->write_weights(i,weights_file_ptr);
}
void network::read_weights(FILE * weights_file_ptr)
{
int i;
for (i=1; i<number_of_layers; i++)
((output_layer *)layer_ptr[i])
->read_weights(i,weights_file_ptr);
}
void network::list_weights()
{
int i;
for (i=1; i<number_of_layers; i++)
{
cout << layer number : <<i<< \n;
((output_layer *)layer_ptr[i])
->list_weights();
}
}
void network::list_outputs()
{
int i;
for (i=1; i<number_of_layers; i++)
{
cout << layer number : <<i<< \n;
((output_layer *)layer_ptr[i])
->list_outputs();
}
}
void network::write_outputs(FILE *outfile)
{
int i, ins, outs;
ins=layer_ptr[0]->num_outputs;
outs=layer_ptr[number_of_layers-1]->num_outputs;
float temp;
fprintf(outfile,for input vector:\n);
for (i=0; i<ins; i++)
{
temp=layer_ptr[0]->outputs[i];
fprintf(outfile,%f ,temp);
}
fprintf(outfile,\noutput vector is:\n);
for (i=0; i<outs; i++)
{
temp=layer_ptr[number_of_layers-1]->
outputs[i];
fprintf(outfile,%f ,temp);
}
if (training==1)
{
fprintf(outfile,\nexpected output vector is:\n);
for (i=0; i<outs; i++)
{
temp=((output_layer *)(layer_ptr[number_of_layers-1]))->
expected_values[i];
fprintf(outfile,%f ,temp);
}
}
fprintf(outfile,\n\n);
}
void network::list_errors()
{
int i;
for (i=1; i<number_of_layers; i++)
{
cout << layer number : <<i<< \n;
((output_layer *)layer_ptr[i])
->list_errors();
}
}
int network::fill_IObuffer(FILE * inputfile)
{
// this routine fills memory with
// an array of input, output vectors
// up to a maximum capacity of
// MAX_INPUT_VECTORS_IN_ARRAY
// the return value is the number of read
// vectors
int i, k, count, veclength;
int ins, outs;
ins=layer_ptr[0]->num_outputs;
outs=layer_ptr[number_of_layers-1]->num_outputs;
if (training==1)
veclength=ins+outs;
else
veclength=ins;
count=0;
while ((count<MAX_VECTORS)&&
(!feof(inputfile)))
{
k=count*(veclength);
for (i=0; i<veclength; i++)
{
fscanf(inputfile,%f,&buffer[k+i]);
}
fscanf(inputfile,\n);
count++;
}
if (!(ferror(inputfile)))
return count;
else return -1; // error condition
}
void network::set_up_pattern(int buffer_index)
{
// read one vector into the network
int i, k;
int ins, outs;
ins=layer_ptr[0]->num_outputs;
outs=layer_ptr[number_of_layers-1]->num_outputs;
if (training==1)
k=buffer_index*(ins+outs);
else
k=buffer_index*ins;
for (i=0; i<ins; i++)
((input_layer*)layer_ptr[0])
->orig_outputs[i]=buffer[k+i];
if (training==1)
{
for (i=0; i<outs; i++)
((output_layer *)layer_ptr[number_of_layers-1])->
expected_values[i]=buffer[k+i+ins];
}
}
void network::forward_prop()
{
int i;
for (i=0; i<number_of_layers; i++)
{
layer_ptr[i]->calc_out(); //polymorphic
// function
}
}
void network::backward_prop(float & toterror)
{
int i;
// error for the output layer
((output_layer*)layer_ptr[number_of_layers-1])->
calc_error(toterror);
// error for the middle layer(s)
for (i=number_of_layers-2; i>0; i)
{
((middle_layer*)layer_ptr[i])->
calc_error();
}
}
void network::set_NF(float noise_fact)
{
((input_layer*)layer_ptr[0])->set_NF(noise_fact);
}
| Previous | Table of Contents | Next |