/src/tesseract/src/lstm/series.cpp
Line | Count | Source |
1 | | /////////////////////////////////////////////////////////////////////// |
2 | | // File: series.cpp |
3 | | // Description: Runs networks in series on the same input. |
4 | | // Author: Ray Smith |
5 | | // |
6 | | // (C) Copyright 2013, Google Inc. |
7 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | // you may not use this file except in compliance with the License. |
9 | | // You may obtain a copy of the License at |
10 | | // http://www.apache.org/licenses/LICENSE-2.0 |
11 | | // Unless required by applicable law or agreed to in writing, software |
12 | | // distributed under the License is distributed on an "AS IS" BASIS, |
13 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | // See the License for the specific language governing permissions and |
15 | | // limitations under the License. |
16 | | /////////////////////////////////////////////////////////////////////// |
17 | | |
18 | | #include "series.h" |
19 | | |
20 | | #include "fullyconnected.h" |
21 | | #include "networkscratch.h" |
22 | | #include "scrollview.h" |
23 | | #include "tesserrstream.h" // for tesserr |
24 | | #include "tprintf.h" |
25 | | |
26 | | namespace tesseract { |
27 | | |
28 | | // ni_ and no_ will be set by AddToStack. |
29 | 8 | Series::Series(const std::string &name) : Plumbing(name) { |
30 | 8 | type_ = NT_SERIES; |
31 | 8 | } |
32 | | |
33 | | // Returns the shape output from the network given an input shape (which may |
34 | | // be partially unknown ie zero). |
35 | 4 | StaticShape Series::OutputShape(const StaticShape &input_shape) const { |
36 | 4 | StaticShape result(input_shape); |
37 | 4 | int stack_size = stack_.size(); |
38 | 24 | for (int i = 0; i < stack_size; ++i) { |
39 | 20 | result = stack_[i]->OutputShape(result); |
40 | 20 | } |
41 | 4 | return result; |
42 | 4 | } |
43 | | |
44 | | // Sets up the network for training. Initializes weights using weights of |
45 | | // scale `range` picked according to the random number generator `randomizer`. |
46 | | // Note that series has its own implementation just for debug purposes. |
47 | 0 | int Series::InitWeights(float range, TRand *randomizer) { |
48 | 0 | num_weights_ = 0; |
49 | 0 | tprintf("Num outputs,weights in Series:\n"); |
50 | 0 | for (auto &i : stack_) { |
51 | 0 | int weights = i->InitWeights(range, randomizer); |
52 | 0 | tprintf(" %s:%d, %d\n", i->spec().c_str(), i->NumOutputs(), weights); |
53 | 0 | num_weights_ += weights; |
54 | 0 | } |
55 | 0 | tprintf("Total weights = %d\n", num_weights_); |
56 | 0 | return num_weights_; |
57 | 0 | } |
58 | | |
59 | | // Recursively searches the network for softmaxes with old_no outputs, |
60 | | // and remaps their outputs according to code_map. See network.h for details. |
61 | 0 | int Series::RemapOutputs(int old_no, const std::vector<int> &code_map) { |
62 | 0 | num_weights_ = 0; |
63 | 0 | tprintf("Num (Extended) outputs,weights in Series:\n"); |
64 | 0 | for (auto &i : stack_) { |
65 | 0 | int weights = i->RemapOutputs(old_no, code_map); |
66 | 0 | tprintf(" %s:%d, %d\n", i->spec().c_str(), i->NumOutputs(), weights); |
67 | 0 | num_weights_ += weights; |
68 | 0 | } |
69 | 0 | tprintf("Total weights = %d\n", num_weights_); |
70 | 0 | no_ = stack_.back()->NumOutputs(); |
71 | 0 | return num_weights_; |
72 | 0 | } |
73 | | |
74 | | // Sets needs_to_backprop_ to needs_backprop and returns true if |
75 | | // needs_backprop || any weights in this network so the next layer forward |
76 | | // can be told to produce backprop for this layer if needed. |
77 | 0 | bool Series::SetupNeedsBackprop(bool needs_backprop) { |
78 | 0 | needs_to_backprop_ = needs_backprop; |
79 | 0 | for (auto &i : stack_) { |
80 | 0 | needs_backprop = i->SetupNeedsBackprop(needs_backprop); |
81 | 0 | } |
82 | 0 | return needs_backprop; |
83 | 0 | } |
84 | | |
85 | | // Returns an integer reduction factor that the network applies to the |
86 | | // time sequence. Assumes that any 2-d is already eliminated. Used for |
87 | | // scaling bounding boxes of truth data. |
88 | | // WARNING: if GlobalMinimax is used to vary the scale, this will return |
89 | | // the last used scale factor. Call it before any forward, and it will return |
90 | | // the minimum scale factor of the paths through the GlobalMinimax. |
91 | 271k | int Series::XScaleFactor() const { |
92 | 271k | int factor = 1; |
93 | 1.35M | for (auto i : stack_) { |
94 | 1.35M | factor *= i->XScaleFactor(); |
95 | 1.35M | } |
96 | 271k | return factor; |
97 | 271k | } |
98 | | |
99 | | // Provides the (minimum) x scale factor to the network (of interest only to |
100 | | // input units) so they can determine how to scale bounding boxes. |
101 | 4 | void Series::CacheXScaleFactor(int factor) { |
102 | 4 | stack_[0]->CacheXScaleFactor(factor); |
103 | 4 | } |
104 | | |
105 | | // Runs forward propagation of activations on the input line. |
106 | | // See NetworkCpp for a detailed discussion of the arguments. |
107 | | void Series::Forward(bool debug, const NetworkIO &input, const TransposedArray *input_transpose, |
108 | 521k | NetworkScratch *scratch, NetworkIO *output) { |
109 | 521k | int stack_size = stack_.size(); |
110 | 521k | ASSERT_HOST(stack_size > 1); |
111 | | // Revolving intermediate buffers. |
112 | 521k | NetworkScratch::IO buffer1(input, scratch); |
113 | 521k | NetworkScratch::IO buffer2(input, scratch); |
114 | | // Run each network in turn, giving the output of n as the input to n + 1, |
115 | | // with the final network providing the real output. |
116 | 521k | stack_[0]->Forward(debug, input, input_transpose, scratch, buffer1); |
117 | 1.30M | for (int i = 1; i < stack_size; i += 2) { |
118 | 1.30M | stack_[i]->Forward(debug, *buffer1, nullptr, scratch, i + 1 < stack_size ? buffer2 : output); |
119 | 1.30M | if (i + 1 == stack_size) { |
120 | 521k | return; |
121 | 521k | } |
122 | 782k | stack_[i + 1]->Forward(debug, *buffer2, nullptr, scratch, |
123 | 782k | i + 2 < stack_size ? buffer1 : output); |
124 | 782k | } |
125 | 521k | } |
126 | | |
127 | | // Runs backward propagation of errors on the deltas line. |
128 | | // See NetworkCpp for a detailed discussion of the arguments. |
129 | | bool Series::Backward(bool debug, const NetworkIO &fwd_deltas, NetworkScratch *scratch, |
130 | 0 | NetworkIO *back_deltas) { |
131 | 0 | if (!IsTraining()) { |
132 | 0 | return false; |
133 | 0 | } |
134 | 0 | int stack_size = stack_.size(); |
135 | 0 | ASSERT_HOST(stack_size > 1); |
136 | | // Revolving intermediate buffers. |
137 | 0 | NetworkScratch::IO buffer1(fwd_deltas, scratch); |
138 | 0 | NetworkScratch::IO buffer2(fwd_deltas, scratch); |
139 | | // Run each network in reverse order, giving the back_deltas output of n as |
140 | | // the fwd_deltas input to n-1, with the 0 network providing the real output. |
141 | 0 | if (!stack_.back()->IsTraining() || |
142 | 0 | !stack_.back()->Backward(debug, fwd_deltas, scratch, buffer1)) { |
143 | 0 | return false; |
144 | 0 | } |
145 | 0 | for (int i = stack_size - 2; i >= 0; i -= 2) { |
146 | 0 | if (!stack_[i]->IsTraining() || |
147 | 0 | !stack_[i]->Backward(debug, *buffer1, scratch, i > 0 ? buffer2 : back_deltas)) { |
148 | 0 | return false; |
149 | 0 | } |
150 | 0 | if (i == 0) { |
151 | 0 | return needs_to_backprop_; |
152 | 0 | } |
153 | 0 | if (!stack_[i - 1]->IsTraining() || |
154 | 0 | !stack_[i - 1]->Backward(debug, *buffer2, scratch, i > 1 ? buffer1 : back_deltas)) { |
155 | 0 | return false; |
156 | 0 | } |
157 | 0 | } |
158 | 0 | return needs_to_backprop_; |
159 | 0 | } |
160 | | |
161 | | // Splits the series after the given index, returning the two parts and |
162 | | // deletes itself. The first part, up to network with index last_start, goes |
163 | | // into start, and the rest goes into end. |
164 | 0 | void Series::SplitAt(unsigned last_start, Series **start, Series **end) { |
165 | 0 | *start = nullptr; |
166 | 0 | *end = nullptr; |
167 | 0 | if (last_start >= stack_.size()) { |
168 | 0 | tesserr << "Invalid split index " << last_start |
169 | 0 | << " must be in range [0," << stack_.size() - 1 << "]!\n"; |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | auto *master_series = new Series("MasterSeries"); |
173 | 0 | auto *boosted_series = new Series("BoostedSeries"); |
174 | 0 | for (unsigned s = 0; s <= last_start; ++s) { |
175 | 0 | if (s + 1 == stack_.size() && stack_[s]->type() == NT_SOFTMAX) { |
176 | | // Change the softmax to a tanh. |
177 | 0 | auto *fc = static_cast<FullyConnected *>(stack_[s]); |
178 | 0 | fc->ChangeType(NT_TANH); |
179 | 0 | } |
180 | 0 | master_series->AddToStack(stack_[s]); |
181 | 0 | stack_[s] = nullptr; |
182 | 0 | } |
183 | 0 | for (unsigned s = last_start + 1; s < stack_.size(); ++s) { |
184 | 0 | boosted_series->AddToStack(stack_[s]); |
185 | 0 | stack_[s] = nullptr; |
186 | 0 | } |
187 | 0 | *start = master_series; |
188 | 0 | *end = boosted_series; |
189 | 0 | delete this; |
190 | 0 | } |
191 | | |
192 | | // Appends the elements of the src series to this, removing from src and |
193 | | // deleting it. |
194 | 0 | void Series::AppendSeries(Network *src) { |
195 | 0 | ASSERT_HOST(src->type() == NT_SERIES); |
196 | 0 | auto *src_series = static_cast<Series *>(src); |
197 | 0 | for (auto &s : src_series->stack_) { |
198 | 0 | AddToStack(s); |
199 | 0 | s = nullptr; |
200 | 0 | } |
201 | 0 | delete src; |
202 | 0 | } |
203 | | |
204 | | } // namespace tesseract. |