tesseract 4.1.1
Loading...
Searching...
No Matches
fullyconnected.cpp
Go to the documentation of this file.
1
2// File: fullyconnected.cpp
3// Description: Simple feed-forward layer with various non-linearities.
4// Author: Ray Smith
5// Created: Wed Feb 26 14:49:15 PST 2014
6//
7// (C) Copyright 2014, Google Inc.
8// Licensed under the Apache License, Version 2.0 (the "License");
9// you may not use this file except in compliance with the License.
10// You may obtain a copy of the License at
11// http://www.apache.org/licenses/LICENSE-2.0
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
18
19#include "fullyconnected.h"
20
21#ifdef _OPENMP
22#include <omp.h>
23#endif
24#include <cstdio>
25#include <cstdlib>
26
27#include "functions.h"
28#include "networkscratch.h"
29
30// Number of threads to use for parallel calculation of Forward and Backward.
31#ifdef _OPENMP
32const int kNumThreads = 4;
33#else
34const int kNumThreads = 1;
35#endif
36
37namespace tesseract {
38
39FullyConnected::FullyConnected(const STRING& name, int ni, int no,
40 NetworkType type)
41 : Network(type, name, ni, no), external_source_(nullptr), int_mode_(false) {
42}
43
44// Returns the shape output from the network given an input shape (which may
45// be partially unknown ie zero).
47 LossType loss_type = LT_NONE;
48 if (type_ == NT_SOFTMAX)
49 loss_type = LT_CTC;
50 else if (type_ == NT_SOFTMAX_NO_CTC)
51 loss_type = LT_SOFTMAX;
52 else if (type_ == NT_LOGISTIC)
53 loss_type = LT_LOGISTIC;
54 StaticShape result(input_shape);
55 result.set_depth(no_);
56 result.set_loss_type(loss_type);
57 return result;
58}
59
60// Suspends/Enables training by setting the training_ flag.
62 if (state == TS_RE_ENABLE) {
63 // Enable only from temp disabled.
65 } else if (state == TS_TEMP_DISABLE) {
66 // Temp disable only from enabled.
67 if (training_ == TS_ENABLED) training_ = state;
68 } else {
69 if (state == TS_ENABLED && training_ != TS_ENABLED)
71 training_ = state;
72 }
73}
74
75// Sets up the network for training. Initializes weights using weights of
76// scale `range` picked according to the random number generator `randomizer`.
77int FullyConnected::InitWeights(float range, TRand* randomizer) {
78 Network::SetRandomizer(randomizer);
80 range, randomizer);
81 return num_weights_;
82}
83
84// Recursively searches the network for softmaxes with old_no outputs,
85// and remaps their outputs according to code_map. See network.h for details.
86
87int FullyConnected::RemapOutputs(int old_no, const std::vector<int>& code_map) {
88 if (type_ == NT_SOFTMAX && no_ == old_no) {
90 no_ = code_map.size();
91 }
92 return num_weights_;
93}
94
95// Converts a float network to an int network.
98}
99
100// Provides debug output on the weights.
103}
104
105// Writes to the given file. Returns false in case of error.
107 if (!Network::Serialize(fp)) return false;
108 if (!weights_.Serialize(IsTraining(), fp)) return false;
109 return true;
110}
111
112// Reads from the given file. Returns false in case of error.
114 return weights_.DeSerialize(IsTraining(), fp);
115}
116
117// Runs forward propagation of activations on the input line.
118// See NetworkCpp for a detailed discussion of the arguments.
119void FullyConnected::Forward(bool debug, const NetworkIO& input,
120 const TransposedArray* input_transpose,
121 NetworkScratch* scratch, NetworkIO* output) {
122 int width = input.Width();
123 if (type_ == NT_SOFTMAX)
124 output->ResizeFloat(input, no_);
125 else
126 output->Resize(input, no_);
127 SetupForward(input, input_transpose);
132 for (int i = 0; i < kNumThreads; ++i) {
133 temp_lines[i].Init(no_, scratch);
134 curr_input[i].Init(ni_, scratch);
135 }
136#ifdef _OPENMP
137#pragma omp parallel for num_threads(kNumThreads)
138 for (int t = 0; t < width; ++t) {
139 // Thread-local pointer to temporary storage.
140 int thread_id = omp_get_thread_num();
141#else
142 for (int t = 0; t < width; ++t) {
143 // Thread-local pointer to temporary storage.
144 int thread_id = 0;
145#endif
146 double* temp_line = temp_lines[thread_id];
147 if (input.int_mode()) {
148 ForwardTimeStep(input.i(t), t, temp_line);
149 } else {
150 input.ReadTimeStep(t, curr_input[thread_id]);
151 ForwardTimeStep(curr_input[thread_id], t, temp_line);
152 }
153 output->WriteTimeStep(t, temp_line);
154 if (IsTraining() && type_ != NT_SOFTMAX) {
155 acts_.CopyTimeStepFrom(t, *output, t);
156 }
157 }
158 // Zero all the elements that are in the padding around images that allows
159 // multiple different-sized images to exist in a single array.
160 // acts_ is only used if this is not a softmax op.
161 if (IsTraining() && type_ != NT_SOFTMAX) {
163 }
164 output->ZeroInvalidElements();
165#if DEBUG_DETAIL > 0
166 tprintf("F Output:%s\n", name_.string());
167 output->Print(10);
168#endif
169 if (debug) DisplayForward(*output);
170}
171
172// Components of Forward so FullyConnected can be reused inside LSTM.
174 const TransposedArray* input_transpose) {
175 // Softmax output is always float, so save the input type.
176 int_mode_ = input.int_mode();
177 if (IsTraining()) {
178 acts_.Resize(input, no_);
179 // Source_ is a transposed copy of input. It isn't needed if provided.
180 external_source_ = input_transpose;
181 if (external_source_ == nullptr) source_t_.ResizeNoInit(ni_, input.Width());
182 }
183}
184
185void FullyConnected::ForwardTimeStep(int t, double* output_line) {
186 if (type_ == NT_TANH) {
187 FuncInplace<GFunc>(no_, output_line);
188 } else if (type_ == NT_LOGISTIC) {
189 FuncInplace<FFunc>(no_, output_line);
190 } else if (type_ == NT_POSCLIP) {
191 FuncInplace<ClipFFunc>(no_, output_line);
192 } else if (type_ == NT_SYMCLIP) {
193 FuncInplace<ClipGFunc>(no_, output_line);
194 } else if (type_ == NT_RELU) {
195 FuncInplace<Relu>(no_, output_line);
196 } else if (type_ == NT_SOFTMAX || type_ == NT_SOFTMAX_NO_CTC) {
197 SoftmaxInPlace(no_, output_line);
198 } else if (type_ != NT_LINEAR) {
199 ASSERT_HOST("Invalid fully-connected type!" == nullptr);
200 }
201}
202
203void FullyConnected::ForwardTimeStep(const double* d_input,
204 int t, double* output_line) {
205 // input is copied to source_ line-by-line for cache coherency.
206 if (IsTraining() && external_source_ == nullptr)
207 source_t_.WriteStrided(t, d_input);
208 weights_.MatrixDotVector(d_input, output_line);
209 ForwardTimeStep(t, output_line);
210}
211
212void FullyConnected::ForwardTimeStep(const int8_t* i_input,
213 int t, double* output_line) {
214 // input is copied to source_ line-by-line for cache coherency.
215 weights_.MatrixDotVector(i_input, output_line);
216 ForwardTimeStep(t, output_line);
217}
218
219// Runs backward propagation of errors on the deltas line.
220// See NetworkCpp for a detailed discussion of the arguments.
221bool FullyConnected::Backward(bool debug, const NetworkIO& fwd_deltas,
222 NetworkScratch* scratch,
223 NetworkIO* back_deltas) {
224 if (debug) DisplayBackward(fwd_deltas);
225 back_deltas->Resize(fwd_deltas, ni_);
228 for (int i = 0; i < kNumThreads; ++i) errors[i].Init(no_, scratch);
230 if (needs_to_backprop_) {
232 for (int i = 0; i < kNumThreads; ++i) temp_backprops[i].Init(ni_, scratch);
233 }
234 int width = fwd_deltas.Width();
236 errors_t.Init(no_, width, scratch);
237#ifdef _OPENMP
238#pragma omp parallel for num_threads(kNumThreads)
239 for (int t = 0; t < width; ++t) {
240 int thread_id = omp_get_thread_num();
241#else
242 for (int t = 0; t < width; ++t) {
243 int thread_id = 0;
244#endif
245 double* backprop = nullptr;
246 if (needs_to_backprop_) backprop = temp_backprops[thread_id];
247 double* curr_errors = errors[thread_id];
248 BackwardTimeStep(fwd_deltas, t, curr_errors, errors_t.get(), backprop);
249 if (backprop != nullptr) {
250 back_deltas->WriteTimeStep(t, backprop);
251 }
252 }
253 FinishBackward(*errors_t.get());
254 if (needs_to_backprop_) {
255 back_deltas->ZeroInvalidElements();
256#if DEBUG_DETAIL > 0
257 tprintf("F Backprop:%s\n", name_.string());
258 back_deltas->Print(10);
259#endif
260 return true;
261 }
262 return false; // No point going further back.
263}
264
265void FullyConnected::BackwardTimeStep(const NetworkIO& fwd_deltas, int t,
266 double* curr_errors,
267 TransposedArray* errors_t,
268 double* backprop) {
269 if (type_ == NT_TANH)
270 acts_.FuncMultiply<GPrime>(fwd_deltas, t, curr_errors);
271 else if (type_ == NT_LOGISTIC)
272 acts_.FuncMultiply<FPrime>(fwd_deltas, t, curr_errors);
273 else if (type_ == NT_POSCLIP)
274 acts_.FuncMultiply<ClipFPrime>(fwd_deltas, t, curr_errors);
275 else if (type_ == NT_SYMCLIP)
276 acts_.FuncMultiply<ClipGPrime>(fwd_deltas, t, curr_errors);
277 else if (type_ == NT_RELU)
278 acts_.FuncMultiply<ReluPrime>(fwd_deltas, t, curr_errors);
279 else if (type_ == NT_SOFTMAX || type_ == NT_SOFTMAX_NO_CTC ||
280 type_ == NT_LINEAR)
281 fwd_deltas.ReadTimeStep(t, curr_errors); // fwd_deltas are the errors.
282 else
283 ASSERT_HOST("Invalid fully-connected type!" == nullptr);
284 // Generate backprop only if needed by the lower layer.
285 if (backprop != nullptr) weights_.VectorDotMatrix(curr_errors, backprop);
286 errors_t->WriteStrided(t, curr_errors);
287}
288
290 if (external_source_ == nullptr)
291 weights_.SumOuterTransposed(errors_t, source_t_, true);
292 else
294}
295
296// Updates the weights using the given learning rate, momentum and adam_beta.
297// num_samples is used in the adam computation iff use_adam_ is true.
298void FullyConnected::Update(float learning_rate, float momentum,
299 float adam_beta, int num_samples) {
300 weights_.Update(learning_rate, momentum, adam_beta, num_samples);
301}
302
303// Sums the products of weight updates in *this and other, splitting into
304// positive (same direction) in *same and negative (different direction) in
305// *changed.
306void FullyConnected::CountAlternators(const Network& other, double* same,
307 double* changed) const {
308 ASSERT_HOST(other.type() == type_);
309 const auto* fc = static_cast<const FullyConnected*>(&other);
310 weights_.CountAlternators(fc->weights_, same, changed);
311}
312
313} // namespace tesseract.
#define ASSERT_HOST(x)
Definition: errcode.h:88
DLLSYM void tprintf(const char *format,...)
Definition: tprintf.cpp:35
const int kNumThreads
void SoftmaxInPlace(int n, T *inout)
Definition: functions.h:146
TrainingState
Definition: network.h:92
@ TS_TEMP_DISABLE
Definition: network.h:97
@ TS_ENABLED
Definition: network.h:95
@ TS_RE_ENABLE
Definition: network.h:99
NetworkType
Definition: network.h:43
@ NT_LINEAR
Definition: network.h:67
@ NT_RELU
Definition: network.h:66
@ NT_SOFTMAX
Definition: network.h:68
@ NT_LOGISTIC
Definition: network.h:62
@ NT_SYMCLIP
Definition: network.h:64
@ NT_POSCLIP
Definition: network.h:63
@ NT_SOFTMAX_NO_CTC
Definition: network.h:69
@ NT_TANH
Definition: network.h:65
@ NF_ADAM
Definition: network.h:88
void init_to_size(int size, const T &t)
void ResizeNoInit(int size1, int size2, int pad=0)
Definition: matrix.h:94
Definition: strngs.h:45
const char * string() const
Definition: strngs.cpp:194
void BackwardTimeStep(const NetworkIO &fwd_deltas, int t, double *curr_errors, TransposedArray *errors_t, double *backprop)
bool DeSerialize(TFile *fp) override
void FinishBackward(const TransposedArray &errors_t)
void SetupForward(const NetworkIO &input, const TransposedArray *input_transpose)
bool Backward(bool debug, const NetworkIO &fwd_deltas, NetworkScratch *scratch, NetworkIO *back_deltas) override
FullyConnected(const STRING &name, int ni, int no, NetworkType type)
void CountAlternators(const Network &other, double *same, double *changed) const override
void SetEnableTraining(TrainingState state) override
const TransposedArray * external_source_
void Update(float learning_rate, float momentum, float adam_beta, int num_samples) override
int InitWeights(float range, TRand *randomizer) override
void Forward(bool debug, const NetworkIO &input, const TransposedArray *input_transpose, NetworkScratch *scratch, NetworkIO *output) override
int RemapOutputs(int old_no, const std::vector< int > &code_map) override
void ForwardTimeStep(int t, double *output_line)
void ConvertToInt() override
StaticShape OutputShape(const StaticShape &input_shape) const override
bool Serialize(TFile *fp) const override
NetworkType type_
Definition: network.h:293
bool needs_to_backprop_
Definition: network.h:295
void DisplayForward(const NetworkIO &matrix)
Definition: network.cpp:288
void DisplayBackward(const NetworkIO &matrix)
Definition: network.cpp:299
bool IsTraining() const
Definition: network.h:115
virtual bool Serialize(TFile *fp) const
Definition: network.cpp:151
bool TestFlag(NetworkFlags flag) const
Definition: network.h:144
int32_t num_weights_
Definition: network.h:299
TrainingState training_
Definition: network.h:294
NetworkType type() const
Definition: network.h:112
virtual void SetRandomizer(TRand *randomizer)
Definition: network.cpp:138
void Resize(const NetworkIO &src, int num_features)
Definition: networkio.h:45
void ZeroInvalidElements()
Definition: networkio.cpp:88
bool int_mode() const
Definition: networkio.h:127
void ResizeFloat(const NetworkIO &src, int num_features)
Definition: networkio.h:52
void Print(int num) const
Definition: networkio.cpp:366
void WriteTimeStep(int t, const double *input)
Definition: networkio.cpp:645
int Width() const
Definition: networkio.h:107
void FuncMultiply(const NetworkIO &v_io, int t, double *product)
Definition: networkio.h:259
void ReadTimeStep(int t, double *output) const
Definition: networkio.cpp:598
const int8_t * i(int t) const
Definition: networkio.h:123
void CopyTimeStepFrom(int dest_t, const NetworkIO &src, int src_t)
Definition: networkio.cpp:383
void Init(int size1, int size2, NetworkScratch *scratch)
void set_loss_type(LossType value)
Definition: static_shape.h:51
void set_depth(int value)
Definition: static_shape.h:49
void WriteStrided(int t, const float *data)
Definition: weightmatrix.h:39
void SumOuterTransposed(const TransposedArray &u, const TransposedArray &v, bool parallel)
bool Serialize(bool training, TFile *fp) const
void Update(double learning_rate, double momentum, double adam_beta, int num_samples)
int InitWeightsFloat(int no, int ni, bool use_adam, float weight_range, TRand *randomizer)
void Debug2D(const char *msg)
void CountAlternators(const WeightMatrix &other, double *same, double *changed) const
int RemapOutputs(const std::vector< int > &code_map)
void MatrixDotVector(const double *u, double *v) const
bool DeSerialize(bool training, TFile *fp)
void VectorDotMatrix(const double *u, double *v) const