// $Id$ // // File: nnann.cc // Created: Fri Mar 18 19:47:27 EDT 2011 // Creator: davidl (on Darwin Amelia.local 9.8.0 i386) // #include #include using namespace std; //--------------------------------- // nnann (Constructor) //--------------------------------- nnann::nnann():activation_function(NULL),initialized(false) { // A single nnann_neuron is used for the bias. The neuron always emits an // ouput of 1.0 allowing the weights to represent the real bias in every // neurons input. The bias neuron is included in the connection layers, but // not in the neuron layers themselves. (See RelinkLayers.) bias.SetOutput(1.0); } //--------------------------------- // nnann (Constructor) //--------------------------------- nnann::nnann(std::string filename):activation_function(NULL),initialized(false) { ReadFile(filename); } //--------------------------------- // ~nnann (Destructor) //--------------------------------- nnann::~nnann() { } //--------------------------------- // AddLayer //--------------------------------- void nnann::AddLayer(unsigned int Nneurons, nnann_function *activation_function) { // Create a layer with specified number of neurons and the specified activation function nnann_layer layer(Nneurons); for(unsigned int i=0; i1)RelinkLayers(); } //--------------------------------- // BackPropagate //--------------------------------- double nnann::BackPropagate(const std::vector& target_vals) { /// Use the specified target_vals to calculate the error on the output layer and /// propagate it back through the ANN, updating the weights. The mean squared /// value of the output errors is returned (i.e. the "MS" not the "RMS"). // Dimension check if(layers.size()<2)throw nnann_exception("Can't back propagate. Network has less than 2 layers!"); nnann_layer &output_layer = layers[layers.size()-1]; if(output_layer.size()!=target_vals.size())throw nnann_exception("Can't back propagate. target vector size does not match network's output layer size!"); // Learning rate (see comment below) double eta = 0.03; // Clear the output errors for all neurons in the ANN for(unsigned int ilayer=0; ilayer0; ilayer--){ // Loop over connections in this connection layer, adding to the output error of the previous layer nnann_weight_layer &weights = weights_layers[ilayer]; for(unsigned int iweight=0; iweight &layer = layers[ilayer]; for(unsigned int ineuron=0; ineuronGetOutput()*output->GetInputError(); //_DBG_<<"layer: "<GetOutput()<<" output's input(err):"<GetInputError()<<" output's output(err):"<GetOutputError()< &inputs, std::vector &outputs) { // Dimension check if(layers.size()<1)throw nnann_exception("Empty network!"); nnann_layer &input_layer = layers[0]; if(inputs.size()!=input_layer.size())throw nnann_exception("Number of inputs don't match network!"); // Clear the inputs for all neurons in the ANN for(unsigned int ilayer=0; ilayer &layer = layers[ilayer+1]; for(unsigned int ineuron=0; ineuron &target_vals) { /// Get the RMS of the output layer errors based on the given target_vals. nnann_layer &output_layer = layers[layers.size()-1]; double sum_diff2 = 0.0; for(unsigned int i=0; i>tok; if(tok.length()>0 && tok[0]=='#'){ // Comment line. Read in rest of line and continue char str[1024]; ifs.getline(str,1024); continue; }else if(tok=="layer"){ // Define a new layer (without weights) unsigned int ilayer, Nneurons; string function, colon; ifs >> ilayer >> colon >> Nneurons >> colon >> function; if(ilayer!=layers.size())throw nnann_exception("corrupt input file (layer number out of order)"); // This will need to be changed to accomodate different activation functions nnann_function *my_function = NULL; if(function==activation_function->GetName())my_function=activation_function; // Create new layer with Nneurons and set the activation function for each AddLayer(Nneurons, my_function); }else if(tok=="Weights"){ // Set the weights for an existing layer string forstr, layerstr; unsigned int ilayer; ifs >> forstr >> layerstr >> ilayer; if(ilayer<1 || ilayer>layers.size())throw nnann_exception("corrupt input file (layer number out of range)"); nnann_weight_layer &weights = weights_layers[ilayer]; for(unsigned int iweight = 0; iweight> w; if(ifs.fail())throw nnann_exception("corrupt input file (unable to read weight!)"); weights[iweight].SetWeight(w); } // If we just read in weights for the last layer, assume we are done. We should // stop reading from the input stream allowing any user extensions to be read // in. The file should be formatted such that the layer info is all read in first and // the weight info last. if(ilayer == (layers.size()-1))break; } } initialized = true; } //--------------------------------- // ReadFile //--------------------------------- void nnann::ReadFile(std::string filename) { /// Open a file with the specified file name and read the network in from it. /// Before closing the input file stream, call LoadExtensionsFromFile() so /// a sub-classes can read in additional, custom information at the end of the /// file. ifstream ifs(filename.c_str()); if(!ifs.is_open()){ throw nnann_exception(string("can't open \""+filename+"\"!")); } // Read core network from file LoadFromFile(ifs); // Call sub-class hook for reading in file. LoadExtensionsFromFile(ifs); ifs.close(); } //--------------------------------- // RelinkLayers //--------------------------------- void nnann::RelinkLayers(void) { /// Create nnann_weight objects to link all of the neurons in the ANN. /// This will allocate the required nnann_weight objects to link all neurons /// between adjacent layers for the whole ANN. Input and output pointer lists /// in the neurons will be updated to point to the new weight objects. /// /// Note that this will preserve the actual weight values for nnann_weight objects /// that already exist (though the memory location of those objects may change). /// New weights will be uninitialied however and so will need /// to either be set by hand or all the weights in the network set to random values /// by calling Initialize(). // Less than 2 layers likely indicates an error if(layers.size()<2)throw nnann_exception("Less than 2 layers defined when nnann::Relink() called!"); // Set the number of weights layers based on the content of layers weights_layers.resize(layers.size()-1); // Loop over pairs of layers starting with the first two for(unsigned int ilayer=0; ilayer>tok; if(tok.length()>0 && tok[0]=='#'){ // Comment line. Read in rest of line and continue char str[1024]; ifs.getline(str,1024); continue; }else if(tok=="inputs"){ // Read in number of inputs ifs >> Ninputs; }else if(tok=="outputs"){ // Read in number of outputs ifs >> Noutputs; }else if(tok=="data:"){ // Check that Ninputs and Noutputs are are correct if(Ninputs!=layers[0].size())throw nnann_exception(string("Number of inputs in training file doesn't match ANN!")); if(Noutputs!=layers[layers.size()-1].size())throw nnann_exception(string("Number of outputs in training file doesn't match ANN!")); // Remember stream position pointer so we can loop streampos pos_start = ifs.tellg(); cout<<"Training on \""< inputs(Ninputs); vector target_vals(Noutputs); for(unsigned int i=0; i> inputs[i]; for(unsigned int i=0; i> target_vals[i]; if(ifs.fail() || ifs.eof())break; // Run the network using the input values vector outputs; FeedForward(inputs, outputs); // Backpropagate based on target values, updating weights. double diff2 = BackPropagate(target_vals); sum_diff2 += diff2; Ndiff2+=1.0; #if 0 cout<<"Training inputs:"; for(unsigned int i=0; iGetName():"Unknown"; ofs<<"layer "<