Tesseract 3.02

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
neural_net.cpp
Go to the documentation of this file.
1 // Copyright 2008 Google Inc.
2 // All Rights Reserved.
3 // Author: ahmadab@google.com (Ahmad Abdulkader)
4 //
5 // neural_net.cpp: Declarations of a class for an object that
6 // represents an arbitrary network of neurons
7 //
8 #include <vector>
9 #include <string>
10 #include "neural_net.h"
11 #include "input_file_buffer.h"
12
13 namespace tesseract {
14
15 NeuralNet::NeuralNet() {
16 Init();
17 }
18
19 NeuralNet::~NeuralNet() {
20 // clean up the wts chunks vector
21 for(int vec = 0; vec < wts_vec_.size(); vec++) {
22 delete wts_vec_[vec];
23 }
24 // clean up neurons
25 delete []neurons_;
26 // clean up nodes
27 for (int node_idx = 0; node_idx < neuron_cnt_; node_idx++) {
28 delete []fast_nodes_[node_idx].inputs;
29 }
30
31 }
32
33 // Initiaization function
34 void NeuralNet::Init() {
35 read_only_ = true;
36 auto_encoder_ = false;
37 alloc_wgt_cnt_ = 0;
38 wts_cnt_ = 0;
39 neuron_cnt_ = 0;
40 in_cnt_ = 0;
41 out_cnt_ = 0;
42 wts_vec_.clear();
43 neurons_ = NULL;
44 inputs_mean_.clear();
45 inputs_std_dev_.clear();
46 inputs_min_.clear();
47 inputs_max_.clear();
48 }
49
50 // Does a fast feedforward for read_only nets
51 // Templatized for float and double Types
52 template <typename Type> bool NeuralNet::FastFeedForward(const Type *inputs,
53 Type *outputs) {
54 int node_idx = 0;
55 Node *node = &fast_nodes_[0];
56 // feed inputs in and offset them by the pre-computed bias
57 for (node_idx = 0; node_idx < in_cnt_; node_idx++, node++) {
58 node->out = inputs[node_idx] - node->bias;
59 }
60 // compute nodes activations and outputs
61 for (;node_idx < neuron_cnt_; node_idx++, node++) {
62 double activation = -node->bias;
63 for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
64 activation += (node->inputs[fan_in_idx].input_weight *
65 node->inputs[fan_in_idx].input_node->out);
66 }
67 node->out = Neuron::Sigmoid(activation);
68 }
69 // copy the outputs to the output buffers
70 node = &fast_nodes_[neuron_cnt_ - out_cnt_];
71 for (node_idx = 0; node_idx < out_cnt_; node_idx++, node++) {
72 outputs[node_idx] = node->out;
73 }
74 return true;
75 }
76
77 // Performs a feedforward for general nets. Used mainly in training mode
78 // Templatized for float and double Types
79 template <typename Type> bool NeuralNet::FeedForward(const Type *inputs,
80 Type *outputs) {
81 // call the fast version in case of readonly nets
82 if (read_only_) {
83 return FastFeedForward(inputs, outputs);
84 }
85 // clear all neurons
86 Clear();
87 // for auto encoders, apply no input normalization
88 if (auto_encoder_) {
89 for (int in = 0; in < in_cnt_; in++) {
90 neurons_[in].set_output(inputs[in]);
91 }
92 } else {
93 // Input normalization : subtract mean and divide by stddev
94 for (int in = 0; in < in_cnt_; in++) {
95 neurons_[in].set_output((inputs[in] - inputs_min_[in]) /
96 (inputs_max_[in] - inputs_min_[in]));
97 neurons_[in].set_output((neurons_[in].output() - inputs_mean_[in]) /
98 inputs_std_dev_[in]);
99 }
100 }
101 // compute the net outputs: follow a pull model each output pulls the
102 // outputs of its input nodes and so on
103 for (int out = neuron_cnt_ - out_cnt_; out < neuron_cnt_; out++) {
104 neurons_[out].FeedForward();
105 // copy the values to the output buffer
106 outputs[out] = neurons_[out].output();
107 }
108 return true;
109 }
110
111 // Sets a connection between two neurons
112 bool NeuralNet::SetConnection(int from, int to) {
113 // allocate the wgt
114 float *wts = AllocWgt(1);
115 if (wts == NULL) {
116 return false;
117 }
118 // register the connection
119 neurons_[to].AddFromConnection(neurons_ + from, wts, 1);
120 return true;
121 }
122
123 // Create a fast readonly version of the net
124 bool NeuralNet::CreateFastNet() {
125 fast_nodes_.resize(neuron_cnt_);
126 // build the node structures
127 int wts_cnt = 0;
128 for (int node_idx = 0; node_idx < neuron_cnt_; node_idx++) {
129 Node *node = &fast_nodes_[node_idx];
130 if (neurons_[node_idx].node_type() == Neuron::Input) {
131 // Input neurons have no fan-in
132 node->fan_in_cnt = 0;
133 node->inputs = NULL;
134 // Input bias is the normalization offset computed from
135 // training input stats
136 if (fabs(inputs_max_[node_idx] - inputs_min_[node_idx]) <
137 kMinInputRange) {
138 // if the range approaches zero, the stdev is not defined,
139 // this indicates that this input does not change.
140 // Set the bias to zero
141 node->bias = 0.0f;
142 } else {
143 node->bias = inputs_min_[node_idx] + (inputs_mean_[node_idx] *
144 (inputs_max_[node_idx] - inputs_min_[node_idx]));
145 }
146 } else {
147 node->bias = neurons_[node_idx].bias();
148 node->fan_in_cnt = neurons_[node_idx].fan_in_cnt();
149 // allocate memory for fan-in nodes
150 node->inputs = new WeightedNode[node->fan_in_cnt];
151 if (node->inputs == NULL) {
152 return false;
153 }
154 for (int fan_in = 0; fan_in < node->fan_in_cnt; fan_in++) {
155 // identify fan-in neuron
156 const int id = neurons_[node_idx].fan_in(fan_in)->id();
157 // Feedback connections are not allowed and should never happen
158 if (id >= node_idx) {
159 return false;
160 }
161 // add the the fan-in neuron and its wgt
162 node->inputs[fan_in].input_node = &fast_nodes_[id];
163 float wgt_val = neurons_[node_idx].fan_in_wts(fan_in);
164 // for input neurons normalize the wgt by the input scaling
165 // values to save time during feedforward
166 if (neurons_[node_idx].fan_in(fan_in)->node_type() == Neuron::Input) {
167 // if the range approaches zero, the stdev is not defined,
168 // this indicates that this input does not change.
169 // Set the weight to zero
170 if (fabs(inputs_max_[id] - inputs_min_[id]) < kMinInputRange) {
171 wgt_val = 0.0f;
172 } else {
173 wgt_val /= ((inputs_max_[id] - inputs_min_[id]) *
174 inputs_std_dev_[id]);
175 }
176 }
177 node->inputs[fan_in].input_weight = wgt_val;
178 }
179 // incr wgt count to validate against at the end
180 wts_cnt += node->fan_in_cnt;
181 }
182 }
183 // sanity check
184 return wts_cnt_ == wts_cnt;
185 }
186
187 // returns a pointer to the requested set of weights
188 // Allocates in chunks
189 float * NeuralNet::AllocWgt(int wgt_cnt) {
190 // see if need to allocate a new chunk of wts
191 if (wts_vec_.size() == 0 || (alloc_wgt_cnt_ + wgt_cnt) > kWgtChunkSize) {
192 // add the new chunck to the wts_chunks vector
193 wts_vec_.push_back(new vector<float> (kWgtChunkSize));
194 alloc_wgt_cnt_ = 0;
195 }
196 float *ret_ptr = &((*wts_vec_.back())[alloc_wgt_cnt_]);
197 // incr usage counts
198 alloc_wgt_cnt_ += wgt_cnt;
199 wts_cnt_ += wgt_cnt;
200 return ret_ptr;
201 }
202
203 // create a new net object using an input file as a source
204 NeuralNet *NeuralNet::FromFile(const string file_name) {
205 // open the file
206 InputFileBuffer input_buff(file_name);
207 // create a new net object using input buffer
208 NeuralNet *net_obj = FromInputBuffer(&input_buff);
209 return net_obj;
210 }
211
212 // create a net object from an input buffer
213 NeuralNet *NeuralNet::FromInputBuffer(InputFileBuffer *ib) {
214 // create a new net object
215 NeuralNet *net_obj = new NeuralNet();
216 if (net_obj == NULL) {
217 return NULL;
218 }
219 // load the net
220 if (!net_obj->ReadBinary(ib)) {
221 delete net_obj;
222 net_obj = NULL;
223 }
224 return net_obj;
225 }
226
227 // Compute the output of a specific output node.
228 // This function is useful for application that are interested in a single
229 // output of the net and do not want to waste time on the rest
230 // This is the fast-read-only version of this function
231 template <typename Type> bool NeuralNet::FastGetNetOutput(const Type *inputs,
232 int output_id,
233 Type *output) {
234 // feed inputs in and offset them by the pre-computed bias
235 int node_idx = 0;
236 Node *node = &fast_nodes_[0];
237 for (node_idx = 0; node_idx < in_cnt_; node_idx++, node++) {
238 node->out = inputs[node_idx] - node->bias;
239 }
240
241 // compute nodes' activations and outputs for hidden nodes if any
242 int hidden_node_cnt = neuron_cnt_ - out_cnt_;
243 for (;node_idx < hidden_node_cnt; node_idx++, node++) {
244 double activation = -node->bias;
245 for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
246 activation += (node->inputs[fan_in_idx].input_weight *
247 node->inputs[fan_in_idx].input_node->out);
248 }
249 node->out = Neuron::Sigmoid(activation);
250 }
251
252 // compute the output of the required output node
253 node += output_id;
254 double activation = -node->bias;
255 for (int fan_in_idx = 0; fan_in_idx < node->fan_in_cnt; fan_in_idx++) {
256 activation += (node->inputs[fan_in_idx].input_weight *
257 node->inputs[fan_in_idx].input_node->out);
258 }
259 (*output) = Neuron::Sigmoid(activation);
260 return true;
261 }
262
263 // Performs a feedforward for general nets. Used mainly in training mode
264 // Templatized for float and double Types
265 template <typename Type> bool NeuralNet::GetNetOutput(const Type *inputs,
266 int output_id,
267 Type *output) {
268 // validate output id
269 if (output_id < 0 || output_id >= out_cnt_) {
270 return false;
271 }
272
273 // call the fast version in case of readonly nets
274 if (read_only_) {
275 return FastGetNetOutput(inputs, output_id, output);
276 }
277
278 // For the slow version, we'll just call FeedForward and return the
279 // appropriate output
280 vector<Type> outputs(out_cnt_);
281 if (!FeedForward(inputs, &outputs[0])) {
282 return false;
283 }
284 (*output) = outputs[output_id];
285
286 return true;
287 }
288
289 // Instantiate all supported templates now that the functions have been defined.
290 template bool NeuralNet::FeedForward(const float *inputs, float *outputs);
291 template bool NeuralNet::FeedForward(const double *inputs, double *outputs);
292 template bool NeuralNet::FastFeedForward(const float *inputs, float *outputs);
293 template bool NeuralNet::FastFeedForward(const double *inputs,
294 double *outputs);
295 template bool NeuralNet::GetNetOutput(const float *inputs, int output_id,
296 float *output);
297 template bool NeuralNet::GetNetOutput(const double *inputs, int output_id,
298 double *output);
299 template bool NeuralNet::FastGetNetOutput(const float *inputs, int output_id,
300 float *output);
301 template bool NeuralNet::FastGetNetOutput(const double *inputs, int output_id,
302 double *output);
303 template bool NeuralNet::ReadBinary(InputFileBuffer *input_buffer);
304
305 }