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