convolutional_layer.cpp
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
16namespace OpenNN
17{
18
21
23{
24 layer_type = Layer::Type::Convolutional;
25}
26
27
34
35ConvolutionalLayer::ConvolutionalLayer(const Tensor<Index, 1>& new_inputs_dimensions,
36 const Tensor<Index, 1>& new_kernels_dimensions) : Layer()
37{
38 layer_type = Layer::Type::Convolutional;
39
40 set(new_inputs_dimensions, new_kernels_dimensions);
41}
42
43
45
47{
48 if(biases.size() == 0 && synaptic_weights.size() == 0)
49 {
50 return true;
51 }
52
53 return false;
54}
55
56
58
59void ConvolutionalLayer::insert_padding(const Tensor<type, 4>& inputs, Tensor<type, 4>& padded_output)
60{
61 switch(convolution_type)
62 {
63 case ConvolutionType::Valid: padded_output = inputs; return;
64
65 case ConvolutionType::Same:
66 {
67 Eigen::array<pair<int, int>, 4> paddings;
68 const int pad = int(0.5 *(get_kernels_rows_number() - 1));
69 paddings[0] = make_pair(0, 0);
70 paddings[1] = make_pair(0, 0);
71 paddings[2] = make_pair(pad, pad);
72 paddings[3] = make_pair(pad, pad);
73 padded_output = inputs.pad(paddings);
74 return;
75 }
76 }
77}
78
79
81
82void ConvolutionalLayer::calculate_convolutions(const Tensor<type, 4>& inputs, Tensor<type, 4> & combinations) const
83{
84 const Index images_number = inputs.dimension(0);
85 const Index kernels_number = synaptic_weights.dimension(0);
86
87 const Eigen::array<ptrdiff_t, 3> dims = {0, 1, 2};
88
89 Tensor<type, 3> kernel;
90
91// #pragma omp parallel for
92 for(Index i = 0; i < images_number; i++)
93 {
94 for(Index j = 0; j < kernels_number; j++)
95 {
96 kernel = synaptic_weights.chip(j, 0);
97
98 combinations.chip(i, 0).chip(j, 0) = inputs.chip(i, 0).convolve(kernel, dims) + biases(j);
99 }
100 }
101}
102
103
104void ConvolutionalLayer::calculate_convolutions(const Tensor<type, 4>& inputs,
105 const Tensor<type, 2>& potential_biases,
106 const Tensor<type, 4>& potential_synaptic_weights,
107 Tensor<type, 4>& convolutions) const
108{
109 const Index number_of_kernels = potential_synaptic_weights.dimension(0);
110 const Index number_of_images = inputs.dimension(0);
111
112 convolutions.resize(number_of_images, number_of_kernels, get_outputs_rows_number(), get_outputs_columns_number());
113
114 const Eigen::array<ptrdiff_t, 3> dims = {0, 1, 2};
115
116 Tensor<type, 3> kernel;
117
118// #pragma omp parallel for
119 for(Index i = 0; i < number_of_images; i++)
120 {
121 for(Index j = 0; j < number_of_kernels; j++)
122 {
123 kernel = potential_synaptic_weights.chip(j, 0);
124
125 convolutions.chip(i, 0).chip(j, 0) = inputs.chip(i, 0).convolve(kernel, dims) + potential_biases(j, 0);
126 }
127 }
128}
129
130
131
133
134void ConvolutionalLayer::calculate_activations(const Tensor<type, 4>& inputs, Tensor<type, 4>& activations) const
135{
136 switch(activation_function)
137 {
138 case ActivationFunction::Linear: linear(inputs, activations); return;
139
140 case ActivationFunction::Logistic: logistic(inputs, activations); return;
141
142 case ActivationFunction::HyperbolicTangent: hyperbolic_tangent(inputs, activations); return;
143
144 case ActivationFunction::Threshold: threshold(inputs, activations); return;
145
146 case ActivationFunction::SymmetricThreshold: symmetric_threshold(inputs, activations); return;
147
148 case ActivationFunction::RectifiedLinear: rectified_linear(inputs, activations); return;
149
150 case ActivationFunction::ScaledExponentialLinear: scaled_exponential_linear(inputs, activations); return;
151
152 case ActivationFunction::SoftPlus: soft_plus(inputs, activations); return;
153
154 case ActivationFunction::SoftSign: soft_sign(inputs, activations); return;
155
156 case ActivationFunction::HardSigmoid: hard_sigmoid(inputs, activations); return;
157
158 case ActivationFunction::ExponentialLinear: exponential_linear(inputs, activations); return;
159 }
160}
161
162
163void ConvolutionalLayer::calculate_activations_derivatives(const Tensor<type, 4>& combinations_4d,
164 Tensor<type, 4>& activations,
165 Tensor<type, 4>& activations_derivatives) const
166{
167 switch(activation_function)
168 {
169 case ActivationFunction::Linear: linear_derivatives(combinations_4d, activations, activations_derivatives); return;
170
171 case ActivationFunction::Logistic: logistic_derivatives(combinations_4d, activations, activations_derivatives); return;
172
173 case ActivationFunction::HyperbolicTangent: hyperbolic_tangent_derivatives(combinations_4d, activations, activations_derivatives); return;
174
175 case ActivationFunction::Threshold: threshold_derivatives(combinations_4d, activations, activations_derivatives); return;
176
177 case ActivationFunction::SymmetricThreshold: symmetric_threshold_derivatives(combinations_4d, activations, activations_derivatives); return;
178
179 case ActivationFunction::RectifiedLinear: rectified_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
180
181 case ActivationFunction::ScaledExponentialLinear: scaled_exponential_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
182
183 case ActivationFunction::SoftPlus: soft_plus_derivatives(combinations_4d, activations, activations_derivatives); return;
184
185 case ActivationFunction::SoftSign: soft_sign_derivatives(combinations_4d, activations, activations_derivatives); return;
186
187 case ActivationFunction::HardSigmoid: hard_sigmoid_derivatives(combinations_4d, activations, activations_derivatives); return;
188
189 case ActivationFunction::ExponentialLinear: exponential_linear_derivatives(combinations_4d, activations, activations_derivatives); return;
190 }
191}
192
193
197
198void ConvolutionalLayer::calculate_outputs(const Tensor<type, 4>& inputs, Tensor<type, 4>& outputs)
199{
200 const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
201
202 outputs.resize(outputs_dimensions(0), outputs_dimensions(1), outputs_dimensions(2), outputs_dimensions(3));
203
204 Tensor<type, 4> combinations(outputs_dimensions(0), outputs_dimensions(1), outputs_dimensions(2), outputs_dimensions(3));
205
206 calculate_convolutions(inputs, combinations);
207
208 calculate_activations(combinations, outputs);
209}
210
211
212void ConvolutionalLayer::forward_propagate(const Tensor<type, 4> &inputs, LayerForwardPropagation* forward_propagation)
213{
214 ConvolutionalLayerForwardPropagation* convolutional_layer_forward_propagation
215 = static_cast<ConvolutionalLayerForwardPropagation*>(forward_propagation);
216
217 const Tensor<Index, 1> outputs_dimensions = get_outputs_dimensions();
218
219 convolutional_layer_forward_propagation->combinations.resize(outputs_dimensions(0),
220 outputs_dimensions(1),
221 outputs_dimensions(2),
222 outputs_dimensions(3));
223
224 convolutional_layer_forward_propagation->activations.resize(outputs_dimensions(0),
225 outputs_dimensions(1),
226 outputs_dimensions(2),
227 outputs_dimensions(3));
228
229 convolutional_layer_forward_propagation->activations_derivatives.resize(outputs_dimensions(0),
230 outputs_dimensions(1),
231 outputs_dimensions(2),
232 outputs_dimensions(3));
233
235 convolutional_layer_forward_propagation->combinations);
236
237 calculate_activations_derivatives(convolutional_layer_forward_propagation->combinations,
238 convolutional_layer_forward_propagation->activations,
239 convolutional_layer_forward_propagation->activations_derivatives);
240
241// to_2d(convolutional_layer_forward_propagation->combinations_4d, convolutional_layer_forward_propagation->combinations);
242// to_2d(convolutional_layer_forward_propagation->activations_4d, convolutional_layer_forward_propagation->activations);
243// to_2d(convolutional_layer_forward_propagation->activations_derivatives_4d, convolutional_layer_forward_propagation->activations_derivatives_2d);
244}
245
246
247void ConvolutionalLayer::forward_propagate(const Tensor<type, 2>&inputs, LayerForwardPropagation* forward_propagation)
248{
249 const Eigen::array<Eigen::Index, 4> four_dims = {input_variables_dimensions(3), // columns number
250 input_variables_dimensions(2), // rows number
251 input_variables_dimensions(1), // channels number
252 inputs.dimension(0)}; // images number
253 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
254 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
255
256 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
257
258 forward_propagate(inputs_4d, forward_propagation);
259}
260
261
262void ConvolutionalLayer::forward_propagate(const Tensor<type, 4>& inputs,
263 Tensor<type, 1> potential_parameters,
264 LayerForwardPropagation* forward_propagation)
265{
266 ConvolutionalLayerForwardPropagation* convolutional_layer_forward_propagation = static_cast<ConvolutionalLayerForwardPropagation*>(forward_propagation);
267
268 const Tensor<Index, 1> output_dimensions = get_outputs_dimensions();
269
270 convolutional_layer_forward_propagation->combinations.resize(output_dimensions(0),
271 output_dimensions(1),
272 output_dimensions(2),
273 output_dimensions(3));
274
275 convolutional_layer_forward_propagation->activations.resize(output_dimensions(0),
276 output_dimensions(1),
277 output_dimensions(2),
278 output_dimensions(3));
279
280 convolutional_layer_forward_propagation->activations_derivatives.resize(output_dimensions(0),
281 output_dimensions(1),
282 output_dimensions(2),
283 output_dimensions(3));
284
285 const Index kernels_number = synaptic_weights.dimension(0);
286
287 const TensorMap<Tensor<type, 2>> potential_biases(potential_parameters.data(),
288 kernels_number,
289 1);
290
291 // TensorMap<Tensor<type, 4>> potential_synaptic_weights(potential_parameters.data() + kernels_number,
292 // synaptic_weights.dimension(3),
293 // synaptic_weights.dimension(2),
294 // synaptic_weights.dimension(1),
295 // kernels_number);
296
297
298 const Index kernels_channels_number = get_kernels_channels_number();
299 const Index kernels_rows_number = get_kernels_rows_number();
300 const Index kernels_columns_number = get_kernels_columns_number();
301
302 Tensor<type, 4> potential_synaptic_weights(kernels_number,
303 kernels_channels_number,
304 kernels_rows_number,
305 kernels_columns_number);
306 Index element_index = 0;
307
308 //#pragma omp for
309 for(Index i = 0; i < kernels_number; i++)
310 {
311 for(Index j = 0; j < kernels_channels_number; j++)
312 {
313 for(Index k = 0; k < kernels_rows_number; k++)
314 {
315 for(Index l = 0; l < kernels_columns_number; l++)
316 {
317 potential_synaptic_weights(i ,j, k, l) = potential_parameters(kernels_number + element_index);
318 element_index ++;
319 }
320 }
321 }
322 }
323
325 potential_biases,
326 potential_synaptic_weights,
327 convolutional_layer_forward_propagation->combinations);
328
329 calculate_activations_derivatives(convolutional_layer_forward_propagation->combinations,
330 convolutional_layer_forward_propagation->activations,
331 convolutional_layer_forward_propagation->activations_derivatives);
332}
333
334
335void ConvolutionalLayer::forward_propagate(const Tensor<type, 2>& inputs,
336 Tensor<type, 1> potential_parameters,
337 LayerForwardPropagation* forward_propagation)
338{
339 const Eigen::array<Eigen::Index, 4> four_dims = {
340 input_variables_dimensions(0),
341 input_variables_dimensions(1),
342 input_variables_dimensions(2),
343 input_variables_dimensions(3)};
344
345 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
346 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
347
348 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
349// const Tensor<type, 4> inputs_4d = inputs.reshape(four_dims);
350
351 forward_propagate(inputs_4d, potential_parameters, forward_propagation);
352}
353
354
355void ConvolutionalLayer::calculate_hidden_delta(Layer* next_layer_pointer,
356 LayerForwardPropagation* forward_propagation,
357 const Tensor<type, 2>& next_layer_delta,
358 Tensor<type, 2>& hidden_delta) const
359{
360 ConvolutionalLayerForwardPropagation* convolutional_layer_forward_propagation = static_cast<ConvolutionalLayerForwardPropagation*>(forward_propagation);
361
362 const Type next_layer_type = next_layer_pointer->get_type();
363
364 if(next_layer_type == Type::Convolutional)
365 {
366 ConvolutionalLayer* convolutional_layer = dynamic_cast<ConvolutionalLayer*>(next_layer_pointer);
367
368// calculate_hidden_delta_convolutional(convolutional_layer,
369// activations,
370// activations_derivatives,
371// next_layer_delta,
372// hidden_delta);
373 }
374 else if(next_layer_type == Type::Pooling)
375 {
376 PoolingLayer* pooling_layer = dynamic_cast<PoolingLayer*>(next_layer_pointer);
377
378 calculate_hidden_delta_pooling(pooling_layer,
379 convolutional_layer_forward_propagation->activations,
380 convolutional_layer_forward_propagation->activations_derivatives,
381 next_layer_delta,
382 hidden_delta);
383 }
384 else if(next_layer_type == Type::Perceptron)
385 {
386 PerceptronLayer* perceptron_layer = static_cast<PerceptronLayer*>(next_layer_pointer);
387
388// calculate_hidden_delta_perceptron(perceptron_layer,
389// convolutional_layer_forward_propagation->activations,
390// convolutional_layer_forward_propagation->activations_derivatives,
391// next_layer_delta,
392// hidden_delta);
393
394 }
395 else if(next_layer_type == Type::Probabilistic)
396 {
397 ProbabilisticLayer* probabilistic_layer = dynamic_cast<ProbabilisticLayer*>(next_layer_pointer);
398
399 calculate_hidden_delta_probabilistic(probabilistic_layer,
400 convolutional_layer_forward_propagation->activations,
401 convolutional_layer_forward_propagation->activations_derivatives,
402 next_layer_delta,
403 hidden_delta);
404 }
405}
406
407
408void ConvolutionalLayer::calculate_hidden_delta_convolutional(ConvolutionalLayer* next_layer_pointer,
409 const Tensor<type, 4>& activations,
410 const Tensor<type, 4>& activations_derivatives,
411 const Tensor<type, 4>& next_layer_delta,
412 Tensor<type, 2>& hidden_delta) const
413{
414
415 // Current layer's values
416
417 const auto images_number = next_layer_delta.dimension(3);
418
419 const Index kernels_number = get_kernels_number();
420 const Index output_rows_number = get_outputs_rows_number();
421 const Index output_columns_number = get_outputs_columns_number();
422
423 // Next layer's values
424
425 const Index next_layers_kernels_number = next_layer_pointer->get_kernels_number();
426 const Index next_layers_kernel_rows = next_layer_pointer->get_kernels_rows_number();
427 const Index next_layers_kernel_columns = next_layer_pointer->get_kernels_columns_number();
428
429 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
430 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
431
432 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
433 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
434
435 const Tensor<type, 4> next_layers_weights = next_layer_pointer->get_synaptic_weights();
436
437 // Hidden delta calculation
438
439// hidden_delta.resize(images_number, kernels_number, output_rows_number, output_columns_number);
440 hidden_delta.resize(images_number, kernels_number * output_rows_number * output_columns_number);
441
442 const Index size = hidden_delta.size();
443
444 #pragma omp parallel for
445
446 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
447 {
448 const Index image_index = tensor_index / (kernels_number * output_rows_number * output_columns_number);
449 const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % kernels_number;
450 const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
451 const Index column_index = tensor_index % output_columns_number;
452
453 type sum = type(0);
454
455 const Index lower_row_index = (row_index - next_layers_kernel_rows) / next_layers_row_stride + 1;
456 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
457 const Index lower_column_index = (column_index - next_layers_kernel_columns) / next_layers_column_stride + 1;
458 const Index upper_column_index = min(column_index / next_layers_column_stride + 1, next_layers_output_columns);
459
460 for(Index i = 0; i < next_layers_kernels_number; i++)
461 {
462 for(Index j = lower_row_index; j < upper_row_index; j++)
463 {
464 for(Index k = lower_column_index; k < upper_column_index; k++)
465 {
466 const type delta_element = next_layer_delta(image_index, i, j, k);
467
468 const type weight = next_layers_weights(row_index - j * next_layers_row_stride,
469 column_index - k * next_layers_column_stride,
470 channel_index,
471 i);
472
473 sum += delta_element*weight;
474 }
475 }
476 }
477// hidden_delta(row_index, column_index, channel_index, image_index) = sum;
478 hidden_delta(row_index, column_index + channel_index + image_index) = sum;
479 }
480
481// hidden_delta = hidden_delta*activations_derivatives;
482}
483
484
485void ConvolutionalLayer::calculate_hidden_delta_pooling(PoolingLayer* next_layer_pointer,
486 const Tensor<type, 4>& activations,
487 const Tensor<type, 4>& activations_derivatives,
488 const Tensor<type, 2>& next_layer_delta,
489 Tensor<type, 2>& hidden_delta) const
490{
491
492 switch(next_layer_pointer->get_pooling_method())
493 {
494 case OpenNN::PoolingLayer::PoolingMethod::NoPooling:
495 {
496// return next_layer_delta;
497 }
498
499 case OpenNN::PoolingLayer::PoolingMethod::AveragePooling:
500 {
501 // Current layer's values
502
503 const Index images_number = next_layer_delta.dimension(0);
504 const Index kernels_number = get_kernels_number();
505 const Index output_rows_number = get_outputs_rows_number();
506 const Index output_columns_number = get_outputs_columns_number();
507
508 // Next layer's values
509
510 const Index next_layers_pool_rows = next_layer_pointer->get_pool_rows_number();
511 const Index next_layers_pool_columns = next_layer_pointer->get_pool_columns_number();
512 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
513 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
514 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
515 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
516
517 // Hidden delta calculation
518
519 hidden_delta.resize(images_number, kernels_number * output_rows_number * output_columns_number);
520
521 const Index size = hidden_delta.size();
522
523 #pragma omp parallel for
524
525 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
526 {
527 const Index image_index = tensor_index/(kernels_number*output_rows_number*output_columns_number);
528 const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%kernels_number;
529 const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
530 const Index column_index = tensor_index%output_columns_number;
531
532 type sum = type(0);
533
534 const Index lower_row_index = (row_index - next_layers_pool_rows)/next_layers_row_stride + 1;
535 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
536 const Index lower_column_index = (column_index - next_layers_pool_columns)/next_layers_column_stride + 1;
537 const Index upper_column_index = min(column_index/next_layers_column_stride + 1, next_layers_output_columns);
538
539 for(Index i = lower_row_index; i < upper_row_index; i++)
540 {
541 for(Index j = lower_column_index; j < upper_column_index; j++)
542 {
543// const type delta_element = next_layer_delta(image_index, channel_index, i, j);
544
545// sum += delta_element;
546 }
547 }
548
549// hidden_delta(image_index, channel_index, row_index, column_index) = sum;
550 hidden_delta(image_index, channel_index + row_index + column_index) = sum;
551 }
552
553// return (hidden_delta*activations_derivatives)/(next_layers_pool_rows*next_layers_pool_columns);
554 }
555
556 case OpenNN::PoolingLayer::PoolingMethod::MaxPooling:
557 {
558 // Current layer's values
559
560 const Index images_number = next_layer_delta.dimension(0);
561 const Index kernels_number = get_kernels_number();
562 const Index output_rows_number = get_outputs_rows_number();
563 const Index output_columns_number = get_outputs_columns_number();
564
565 // Next layer's values
566
567 const Index next_layers_pool_rows = next_layer_pointer->get_pool_rows_number();
568 const Index next_layers_pool_columns = next_layer_pointer->get_pool_columns_number();
569 const Index next_layers_output_rows = next_layer_pointer->get_outputs_rows_number();
570 const Index next_layers_output_columns = next_layer_pointer->get_outputs_columns_number();
571 const Index next_layers_row_stride = next_layer_pointer->get_row_stride();
572 const Index next_layers_column_stride = next_layer_pointer->get_column_stride();
573
574 // Hidden delta calculation
575
576 hidden_delta.resize(images_number, kernels_number * output_rows_number * output_columns_number);
577
578 const Index size = hidden_delta.size();
579
580 #pragma omp parallel for
581
582 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
583 {
584 const Index image_index = tensor_index/(kernels_number*output_rows_number*output_columns_number);
585 const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%kernels_number;
586 const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
587 const Index column_index = tensor_index%output_columns_number;
588
589 type sum = type(0);
590
591 const Index lower_row_index = (row_index - next_layers_pool_rows)/next_layers_row_stride + 1;
592 const Index upper_row_index = min(row_index/next_layers_row_stride + 1, next_layers_output_rows);
593 const Index lower_column_index = (column_index - next_layers_pool_columns)/next_layers_column_stride + 1;
594 const Index upper_column_index = min(column_index/next_layers_column_stride + 1, next_layers_output_columns);
595
596 for(Index i = lower_row_index; i < upper_row_index; i++)
597 {
598 for(Index j = lower_column_index; j < upper_column_index; j++)
599 {
600// const type delta_element = next_layer_delta(image_index, channel_index, i, j);
601
602 Tensor<type, 2> activations_current_submatrix(next_layers_pool_rows, next_layers_pool_columns);
603
604 for(Index submatrix_row_index = 0; submatrix_row_index < next_layers_pool_rows; submatrix_row_index++)
605 {
606 for(Index submatrix_column_index = 0; submatrix_column_index < next_layers_pool_columns; submatrix_column_index++)
607 {
608// activations_current_submatrix(submatrix_row_index, submatrix_column_index) =
609// activations(image_index, channel_index, i*next_layers_row_stride + submatrix_row_index, j*next_layers_column_stride + submatrix_column_index);
610 }
611 }
612
613 Tensor<type, 2> multiply_not_multiply(next_layers_pool_rows, next_layers_pool_columns);
614
615 type max_value = activations_current_submatrix(0,0);
616
617 for(Index submatrix_row_index = 0; submatrix_row_index < next_layers_pool_rows; submatrix_row_index++)
618 {
619 for(Index submatrix_column_index = 0; submatrix_column_index < next_layers_pool_columns; submatrix_column_index++)
620 {
621 if(activations_current_submatrix(submatrix_row_index, submatrix_column_index) > max_value)
622 {
623 max_value = activations_current_submatrix(submatrix_row_index, submatrix_column_index);
624
625// multiply_not_multiply.resize(next_layers_pool_rows, next_layers_pool_columns, 0.0);
626 multiply_not_multiply(submatrix_row_index, submatrix_column_index) = type(1);
627 }
628 }
629 }
630
631 const type max_derivative = multiply_not_multiply(row_index - i*next_layers_row_stride, column_index - j*next_layers_column_stride);
632
633// sum += delta_element*max_derivative;
634 }
635 }
636
637// hidden_delta(image_index, channel_index, row_index, column_index) = sum;
638 hidden_delta(image_index + channel_index + row_index + column_index) = sum;
639 }
640
641// return hidden_delta*activations_derivatives;
642 }
643 }
644}
645
646
648
650 const Tensor<type, 4>& ,
651 const Tensor<type, 2>& activations_derivatives,
652 const Tensor<type, 2>& next_layer_delta,
653 Tensor<type, 2>& hidden_delta) const
654{
655 // activations, activations_derivatives: images number, kernels number, output rows number, output columns number
656
657 // Convolutional layer
658
659// const Index images_number = next_layer_delta.dimension(0);
660// const Index kernels_number = get_kernels_number();
661// const Index output_rows_number = get_outputs_rows_number();
662// const Index output_columns_number = get_outputs_columns_number();
663
664 // Perceptron layer
665
666 const Tensor<type, 2>& next_layer_weights = next_layer_pointer->get_synaptic_weights();
667
668 hidden_delta = next_layer_delta.contract(next_layer_weights, A_BT);
669
670 hidden_delta.device(*thread_pool_device) = hidden_delta*activations_derivatives;
671
672 // Next layer's values
673
674 const Index next_layers_output_columns = next_layer_delta.dimension(1);
675
676 const Tensor<type, 2>& next_layers_weights = next_layer_pointer->get_synaptic_weights();
677
678 // Hidden delta calculation
679
680// hidden_delta.resize(images_number, kernels_number * output_rows_number * output_columns_number);
681
682 const Index size = hidden_delta.size();
683
684// #pragma omp parallel for
685
686 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
687 {
688// const Index image_index = tensor_index/(kernels_number*output_rows_number*output_columns_number);
689// const Index channel_index = (tensor_index/(output_rows_number*output_columns_number))%kernels_number;
690// const Index row_index = (tensor_index/output_columns_number)%output_rows_number;
691// const Index column_index = tensor_index%output_columns_number;
692
693 type sum = type(0);
694
695// for(Index sum_index = 0; sum_index < next_layers_output_columns; sum_index++)
696// {
697// const type delta_element = next_layer_delta(image_index, sum_index);
698
699// const type weight = next_layers_weights(channel_index + row_index*kernels_number + column_index*kernels_number*output_rows_number, sum_index);
700
701// sum += delta_element*weight;
702// }
703// hidden_delta(row_index, column_index, channel_index, image_index) = sum;
704// hidden_delta(row_index, column_index + channel_index + image_index) = sum;
705 }
706}
707
708
709void ConvolutionalLayer::calculate_hidden_delta_probabilistic(ProbabilisticLayer* next_layer_pointer,
710 const Tensor<type, 4>& activations,
711 const Tensor<type, 4>& activations_derivatives,
712 const Tensor<type, 2>& next_layer_delta,
713 Tensor<type, 2>& hidden_delta) const
714{
715 // Current layer's values
716 const Index images_number = next_layer_delta.dimension(0);
717 const Index kernels_number = get_kernels_number();
718 const Index output_rows_number = get_outputs_rows_number();
719 const Index output_columns_number = get_outputs_columns_number();
720
721 // Next layer's values
722
723 const Index next_layers_output_columns = next_layer_delta.dimension(1);
724
725 const Tensor<type, 2> next_layers_weights = next_layer_pointer->get_synaptic_weights();
726
727 // Hidden delta calculation
728
729 hidden_delta.resize(images_number, kernels_number * output_rows_number * output_columns_number);
730
731 const Index size = hidden_delta.size(); // Number of total parameters
732
733 #pragma omp parallel for
734
735 for(Index tensor_index = 0; tensor_index < size; tensor_index++)
736 {
737// const Index image_index = tensor_index / (kernels_number * output_rows_number * output_columns_number);
738
739// const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % kernels_number;
740// const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
741// const Index column_index = tensor_index % output_columns_number;
742
743 const Index image_index = tensor_index / (kernels_number * output_rows_number * output_columns_number);
744
745 const Index channel_index = (tensor_index / (output_rows_number * output_columns_number)) % kernels_number;
746 const Index row_index = (tensor_index / output_columns_number) % output_rows_number;
747 const Index column_index = tensor_index % output_columns_number;
748
749 type sum = type(0);
750
751 for(Index sum_index = 0; sum_index < next_layers_output_columns; sum_index++)
752 {
753// const type delta_element = next_layer_delta(image_index, sum_index);
754
755// const type weight = next_layers_weights(sum_index,
756// channel_index + (row_index * kernels_number) + (column_index * kernels_number * output_rows_number));
757
758 const type delta_element = next_layer_delta(image_index, sum_index);
759
760
761 const type weight = next_layers_weights(channel_index + row_index + column_index,
762 sum_index);
763
764// sum += delta_element * weight;
765 sum += type(0);
766 }
767
768// hidden_delta(image_index, channel_index, row_index, column_index) = sum;
769 hidden_delta(image_index, channel_index + row_index + column_index) = sum;
770 }
771
772 hidden_delta.device(*thread_pool_device) = hidden_delta * activations_derivatives;
773}
774
775
776void ConvolutionalLayer::calculate_error_gradient(const Tensor<type, 4>& inputs,
777 LayerForwardPropagation* forward_propagation,
778 LayerBackPropagation& back_propagation) const
779{
780 Tensor<type, 4> layers_inputs;
781
782 switch(convolution_type) {
783
784 case OpenNN::ConvolutionalLayer::ConvolutionType::Valid:
785 {
786 layers_inputs = inputs;
787 }
788 break;
789
790 case OpenNN::ConvolutionalLayer::ConvolutionType::Same:
791 {
792 layers_inputs = inputs;
793
794// layers_inputs.resize(inputs.dimension(0) + get_padding_height(),
795// inputs.dimension(1) + get_padding_width(),
796// inputs.dimension(2),
797// inputs.dimension(3));
798
799// for(Index image_number = 0; image_number < inputs.dimension(0); image_number++)
800// {
801// layers_inputs.set_tensor(image_number, insert_padding(previous_layers_outputs.get_tensor(image_number)));
802// }
803 }
804 break;
805 }
806
807 const Index images_number = inputs.dimension(0);
808 const Index kernels_number = get_kernels_number();
809
810 const Index kernel_channels_number = get_kernels_channels_number();
811
812 const Index kernels_rows_number = get_kernels_rows_number();
813 const Index kernels_columns_number = get_kernels_columns_number();
814
815 const Eigen::array<ptrdiff_t, 3> dims = {0, 1, 2};
816
817 Index image_index = 0;
818 Index kernel_index = 0;
819
820 for(Index i = 0; i < images_number; i++)
821 {
822 image_index = i*kernels_number*(kernels_rows_number*kernels_columns_number);
823
824 for(Index j = 0; j < kernels_number; j++)
825 {
826 // Extract needed hidden_delta
827 // Convolve layer_inputs and hidden_delta
828
829 kernel_index = j*kernels_rows_number*kernels_columns_number;
830
831// const TensorMap<Tensor<type, 3>> delta_map(back_propagation.delta.data() + image_index + kernel_index,
832// Eigen::array<Index,3>({kernel_channels_number, kernels_rows_number, kernels_columns_number}));
833 }
834 }
835}
836
837
838void ConvolutionalLayer::calculate_error_gradient(const Tensor<type, 2>& inputs,
839 LayerForwardPropagation* forward_propagation,
840 LayerBackPropagation& back_propagation) const
841{
842 const Eigen::array<Eigen::Index, 4> four_dims = {input_variables_dimensions(3), // columns number
843 input_variables_dimensions(2), // rows number
844 input_variables_dimensions(1), // channels number
845 inputs.dimension(0)}; // images number
846
847 const Eigen::array<Eigen::Index, 2> shuffle_dims_2D = {1, 0};
848 const Eigen::array<Eigen::Index, 4> shuffle_dims_4D = {3, 2, 1, 0};
849
850 const Tensor<type, 4> inputs_4d = inputs.shuffle(shuffle_dims_2D).reshape(four_dims).shuffle(shuffle_dims_4D);
851
852 calculate_error_gradient(inputs_4d, forward_propagation, back_propagation);
853}
854
855
856void ConvolutionalLayer::insert_gradient(LayerBackPropagation* back_propagation, const Index& index, Tensor<type, 1>& gradient) const
857{
858 ConvolutionalLayerBackPropagation* convolutional_layer_back_propagation =
859 static_cast<ConvolutionalLayerBackPropagation*>(back_propagation);
860
861 const Index biases_number = biases.size();
862 const Index synaptic_weights_number = synaptic_weights.size();
863
864 memcpy(gradient.data() + index,
865 convolutional_layer_back_propagation->biases_derivatives.data(),
866 static_cast<size_t>(biases_number)*sizeof(type));
867
868 memcpy(gradient.data() + index + biases_number,
869 convolutional_layer_back_propagation->synaptic_weights_derivatives.data(),
870 static_cast<size_t>(synaptic_weights_number)*sizeof(type));
871}
872
873
875
877{
878 return activation_function;
879}
880
881
883
885{
886 const Index kernels_rows_number = get_kernels_rows_number();
887
888 const Index padding_height = get_padding_height();
889
890 return ((input_variables_dimensions(2) - kernels_rows_number + 2 * padding_height)/row_stride) + 1;
891}
892
893
895
897{
898 const Index kernels_columns_number = get_kernels_columns_number();
899
900 const Index padding_width = get_padding_width();
901
902 return ((input_variables_dimensions(3) - kernels_columns_number + 2 * padding_width)/column_stride) + 1;
903}
904
905
907
909{
910 Tensor<Index, 1> outputs_dimensions(4);
911
912 outputs_dimensions(0) = input_variables_dimensions(0); // Number of images
913 outputs_dimensions(1) = get_kernels_number();
914 outputs_dimensions(2) = get_outputs_rows_number();
915 outputs_dimensions(3) = get_outputs_columns_number();
916
917 return outputs_dimensions;
918}
919
920
922
924{
925 return input_variables_dimensions;
926}
927
928
930
931ConvolutionalLayer::ConvolutionType ConvolutionalLayer::get_convolution_type() const
932{
933 return convolution_type;
934}
935
936
938
940{
941 return column_stride;
942}
943
944
946
948{
949 return row_stride;
950}
951
952
954
956{
957 return synaptic_weights.dimension(0);
958}
959
960
962
964{
965 return synaptic_weights.dimension(1);
966}
967
968
970
972{
973 return synaptic_weights.dimension(2);
974}
975
976
978
980{
981 return synaptic_weights.dimension(3);
982}
983
984
986
988{
989 switch(convolution_type)
990 {
991 case ConvolutionType::Valid:
992 {
993 return 0;
994 }
995
996 case ConvolutionType::Same:
997 {
998 return column_stride*(input_variables_dimensions[2] - 1) - input_variables_dimensions[2] + get_kernels_columns_number();
999 }
1000 }
1001
1002 return 0;
1003}
1004
1005
1007
1009{
1010 switch(convolution_type)
1011 {
1012 case ConvolutionType::Valid:
1013 {
1014 return 0;
1015 }
1016
1017 case ConvolutionType::Same:
1018 {
1019 return row_stride*(input_variables_dimensions[1] - 1) - input_variables_dimensions[1] + get_kernels_rows_number();
1020 }
1021 }
1022
1023 return 0;
1024}
1025
1026
1028
1030{
1032}
1033
1034
1036
1038{
1039 const Index kernels_number = get_kernels_number();
1040 const Index kernels_rows_number = get_kernels_rows_number();
1041 const Index kernels_columns_number = get_kernels_columns_number();
1042
1043 return kernels_number*kernels_rows_number*kernels_columns_number;
1044}
1045
1046
1048
1050{
1051// Tensor<type, 1> parameters = synaptic_weights.reshape(Eigen::array<Index, 1>{get_synaptic_weights_number()});
1052 Tensor<type, 1> parameters(get_parameters_number());
1053
1054 const Index kernels_number = get_kernels_number();
1055 const Index kernels_channels_number = get_kernels_channels_number();
1056 const Index kernels_rows_number = get_kernels_rows_number();
1057 const Index kernels_columns_number = get_kernels_columns_number();
1058
1059
1060 Index element_index = 0;
1061#pragma omp for
1062 for(Index i = 0; i < kernels_number; i++)
1063 {
1064 for(Index j = 0; j < kernels_channels_number; j++)
1065 {
1066 for(Index k = 0; k < kernels_rows_number; k++)
1067 {
1068 for(Index l = 0; l < kernels_columns_number; l++)
1069 {
1070 parameters(element_index + kernels_number) = synaptic_weights(i ,j, k, l);
1071 element_index ++;
1072 }
1073 }
1074 }
1075 }
1076
1077 for(int i = 0; i < biases.size(); i++)
1078 {
1079 parameters(i) = biases(i);
1080 }
1081
1082 return parameters;
1083
1084}
1085
1086
1088
1090{
1091 return synaptic_weights.size() + biases.size();
1092}
1093
1094
1099
1100void ConvolutionalLayer::set(const Tensor<Index, 1>& new_inputs_dimensions, const Tensor<Index, 1>& new_kernels_dimensions)
1101{
1102#ifdef OPENNN_DEBUG
1103
1104 const Index inputs_dimensions_number = new_inputs_dimensions.size();
1105
1106 if(inputs_dimensions_number != 4)
1107 {
1108 ostringstream buffer;
1109 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1110 << "ConvolutionalLayer(const Tensor<Index, 1>&) constructor.\n"
1111 << "Number of inputs dimensions (" << inputs_dimensions_number << ") must be 4 (number of images, channels, rows, columns).\n";
1112
1113 throw logic_error(buffer.str());
1114 }
1115
1116#endif
1117
1118#ifdef OPENNN_DEBUG
1119
1120 const Index kernels_dimensions_number = new_kernels_dimensions.size();
1121
1122 if(kernels_dimensions_number != 4)
1123 {
1124 ostringstream buffer;
1125
1126 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1127 << "void set(const Tensor<Index, 1>&) method.\n"
1128 << "Number of kernels dimensions (" << kernels_dimensions_number << ") must be 4 (number of images, kernels, rows, columns).\n";
1129
1130 throw logic_error(buffer.str());
1131 }
1132
1133#endif
1134
1135 const Index kernels_number = new_kernels_dimensions[0];
1136 const Index kernels_channels_number = new_inputs_dimensions[1];
1137 const Index kernels_rows_number = new_kernels_dimensions[2];
1138 const Index kernels_columns_number = new_kernels_dimensions[3];
1139
1140 biases.resize(kernels_number);
1141 biases.setRandom();
1142
1143 synaptic_weights.resize(kernels_number, kernels_channels_number, kernels_rows_number, kernels_columns_number);
1144 synaptic_weights.setRandom();
1145
1146 input_variables_dimensions = new_inputs_dimensions;
1147}
1148
1149
1155
1156void ConvolutionalLayer::set(const Tensor<type, 4>& new_inputs, const Tensor<type, 4>& new_kernels, const Tensor<type, 1>& new_biases)
1157{
1158#ifdef OPENNN_DEBUG
1159
1160 if(new_kernels.dimension(3) != new_biases.size())
1161 {
1162 ostringstream buffer;
1163
1164 buffer << "OpenNN Exception: ConvolutionalLayer class.\n"
1165 << "void set(const Tensor<type, 4>& , const Tensor<type, 4>& , const Tensor<type, 1>& ) method.\n"
1166 << "Biases size must be equal to number of kernels.\n";
1167
1168 throw logic_error(buffer.str());
1169 }
1170
1171#endif
1172
1173// input_variables_dimensions.set(new_inputs_dimensions);
1174
1175 Tensor<Index, 1> new_inputs_dimensions(4);
1176 new_inputs_dimensions(0) = new_inputs.dimension(0);
1177 new_inputs_dimensions(1) = new_inputs.dimension(1);
1178 new_inputs_dimensions(2) = new_inputs.dimension(2);
1179 new_inputs_dimensions(3) = new_inputs.dimension(3);
1180
1181 synaptic_weights = new_kernels;
1182
1183 biases = new_biases;
1184
1185 input_variables_dimensions = new_inputs_dimensions;
1186}
1187
1188
1191
1193{
1194 biases.setConstant(value);
1195}
1196
1197
1200
1202{
1203 synaptic_weights.setConstant(value);
1204}
1205
1206
1209
1211{
1212 set_biases_constant(value);
1213
1215}
1216
1217
1218// Sets the parameters to random numbers using Eigen's setRandom.
1219
1220void ConvolutionalLayer::set_parameters_random()
1221{
1222 biases.setRandom();
1223
1224 synaptic_weights.setRandom();
1225}
1226
1227
1230
1232{
1233 activation_function = new_activation_function;
1234}
1235
1236
1239
1240void ConvolutionalLayer::set_biases(const Tensor<type, 1>& new_biases)
1241{
1242 biases = new_biases;
1243}
1244
1245
1248
1249void ConvolutionalLayer::set_synaptic_weights(const Tensor<type, 4>& new_synaptic_weights)
1250{
1251 synaptic_weights = new_synaptic_weights;
1252}
1253
1254
1257
1258void ConvolutionalLayer::set_convolution_type(const ConvolutionalLayer::ConvolutionType& new_convolution_type)
1259{
1260 convolution_type = new_convolution_type;
1261}
1262
1263
1266
1267void ConvolutionalLayer::set_row_stride(const Index& new_stride_row)
1268{
1269 if(new_stride_row <= 0)
1270 {
1271 throw ("EXCEPTION: new_stride_row must be a positive number");
1272 }
1273
1274 row_stride = new_stride_row;
1275}
1276
1277
1280
1281void ConvolutionalLayer::set_column_stride(const Index& new_stride_column)
1282{
1283 if(new_stride_column <= 0)
1284 {
1285 throw ("EXCEPTION: new_stride_column must be a positive number");
1286 }
1287
1288 column_stride = new_stride_column;
1289}
1290
1291
1294
1295void ConvolutionalLayer::set_parameters(const Tensor<type, 1>& new_parameters, const Index& )
1296{
1297 const Index kernels_number = get_kernels_number();
1298 const Index kernels_channels_number = get_kernels_channels_number();
1299 const Index kernels_rows_number = get_kernels_rows_number();
1300 const Index kernels_columns_number = get_kernels_columns_number();
1301
1302 synaptic_weights.resize(kernels_number, kernels_channels_number, kernels_rows_number, kernels_columns_number);
1303 biases.resize(kernels_number);
1304
1305 memcpy(biases.data(),
1306 new_parameters.data(),
1307 static_cast<size_t>(kernels_number)*sizeof(type));
1308
1309 Index element_index = kernels_number;
1310
1311#pragma omp for
1312 for(Index i = 0; i < kernels_number; i++)
1313 {
1314 for(Index j = 0; j < kernels_channels_number; j++)
1315 {
1316 for(Index k = 0; k < kernels_rows_number; k++)
1317 {
1318 for(Index l = 0; l < kernels_columns_number; l++)
1319 {
1320 synaptic_weights(i ,j, k, l) = new_parameters(element_index);
1321 element_index ++;
1322 }
1323 }
1324 }
1325 }
1326}
1327
1328
1330
1331const Tensor<type, 1>& ConvolutionalLayer::get_biases() const
1332{
1333 return biases;
1334}
1335
1336
1338
1339const Tensor<type, 4>& ConvolutionalLayer::get_synaptic_weights() const
1340{
1341 return synaptic_weights;
1342}
1343
1344
1346
1348{
1349 return synaptic_weights.size();
1350}
1351
1352
1354
1356{
1357 return input_variables_dimensions[1];
1358}
1359
1360
1362
1364{
1365 return input_variables_dimensions[2];
1366}
1367
1368
1370
1372{
1373 return input_variables_dimensions[3];
1374}
1375
1376
1377void ConvolutionalLayer::to_2d(const Tensor<type, 4>& input_4d, Tensor<type, 2>& output_2d) const
1378{
1379 Eigen::array<Index, 2> dimensions =
1380 {Eigen::array<Index, 2>({input_4d.dimension(0), input_4d.dimension(1) * input_4d.dimension(2) * input_4d.dimension(3)})};
1381
1382 output_2d = input_4d.reshape(dimensions);
1383}
1384
1385}
1386
1387// OpenNN: Open Neural Networks Library.
1388// Copyright(C) 2005-2021 Artificial Intelligence Techniques, SL.
1389//
1390// This library is free software; you can redistribute it and/or
1391// modify it under the terms of the GNU Lesser General Public
1392// License as published by the Free Software Foundation; either
1393// version 2.1 of the License, or any later version.
1394//
1395// This library is distributed in the hope that it will be useful,
1396// but WITHOUT ANY WARRANTY; without even the implied warranty of
1397// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1398// Lesser General Public License for more details.
1399
1400// You should have received a copy of the GNU Lesser General Public
1401// License along with this library; if not, write to the Free Software
1402// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Tensor< Index, 1 > get_input_variables_dimensions() const
Returns the dimension of the input variables.
void set_parameters_constant(const type &)
Index get_synaptic_weights_number() const
Returns the number of layer's synaptic weights.
Index get_column_stride() const
Returns the column stride.
Index get_inputs_number() const
Returns the number of inputs.
Index get_kernels_number() const
Returns the number of kernels of the layer.
Index get_outputs_rows_number() const
Returns the number of rows the result of applying the layer's kernels to an image will have.
void set_biases_constant(const type &)
Tensor< type, 4 > synaptic_weights
This tensor containing conection strengths from a layer's inputs to its neurons.
Index get_inputs_channels_number() const
Returns the number of channels of the input.
Index get_kernels_channels_number() const
Returns the number of channels of the layer's kernels.
ActivationFunction
Enumeration of available activation functions for the convolutional layer.
bool is_empty() const
Returns a boolean, true if convolutional layer is empty and false otherwise.
void set_synaptic_weights(const Tensor< type, 4 > &)
Index get_inputs_rows_number() const
Returns the number of rows of the input.
void set_activation_function(const ActivationFunction &)
Index get_inputs_columns_number() const
Returns the number of columns of the input.
void set(const Tensor< Index, 1 > &, const Tensor< Index, 1 > &)
void insert_padding(const Tensor< type, 4 > &, Tensor< type, 4 > &)
const Tensor< type, 1 > & get_biases() const
Returns the layer's biases.
Index get_outputs_columns_number() const
Returns the number of columns the result of applying the layer's kernels to an image will have.
void set_column_stride(const Index &)
Index get_padding_width() const
Returns the total number of columns of zeroes to be added to an image before applying a kernel,...
Index get_neurons_number() const
Returns the number of neurons.
ConvolutionType get_convolution_type() const
Returns the padding option.
void set_biases(const Tensor< type, 1 > &)
Index get_kernels_rows_number() const
Returns the number of rows of the layer's kernels.
Tensor< Index, 1 > get_outputs_dimensions() const
Returns a vector containing the number of channels, rows and columns of the result of applying the la...
void calculate_activations(const Tensor< type, 4 > &, Tensor< type, 4 > &) const
Calculates activations.
ActivationFunction get_activation_function() const
Returns the convolutional layer's activation function.
Index get_row_stride() const
Returns the row stride.
void set_synaptic_weights_constant(const type &)
Index get_padding_height() const
Returns the total number of rows of zeroes to be added to an image before applying a kernel,...
const Tensor< type, 4 > & get_synaptic_weights() const
Returns the layer's synaptic weights.
Index get_kernels_columns_number() const
Returns the number of columns of the layer's kernels.
void set_convolution_type(const ConvolutionType &)
void calculate_convolutions(const Tensor< type, 4 > &, Tensor< type, 4 > &) const
Calculate convolutions.
Index get_parameters_number() const
Returns the number of parameters of the layer.
void calculate_hidden_delta_perceptron(const PerceptronLayer *, const Tensor< type, 4 > &, const Tensor< type, 2 > &, const Tensor< type, 2 > &, Tensor< type, 2 > &) const
void calculate_outputs(const Tensor< type, 4 > &, Tensor< type, 4 > &)
void set_parameters(const Tensor< type, 1 > &, const Index &index)
Tensor< type, 1 > get_parameters() const
Returns the layer's parameters in the form of a vector.
This abstract class represents the concept of layer of neurons in OpenNN.
Definition: layer.h:53
Type
This enumeration represents the possible types of layers.
Definition: layer.h:61
Type layer_type
Layer type.
Definition: layer.h:183
This class represents a layer of perceptrons.
const Tensor< type, 2 > & get_synaptic_weights() const
This class represents a layer of probabilistic neurons.
const Tensor< type, 2 > & get_synaptic_weights() const
Returns the synaptic weights of the layer.