Tesseract
3.02
|
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 }