Tesseract  3.02
tesseract-ocr/neural_networks/runtime/neural_net.cpp
Go to the documentation of this file.
00001 // Copyright 2008 Google Inc.
00002 // All Rights Reserved.
00003 // Author: ahmadab@google.com (Ahmad Abdulkader)
00004 //
00005 // neural_net.cpp: Declarations of a class for an object that
00006 // represents an arbitrary network of neurons
00007 //
00008 #include <vector>
00009 #include <string>
00010 #include "neural_net.h"
00011 #include "input_file_buffer.h"
00012 
00013 namespace tesseract {
00014 
00015 NeuralNet::NeuralNet() {
00016   Init();
00017 }
00018 
00019 NeuralNet::~NeuralNet() {
00020   // clean up the wts chunks vector
00021   for(int vec = 0; vec < wts_vec_.size(); vec++) {
00022     delete wts_vec_[vec];
00023   }
00024   // clean up neurons
00025   delete []neurons_;
00026   // clean up nodes
00027   for (int node_idx = 0; node_idx < neuron_cnt_; node_idx++) {
00028     delete []fast_nodes_[node_idx].inputs;
00029   }
00030 
00031 }
00032 
00033 // Initiaization function
00034 void NeuralNet::Init() {
00035   read_only_ = true;
00036   auto_encoder_ = false;
00037   alloc_wgt_cnt_ = 0;
00038   wts_cnt_ = 0;
00039   neuron_cnt_ = 0;
00040   in_cnt_ = 0;
00041   out_cnt_ = 0;
00042   wts_vec_.clear();
00043   neurons_ = NULL;
00044   inputs_mean_.clear();
00045   inputs_std_dev_.clear();
00046   inputs_min_.clear();
00047   inputs_max_.clear();
00048 }
00049 
00050 // Does a fast feedforward for read_only nets
00051 // Templatized for float and double Types
00052 template <typename Type> bool NeuralNet::FastFeedForward(const Type *inputs,
00053                                                          Type *outputs) {
00054   int node_idx = 0;
00055   Node *node = &fast_nodes_[0];
00056   // feed inputs in and offset them by the pre-computed bias
00057   for (node_idx = 0; node_idx < in_cnt_; node_idx++, node++) {
00058     node->out = inputs[node_idx] - node->bias;
00059   }
00060   // compute nodes activations and outputs
00061   for (;node_idx < neuron_cnt_; node_idx++, node++) {
00062     double activation = -node->bias;
00063     for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
00064       activation += (node->inputs[fan_in_idx].input_weight *
00065                      node->inputs[fan_in_idx].input_node->out);
00066     }
00067     node->out = Neuron::Sigmoid(activation);
00068   }
00069   // copy the outputs to the output buffers
00070   node = &fast_nodes_[neuron_cnt_ - out_cnt_];
00071   for (node_idx = 0; node_idx < out_cnt_; node_idx++, node++) {
00072     outputs[node_idx] = node->out;
00073   }
00074   return true;
00075 }
00076 
00077 // Performs a feedforward for general nets. Used mainly in training mode
00078 // Templatized for float and double Types
00079 template <typename Type> bool NeuralNet::FeedForward(const Type *inputs,
00080                                                      Type *outputs) {
00081   // call the fast version in case of readonly nets
00082   if (read_only_) {
00083     return FastFeedForward(inputs, outputs);
00084   }
00085   // clear all neurons
00086   Clear();
00087   // for auto encoders, apply no input normalization
00088   if (auto_encoder_) {
00089     for (int in = 0; in < in_cnt_; in++) {
00090       neurons_[in].set_output(inputs[in]);
00091     }
00092   } else {
00093     // Input normalization : subtract mean and divide by stddev
00094     for (int in = 0; in < in_cnt_; in++) {
00095       neurons_[in].set_output((inputs[in] - inputs_min_[in]) /
00096                               (inputs_max_[in] - inputs_min_[in]));
00097       neurons_[in].set_output((neurons_[in].output() - inputs_mean_[in]) /
00098                               inputs_std_dev_[in]);
00099     }
00100   }
00101   // compute the net outputs: follow a pull model each output pulls the
00102   // outputs of its input nodes and so on
00103   for (int out = neuron_cnt_ - out_cnt_; out < neuron_cnt_; out++) {
00104     neurons_[out].FeedForward();
00105     // copy the values to the output buffer
00106     outputs[out] = neurons_[out].output();
00107   }
00108   return true;
00109 }
00110 
00111 // Sets a connection between two neurons
00112 bool NeuralNet::SetConnection(int from, int to) {
00113   // allocate the wgt
00114   float *wts  =  AllocWgt(1);
00115   if (wts == NULL) {
00116     return false;
00117   }
00118   // register the connection
00119   neurons_[to].AddFromConnection(neurons_ + from, wts, 1);
00120   return true;
00121 }
00122 
00123 // Create a fast readonly version of the net
00124 bool NeuralNet::CreateFastNet() {
00125   fast_nodes_.resize(neuron_cnt_);
00126   // build the node structures
00127   int wts_cnt = 0;
00128   for (int node_idx = 0; node_idx < neuron_cnt_; node_idx++) {
00129     Node *node = &fast_nodes_[node_idx];
00130     if (neurons_[node_idx].node_type() == Neuron::Input) {
00131       // Input neurons have no fan-in
00132       node->fan_in_cnt = 0;
00133       node->inputs = NULL;
00134       // Input bias is the normalization offset computed from
00135       // training input stats
00136       if (fabs(inputs_max_[node_idx] - inputs_min_[node_idx]) <
00137           kMinInputRange) {
00138         // if the range approaches zero, the stdev is not defined,
00139         // this indicates that this input does not change.
00140         // Set the bias to zero
00141         node->bias = 0.0f;
00142       } else {
00143         node->bias = inputs_min_[node_idx] + (inputs_mean_[node_idx] *
00144             (inputs_max_[node_idx] - inputs_min_[node_idx]));
00145       }
00146     } else {
00147       node->bias = neurons_[node_idx].bias();
00148       node->fan_in_cnt = neurons_[node_idx].fan_in_cnt();
00149       // allocate memory for fan-in nodes
00150       node->inputs = new WeightedNode[node->fan_in_cnt];
00151       if (node->inputs == NULL) {
00152         return false;
00153       }
00154       for (int fan_in = 0; fan_in < node->fan_in_cnt; fan_in++) {
00155         // identify fan-in neuron
00156         const int id = neurons_[node_idx].fan_in(fan_in)->id();
00157         // Feedback connections are not allowed and should never happen
00158         if (id >= node_idx) {
00159           return false;
00160         }
00161         // add the the fan-in neuron and its wgt
00162         node->inputs[fan_in].input_node = &fast_nodes_[id];
00163         float wgt_val = neurons_[node_idx].fan_in_wts(fan_in);
00164         // for input neurons normalize the wgt by the input scaling
00165         // values to save time during feedforward
00166         if (neurons_[node_idx].fan_in(fan_in)->node_type() == Neuron::Input) {
00167           // if the range approaches zero, the stdev is not defined,
00168           // this indicates that this input does not change.
00169           // Set the weight to zero
00170           if (fabs(inputs_max_[id] - inputs_min_[id]) < kMinInputRange) {
00171             wgt_val = 0.0f;
00172           } else {
00173             wgt_val /= ((inputs_max_[id] - inputs_min_[id]) *
00174                 inputs_std_dev_[id]);
00175           }
00176         }
00177         node->inputs[fan_in].input_weight = wgt_val;
00178       }
00179       // incr wgt count to validate against at the end
00180       wts_cnt += node->fan_in_cnt;
00181     }
00182   }
00183   // sanity check
00184   return wts_cnt_ == wts_cnt;
00185 }
00186 
00187 // returns a pointer to the requested set of weights
00188 // Allocates in chunks
00189 float * NeuralNet::AllocWgt(int wgt_cnt) {
00190   // see if need to allocate a new chunk of wts
00191   if (wts_vec_.size() == 0 || (alloc_wgt_cnt_ + wgt_cnt) > kWgtChunkSize) {
00192     // add the new chunck to the wts_chunks vector
00193     wts_vec_.push_back(new vector<float> (kWgtChunkSize));
00194     alloc_wgt_cnt_ = 0;
00195   }
00196   float *ret_ptr = &((*wts_vec_.back())[alloc_wgt_cnt_]);
00197   // incr usage counts
00198   alloc_wgt_cnt_ += wgt_cnt;
00199   wts_cnt_ += wgt_cnt;
00200   return ret_ptr;
00201 }
00202 
00203 // create a new net object using an input file as a source
00204 NeuralNet *NeuralNet::FromFile(const string file_name) {
00205   // open the file
00206   InputFileBuffer   input_buff(file_name);
00207   // create a new net object using input buffer
00208   NeuralNet *net_obj = FromInputBuffer(&input_buff);
00209   return net_obj;
00210 }
00211 
00212 // create a net object from an input buffer
00213 NeuralNet *NeuralNet::FromInputBuffer(InputFileBuffer *ib) {
00214       // create a new net object
00215   NeuralNet *net_obj = new NeuralNet();
00216   if (net_obj == NULL) {
00217     return NULL;
00218   }
00219       // load the net
00220   if (!net_obj->ReadBinary(ib)) {
00221     delete net_obj;
00222     net_obj = NULL;
00223   }
00224   return net_obj;
00225 }
00226 
00227 // Compute the output of a specific output node.
00228 // This function is useful for application that are interested in a single
00229 // output of the net and do not want to waste time on the rest
00230 // This is the fast-read-only version of this function
00231 template <typename Type> bool NeuralNet::FastGetNetOutput(const Type *inputs,
00232                                                           int output_id,
00233                                                           Type *output) {
00234   // feed inputs in and offset them by the pre-computed bias
00235   int node_idx = 0;
00236   Node *node = &fast_nodes_[0];
00237   for (node_idx = 0; node_idx < in_cnt_; node_idx++, node++) {
00238     node->out = inputs[node_idx] - node->bias;
00239   }
00240 
00241   // compute nodes' activations and outputs for hidden nodes if any
00242   int hidden_node_cnt = neuron_cnt_ - out_cnt_;
00243   for (;node_idx < hidden_node_cnt; node_idx++, node++) {
00244     double activation = -node->bias;
00245     for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
00246       activation += (node->inputs[fan_in_idx].input_weight *
00247                      node->inputs[fan_in_idx].input_node->out);
00248     }
00249     node->out = Neuron::Sigmoid(activation);
00250   }
00251 
00252   // compute the output of the required output node
00253   node += output_id;
00254   double activation = -node->bias;
00255   for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
00256     activation += (node->inputs[fan_in_idx].input_weight *
00257                    node->inputs[fan_in_idx].input_node->out);
00258   }
00259   (*output) = Neuron::Sigmoid(activation);
00260   return true;
00261 }
00262 
00263 // Performs a feedforward for general nets. Used mainly in training mode
00264 // Templatized for float and double Types
00265 template <typename Type> bool NeuralNet::GetNetOutput(const Type *inputs,
00266                                                       int output_id,
00267                                                       Type *output) {
00268   // validate output id
00269   if (output_id < 0 || output_id >= out_cnt_) {
00270     return false;
00271   }
00272 
00273   // call the fast version in case of readonly nets
00274   if (read_only_) {
00275     return FastGetNetOutput(inputs, output_id, output);
00276   }
00277 
00278   // For the slow version, we'll just call FeedForward and return the
00279   // appropriate output
00280   vector<Type> outputs(out_cnt_);
00281   if (!FeedForward(inputs, &outputs[0])) {
00282     return false;
00283   }
00284   (*output) = outputs[output_id];
00285 
00286   return true;
00287 }
00288 
00289 // Instantiate all supported templates now that the functions have been defined.
00290 template bool NeuralNet::FeedForward(const float *inputs, float *outputs);
00291 template bool NeuralNet::FeedForward(const double *inputs, double *outputs);
00292 template bool NeuralNet::FastFeedForward(const float *inputs, float *outputs);
00293 template bool NeuralNet::FastFeedForward(const double *inputs,
00294                                          double *outputs);
00295 template bool NeuralNet::GetNetOutput(const float *inputs, int output_id,
00296                                       float *output);
00297 template bool NeuralNet::GetNetOutput(const double *inputs, int output_id,
00298                                       double *output);
00299 template bool NeuralNet::FastGetNetOutput(const float *inputs, int output_id,
00300                                           float *output);
00301 template bool NeuralNet::FastGetNetOutput(const double *inputs, int output_id,
00302                                           double *output);
00303 template bool NeuralNet::ReadBinary(InputFileBuffer *input_buffer);
00304 
00305 }