tiny_dnn 1.0.0
A header only, dependency-free deep learning framework in C++11
Loading...
Searching...
No Matches
tiny_quantized_deconv2d_kernel.h
1/*
2 Copyright (c) 2016, Taiga Nomi, Edgar Riba
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the <organization> nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
17 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#pragma once
28
29#include "tiny_dnn/core/params/deconv_params.h"
30#include "tiny_dnn/core/kernels/tiny_quantization_kernel.h"
31
32namespace tiny_dnn {
33namespace core {
34namespace kernels {
35
36inline void tiny_quantized_deconv2d_kernel(const deconv_params& params,
37 const vec_t& in,
38 const vec_t& W,
39 const vec_t& bias,
40 vec_t& a,
41 const bool layer_parallelize) {
42 // image quantization
43 float_t min_input(in[0]);
44 float_t max_input(in[0]);
45 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
46 for (serial_size_t ins = 0; ins < params.in.height_*params.in.height_; ins++) {
47 serial_size_t idx = params.in.get_index(0, 0, inc);
48 min_input = std::min(min_input, (&in[idx])[ins]);
49 max_input = std::max(max_input, (&in[idx])[ins]);
50 }
51 }
52 std::vector<uint8_t> in_quantized =
53 float_tensor_to_quantized<uint8_t>(in, min_input, max_input);
54 // filter quantization
55 float_t min_filter(W[0]);
56 float_t max_filter(W[0]);
57 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
58 for (serial_size_t ins = 0; ins < params.weight.height_*params.weight.height_; ins++) {
59 serial_size_t idx = params.in.get_index(0, 0, inc);
60 min_filter = std::min(min_filter, (&W[idx])[ins]);
61 max_filter = std::max(max_filter, (&W[idx])[ins]);
62 }
63 }
64 if (min_filter == max_filter) {
65 max_filter = W[0] + 1e-3f;
66 min_filter = W[0] - 1e-3f;
67 }
68 std::vector<uint8_t> W_quantized =
69 float_tensor_to_quantized<uint8_t>(W, min_filter, max_filter);
70 // bias quantization
71 float_t min_bias(0);
72 float_t max_bias(0);
73 std::vector<uint8_t> bias_quantized;
74 if (params.has_bias) {
75 for (serial_size_t inc = 0; inc < params.out.depth_; inc++) {
76 min_bias = std::min(min_bias, bias[inc]);
77 max_bias = std::max(max_bias, bias[inc]);
78 }
79 if (min_bias == max_bias) {
80 max_bias = bias[0] + 1e-3f;
81 min_bias = bias[0] - 1e-3f;
82 }
83 bias_quantized =
84 float_tensor_to_quantized<uint8_t>(bias, min_bias, max_bias);
85 }
86
87 // output range
88 float_t min_output_value;
89 float_t max_output_value;
90 quantization_range_for_multiplication<uint8_t, uint8_t, int32_t>(
91 min_input, max_input, min_filter, max_filter, &min_output_value,
92 &max_output_value);
93
94 std::vector<int32_t> a_quantized(a.size(), static_cast<int32_t>(0));
95
96 // calculating offset
97 const int32_t offset_input =
98 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_input, max_input));
99 const int32_t offset_filter =
100 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_filter, max_filter));
101 const int32_t zero_in_total_space =
102 int64_to_int32(float_to_quantized<int32_t>(0.0f, min_output_value, max_output_value));
103
104 for_i(layer_parallelize, params.out.depth_, [&](int o) {
105 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
106 if (!params.tbl.is_connected(o, inc)) continue;
107
108 serial_size_t idx = 0;
109 idx = params.in.depth_ * o + inc;
110 idx = params.weight.get_index(0, 0, idx);
111 const uint8_t *pw = &W_quantized[idx];
112
113 idx = params.in.get_index(0, 0, inc);
114 const uint8_t *pi = &in_quantized[idx];
115
116 idx = params.out.get_index(0, 0, o);
117 int32_t *pa_quantized = &a_quantized[idx];
118
119 for (serial_size_t y = 0; y < params.in.height_; y++) {
120 for (serial_size_t x = 0; x < params.in.width_; x++) {
121 const uint8_t * ppw = pw;
122 const uint8_t * ppi = pi + y * params.in.width_ + x;
123 // should be optimized for small kernel(3x3,5x5)
124 for (serial_size_t wy = 0; wy < params.weight.height_; wy++) {
125 for (serial_size_t wx = 0; wx < params.weight.width_; wx++) {
126 pa_quantized[(y * params.h_stride + wy) *
127 params.out.width_ + (x *
128 params.w_stride + wx)] += static_cast<int32_t>(ppw[wy *
129 params.weight.width_ + wx] - offset_filter) *
130 static_cast<int32_t>(*ppi - offset_input);
131 }
132 }
133 }
134 }
135 }
136 if (params.has_bias) {
137 int32_t * pa_quantized = &a_quantized[params.out.get_index(0, 0, o)];
138 int32_t * paa_quantized = pa_quantized + params.out.width_ * params.out.height_;
139 std::for_each(pa_quantized, paa_quantized, [&](int32_t& f) {
140 f += (bias_quantized[o] - zero_in_total_space);
141 });
142 }
143 });
144
145 float_t min_output_requantized;
146 float_t max_output_requantized;
147 std::vector<uint8_t> a_requantized(a_quantized.size(), static_cast<uint8_t>(0));
148
149 // Requantize from 32bits to 8 bits for next layer
150 quantize_down_and_shrink_range<int32_t, uint8_t>(a_quantized, min_output_value, max_output_value,
151 &min_output_requantized, &max_output_requantized, &a_requantized);
152
153 // dequantize to flaot, this could be removed within concatenated quantized network
154 a = quantized_tensor_to_float<uint8_t>(a_requantized, min_output_requantized, max_output_requantized);
155}
156
157inline void tiny_quantized_deconv2d_back_kernel(const deconv_params& params,
158 const vec_t& prev_out,
159 const vec_t& W,
160 vec_t& dW,
161 vec_t& db,
162 vec_t& curr_delta,
163 vec_t* prev_delta) {
164 // previous output quantization
165 float_t min_prev_out(prev_out[0]);
166 float_t max_prev_out(prev_out[0]);
167 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
168 for (serial_size_t ins = 0; ins < params.in.height_*params.in.height_; ins++) {
169 serial_size_t idx = params.in.get_index(0, 0, inc);
170 min_prev_out = std::min(min_prev_out, (&prev_out[idx])[ins]);
171 max_prev_out = std::max(min_prev_out, (&prev_out[idx])[ins]);
172 }
173 }
174 std::vector<uint8_t> prev_out_quantized =
175 float_tensor_to_quantized<uint8_t>(prev_out, min_prev_out, max_prev_out);
176
177 // filter quantization
178 float_t min_filter(W[0]);
179 float_t max_filter(W[0]);
180 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
181 for (serial_size_t ins = 0; ins < params.weight.height_*params.weight.height_; ins++) {
182 serial_size_t idx = params.in.get_index(0, 0, inc);
183 min_filter = std::min(min_filter, (&W[idx])[ins]);
184 max_filter = std::max(max_filter, (&W[idx])[ins]);
185 }
186 }
187 if (min_filter == max_filter) {
188 max_filter = W[0] + 1e-3f;
189 min_filter = W[0] - 1e-3f;
190 }
191 std::vector<uint8_t> W_quantized =
192 float_tensor_to_quantized<uint8_t>(W, min_filter, max_filter);
193
194 // current delta quantization
195 float_t min_curr_delta(curr_delta[0]);
196 float_t max_curr_delta(curr_delta[0]);
197 for (serial_size_t inc = 0; inc < params.out.depth_; inc++) {
198 for (serial_size_t ins = 0; ins < params.out.height_*params.out.height_; ins++) {
199 serial_size_t idx = params.out.get_index(0, 0, inc);
200 min_curr_delta = std::min(min_curr_delta, (&curr_delta[idx])[ins]);
201 max_curr_delta = std::max(max_curr_delta, (&curr_delta[idx])[ins]);
202 }
203 }
204 std::vector<uint8_t> curr_delta_quantized =
205 float_tensor_to_quantized<uint8_t>(curr_delta, min_curr_delta, max_curr_delta);
206
207 // output range for previous delta
208 float_t min_prev_delta_value;
209 float_t max_prev_delta_value;
210 quantization_range_for_multiplication<uint8_t, uint8_t, int32_t>(
211 min_curr_delta, max_curr_delta, min_filter, max_filter, &min_prev_delta_value,
212 &max_prev_delta_value);
213
214 std::vector<int32_t> prev_delta_quantized(prev_delta->size(), static_cast<int32_t>(0));
215
216 // output range for dW
217 float_t min_dW_value;
218 float_t max_dW_value;
219 quantization_range_for_multiplication<uint8_t, uint8_t, int32_t>(
220 min_curr_delta, max_curr_delta, min_prev_out, max_prev_out, &min_dW_value,
221 &max_dW_value);
222
223 std::vector<int32_t> dW_quantized(dW.size(), static_cast<int32_t>(0));
224
225 // calculating offset
226 // TODO wangdiya: do we need to check overflows?
227 const int32_t offset_prev_out =
228 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_prev_out, max_prev_out));
229 const int32_t offset_filter =
230 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_filter, max_filter));
231 const int32_t offset_curr_delta =
232 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_curr_delta, max_curr_delta));
233 // const int32_t zero_in_prev_delta =
234 // float_to_quantized<int32_t>(0.0f, min_prev_delta_value, max_prev_delta_value);
235
236 // propagate delta to previous layer
237 for_i(params.in.depth_, [&](int inc) {
238 for (serial_size_t outc = 0; outc < params.out.depth_; outc++) {
239 if (!params.tbl.is_connected(outc, inc)) continue;
240
241 serial_size_t idx = 0;
242 idx = params.in.depth_ * outc + inc;
243 idx = params.weight.get_index(0, 0, idx);
244 const uint8_t *pw = &W_quantized[idx];
245
246 idx = params.out_unpadded.get_index(0, 0, outc);
247 const uint8_t *pdelta_src = &curr_delta_quantized[idx];
248
249 idx = params.in.get_index(0, 0, inc);
250 int32_t *pdelta_quantized_dst = &(prev_delta_quantized)[idx];
251
252 for (serial_size_t y = 0; y < params.in.height_; y++) {
253 for (serial_size_t x = 0; x < params.in.width_; x++) {
254 const uint8_t * ppw = pw;
255 int32_t * ppdelta_quantized_dst = pdelta_quantized_dst + y * params.in.width_ + x;
256 int32_t sum = int32_t(0);
257
258 for (serial_size_t wy = 0; wy < params.weight.height_; wy++) {
259 for (serial_size_t wx = 0; wx < params.weight.width_; wx++) {
260 sum += static_cast<int32_t>(ppw[wy * params.weight.width_ + wx] - offset_filter) *
261 static_cast<int32_t>(pdelta_src[(y * params.h_stride + wy) *
262 params.out.width_ + (x *
263 params.w_stride + wx)] -
264 offset_curr_delta);
265 }
266 }
267 *ppdelta_quantized_dst += sum;
268 }
269 }
270 }
271 });
272
273 float_t min_prev_delta_requantized;
274 float_t max_prev_delta_requantized;
275 std::vector<uint8_t> prev_delta_requantized(prev_delta_quantized.size(), static_cast<uint8_t>(0));
276
277 // Requantize from 32bits to 8 bits for next layer
278 quantize_down_and_shrink_range<int32_t, uint8_t>(prev_delta_quantized, min_prev_delta_value, max_prev_delta_value,
279 &min_prev_delta_requantized, &max_prev_delta_requantized, &prev_delta_requantized);
280
281 // dequantize to flaot, this could be removed within concatenated quantized network
282 vec_t prev_delta_vec = quantized_tensor_to_float<uint8_t>(prev_delta_requantized, min_prev_delta_requantized, max_prev_delta_requantized);
283 prev_delta = &prev_delta_vec;
284
285 // Accumulate dw
286 for_i(params.in.depth_, [&](int inc) {
287 for (serial_size_t outc = 0; outc < params.out.depth_; outc++) {
288 if (!params.tbl.is_connected(outc, inc)) continue;
289
290 for (serial_size_t wy = 0; wy < params.weight.height_; wy++) {
291 for (serial_size_t wx = 0; wx < params.weight.width_; wx++) {
292 int32_t dst = int32_t(0);
293
294 serial_size_t idx = 0;
295 idx = params.in.get_index(0, 0, inc);
296 const uint8_t * prevo = &prev_out_quantized[idx];
297
298 idx = params.out.get_index(wx, wy, outc);
299 const uint8_t * delta = &curr_delta_quantized[idx];
300
301 for (serial_size_t y = 0; y < params.in.height_; y++) {
302 for (serial_size_t x = 0; x < params.in.width_; x++) {
303 dst += (static_cast<int32_t>(*(prevo + y * params.in.width_ + x)) - offset_prev_out) *
304 (static_cast<int32_t>(*(delta + y * params.out.width_ + x)) - offset_curr_delta);
305 }
306 }
307
308 idx = params.in.depth_ * outc + inc;
309 dW_quantized[params.weight.get_index(wx, wy, idx)] += dst;
310 }
311 }
312 }
313 });
314
315 float_t min_dW_requantized;
316 float_t max_dW_requantized;
317 std::vector<uint8_t> dW_requantized(dW_quantized.size(), static_cast<uint8_t>(0));
318
319 // requantize from 32bits to 8 bits for next layer
320 quantize_down_and_shrink_range<int32_t, uint8_t>(dW_quantized, min_dW_value, max_dW_value,
321 &min_dW_requantized, &max_dW_requantized, &dW_requantized);
322
323 // dequantize to flaot, this could be removed within concatenated quantized network
324 dW = quantized_tensor_to_float<uint8_t>(dW_requantized, min_dW_requantized, max_dW_requantized);
325
326 // Accumulate db
327 if (params.has_bias) {
328 //vec_t& db = *in_grad[2];
329
330 for (serial_size_t outc = 0; outc < params.out.depth_; outc++) {
331 serial_size_t idx = params.out.get_index(0, 0, outc);
332 const float_t * delta = &curr_delta[idx];
333 const float_t * deltaa = delta + params.out.width_ *
334 params.out.height_;
335 db[outc] += std::accumulate(delta, deltaa, float_t(0));
336 }
337 }
338}
339
340inline void tiny_quantized_deconv2d_kernel(const deconv_params& params,
341 const vec_t& in,
342 const vec_t& W,
343 const vec_t& bias,
344 const vec_t& in_r,
345 const vec_t& W_r,
346 const vec_t& b_r,
347 vec_t& a,
348 vec_t& a_r,
349 const bool layer_parallelize) {
350 // filter range
351 float_t min_filter(W_r[0]);
352 float_t max_filter(W_r[1]);
353 if (W_r[0] == W_r[1]) {
354 max_filter = W_r[1] + 1e-3f;
355 min_filter = W_r[0] - 1e-3f;
356 }
357 // bias range
358 float_t min_bias(b_r[0]);
359 float_t max_bias(b_r[1]);
360 if (params.has_bias) {
361 if (min_bias == max_bias) {
362 max_bias = b_r[1] + 1e-3f;
363 min_bias = b_r[0] - 1e-3f;
364 }
365 }
366 // output range
367 float_t min_output_value;
368 float_t max_output_value;
369 quantization_range_for_multiplication<uint8_t, uint8_t, int32_t>(
370 in_r[0], in_r[1], min_filter, max_filter, &min_output_value,
371 &max_output_value);
372 // data type restore
373 std::vector<uint8_t> in_quantized, W_quantized, bias_quantized;
374 for (size_t i = 0; i < in.size(); i++) {
375 in_quantized.push_back(static_cast<uint8_t>(in[i]));
376 }
377 for (size_t i = 0; i < W.size(); i++) {
378 W_quantized.push_back(static_cast<uint8_t>(W[i]));
379 }
380 for (size_t i = 0; i < bias.size(); i++) {
381 bias_quantized.push_back(static_cast<uint8_t>(bias[i]));
382 }
383
384 std::vector<int32_t> a_quantized(a.size(), static_cast<int32_t>(0));
385
386 // calculating offset
387 const int32_t offset_input =
388 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, in_r[0], in_r[1]));
389 const int32_t offset_filter =
390 int64_to_int32(float_to_quantized_unclamped<uint8_t>(0.0f, min_filter, max_filter));
391 const int32_t zero_in_total_space =
392 int64_to_int32(float_to_quantized<int32_t>(0.0f, min_output_value, max_output_value));
393
394 for_i(layer_parallelize, params.out.depth_, [&](int o) {
395 for (serial_size_t inc = 0; inc < params.in.depth_; inc++) {
396 if (!params.tbl.is_connected(o, inc)) continue;
397
398 serial_size_t idx = 0;
399 idx = params.in.depth_ * o + inc;
400 idx = params.weight.get_index(0, 0, idx);
401 const uint8_t *pw = &W_quantized[idx];
402
403 idx = params.in.get_index(0, 0, inc);
404 const uint8_t *pi = &in_quantized[idx];
405
406 idx = params.out.get_index(0, 0, o);
407 int32_t *pa_quantized = &a_quantized[idx];
408
409 for (serial_size_t y = 0; y < params.in.height_; y++) {
410 for (serial_size_t x = 0; x < params.in.width_; x++) {
411 const uint8_t * ppw = pw;
412 const uint8_t * ppi = pi + y * params.in.width_ + x;
413 // should be optimized for small kernel(3x3,5x5)
414 for (serial_size_t wy = 0; wy < params.weight.height_; wy++) {
415 for (serial_size_t wx = 0; wx < params.weight.width_; wx++) {
416 pa_quantized[(y * params.h_stride + wy) *
417 params.out.width_ + (x *
418 params.w_stride + wx)] += static_cast<int32_t>(ppw[wy *
419 params.weight.width_ + wx] - offset_filter) *
420 static_cast<int32_t>(*ppi - offset_input);
421 }
422 }
423 }
424 }
425 }
426 if (params.has_bias) {
427 int32_t * pa_quantized = &a_quantized[params.out.get_index(0, 0, o)];
428 int32_t * paa_quantized = pa_quantized + params.out.width_ * params.out.height_;
429 std::for_each(pa_quantized, paa_quantized, [&](int32_t& f) {
430 f += static_cast<int32_t>((bias[o] - zero_in_total_space));
431 });
432 }
433 });
434
435 float_t min_output_requantized;
436 float_t max_output_requantized;
437 std::vector<uint8_t> a_requantized(a_quantized.size(), static_cast<uint8_t>(0));
438
439 // Requantize from 32bits to 8 bits for next layer
440 quantize_down_and_shrink_range<int32_t, uint8_t>(a_quantized, min_output_value, max_output_value,
441 &min_output_requantized, &max_output_requantized, &a_requantized);
442 // store directly in float datatype
443 for (size_t i = 0; i < a_requantized.size(); i++) {
444 a[i] = static_cast<float>(a_requantized[i]);
445 }
446 a_r[0] = min_output_requantized;
447 a_r[1] = max_output_requantized;
448}
449
450} // namespace kernels
451} // namespace core
452} // namespace tiny_dnn