1 // OpenNN: Open Neural Networks Library
2 // www.opennn.net
3 //
4 // C O N V O L U T I O N A L L A Y E R C L A S S
5 //
6 // Artificial Intelligence Techniques SL
7 // artelnics@artelnics.com
8
9 #include "convolutional_layer.h"
10 #include "pooling_layer.h"
11 #include "perceptron_layer.h"
12 #include "probabilistic_layer.h"
13
14 #include "numerical_differentiation.h"
15
16 namespace OpenNN
17 {
18
19 /// Default constructor.
20 /// It creates an empty ConvolutionalLayer object.
21
ConvolutionalLayer()22 ConvolutionalLayer::ConvolutionalLayer() : Layer()
23 {
24 layer_type = Layer::Convolutional;
25 }
26
27
28 /// Inputs' dimensions modifier constructor.
29 /// After setting new dimensions for the inputs, it creates and initializes a ConvolutionalLayer object
30 /// with a number of filters of a given size.
31 /// The initialization values are random values from a normal distribution.
32 /// @param new_inputs_dimensions A vector containing the new inputs' dimensions.
33 /// @param filters_dimensions A vector containing the number of filters, their rows and columns.
34
ConvolutionalLayer(const Tensor<Index,1> & new_inputs_dimensions,const Tensor<Index,1> & new_filters_dimensions)35 ConvolutionalLayer::ConvolutionalLayer(const Tensor<Index, 1>& new_inputs_dimensions,
36 const Tensor<Index, 1>& new_filters_dimensions) : Layer()
37 {
38 layer_type = Layer::Convolutional;
39
40 set(new_inputs_dimensions, new_filters_dimensions);
41 }
42
43
44 /// Returns a boolean, true if convolutional layer is empty and false otherwise.
45
is_empty() const46 bool ConvolutionalLayer::is_empty() const
47 {
48 if(biases.size() == 0 && synaptic_weights.size() == 0)
49 {
50 return true;
51 }
52
53 return false;
54 }
55
56
insert_padding(const Tensor<type,4> & inputs,Tensor<type,4> & padded_output)57 void ConvolutionalLayer::insert_padding(const Tensor<type, 4>& inputs, Tensor<type, 4>& padded_output) // @todo Add stride
58 {
59 switch(convolution_type)
60 {
61 case Valid: padded_output = inputs; return;
62
63 case Same:
64 {
65 Eigen::array<pair<int, int>, 4> paddings;
66 const int pad = int(0.5 *(get_filters_rows_number() - 1));
67 paddings[0] = make_pair(0, 0);
68 paddings[1] = make_pair(0, 0);
69 paddings[2] = make_pair(pad, pad);
70 paddings[3] = make_pair(pad, pad);
71 padded_output = inputs.pad(paddings);
72 return;
73 }
74
75 default: return;
76 }
77 }
78
79
80 /// Calculate combinations
calculate_combinations(const Tensor<type,4> & inputs,Tensor<type,4> & combinations) const81 void ConvolutionalLayer::calculate_combinations(const Tensor<type, 4>& inputs, Tensor<type, 4> & combinations) const
82 {
83 const Index number_of_kernels = synaptic_weights.dimension(0);
84 const Index number_of_images = inputs.dimension(0);
85
86 const Eigen::array<ptrdiff_t, 3> dims = {0, 1, 2};
87
88 Tensor<type, 3> kernel;
89
90 #pragma omp parallel for
91 for(Index i = 0; i < number_of_images; i++)
92 {
93 for(Index j = 0; j < number_of_kernels; j++)
94 {
95 kernel = synaptic_weights.chip(j, 0);
96 combinations.chip(i, 0).chip(j, 0) = inputs.chip(i, 0).convolve(kernel, dims) + biases(j);
97 }
98 }
99 }
100
101
calculate_combinations(const Tensor<type,4> & inputs,const Tensor<type,2> & potential_biases,const Tensor<type,4> & potential_synaptic_weights,Tensor<type,4> & combinations_4d) const102 void ConvolutionalLayer::calculate_combinations(const Tensor<type, 4>& inputs,
103 const Tensor<type, 2>& potential_biases,
104 const Tensor<type, 4>& potential_synaptic_weights,
105 Tensor<type, 4>& combinations_4d) const
106 {
107 const Index number_of_kernels = potential_synaptic_weights.dimension(0);
108 const Index number_of_images = inputs.dimension(0);
109
110 combinations_4d.resize(number_of_images, number_of_kernels, get_outputs_rows_number(), get_outputs_columns_number());
111
112 const Eigen::array<ptrdiff_t, 3> dims = {0, 1, 2};
113
114 Tensor<type, 3> kernel;
115
116 #pragma omp parallel for
117 for(Index i = 0; i < number_of_images; i++)
118 {
119 for(Index j = 0; j < number_of_kernels; j++)
120 {
121 kernel = potential_synaptic_weights.chip(j, 0);
122 combinations_4d.chip(i, 0).chip(j, 0) = inputs.chip(i, 0).convolve(kernel, dims) + potential_biases(j, 0);
123 }
124 }
125 }
126
127
128
129 /// Calculates activations
calculate_activations(const Tensor<type,4> & inputs,Tensor<type,4> & activations) const130 void ConvolutionalLayer::calculate_activations(const Tensor<type, 4>& inputs, Tensor<type, 4>& activations) const
131 {
132 switch(activation_function)
133 {
134 case Linear: linear(inputs, activations); return;
135
136 case Logistic: logistic(inputs, activations); return;
137
138 case HyperbolicTangent: hyperbolic_tangent(inputs, activations); return;
139
140 case Threshold: threshold(inputs, activations); return;
141
142 case SymmetricThreshold: symmetric_threshold(inputs, activations); return;
143
144 case RectifiedLinear: rectified_linear(inputs, activations); return;
145
146 case ScaledExponentialLinear: scaled_exponential_linear(inputs, activations); return;
147
148 case SoftPlus: soft_plus(inputs, activations); return;
149
150 case SoftSign: soft_sign(inputs, activations); return;
151
152 case HardSigmoid: hard_sigmoid(inputs, activations); return;
153
154 case ExponentialLinear: exponential_linear(inputs, activations); return;
155 }
156 }
157
158
calculate_activations_derivatives(const Tensor<type,4> & combinations_4d,Tensor<type,4> & activations,Tensor<type,4> & activations_derivatives) const159 void ConvolutionalLayer::calculate_activations_derivatives(const Tensor<type, 4>& combinations_4d,
160 Tensor<type, 4>& activations,
161 Tensor<type, 4>& activations_derivatives) const
162 {
163 switch(activation_function)
164 {
165 case Linear: linear_derivatives(combinations_4d, activations, activations_derivatives); return;
166
167 case Logistic: logistic_derivatives(combinations_4d, activations, activations_derivatives); return;
168
169 case HyperbolicTangent: hyperbolic_tangent_derivatives(combinations_4d, activations, activations_derivatives); return;
170
171 case Threshold: threshold_derivatives(combinations_4d, activations, activations_derivatives); return;
172
173 case SymmetricThreshold: symmetric_threshold_derivatives(combinations_4d, activations, activations_derivatives); return;
174
175 case RectifiedLinear: rectified_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
176
177 case ScaledExponentialLinear: scaled_exponential_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
178
179 case SoftPlus: soft_plus_derivatives(combinations_4d, activations, activations_derivatives); return;
180
181 case SoftSign: soft_sign_derivatives(combinations_4d, activations, activations_derivatives); return;
182
183 case HardSigmoid: hard_sigmoid_derivatives(combinations_4d, activations, activations_derivatives); return;
184
185 case ExponentialLinear: exponential_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
186 }
187 }
188
189
190 /// Returns the output of the convolutional layer applied to a batch of images.
191 /// @param inputs The batch of images.
192 /// @param outputs Tensor where the outputs will be stored.
193
calculate_outputs(const Tensor<type,4> & inputs,Tensor<type,4> & outputs)194 void ConvolutionalLayer::calculate_outputs(const Tensor<type, 4>& inputs, Tensor<type, 4>& outputs)
195 {
196 const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
197
198 outputs.resize(outputs_dimensions(0), outputs_dimensions(1), outputs_dimensions(2), outputs_dimensions(3));
199
200 Tensor<type, 4> combinations(outputs_dimensions(0), outputs_dimensions(1), outputs_dimensions(2), outputs_dimensions(3));
201
202 calculate_combinations(inputs, combinations);
203 calculate_activations(combinations, outputs);
204 }
205
206
207 //void ConvolutionalLayer::calculate_outputs(const Tensor<type, 2>& inputs, Tensor<type, 2>& outputs)
208 //{
209 // const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
210
211 // const Tensor<Index, 1> inputs_dimensions = get_input_variables_dimensions();
212
213 // const Eigen::array<Eigen::Index, 2> shuffle_dims = {1, 0};
214 // const Eigen::array<Eigen::Index, 4> inputs_dimensions_array = {inputs_dimensions(0),
215 // inputs_dimensions(1),
216 // inputs_dimensions(2),
217 // inputs_dimensions(3)};
218
219 // const Eigen::array<Eigen::Index, 4> outputs_dimensions_array = {outputs_dimensions(0),
220 // outputs_dimensions(1),
221 // outputs_dimensions(2),
222 // outputs_dimensions(3)};
223
224 // const Tensor<type, 4> inputs_temp = inputs.shuffle(shuffle_dims).reshape(inputs_dimensions_array);
225 // Tensor<type, 4> outputs_temp = outputs.reshape(outputs_dimensions_array);
226
227 // calculate_outputs(inputs_temp, outputs_temp);
228
229 // const Eigen::array<Eigen::Index, 2> output_dims = {outputs_dimensions(0) * outputs_dimensions(1) * outputs_dimensions(2), outputs_dimensions(3)};
230
231 // outputs = outputs_temp.reshape(output_dims).shuffle(shuffle_dims);
232 //}
233
234
calculate_outputs(const Tensor<type,4> & inputs,Tensor<type,2> & outputs)235 void ConvolutionalLayer::calculate_outputs(const Tensor<type, 4>& inputs, Tensor<type, 2>& outputs)
236 {
237 const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
238
239 const Tensor<Index, 1> inputs_dimensions = get_input_variables_dimensions();
240
241 const Eigen::array<Eigen::Index, 2> shuffle_dims = {1, 0};
242
243 const Eigen::array<Eigen::Index, 4> outputs_dimensions_array = {outputs_dimensions(0),
244 outputs_dimensions(1),
245 outputs_dimensions(2),
246 outputs_dimensions(3)};
247
248 Tensor<type, 4> outputs_temp = outputs.reshape(outputs_dimensions_array);
249
250 calculate_outputs(inputs, outputs_temp);
251
252 const Eigen::array<Eigen::Index, 2> output_dims = {outputs_dimensions(0) * outputs_dimensions(1) * outputs_dimensions(2), outputs_dimensions(3)};
253
254 outputs = outputs_temp.reshape(output_dims).shuffle(shuffle_dims);
255 }
256
257
forward_propagate(const Tensor<type,4> & inputs,ForwardPropagation & forward_propagation) const258 void ConvolutionalLayer::forward_propagate(const Tensor<type, 4> &inputs, ForwardPropagation &forward_propagation) const
259 {
260 const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
261
262 forward_propagation.combinations_4d.resize(outputs_dimensions(0),
263 outputs_dimensions(1),
264 outputs_dimensions(2),
265 outputs_dimensions(3));
266
267 forward_propagation.activations_4d.resize(outputs_dimensions(0),
268 outputs_dimensions(1),
269 outputs_dimensions(2),
270 outputs_dimensions(3));
271
272 forward_propagation.activations_derivatives_4d.resize(outputs_dimensions(0),
273 outputs_dimensions(1),
274 outputs_dimensions(2),
275 outputs_dimensions(3));
276
277 calculate_combinations(inputs,
278 forward_propagation.combinations_4d);
279
280 calculate_activations_derivatives(forward_propagation.combinations_4d,
281 forward_propagation.activations_4d,
282 forward_propagation.activations_derivatives_4d);
283
284 to_2d(forward_propagation.combinations_4d, forward_propagation.combinations_2d);
285 to_2d(forward_propagation.activations_4d, forward_propagation.activations_2d);
286 to_2d(forward_propagation.activations_derivatives_4d, forward_propagation.activations_derivatives_2d);
287
288 }
289
290
forward_propagate(const Tensor<type,2> & inputs,ForwardPropagation & forward_propagation) const291 void ConvolutionalLayer::forward_propagate(const Tensor<type, 2> &inputs, ForwardPropagation &forward_propagation) const
292 {
293 const Eigen::array<Eigen::Index, 4> four_dims = {input_variables_dimensions(3), // columns number
294 input_variables_dimensions(2), // rows number
295 input_variables_dimensions(1), // channels number
296 inputs.dimension(0)}; // images number
297 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
298 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
299
300 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
301
302 forward_propagate(inputs_4d, forward_propagation);
303 }
304
305
forward_propagate(const Tensor<type,4> & inputs,Tensor<type,1> potential_parameters,ForwardPropagation & forward_propagation) const306 void ConvolutionalLayer::forward_propagate(const Tensor<type, 4>& inputs,
307 Tensor<type, 1> potential_parameters,
308 ForwardPropagation& forward_propagation) const
309 {
310 const Tensor<Index, 1> output_dimensions = get_outputs_dimensions();
311
312 forward_propagation.combinations_4d.resize(output_dimensions(0),
313 output_dimensions(1),
314 output_dimensions(2),
315 output_dimensions(3));
316
317 forward_propagation.activations_4d.resize(output_dimensions(0),
318 output_dimensions(1),
319 output_dimensions(2),
320 output_dimensions(3));
321
322 forward_propagation.activations_derivatives_4d.resize(output_dimensions(0),
323 output_dimensions(1),
324 output_dimensions(2),
325 output_dimensions(3));
326
327 const Index filters_number = synaptic_weights.dimension(0);
328
329 const TensorMap<Tensor<type, 2>> potential_biases(potential_parameters.data(),
330 filters_number,
331 1);
332
333 // TensorMap<Tensor<type, 4>> potential_synaptic_weights(potential_parameters.data() + filters_number,
334 // synaptic_weights.dimension(3),
335 // synaptic_weights.dimension(2),
336 // synaptic_weights.dimension(1),
337 // filters_number);
338
339
340 const Index filters_channels_number = get_filters_channels_number();
341 const Index filters_rows_number = get_filters_rows_number();
342 const Index filters_columns_number = get_filters_columns_number();
343
344 Tensor<type, 4> potential_synaptic_weights(filters_number,
345 filters_channels_number,
346 filters_rows_number,
347 filters_columns_number);
348 Index element_index = 0;
349
350 //#pragma omp for
351 for(Index i = 0; i < filters_number; i++)
352 {
353 for(Index j = 0; j < filters_channels_number; j++)
354 {
355 for(Index k = 0; k < filters_rows_number; k++)
356 {
357 for(Index l = 0; l < filters_columns_number; l++)
358 {
359 potential_synaptic_weights(i ,j, k, l) = potential_parameters(filters_number + element_index);
360 element_index ++;
361 }
362 }
363 }
364 }
365
366 calculate_combinations(inputs,
367 potential_biases,
368 potential_synaptic_weights,
369 forward_propagation.combinations_4d);
370
371 calculate_activations_derivatives(forward_propagation.combinations_4d,
372 forward_propagation.activations_4d,
373 forward_propagation.activations_derivatives_4d);
374
375 to_2d(forward_propagation.combinations_4d, forward_propagation.combinations_2d);
376 to_2d(forward_propagation.activations_4d, forward_propagation.activations_2d);
377 to_2d(forward_propagation.activations_derivatives_4d, forward_propagation.activations_derivatives_2d);
378 }
379
380
forward_propagate(const Tensor<type,2> & inputs,Tensor<type,1> potential_parameters,ForwardPropagation & forward_propagation) const381 void ConvolutionalLayer::forward_propagate(const Tensor<type, 2>& inputs,
382 Tensor<type, 1> potential_parameters,
383 ForwardPropagation& forward_propagation) const
384 {
385 const Eigen::array<Eigen::Index, 4> four_dims = {input_variables_dimensions(3), // columns number
386 input_variables_dimensions(2), // rows number
387 input_variables_dimensions(1), // channels number
388 inputs.dimension(0)}; // images number
389 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
390 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
391
392 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
393
394 forward_propagate(inputs_4d, potential_parameters, forward_propagation);
395 }
396
397
calculate_hidden_delta(Layer * next_layer_pointer,const Tensor<type,2> &,ForwardPropagation & forward_propagation,const Tensor<type,2> & next_layer_delta,Tensor<type,2> & hidden_delta) const398 void ConvolutionalLayer::calculate_hidden_delta(Layer* next_layer_pointer,
399 const Tensor<type, 2>&,
400 ForwardPropagation& forward_propagation,
401 const Tensor<type, 2>& next_layer_delta,
402 Tensor<type, 2>& hidden_delta) const
403 {
404 const Type next_layer_type = next_layer_pointer->get_type();
405
406 if(next_layer_type == Convolutional)
407 {
408 ConvolutionalLayer* convolutional_layer = dynamic_cast<ConvolutionalLayer*>(next_layer_pointer);
409
410 // calculate_hidden_delta_convolutional(convolutional_layer,
411 // activations_2d,
412 // activations_derivatives,
413 // next_layer_delta,
414 // hidden_delta);
415 }
416 else if(next_layer_type == Pooling)
417 {
418 PoolingLayer* pooling_layer = dynamic_cast<PoolingLayer*>(next_layer_pointer);
419
420 calculate_hidden_delta_pooling(pooling_layer,
421 forward_propagation.activations_4d,
422 forward_propagation.activations_derivatives_4d,
423 next_layer_delta,
424 hidden_delta);
425 }
426 else if(next_layer_type == Perceptron)
427 {
428 PerceptronLayer* perceptron_layer = dynamic_cast<PerceptronLayer*>(next_layer_pointer);
429
430 calculate_hidden_delta_perceptron(perceptron_layer,
431 forward_propagation.activations_4d,
432 forward_propagation.activations_derivatives_4d,
433 next_layer_delta,
434 hidden_delta);
435 }
436 else if(next_layer_type == Probabilistic)
437 {
438 ProbabilisticLayer* probabilistic_layer = dynamic_cast<ProbabilisticLayer*>(next_layer_pointer);
439
440 calculate_hidden_delta_probabilistic(probabilistic_layer,
441 forward_propagation.activations_4d,
442 forward_propagation.activations_derivatives_4d,
443 next_layer_delta,
444 hidden_delta);
445 }
446 }
447
448
calculate_hidden_delta_convolutional(ConvolutionalLayer * next_layer_pointer,const Tensor<type,4> & activations,const Tensor<type,4> & activations_derivatives,const Tensor<type,4> & next_layer_delta,Tensor<type,2> & hidden_delta) const449 void ConvolutionalLayer::calculate_hidden_delta_convolutional(ConvolutionalLayer* next_layer_pointer,
450 const Tensor<type, 4>& activations,
451 const Tensor<type, 4>& activations_derivatives,
452 const Tensor<type, 4>& next_layer_delta,
453 Tensor<type, 2>& hidden_delta) const
454 {
455
456 // Current layer's values
457
458 const auto images_number = next_layer_delta.dimension(3);
459
460 const Index filters_number = get_filters_number();
461 const Index output_rows_number = get_outputs_rows_number();
462 const Index output_columns_number = get_outputs_columns_number();
463
464 // Next layer's values
465
466 const Index next_layers_filters_number = next_layer_pointer->get_filters_number();
467 const Index next_layers_filter_rows = next_layer_pointer->get_filters_rows_number();
468 const Index next_layers_filter_columns = next_layer_pointer->get_filters_columns_number();
469
470 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
471 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
472
473 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
474 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
475
476 const Tensor<type, 4> next_layers_weights = next_layer_pointer->get_synaptic_weights();
477
478 // Hidden delta calculation
479
480 // hidden_delta.resize(images_number, filters_number, output_rows_number, output_columns_number);
481 hidden_delta.resize(images_number, filters_number * output_rows_number * output_columns_number);
482
483 const Index size = hidden_delta.size();
484
485 #pragma omp parallel for
486
487 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
488 {
489 const Index image_index = tensor_index / (filters_number * output_rows_number * output_columns_number);
490 const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % filters_number;
491 const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
492 const Index column_index = tensor_index % output_columns_number;
493
494 type sum = 0;
495
496 const Index lower_row_index = (row_index - next_layers_filter_rows) / next_layers_row_stride + 1;
497 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
498 const Index lower_column_index = (column_index - next_layers_filter_columns) / next_layers_column_stride + 1;
499 const Index upper_column_index = min(column_index / next_layers_column_stride + 1, next_layers_output_columns);
500
501 for(Index i = 0; i < next_layers_filters_number; i++)
502 {
503 for(Index j = lower_row_index; j < upper_row_index; j++)
504 {
505 for(Index k = lower_column_index; k < upper_column_index; k++)
506 {
507 const type delta_element = next_layer_delta(image_index, i, j, k);
508
509 const type weight = next_layers_weights(row_index - j * next_layers_row_stride,
510 column_index - k * next_layers_column_stride,
511 channel_index,
512 i);
513
514 sum += delta_element*weight;
515 }
516 }
517 }
518 // hidden_delta(row_index, column_index, channel_index, image_index) = sum;
519 hidden_delta(row_index, column_index + channel_index + image_index) = sum;
520 }
521
522 // hidden_delta = hidden_delta*activations_derivatives;
523 }
524
525
calculate_hidden_delta_pooling(PoolingLayer * next_layer_pointer,const Tensor<type,4> & activations_2d,const Tensor<type,4> & activations_derivatives,const Tensor<type,2> & next_layer_delta,Tensor<type,2> & hidden_delta) const526 void ConvolutionalLayer::calculate_hidden_delta_pooling(PoolingLayer* next_layer_pointer,
527 const Tensor<type, 4>& activations_2d,
528 const Tensor<type, 4>& activations_derivatives,
529 const Tensor<type, 2>& next_layer_delta,
530 Tensor<type, 2>& hidden_delta) const
531 {
532
533 switch(next_layer_pointer->get_pooling_method())
534 {
535 case OpenNN::PoolingLayer::PoolingMethod::NoPooling:
536 {
537 // return next_layer_delta;
538 }
539
540 case OpenNN::PoolingLayer::PoolingMethod::AveragePooling:
541 {
542 // Current layer's values
543
544 const Index images_number = next_layer_delta.dimension(0);
545 const Index filters_number = get_filters_number();
546 const Index output_rows_number = get_outputs_rows_number();
547 const Index output_columns_number = get_outputs_columns_number();
548
549 // Next layer's values
550
551 const Index next_layers_pool_rows = next_layer_pointer->get_pool_rows_number();
552 const Index next_layers_pool_columns = next_layer_pointer->get_pool_columns_number();
553 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
554 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
555 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
556 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
557
558 // Hidden delta calculation
559
560 hidden_delta.resize(images_number, filters_number * output_rows_number * output_columns_number);
561
562 const Index size = hidden_delta.size();
563
564 #pragma omp parallel for
565
566 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
567 {
568 const Index image_index = tensor_index/(filters_number*output_rows_number*output_columns_number);
569 const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%filters_number;
570 const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
571 const Index column_index = tensor_index%output_columns_number;
572
573 type sum = 0;
574
575 const Index lower_row_index = (row_index - next_layers_pool_rows)/next_layers_row_stride + 1;
576 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
577 const Index lower_column_index = (column_index - next_layers_pool_columns)/next_layers_column_stride + 1;
578 const Index upper_column_index = min(column_index/next_layers_column_stride + 1, next_layers_output_columns);
579
580 for(Index i = lower_row_index; i < upper_row_index; i++)
581 {
582 for(Index j = lower_column_index; j < upper_column_index; j++)
583 {
584 // const type delta_element = next_layer_delta(image_index, channel_index, i, j);
585
586 // sum += delta_element;
587 }
588 }
589
590 // hidden_delta(image_index, channel_index, row_index, column_index) = sum;
591 hidden_delta(image_index, channel_index + row_index + column_index) = sum;
592 }
593
594 // return (hidden_delta*activations_derivatives)/(next_layers_pool_rows*next_layers_pool_columns);
595 }
596
597 case OpenNN::PoolingLayer::PoolingMethod::MaxPooling:
598 {
599 // Current layer's values
600
601 const Index images_number = next_layer_delta.dimension(0);
602 const Index filters_number = get_filters_number();
603 const Index output_rows_number = get_outputs_rows_number();
604 const Index output_columns_number = get_outputs_columns_number();
605
606 // Next layer's values
607
608 const Index next_layers_pool_rows = next_layer_pointer->get_pool_rows_number();
609 const Index next_layers_pool_columns = next_layer_pointer->get_pool_columns_number();
610 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
611 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
612 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
613 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
614
615 // Hidden delta calculation
616
617 hidden_delta.resize(images_number, filters_number * output_rows_number * output_columns_number);
618
619 const Index size = hidden_delta.size();
620
621 #pragma omp parallel for
622
623 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
624 {
625 const Index image_index = tensor_index/(filters_number*output_rows_number*output_columns_number);
626 const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%filters_number;
627 const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
628 const Index column_index = tensor_index%output_columns_number;
629
630 type sum = 0;
631
632 const Index lower_row_index = (row_index - next_layers_pool_rows)/next_layers_row_stride + 1;
633 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
634 const Index lower_column_index = (column_index - next_layers_pool_columns)/next_layers_column_stride + 1;
635 const Index upper_column_index = min(column_index/next_layers_column_stride + 1, next_layers_output_columns);
636
637 for(Index i = lower_row_index; i < upper_row_index; i++)
638 {
639 for(Index j = lower_column_index; j < upper_column_index; j++)
640 {
641 // const type delta_element = next_layer_delta(image_index, channel_index, i, j);
642
643 Tensor<type, 2> activations_current_submatrix(next_layers_pool_rows, next_layers_pool_columns);
644
645 for(Index submatrix_row_index = 0; submatrix_row_index < next_layers_pool_rows; submatrix_row_index++)
646 {
647 for(Index submatrix_column_index = 0; submatrix_column_index < next_layers_pool_columns; submatrix_column_index++)
648 {
649 // activations_current_submatrix(submatrix_row_index, submatrix_column_index) =
650 // activations_2d(image_index, channel_index, i*next_layers_row_stride + submatrix_row_index, j*next_layers_column_stride + submatrix_column_index);
651 }
652 }
653
654 Tensor<type, 2> multiply_not_multiply(next_layers_pool_rows, next_layers_pool_columns);
655
656 type max_value = activations_current_submatrix(0,0);
657
658 for(Index submatrix_row_index = 0; submatrix_row_index < next_layers_pool_rows; submatrix_row_index++)
659 {
660 for(Index submatrix_column_index = 0; submatrix_column_index < next_layers_pool_columns; submatrix_column_index++)
661 {
662 if(activations_current_submatrix(submatrix_row_index, submatrix_column_index) > max_value)
663 {
664 max_value = activations_current_submatrix(submatrix_row_index, submatrix_column_index);
665
666 // multiply_not_multiply.resize(next_layers_pool_rows, next_layers_pool_columns, 0.0);
667 multiply_not_multiply(submatrix_row_index, submatrix_column_index) = 1.0;
668 }
669 }
670 }
671
672 const type max_derivative = multiply_not_multiply(row_index - i*next_layers_row_stride, column_index - j*next_layers_column_stride);
673
674 // sum += delta_element*max_derivative;
675 }
676 }
677
678 // hidden_delta(image_index, channel_index, row_index, column_index) = sum;
679 hidden_delta(image_index + channel_index + row_index + column_index) = sum;
680 }
681
682 // return hidden_delta*activations_derivatives;
683 }
684 }
685 }
686
687
calculate_hidden_delta_perceptron(const PerceptronLayer * next_layer_pointer,const Tensor<type,4> &,const Tensor<type,4> & activations_derivatives,const Tensor<type,2> & next_layer_delta,Tensor<type,2> & hidden_delta) const688 void ConvolutionalLayer::calculate_hidden_delta_perceptron(const PerceptronLayer* next_layer_pointer,
689 const Tensor<type, 4>&,
690 const Tensor<type, 4>& activations_derivatives,
691 const Tensor<type, 2>& next_layer_delta,
692 Tensor<type, 2>& hidden_delta) const
693 {
694
695 // Current layer's values
696
697 const Index images_number = next_layer_delta.dimension(0);
698 const Index filters_number = get_filters_number();
699 const Index output_rows_number = get_outputs_rows_number();
700 const Index output_columns_number = get_outputs_columns_number();
701
702 // Next layer's values
703
704 const Index next_layers_output_columns = next_layer_delta.dimension(1);
705
706 const Tensor<type, 2>& next_layers_weights = next_layer_pointer->get_synaptic_weights();
707
708 // Hidden delta calculation
709
710 hidden_delta.resize(images_number, filters_number * output_rows_number * output_columns_number);
711
712 const Index size = hidden_delta.size();
713
714 #pragma omp parallel for
715
716 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
717 {
718 const Index image_index = tensor_index/(filters_number*output_rows_number*output_columns_number);
719 const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%filters_number;
720 const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
721 const Index column_index = tensor_index%output_columns_number;
722
723 type sum = 0;
724
725 for(Index sum_index = 0; sum_index < next_layers_output_columns; sum_index++)
726 {
727 const type delta_element = next_layer_delta(image_index, sum_index);
728
729 const type weight = next_layers_weights(channel_index + row_index*filters_number + column_index*filters_number*output_rows_number, sum_index);
730
731 sum += delta_element*weight;
732 }
733 // hidden_delta(row_index, column_index, channel_index, image_index) = sum;
734 hidden_delta(row_index, column_index + channel_index + image_index) = sum;
735 }
736 // return hidden_delta*activations_derivatives;
737 }
738
739
calculate_hidden_delta_probabilistic(ProbabilisticLayer * next_layer_pointer,const Tensor<type,4> & activations,const Tensor<type,4> & activations_derivatives,const Tensor<type,2> & next_layer_delta,Tensor<type,2> & hidden_delta) const740 void ConvolutionalLayer::calculate_hidden_delta_probabilistic(ProbabilisticLayer* next_layer_pointer,
741 const Tensor<type, 4>& activations,
742 const Tensor<type, 4>& activations_derivatives,
743 const Tensor<type, 2>& next_layer_delta,
744 Tensor<type, 2>& hidden_delta) const
745 {
746
747 // Current layer's values
748 const Index images_number = next_layer_delta.dimension(0);
749 const Index filters_number = get_filters_number();
750 const Index output_rows_number = get_outputs_rows_number();
751 const Index output_columns_number = get_outputs_columns_number();
752
753 // Next layer's values
754
755 const Index next_layers_output_columns = next_layer_delta.dimension(1);
756
757 const Tensor<type, 2> next_layers_weights = next_layer_pointer->get_synaptic_weights();
758
759 // Hidden delta calculation
760
761 hidden_delta.resize(images_number, filters_number * output_rows_number * output_columns_number);
762
763 const Index size = hidden_delta.size(); // Number of total parameters
764
765 #pragma omp parallel for
766
767 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
768 {
769 // const Index image_index = tensor_index / (filters_number * output_rows_number * output_columns_number);
770
771 // const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % filters_number;
772 // const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
773 // const Index column_index = tensor_index % output_columns_number;
774
775 const Index image_index = tensor_index / (filters_number * output_rows_number * output_columns_number);
776
777 const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % filters_number;
778 const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
779 const Index column_index = tensor_index % output_columns_number;
780
781 type sum = 0;
782
783 for(Index sum_index = 0; sum_index < next_layers_output_columns; sum_index++)
784 {
785 // const type delta_element = next_layer_delta(image_index, sum_index);
786
787 // const type weight = next_layers_weights(sum_index,
788 // channel_index + (row_index * filters_number) + (column_index * filters_number * output_rows_number));
789
790 const type delta_element = next_layer_delta(image_index, sum_index);
791
792
793 const type weight = next_layers_weights(channel_index + row_index + column_index,
794 sum_index);
795
796 // sum += delta_element * weight;
797 sum += 0;
798 }
799
800 // hidden_delta(image_index, channel_index, row_index, column_index) = sum;
801 hidden_delta(image_index, channel_index + row_index + column_index) = sum;
802 }
803
804 hidden_delta.device(*thread_pool_device) = hidden_delta * activations_derivatives;
805 }
806
807
calculate_error_gradient(const Tensor<type,4> & previous_layers_outputs,const Layer::ForwardPropagation & forward_propagation,const Tensor<type,4> & layer_deltas,Tensor<type,1> & layer_error_gradient)808 void ConvolutionalLayer::calculate_error_gradient(const Tensor<type, 4>& previous_layers_outputs,
809 const Layer::ForwardPropagation& forward_propagation,
810 const Tensor<type, 4>& layer_deltas,
811 Tensor<type, 1>& layer_error_gradient)
812 {
813 Tensor<type, 4> layers_inputs;
814
815 switch(convolution_type) {
816
817 case OpenNN::ConvolutionalLayer::ConvolutionType::Valid:
818 {
819 layers_inputs = previous_layers_outputs;
820 }
821 break;
822
823 case OpenNN::ConvolutionalLayer::ConvolutionType::Same:
824 {
825 layers_inputs.resize(previous_layers_outputs.dimension(0) + get_padding_height(),
826 previous_layers_outputs.dimension(1) + get_padding_width(),
827 previous_layers_outputs.dimension(2),
828 previous_layers_outputs.dimension(3));
829
830 for(Index image_number = 0; image_number < previous_layers_outputs.dimension(0); image_number++)
831 {
832 // layers_inputs.set_tensor(image_number, insert_padding(previous_layers_outputs.get_tensor(image_number)));
833 }
834 }
835 break;
836 }
837
838 // Gradient declaration and values used
839
840 const Index parameters_number = get_parameters_number();
841
842 layer_error_gradient.resize(parameters_number);
843 layer_error_gradient.setZero();
844
845 const Index images_number = layer_deltas.dimension(0);
846 const Index filters_number = get_filters_number();
847 const Index filters_channels_number = get_filters_channels_number();
848 const Index filters_rows_number = get_filters_rows_number();
849 const Index filters_columns_number = get_filters_columns_number();
850 const Index output_rows_number = get_outputs_rows_number();
851 const Index output_columns_number = get_outputs_columns_number();
852
853 // Synaptic weights
854
855 const Index synaptic_weights_number = get_synaptic_weights_number();
856
857 for(Index gradient_index = 0; gradient_index < synaptic_weights_number; gradient_index++)
858 {
859
860 Index filter_index = gradient_index % filters_number;
861 Index channel_index = (gradient_index / filters_number) % filters_channels_number;
862 Index row_index = (gradient_index / (filters_number * filters_channels_number)) % filters_rows_number;
863 Index column_index = (gradient_index / (filters_number * filters_channels_number * filters_rows_number)) % filters_columns_number;
864
865 type sum = 0;
866
867 for(Index i = 0; i < output_rows_number; i++)
868 {
869 for(Index j = 0; j < output_columns_number; j++)
870 {
871 for(Index k = 0; k < images_number; k++)
872 {
873 const type delta_element = layer_deltas(i, filter_index, j, k);
874
875 const type input_element = layers_inputs(i * row_stride + row_index, j * column_stride + column_index, channel_index, k);
876
877 sum += delta_element * input_element;
878 }
879 }
880 }
881
882 layer_error_gradient(gradient_index) = sum;
883 }
884
885 // Biases
886
887 for(Index gradient_index = synaptic_weights_number; gradient_index < parameters_number; gradient_index++) // Start after the synaptic weights
888 {
889 Index bias_index = gradient_index - synaptic_weights_number; // Increment 1
890
891 type sum = 0;
892
893 for(Index i = 0; i < images_number; i++)
894 {
895 for(Index j = 0; j < output_rows_number; j++)
896 {
897 for(Index k = 0; k < output_columns_number; k++)
898 {
899 sum += layer_deltas(i, bias_index, j, k);
900 }
901 }
902 }
903
904 layer_error_gradient(gradient_index) = sum;
905 }
906 }
907
908
calculate_error_gradient(const Tensor<type,4> & inputs,const Layer::ForwardPropagation & forward_propagation,Layer::BackPropagation & back_propagation) const909 void ConvolutionalLayer::calculate_error_gradient(const Tensor<type, 4>& inputs,
910 const Layer::ForwardPropagation& forward_propagation,
911 Layer::BackPropagation& back_propagation) const
912 {
913 Tensor<type, 4> layers_inputs;
914
915 switch(convolution_type) {
916
917 case OpenNN::ConvolutionalLayer::ConvolutionType::Valid:
918 {
919 layers_inputs = inputs;
920 }
921 break;
922
923 case OpenNN::ConvolutionalLayer::ConvolutionType::Same:
924 {
925 layers_inputs.resize(inputs.dimension(0) + get_padding_height(),
926 inputs.dimension(1) + get_padding_width(),
927 inputs.dimension(2),
928 inputs.dimension(3));
929
930 for(Index image_number = 0; image_number < inputs.dimension(0); image_number++)
931 {
932 // layers_inputs.set_tensor(image_number, insert_padding(previous_layers_outputs.get_tensor(image_number)));
933 }
934 }
935 break;
936 }
937
938 // Gradient declaration and values used
939
940 const Index parameters_number = get_parameters_number();
941
942 back_propagation.synaptic_weights_derivatives.resize(1, parameters_number);
943 back_propagation.synaptic_weights_derivatives.setZero();
944
945 const Index images_number = back_propagation.delta.dimension(0);
946 const Index filters_number = get_filters_number();
947 const Index filters_channels_number = get_filters_channels_number();
948 const Index filters_rows_number = get_filters_rows_number();
949 const Index filters_columns_number = get_filters_columns_number();
950 const Index output_rows_number = get_outputs_rows_number();
951 const Index output_columns_number = get_outputs_columns_number();
952
953 // Synaptic weights
954
955 const Index synaptic_weights_number = get_synaptic_weights_number();
956
957 for(Index gradient_index = 0; gradient_index < synaptic_weights_number; gradient_index++)
958 {
959
960 Index filter_index = gradient_index % filters_number;
961 Index channel_index = (gradient_index / filters_number) % filters_channels_number;
962 Index row_index = (gradient_index / (filters_number * filters_channels_number)) % filters_rows_number;
963 Index column_index = (gradient_index / (filters_number * filters_channels_number * filters_rows_number)) % filters_columns_number;
964
965 type sum = 0;
966
967 for(Index i = 0; i < output_rows_number; i++)
968 {
969 for(Index j = 0; j < output_columns_number; j++)
970 {
971 for(Index k = 0; k < images_number; k++)
972 {
973 // const type delta_element = back_propagation.delta_4d(i, filter_index, j, k);
974 const type delta_element = 0;
975
976 // const type input_element = layers_inputs(i * row_stride + row_index, j * column_stride + column_index, channel_index, k);
977 const type input_element = 0;
978
979 sum += delta_element * input_element;
980 }
981 }
982 }
983
984 back_propagation.synaptic_weights_derivatives(gradient_index) = sum;
985 }
986
987 // Biases
988
989 for(Index gradient_index = synaptic_weights_number; gradient_index < parameters_number; gradient_index++) // Start after the synaptic weights
990 {
991 Index bias_index = gradient_index - synaptic_weights_number; // Increment 1
992
993 type sum = 0;
994
995 for(Index i = 0; i < images_number; i++)
996 {
997 for(Index j = 0; j < output_rows_number; j++)
998 {
999 for(Index k = 0; k < output_columns_number; k++)
1000 {
1001 // sum += back_propagation.delta_4d(i, bias_index, j, k);
1002 sum += 0;
1003 }
1004 }
1005 }
1006
1007 back_propagation.synaptic_weights_derivatives(gradient_index) = sum;
1008 }
1009 }
1010
calculate_error_gradient(const Tensor<type,2> & inputs,const Layer::ForwardPropagation & forward_propagation,Layer::BackPropagation & back_propagation) const1011 void ConvolutionalLayer::calculate_error_gradient(const Tensor<type, 2>& inputs,
1012 const Layer::ForwardPropagation& forward_propagation,
1013 Layer::BackPropagation& back_propagation) const
1014 {
1015 const Eigen::array<Eigen::Index, 4> four_dims = {input_variables_dimensions(3), // columns number
1016 input_variables_dimensions(2), // rows number
1017 input_variables_dimensions(1), // channels number
1018 inputs.dimension(0)}; // images number
1019 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
1020 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
1021
1022 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
1023
1024 calculate_error_gradient(inputs_4d, forward_propagation, back_propagation);
1025 }
1026
1027
insert_gradient(const BackPropagation & back_propagation,const Index & index,Tensor<type,1> & gradient) const1028 void ConvolutionalLayer::insert_gradient(const BackPropagation& back_propagation, const Index& index, Tensor<type, 1>& gradient) const
1029 {
1030 const Index biases_number = biases.size();
1031 const Index synaptic_weights_number = synaptic_weights.size();
1032
1033 memcpy(gradient.data() + index,
1034 back_propagation.biases_derivatives.data(),
1035 static_cast<size_t>(biases_number)*sizeof(type));
1036
1037 memcpy(gradient.data() + index + biases_number,
1038 back_propagation.synaptic_weights_derivatives.data(),
1039 static_cast<size_t>(synaptic_weights_number)*sizeof(type));
1040 }
1041
1042
1043 /// Returns the convolutional layer's activation function.
1044
get_activation_function() const1045 ConvolutionalLayer::ActivationFunction ConvolutionalLayer::get_activation_function() const
1046 {
1047 return activation_function;
1048 }
1049
1050
1051 /// Returns the number of rows the result of applying the layer's filters to an image will have.
1052
get_outputs_rows_number() const1053 Index ConvolutionalLayer::get_outputs_rows_number() const
1054 {
1055 const Index filters_rows_number = get_filters_rows_number();
1056
1057 const Index padding_height = get_padding_height();
1058
1059 return ((input_variables_dimensions(2) - filters_rows_number + 2 * padding_height)/row_stride) + 1;
1060 }
1061
1062
1063 /// Returns the number of columns the result of applying the layer's filters to an image will have.
1064
get_outputs_columns_number() const1065 Index ConvolutionalLayer::get_outputs_columns_number() const
1066 {
1067 const Index filters_columns_number = get_filters_columns_number();
1068
1069 const Index padding_width = get_padding_width();
1070
1071 return ((input_variables_dimensions(3) - filters_columns_number + 2 * padding_width)/column_stride) + 1;
1072 }
1073
1074
1075 /// Returns a vector containing the number of channels, rows and columns of the result of applying the layer's filters to an image.
1076
get_outputs_dimensions() const1077 Tensor<Index, 1> ConvolutionalLayer::get_outputs_dimensions() const
1078 {
1079 Tensor<Index, 1> outputs_dimensions(4);
1080
1081 outputs_dimensions(0) = input_variables_dimensions(0); // Number of images
1082 outputs_dimensions(1) = get_filters_number();
1083 outputs_dimensions(2) = get_outputs_rows_number();
1084 outputs_dimensions(3) = get_outputs_columns_number();
1085
1086 return outputs_dimensions;
1087 }
1088
1089
1090 /// Returns the dimension of the input variables
1091
get_input_variables_dimensions() const1092 Tensor<Index, 1> ConvolutionalLayer::get_input_variables_dimensions() const
1093 {
1094 return input_variables_dimensions;
1095 }
1096
1097
1098 /// Returns the padding option.
1099
get_convolution_type() const1100 ConvolutionalLayer::ConvolutionType ConvolutionalLayer::get_convolution_type() const
1101 {
1102 return convolution_type;
1103 }
1104
1105
1106 /// Returns the column stride.
1107
get_column_stride() const1108 Index ConvolutionalLayer::get_column_stride() const
1109 {
1110 return column_stride;
1111 }
1112
1113
1114 /// Returns the row stride.
1115
get_row_stride() const1116 Index ConvolutionalLayer::get_row_stride() const
1117 {
1118 return row_stride;
1119 }
1120
1121
1122 ///Returns the number of filters of the layer.
1123
get_filters_number() const1124 Index ConvolutionalLayer::get_filters_number() const
1125 {
1126 return synaptic_weights.dimension(0);
1127 }
1128
1129
1130 /// Returns the number of channels of the layer's filters.
1131
get_filters_channels_number() const1132 Index ConvolutionalLayer::get_filters_channels_number() const
1133 {
1134 return synaptic_weights.dimension(1);
1135 }
1136
1137
1138 /// Returns the number of rows of the layer's filters.
1139
get_filters_rows_number() const1140 Index ConvolutionalLayer::get_filters_rows_number() const
1141 {
1142 return synaptic_weights.dimension(2);
1143 }
1144
1145
1146 /// Returns the number of columns of the layer's filters.
1147
get_filters_columns_number() const1148 Index ConvolutionalLayer::get_filters_columns_number() const
1149 {
1150 return synaptic_weights.dimension(3);
1151 }
1152
1153
1154 /// Returns the total number of columns of zeroes to be added to an image before applying a filter, which depends on the padding option set.
1155
get_padding_width() const1156 Index ConvolutionalLayer::get_padding_width() const
1157 {
1158 switch(convolution_type)
1159 {
1160 case Valid:
1161 {
1162 return 0;
1163 }
1164
1165 case Same:
1166 {
1167 return column_stride*(input_variables_dimensions[2] - 1) - input_variables_dimensions[2] + get_filters_columns_number();
1168 }
1169 }
1170
1171 return 0;
1172 }
1173
1174
1175 /// Returns the total number of rows of zeroes to be added to an image before applying a filter, which depends on the padding option set.
1176
get_padding_height() const1177 Index ConvolutionalLayer::get_padding_height() const
1178 {
1179 switch(convolution_type)
1180 {
1181 case Valid:
1182 {
1183 return 0;
1184 }
1185
1186 case Same:
1187 {
1188 return row_stride*(input_variables_dimensions[1] - 1) - input_variables_dimensions[1] + get_filters_rows_number();
1189 }
1190 }
1191
1192 return 0;
1193 }
1194
1195
1196 /// Returns the number of inputs
1197
get_inputs_number() const1198 Index ConvolutionalLayer::get_inputs_number() const
1199 {
1200 return get_inputs_channels_number() * get_inputs_rows_number() * get_inputs_columns_number();
1201 }
1202
1203
1204 /// Returns the number of neurons
1205
get_neurons_number() const1206 Index ConvolutionalLayer::get_neurons_number() const
1207 {
1208 // return get_filters_number() * get_outputs_rows_number() * get_outputs_columns_number();
1209 return get_filters_number();
1210 }
1211
1212
1213 /// Returns the layer's parameters in the form of a vector.
1214
get_parameters() const1215 Tensor<type, 1> ConvolutionalLayer::get_parameters() const
1216 {
1217 // Tensor<type, 1> parameters = synaptic_weights.reshape(Eigen::array<Index, 1>{get_synaptic_weights_number()});
1218 Tensor<type, 1> parameters(get_parameters_number());
1219
1220 const Index filters_number = get_filters_number();
1221 const Index filters_channels_number = get_filters_channels_number();
1222 const Index filters_rows_number = get_filters_rows_number();
1223 const Index filters_columns_number = get_filters_columns_number();
1224
1225
1226 Index element_index = 0;
1227 #pragma omp for
1228 for(Index i = 0; i < filters_number; i++)
1229 {
1230 for(Index j = 0; j < filters_channels_number; j++)
1231 {
1232 for(Index k = 0; k < filters_rows_number; k++)
1233 {
1234 for(Index l = 0; l < filters_columns_number; l++)
1235 {
1236 parameters(element_index + filters_number) = synaptic_weights(i ,j, k, l);
1237 element_index ++;
1238 }
1239 }
1240 }
1241 }
1242
1243 for(int i = 0; i < biases.size(); i++)
1244 {
1245 parameters(i) = biases(i);
1246 }
1247
1248 return parameters;
1249
1250 }
1251
1252
1253 /// Returns the number of parameters of the layer.
1254
get_parameters_number() const1255 Index ConvolutionalLayer::get_parameters_number() const
1256 {
1257 return synaptic_weights.size() + biases.size();
1258 }
1259
1260
1261 /// Sets and initializes the layer's parameters in accordance with the dimensions taken as input.
1262 /// The initialization values are random values from a normal distribution.
1263 /// @param new_inputs_dimensions A vector containing the desired inputs' dimensions (rows number, columns number, number of channels, number of images).
1264 /// @param new_filters_dimensions A vector containing the desired filters' dimensions (rows number, columns number, number of channels, number of filters).
1265
set(const Tensor<Index,1> & new_inputs_dimensions,const Tensor<Index,1> & new_filters_dimensions)1266 void ConvolutionalLayer::set(const Tensor<Index, 1>& new_inputs_dimensions, const Tensor<Index, 1>& new_filters_dimensions)
1267 {
1268 #ifdef __OPENNN_DEBUG__
1269
1270 const Index inputs_dimensions_number = new_inputs_dimensions.size();
1271
1272 if(inputs_dimensions_number != 4)
1273 {
1274 ostringstream buffer;
1275
1276 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1277 << "ConvolutionalLayer(const Tensor<Index, 1>&) constructor.\n"
1278 << "Number of inputs dimensions (" << inputs_dimensions_number << ") must be 4 (number of images, channels, rows, columns).\n";
1279
1280 throw logic_error(buffer.str());
1281 }
1282
1283 #endif
1284
1285 #ifdef __OPENNN_DEBUG__
1286
1287 const Index filters_dimensions_number = new_filters_dimensions.size();
1288
1289 if(filters_dimensions_number != 4)
1290 {
1291 ostringstream buffer;
1292
1293 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1294 << "void set(const Tensor<Index, 1>&) method.\n"
1295 << "Number of filters dimensions (" << filters_dimensions_number << ") must be 4 (number of images, filters, rows, columns).\n";
1296
1297 throw logic_error(buffer.str());
1298 }
1299
1300 #endif
1301
1302 const Index filters_number = new_filters_dimensions[0];
1303 const Index filters_channels_number = new_inputs_dimensions[1];
1304 const Index filters_rows_number = new_filters_dimensions[2];
1305 const Index filters_columns_number = new_filters_dimensions[3];
1306
1307 biases.resize(filters_number);
1308 biases.setRandom();
1309
1310
1311 synaptic_weights.resize(filters_number, filters_channels_number, filters_rows_number, filters_columns_number);
1312 synaptic_weights.setRandom();
1313
1314 input_variables_dimensions = new_inputs_dimensions;
1315 }
1316
1317
1318 /// Sets and initializes the layer's parameters in accordance with the dimensions taken as input.
1319 /// The initialization values are random values from a normal distribution.
1320 /// @param new_inputs Layer inputs.
1321 /// @param new_filters Layer synaptic weights.
1322 /// @param new_biases Layer biases.
1323
set(const Tensor<type,4> & new_inputs,const Tensor<type,4> & new_kernels,const Tensor<type,1> & new_biases)1324 void ConvolutionalLayer::set(const Tensor<type, 4>& new_inputs, const Tensor<type, 4>& new_kernels, const Tensor<type, 1>& new_biases)
1325 {
1326 #ifdef __OPENNN_DEBUG__
1327
1328 if(new_kernels.dimension(3) != new_biases.size())
1329 {
1330 ostringstream buffer;
1331
1332 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1333 << "void set(const Tensor<type, 4>& , const Tensor<type, 4>& , const Tensor<type, 1>& ) method.\n"
1334 << "Biases size must be equal to number of filters.\n";
1335
1336 throw logic_error(buffer.str());
1337 }
1338
1339 #endif
1340
1341 // input_variables_dimensions.set(new_inputs_dimensions);
1342
1343 Tensor<Index, 1> new_inputs_dimensions(4);
1344 new_inputs_dimensions(0) = new_inputs.dimension(0);
1345 new_inputs_dimensions(1) = new_inputs.dimension(1);
1346 new_inputs_dimensions(2) = new_inputs.dimension(2);
1347 new_inputs_dimensions(3) = new_inputs.dimension(3);
1348
1349 synaptic_weights = new_kernels;
1350
1351 biases = new_biases;
1352
1353 input_variables_dimensions = new_inputs_dimensions;
1354 }
1355
1356
1357 /// Initializes the layer's biases to a given value.
1358 /// @param value The desired value.
1359
set_biases_constant(const type & value)1360 void ConvolutionalLayer::set_biases_constant(const type& value)
1361 {
1362 biases.setConstant(value);
1363 }
1364
1365
1366 /// Initializes the layer's synaptic weights to a given value.
1367 /// @param value The desired value.
1368
set_synaptic_weights_constant(const type & value)1369 void ConvolutionalLayer::set_synaptic_weights_constant(const type& value)
1370 {
1371 synaptic_weights.setConstant(value);
1372 }
1373
1374
1375 /// Initializes the layer's parameters to a given value.
1376 /// @param value The desired value.
1377
set_parameters_constant(const type & value)1378 void ConvolutionalLayer::set_parameters_constant(const type& value)
1379 {
1380 set_biases_constant(value);
1381
1382 set_synaptic_weights_constant(value);
1383 }
1384
1385
1386 // Sets the parameters to random numbers using Eigen's setRandom.
1387
set_parameters_random()1388 void ConvolutionalLayer::set_parameters_random()
1389 {
1390 biases.setRandom();
1391
1392 synaptic_weights.setRandom();
1393 }
1394
1395
1396 /// Sets the layer's activation function.
1397 /// @param new_activation_function The desired activation function.
1398
set_activation_function(const ConvolutionalLayer::ActivationFunction & new_activation_function)1399 void ConvolutionalLayer::set_activation_function(const ConvolutionalLayer::ActivationFunction& new_activation_function)
1400 {
1401 activation_function = new_activation_function;
1402 }
1403
1404
1405 /// Sets the layer's biases.
1406 /// @param new_biases The desired biases.
1407
set_biases(const Tensor<type,1> & new_biases)1408 void ConvolutionalLayer::set_biases(const Tensor<type, 1>& new_biases)
1409 {
1410 biases = new_biases;
1411 }
1412
1413
1414 /// Sets the layer's synaptic weights.
1415 /// @param new_synaptic_weights The desired synaptic weights.
1416
set_synaptic_weights(const Tensor<type,4> & new_synaptic_weights)1417 void ConvolutionalLayer::set_synaptic_weights(const Tensor<type, 4>& new_synaptic_weights)
1418 {
1419 synaptic_weights = new_synaptic_weights;
1420 }
1421
1422
1423 /// Sets the padding option.
1424 /// @param new_convolution_type The desired convolution type.
1425
set_convolution_type(const ConvolutionalLayer::ConvolutionType & new_convolution_type)1426 void ConvolutionalLayer::set_convolution_type(const ConvolutionalLayer::ConvolutionType& new_convolution_type)
1427 {
1428 convolution_type = new_convolution_type;
1429 }
1430
1431
1432 /// Sets the filters' row stride.
1433 /// @param new_stride_row The desired row stride.
1434
set_row_stride(const Index & new_stride_row)1435 void ConvolutionalLayer::set_row_stride(const Index& new_stride_row)
1436 {
1437 if(new_stride_row <= 0)
1438 {
1439 throw ("EXCEPTION: new_stride_row must be a positive number");
1440 }
1441
1442 row_stride = new_stride_row;
1443 }
1444
1445
1446 /// Sets the filters' column stride.
1447 /// @param new_stride_row The desired column stride.
1448
set_column_stride(const Index & new_stride_column)1449 void ConvolutionalLayer::set_column_stride(const Index& new_stride_column)
1450 {
1451 if(new_stride_column <= 0)
1452 {
1453 throw ("EXCEPTION: new_stride_column must be a positive number");
1454 }
1455
1456 column_stride = new_stride_column;
1457 }
1458
1459
1460 /// Sets the synaptic weights and biases to the given values.
1461 /// @param new_parameters A vector containing the synaptic weights and biases, in this order.
1462
set_parameters(const Tensor<type,1> & new_parameters,const Index &)1463 void ConvolutionalLayer::set_parameters(const Tensor<type, 1>& new_parameters, const Index& )
1464 {
1465 const Index filters_number = get_filters_number();
1466 const Index filters_channels_number = get_filters_channels_number();
1467 const Index filters_rows_number = get_filters_rows_number();
1468 const Index filters_columns_number = get_filters_columns_number();
1469
1470 synaptic_weights.resize(filters_number, filters_channels_number, filters_rows_number, filters_columns_number);
1471 biases.resize(filters_number);
1472
1473 const Index synaptic_weights_number = synaptic_weights.size();
1474
1475 Index element_index = 0;
1476 #pragma omp for
1477 for(Index i = 0; i < filters_number; i++)
1478 {
1479 for(Index j = 0; j < filters_channels_number; j++)
1480 {
1481 for(Index k = 0; k < filters_rows_number; k++)
1482 {
1483 for(Index l = 0; l < filters_columns_number; l++)
1484 {
1485 synaptic_weights(i ,j, k, l) = new_parameters(filters_number + element_index);
1486 element_index ++;
1487 }
1488 }
1489 }
1490 }
1491
1492 for(Index i = 0; i < filters_number; i ++)
1493 {
1494 biases(i) = new_parameters(i);
1495 }
1496 }
1497
1498
1499 /// Returns the layer's biases.
get_biases() const1500 const Tensor<type, 1>& ConvolutionalLayer::get_biases() const
1501 {
1502 return biases;
1503 }
1504
1505
1506
1507 /// Returns the layer's synaptic weights.
1508
get_synaptic_weights() const1509 const Tensor<type, 4>& ConvolutionalLayer::get_synaptic_weights() const
1510 {
1511 return synaptic_weights;
1512 }
1513
1514
1515 /// Returns the number of layer's synaptic weights
1516
get_synaptic_weights_number() const1517 Index ConvolutionalLayer::get_synaptic_weights_number() const
1518 {
1519 return synaptic_weights.size();
1520 }
1521
1522
1523 /// Returns the number of channels of the input.
1524
get_inputs_channels_number() const1525 Index ConvolutionalLayer::get_inputs_channels_number() const
1526 {
1527 return input_variables_dimensions[1];
1528 }
1529
1530
1531 /// Returns the number of rows of the input.
1532
get_inputs_rows_number() const1533 Index ConvolutionalLayer::get_inputs_rows_number() const
1534 {
1535 return input_variables_dimensions[2];
1536 }
1537
1538
1539 /// Returns the number of columns of the input.
1540
get_inputs_columns_number() const1541 Index ConvolutionalLayer::get_inputs_columns_number() const
1542 {
1543 return input_variables_dimensions[3];
1544 }
1545
1546
to_2d(const Tensor<type,4> & inputs_4d,Tensor<type,2> output_2d) const1547 void ConvolutionalLayer::to_2d(const Tensor<type, 4>& inputs_4d, Tensor<type, 2> output_2d) const
1548 {
1549
1550 output_2d.resize(inputs_4d.dimension(0),
1551 inputs_4d.dimension(1) * inputs_4d.dimension(2) * inputs_4d.dimension(3));
1552
1553 Index element_index = 0;
1554
1555 #pragma omp for
1556 for(Index i = 0; i < inputs_4d.dimension(0); i++)
1557 {
1558 for(Index j = 0; j < inputs_4d.dimension(1); j++)
1559 {
1560 for(Index k = 0; k < inputs_4d.dimension(2); k++)
1561 {
1562 for(Index l = 0; l < inputs_4d.dimension(3); l++)
1563 {
1564 output_2d(element_index) = inputs_4d(i ,j, k, l);
1565 element_index ++;
1566 }
1567 }
1568 }
1569 }
1570 }
1571
1572 }
1573
1574 // OpenNN: Open Neural Networks Library.
1575 // Copyright(C) 2005-2020 Artificial Intelligence Techniques, SL.
1576 //
1577 // This library is free software; you can redistribute it and/or
1578 // modify it under the terms of the GNU Lesser General Public
1579 // License as published by the Free Software Foundation; either
1580 // version 2.1 of the License, or any later version.
1581 //
1582 // This library is distributed in the hope that it will be useful,
1583 // but WITHOUT ANY WARRANTY; without even the implied warranty of
1584 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1585 // Lesser General Public License for more details.
1586
1587 // You should have received a copy of the GNU Lesser General Public
1588 // License along with this library; if not, write to the Free Software
1589 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1590