recurrent_layer.cpp
1// OpenNN: Open Neural Networks Library
2// www.opennn.net
3//
4// R E C U R R E N T L A Y E R C L A S S
5//
6// Artificial Intelligence Techniques SL
7// artelnics@artelnics.com
8
9#include "recurrent_layer.h"
10
11namespace OpenNN
12{
13
17
19{
20 set();
21
22 layer_type = Type::Recurrent;
23}
24
25
32
33RecurrentLayer::RecurrentLayer(const Index& new_inputs_number, const Index& new_neurons_number) : Layer()
34{
35 set(new_inputs_number, new_neurons_number);
36
37 layer_type = Type::Recurrent;
38}
39
40
43
45{
46}
47
48
50
52{
53 return input_weights.dimension(0);
54}
55
56
58
60{
61 return biases.size();
62}
63
64
66
67const Tensor<type, 1>& RecurrentLayer::get_hidden_states() const
68{
69 return hidden_states;
70}
71
72
74
76{
77 const Index neurons_number = get_neurons_number();
78 const Index inputs_number = get_inputs_number();
79
80 return neurons_number * (1 + inputs_number + neurons_number);
81}
82
83
84Index RecurrentLayer::get_timesteps() const
85{
86 return timesteps;
87}
88
89
93
94Tensor<type, 1> RecurrentLayer::get_biases() const
95{
96 return biases;
97}
98
99
104
105const Tensor<type, 2>& RecurrentLayer::get_input_weights() const
106{
107 return input_weights;
108}
109
110
115
116const Tensor<type, 2>& RecurrentLayer::get_recurrent_weights() const
117{
118 return recurrent_weights;
119}
120
121
122Index RecurrentLayer::get_biases_number() const
123{
124 return biases.size();
125}
126
127
128Index RecurrentLayer::get_input_weights_number() const
129{
130 return input_weights.size();
131}
132
133
134Index RecurrentLayer::get_recurrent_weights_number() const
135{
136 return recurrent_weights.size();
137}
138
139
143
144Tensor<type, 1> RecurrentLayer::get_parameters() const
145{
146 const Index parameters_number = get_parameters_number();
147
148 Tensor<type, 1> parameters(parameters_number);
149
150 Index current_position = 0;
151
152 // Biases
153
154 for(Index i = 0; i < biases.size(); i++) fill_n(parameters.data()+current_position+i, 1, biases(i));
155
156 current_position += biases.size();
157
158 // Weights
159
160 for(Index i = 0; i < input_weights.size(); i++) fill_n(parameters.data()+current_position+i, 1, input_weights(i));
161
162 current_position += input_weights.size();
163
164 // Recurrent weights
165
166 for(Index i = 0; i < recurrent_weights.size(); i++) fill_n(parameters.data()+current_position+i, 1, recurrent_weights(i));
167
168 return parameters;
169}
170
171
173
175{
176 return activation_function;
177}
178
179
183
184Tensor<type, 2> RecurrentLayer::get_biases(const Tensor<type, 1>& parameters) const
185{
186 const Index biases_number = get_biases_number();
187 const Index input_weights_number = get_input_weights_number();
188
189 Tensor<type, 1> new_biases(biases_number);
190
191 new_biases = parameters.slice(Eigen::array<Eigen::Index, 1>({input_weights_number}), Eigen::array<Eigen::Index, 1>({biases_number}));
192
193 Eigen::array<Index, 2> two_dim{{1, biases.dimension(1)}};
194
195 return new_biases.reshape(two_dim);
196}
197
198
203
204Tensor<type, 2> RecurrentLayer::get_input_weights(const Tensor<type, 1>& parameters) const
205{
206 const Index inputs_number = get_inputs_number();
207 const Index neurons_number = get_neurons_number();
208 const Index input_weights_number = get_input_weights_number();
209
210 const Tensor<type, 1> new_inputs_weights
211 = parameters.slice(Eigen::array<Eigen::Index, 1>({0}), Eigen::array<Eigen::Index, 1>({input_weights_number}));
212
213 const Eigen::array<Index, 2> two_dim{{inputs_number, neurons_number}};
214
215 return new_inputs_weights.reshape(two_dim);
216}
217
218
223
224Tensor<type, 2> RecurrentLayer::get_recurrent_weights(const Tensor<type, 1>& parameters) const
225{
226 const Index neurons_number = get_neurons_number();
227 const Index recurrent_weights_number = recurrent_weights.size();
228
229 const Index parameters_size = parameters.size();
230
231 const Index start_recurrent_weights_number = (parameters_size - recurrent_weights_number);
232
233 const Tensor<type, 1> new_synaptic_weights
234 = parameters.slice(Eigen::array<Eigen::Index, 1>({start_recurrent_weights_number}), Eigen::array<Eigen::Index, 1>({recurrent_weights_number}));
235
236 const Eigen::array<Index, 2> two_dim{{neurons_number, neurons_number}};
237
238 return new_synaptic_weights.reshape(two_dim);
239}
240
241
244
246{
247 switch(activation_function)
248 {
249 case ActivationFunction::Logistic: return "Logistic";
250
251 case ActivationFunction::HyperbolicTangent: return "HyperbolicTangent";
252
253 case ActivationFunction::Threshold: return "Threshold";
254
255 case ActivationFunction::SymmetricThreshold: return "SymmetricThreshold";
256
257 case ActivationFunction::Linear: return "Linear";
258
259 case ActivationFunction::RectifiedLinear: return "RectifiedLinear";
260
261 case ActivationFunction::ScaledExponentialLinear: return "ScaledExponentialLinear";
262
263 case ActivationFunction::SoftPlus: return "SoftPlus";
264
265 case ActivationFunction::SoftSign: return "SoftSign";
266
267 case ActivationFunction::HardSigmoid: return "HardSigmoid";
268
269 case ActivationFunction::ExponentialLinear: return "ExponentialLinear";
270 }
271
272 return string();
273}
274
275
278
280{
281 return display;
282}
283
284
287
289{
290 set_default();
291}
292
293
298
299void RecurrentLayer::set(const Index& new_inputs_number, const Index& new_neurons_number)
300{
301 biases.resize(new_neurons_number);
302
303 input_weights.resize(new_inputs_number, new_neurons_number);
304
305 recurrent_weights.resize(new_neurons_number, new_neurons_number);
306
307 hidden_states.resize(new_neurons_number); // memory
308
309 hidden_states.setConstant(type(0));
310
312
313 set_default();
314}
315
316
319
320void RecurrentLayer::set(const RecurrentLayer& other_neuron_layer)
321{
322 activation_function = other_neuron_layer.activation_function;
323
324 display = other_neuron_layer.display;
325
326 set_default();
327}
328
329
336
338{
339 layer_name = "recurrent_layer";
340
341 display = true;
342
343 layer_type = Type::Recurrent;
344}
345
346
350
351void RecurrentLayer::set_inputs_number(const Index& new_inputs_number)
352{
353 const Index neurons_number = get_neurons_number();
354
355 input_weights.resize(new_inputs_number, neurons_number);
356}
357
358
359void RecurrentLayer::set_input_shape(const Tensor<Index, 1>& size)
360{
361 const Index new_size = size[0];
362
363 set_inputs_number(new_size);
364}
365
366
370
371void RecurrentLayer::set_neurons_number(const Index& new_neurons_number)
372{
373 const Index inputs_number = get_inputs_number();
374
375 biases.resize(new_neurons_number);
376
377 input_weights.resize(inputs_number, new_neurons_number);
378
379 recurrent_weights.resize(new_neurons_number, new_neurons_number);
380}
381
382
383void RecurrentLayer::set_timesteps(const Index& new_timesteps)
384{
385 timesteps = new_timesteps;
386}
387
388
389void RecurrentLayer::set_biases(const Tensor<type, 1>& new_biases)
390{
391 biases = new_biases;
392}
393
394
395void RecurrentLayer::set_input_weights(const Tensor<type, 2>& new_input_weights)
396{
397 input_weights = new_input_weights;
398}
399
400
401void RecurrentLayer::set_recurrent_weights(const Tensor<type, 2>& new_recurrent_weights)
402{
403 recurrent_weights = new_recurrent_weights;
404}
405
406
409
410void RecurrentLayer::set_parameters(const Tensor<type, 1>& new_parameters, const Index& index)
411{
412#ifdef OPENNN_DEBUG
413check_size(new_parameters, get_parameters_number(), LOG);
414#endif
415
416 const Index biases_number = get_biases_number();
417 const Index inputs_weights_number = get_input_weights_number();
418 const Index recurrent_weights_number = get_recurrent_weights_number();
419
420 memcpy(biases.data(),
421 new_parameters.data() + index,
422 static_cast<size_t>(biases_number)*sizeof(type));
423
424 memcpy(input_weights.data(),
425 new_parameters.data() + index + biases_number,
426 static_cast<size_t>(inputs_weights_number)*sizeof(type));
427
428 memcpy(recurrent_weights.data(),
429 new_parameters.data() + biases_number + inputs_weights_number + index,
430 static_cast<size_t>(recurrent_weights_number)*sizeof(type));
431}
432
433
436
438{
439 activation_function = new_activation_function;
440}
441
442
446
447void RecurrentLayer::set_activation_function(const string& new_activation_function_name)
448{
449 if(new_activation_function_name == "Logistic")
450 {
451 activation_function = ActivationFunction::Logistic;
452 }
453 else if(new_activation_function_name == "HyperbolicTangent")
454 {
455 activation_function = ActivationFunction::HyperbolicTangent;
456 }
457 else if(new_activation_function_name == "Threshold")
458 {
459 activation_function = ActivationFunction::Threshold;
460 }
461 else if(new_activation_function_name == "SymmetricThreshold")
462 {
463 activation_function = ActivationFunction::SymmetricThreshold;
464 }
465 else if(new_activation_function_name == "Linear")
466 {
467 activation_function = ActivationFunction::Linear;
468 }
469 else if(new_activation_function_name == "RectifiedLinear")
470 {
471 activation_function = ActivationFunction::RectifiedLinear;
472 }
473 else if(new_activation_function_name == "ScaledExponentialLinear")
474 {
475 activation_function = ActivationFunction::ScaledExponentialLinear;
476 }
477 else if(new_activation_function_name == "SoftPlus")
478 {
479 activation_function = ActivationFunction::SoftPlus;
480 }
481 else if(new_activation_function_name == "SoftSign")
482 {
483 activation_function = ActivationFunction::SoftSign;
484 }
485 else if(new_activation_function_name == "HardSigmoid")
486 {
487 activation_function = ActivationFunction::HardSigmoid;
488 }
489 else if(new_activation_function_name == "ExponentialLinear")
490 {
491 activation_function = ActivationFunction::ExponentialLinear;
492 }
493 else
494 {
495 ostringstream buffer;
496
497 buffer << "OpenNN Exception: neuron class.\n"
498 << "void set_activation_function(const string&) method.\n"
499 << "Unknown activation function: " << new_activation_function_name << ".\n";
500
501 throw logic_error(buffer.str());
502 }
503}
504
505
510
511void RecurrentLayer::set_display(const bool& new_display)
512{
513 display = new_display;
514}
515
516
519
521{
522 hidden_states.setConstant(value);
523}
524
525
528
530{
531 biases.setConstant(value);
532}
533
534
537
539{
540 input_weights.setConstant(value);
541}
542
543
546
548{
549 recurrent_weights.setConstant(value);
550}
551
552
554
556{
557 input_weights.setRandom();
558}
559
560
563
565{
566 biases.setConstant(value);
567
568 input_weights.setConstant(value);
569
570 recurrent_weights.setConstant(value);
571
572 hidden_states.setZero();
573}
574
575
578
580{
581 const type minimum = type(-0.2);
582 const type maximum = type(0.2);
583
584 // Biases
585
586 for(Index i = 0; i < biases.size(); i++)
587 {
588 const type random = static_cast<type>(rand()/(RAND_MAX+1.0));
589
590 biases(i) = minimum + (maximum - minimum)*random;
591 }
592
593 // Weights
594
595 for(Index i = 0; i < input_weights.size(); i++)
596 {
597 const type random = static_cast<type>(rand()/(RAND_MAX+1.0));
598
599 input_weights(i) = minimum + (maximum - minimum)*random;
600 }
601
602 // Recurrent weights
603
604 for(Index i = 0; i < recurrent_weights.size(); i++)
605 {
606 const type random = static_cast<type>(rand()/(RAND_MAX+1.0));
607
608 recurrent_weights(i) = minimum + (maximum - minimum)*random;
609 }
610}
611
612
613void RecurrentLayer::calculate_combinations(const Tensor<type, 1>& inputs,
614 const Tensor<type, 2>& input_weights,
615 const Tensor<type, 2>& recurrent_weights,
616 const Tensor<type, 1>& biases,
617 Tensor<type, 1>& combinations) const
618{
619 combinations.device(*thread_pool_device) = inputs.contract(input_weights, AT_B);
620
621 combinations.device(*thread_pool_device) += biases;
622
623 combinations.device(*thread_pool_device) += hidden_states.contract(recurrent_weights, AT_B);
624}
625
626
627void RecurrentLayer::calculate_activations(const Tensor<type, 1>& combinations_1d,
628 Tensor<type, 1>& activations_1d) const
629{
630#ifdef OPENNN_DEBUG
631 check_size(combinations_1d, get_neurons_number(), LOG);
632 check_size(activations_1d, get_neurons_number(), LOG);
633#endif
634
635 switch(activation_function)
636 {
637 case ActivationFunction::Linear: linear(combinations_1d, activations_1d); return;
638
639 case ActivationFunction::Logistic: logistic(combinations_1d, activations_1d); return;
640
641 case ActivationFunction::HyperbolicTangent: hyperbolic_tangent(combinations_1d, activations_1d); return;
642
643 case ActivationFunction::Threshold: threshold(combinations_1d, activations_1d); return;
644
645 case ActivationFunction::SymmetricThreshold: symmetric_threshold(combinations_1d, activations_1d); return;
646
647 case ActivationFunction::RectifiedLinear: rectified_linear(combinations_1d, activations_1d); return;
648
649 case ActivationFunction::ScaledExponentialLinear: scaled_exponential_linear(combinations_1d, activations_1d); return;
650
651 case ActivationFunction::SoftPlus: soft_plus(combinations_1d, activations_1d); return;
652
653 case ActivationFunction::SoftSign: soft_sign(combinations_1d, activations_1d); return;
654
655 case ActivationFunction::HardSigmoid: hard_sigmoid(combinations_1d, activations_1d); return;
656
657 case ActivationFunction::ExponentialLinear: exponential_linear(combinations_1d, activations_1d); return;
658 }
659}
660
661
662void RecurrentLayer::calculate_activations_derivatives(const Tensor<type, 1>& combinations_1d,
663 Tensor<type, 1>& activations_1d,
664 Tensor<type, 1>& activations_derivatives_1d) const
665{
666 #ifdef OPENNN_DEBUG
667
668 const Index neurons_number = get_neurons_number();
669
670 const Index combinations_columns_number = combinations_1d.dimension(1);
671
672 if(combinations_columns_number != neurons_number)
673 {
674 ostringstream buffer;
675
676 buffer << "OpenNN Exception: RecurrentLayer class.\n"
677 << "void calculate_activations_derivatives(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
678 << "Number of combinations_1d columns (" << combinations_columns_number
679 << ") must be equal to number of neurons (" << neurons_number << ").\n";
680
681 throw logic_error(buffer.str());
682 }
683
684 #endif
685
686 switch(activation_function)
687 {
688 case ActivationFunction::Linear: linear_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
689
690 case ActivationFunction::Logistic: logistic_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
691
692 case ActivationFunction::HyperbolicTangent: hyperbolic_tangent_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
693
694 case ActivationFunction::Threshold: threshold_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
695
696 case ActivationFunction::SymmetricThreshold: symmetric_threshold_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
697
698 case ActivationFunction::RectifiedLinear: rectified_linear_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
699
700 case ActivationFunction::ScaledExponentialLinear: scaled_exponential_linear_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
701
702 case ActivationFunction::SoftPlus: soft_plus_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
703
704 case ActivationFunction::SoftSign: soft_sign_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
705
706 case ActivationFunction::HardSigmoid: hard_sigmoid_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
707
708 case ActivationFunction::ExponentialLinear: exponential_linear_derivatives(combinations_1d, activations_1d, activations_derivatives_1d); return;
709 }
710}
711
712void RecurrentLayer::calculate_activations_derivatives(const Tensor<type, 2>& combinations_2d,
713 Tensor<type, 2>& activations_2d,
714 Tensor<type, 2>& activations_derivatives_2d) const
715{
716 #ifdef OPENNN_DEBUG
717
718 const Index neurons_number = get_neurons_number();
719
720 const Index combinations_columns_number = combinations_2d.dimension(1);
721
722 if(combinations_columns_number != neurons_number)
723 {
724 ostringstream buffer;
725
726 buffer << "OpenNN Exception: RecurrentLayer class.\n"
727 << "void calculate_activations_derivatives(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
728 << "Number of combinations_2d columns (" << combinations_columns_number
729 << ") must be equal to number of neurons (" << neurons_number << ").\n";
730
731 throw logic_error(buffer.str());
732 }
733
734 #endif
735
736 switch(activation_function)
737 {
738 case ActivationFunction::Linear: linear_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
739
740 case ActivationFunction::Logistic: logistic_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
741
742 case ActivationFunction::HyperbolicTangent: hyperbolic_tangent_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
743
744 case ActivationFunction::Threshold: threshold_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
745
746 case ActivationFunction::SymmetricThreshold: symmetric_threshold_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
747
748 case ActivationFunction::RectifiedLinear: rectified_linear_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
749
750 case ActivationFunction::ScaledExponentialLinear: scaled_exponential_linear_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
751
752 case ActivationFunction::SoftPlus: soft_plus_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
753
754 case ActivationFunction::SoftSign: soft_sign_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
755
756 case ActivationFunction::HardSigmoid: hard_sigmoid_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
757
758 case ActivationFunction::ExponentialLinear: exponential_linear_derivatives(combinations_2d, activations_2d, activations_derivatives_2d); return;
759 }
760}
761
762
763void RecurrentLayer::forward_propagate(const Tensor<type, 2>& inputs, LayerForwardPropagation* forward_propagation)
764{
765#ifdef OPENNN_DEBUG
766check_columns_number(inputs, get_inputs_number(), LOG);
767#endif
768
769 RecurrentLayerForwardPropagation* recurrent_layer_forward_propagation = static_cast<RecurrentLayerForwardPropagation*>(forward_propagation);
770
771 const Index samples_number = inputs.dimension(0);
772 const Index neurons_number = get_neurons_number();
773
774 for(Index i = 0; i < samples_number; i++)
775 {
776 if(i%timesteps == 0) hidden_states.setZero();
777
778 recurrent_layer_forward_propagation->current_inputs = inputs.chip(i, 0);
779
780 calculate_combinations(recurrent_layer_forward_propagation->current_inputs,
781 input_weights,
783 biases,
784 recurrent_layer_forward_propagation->current_combinations);
785
786 calculate_activations_derivatives(recurrent_layer_forward_propagation->current_combinations,
787 hidden_states,
788 recurrent_layer_forward_propagation->current_activations_derivatives);
789
790 for(Index j = 0; j < neurons_number; j++)
791 {
792 recurrent_layer_forward_propagation->combinations(i,j) = recurrent_layer_forward_propagation->current_combinations(j);
793 recurrent_layer_forward_propagation->activations(i,j) = hidden_states(j);
794 recurrent_layer_forward_propagation->activations_derivatives(i,j) = recurrent_layer_forward_propagation->current_activations_derivatives(j);
795 }
796 }
797}
798
799
800void RecurrentLayer::forward_propagate(const Tensor<type, 2>&inputs,
801 Tensor<type, 1> parameters,
802 LayerForwardPropagation* forward_propagation)
803{
804 RecurrentLayerForwardPropagation* recurrent_layer_forward_propagation
805 = static_cast<RecurrentLayerForwardPropagation*>(forward_propagation);
806
807 const Index neurons_number = get_neurons_number();
808 const Index inputs_number = get_inputs_number();
809
810 const TensorMap<Tensor<type, 1>> biases(parameters.data(), neurons_number);
811 const TensorMap<Tensor<type, 2>> input_weights(parameters.data()+neurons_number, inputs_number, neurons_number);
812 const TensorMap<Tensor<type, 2>> recurrent_weights(parameters.data()+neurons_number+inputs_number*neurons_number, neurons_number, neurons_number);
813
814 const Index samples_number = inputs.dimension(0);
815
816 for(Index i = 0; i < samples_number; i++)
817 {
818 if(i%timesteps == 0) hidden_states.setZero();
819
820 recurrent_layer_forward_propagation->current_inputs = inputs.chip(i, 0);
821
822 calculate_combinations(recurrent_layer_forward_propagation->current_inputs,
823 input_weights,
825 biases,
826 recurrent_layer_forward_propagation->current_combinations);
827
828 calculate_activations_derivatives(recurrent_layer_forward_propagation->current_combinations,
829 hidden_states,
830 recurrent_layer_forward_propagation->current_activations_derivatives);
831
832 for(Index j = 0; j < neurons_number; j++)
833 {
834 recurrent_layer_forward_propagation->combinations(i,j)
835 = recurrent_layer_forward_propagation->current_combinations(j);
836
837 recurrent_layer_forward_propagation->activations(i,j) = hidden_states(j);
838
839 recurrent_layer_forward_propagation->activations_derivatives(i,j)
840 = recurrent_layer_forward_propagation->current_activations_derivatives(j);
841 }
842 }
843}
844
845
846Tensor<type, 2> RecurrentLayer::calculate_outputs(const Tensor<type, 2>& inputs)
847{
848#ifdef OPENNN_DEBUG
849
850 const Index inputs_number = get_inputs_number();
851
852 const Index inputs_columns_number = inputs.dimension(1);
853
854 if(inputs_columns_number != inputs_number)
855 {
856 ostringstream buffer;
857
858 buffer << "OpenNN Exception: RecurrentLayer class.\n"
859 << "Tensor<type, 2> calculate_outputs(const Tensor<type, 2>&) const method.\n"
860 << "Number of columns("<<inputs_columns_number<<") of inputs matrix must be equal to number of inputs("<<inputs_number<<").\n";
861
862 throw logic_error(buffer.str());
863 }
864#endif
865
866 const Index samples_number = inputs.dimension(0);
867
868 const Index neurons_number = get_neurons_number();
869
870 Tensor<type, 1> current_inputs(neurons_number);
871
872 Tensor<type, 1> current_outputs(neurons_number);
873
874 Tensor<type, 2> outputs(samples_number, neurons_number);
875
876 for(Index i = 0; i < samples_number; i++)
877 {
878 if(i%timesteps == 0) hidden_states.setZero();
879
880 current_inputs = inputs.chip(i, 0);
881
882 calculate_combinations(current_inputs, input_weights, recurrent_weights, biases, current_outputs);
883
884 calculate_activations(current_outputs, hidden_states);
885
886 for(Index j = 0; j < neurons_number; j++)
887 outputs(i,j) = hidden_states(j);
888 }
889
890 return outputs;
891}
892
893
894void RecurrentLayer::calculate_hidden_delta(LayerForwardPropagation* next_layer_forward_propagation,
895 LayerBackPropagation* next_layer_back_propagation,
896 LayerBackPropagation* current_layer_back_propagation) const
897{
898 RecurrentLayerBackPropagation* recurrent_layer_back_propagation =
899 static_cast<RecurrentLayerBackPropagation*>(current_layer_back_propagation);
900
901 switch(next_layer_back_propagation->layer_pointer->get_type())
902 {
903 case Type::Perceptron:
904 {
905 PerceptronLayerForwardPropagation* perceptron_layer_forward_propagation =
906 static_cast<PerceptronLayerForwardPropagation*>(next_layer_forward_propagation);
907
908 PerceptronLayerBackPropagation* perceptron_layer_back_propagation =
909 static_cast<PerceptronLayerBackPropagation*>(next_layer_back_propagation);
910
911 calculate_hidden_delta_perceptron(perceptron_layer_forward_propagation,
912 perceptron_layer_back_propagation,
913 recurrent_layer_back_propagation);
914 }
915 break;
916
917 case Type::Probabilistic:
918 {
919 ProbabilisticLayerForwardPropagation* probabilistic_layer_forward_propagation =
920 static_cast<ProbabilisticLayerForwardPropagation*>(next_layer_forward_propagation);
921
922 ProbabilisticLayerBackPropagation* probabilistic_layer_back_propagation =
923 static_cast<ProbabilisticLayerBackPropagation*>(next_layer_back_propagation);
924
925 calculate_hidden_delta_probabilistic(probabilistic_layer_forward_propagation,
926 probabilistic_layer_back_propagation,
927 recurrent_layer_back_propagation);
928 }
929 break;
930
931 default: return;
932 }
933}
934
935
936void RecurrentLayer::calculate_hidden_delta_perceptron(PerceptronLayerForwardPropagation* next_forward_propagation,
937 PerceptronLayerBackPropagation* next_back_propagation,
938 RecurrentLayerBackPropagation* back_propagation) const
939{
940 const Tensor<type, 2>& next_synaptic_weights
941 = static_cast<PerceptronLayer*>(next_back_propagation->layer_pointer)->get_synaptic_weights();
942
943 back_propagation->delta.device(*thread_pool_device) =
944 (next_back_propagation->delta*next_forward_propagation->activations_derivatives).contract(next_synaptic_weights, A_BT);
945}
946
947
948void RecurrentLayer::calculate_hidden_delta_probabilistic(ProbabilisticLayerForwardPropagation* next_forward_propagation,
949 ProbabilisticLayerBackPropagation* next_back_propagation,
950 RecurrentLayerBackPropagation* back_propagation) const
951{
952 const ProbabilisticLayer* probabilistic_layer_pointer = static_cast<ProbabilisticLayer*>(next_back_propagation->layer_pointer);
953
954 const Tensor<type, 2>& next_synaptic_weights = probabilistic_layer_pointer->get_synaptic_weights();
955
956 if(probabilistic_layer_pointer->get_neurons_number() == 1) // Binary
957 {
958 back_propagation->delta.device(*thread_pool_device) =
959 (next_back_propagation->delta*next_forward_propagation->activations_derivatives).contract(next_synaptic_weights, A_BT);
960 }
961 else // Multiple
962 {
963 const Index samples_number = next_back_propagation->delta.dimension(0);
964 const Index outputs_number = next_back_propagation->delta.dimension(1);
965 const Index next_layer_neurons_number = probabilistic_layer_pointer->get_neurons_number();
966
967 if(outputs_number != next_layer_neurons_number)
968 {
969 ostringstream buffer;
970
971 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
972 << "void calculate_hidden_delta_probabilistic(ProbabilisticLayerForwardPropagation*,ProbabilisticLayerBackPropagation*,RecurrentLayerBackPropagation*) const.\n"
973 << "Number of columns in delta (" << outputs_number << ") must be equal to number of neurons in probabilistic layer (" << next_layer_neurons_number << ").\n";
974
975 throw logic_error(buffer.str());
976 }
977
978 if(next_forward_propagation->activations_derivatives.dimension(1) != next_layer_neurons_number)
979 {
980 ostringstream buffer;
981
982 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
983 << "void calculate_hidden_delta_probabilistic(ProbabilisticLayerForwardPropagation*,ProbabilisticLayerBackPropagation*,RecurrentLayerBackPropagation*) const.\n"
984 << "Dimension 1 of activations derivatives (" << outputs_number << ") must be equal to number of neurons in probabilistic layer (" << next_layer_neurons_number << ").\n";
985
986 throw logic_error(buffer.str());
987 }
988
989 if(next_forward_propagation->activations_derivatives.dimension(2) != next_layer_neurons_number)
990 {
991 ostringstream buffer;
992
993 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
994 << "void calculate_hidden_delta_probabilistic(ProbabilisticLayerForwardPropagation*,ProbabilisticLayerBackPropagation*,RecurrentLayerBackPropagation*) const.\n"
995 << "Dimension 2 of activations derivatives (" << outputs_number << ") must be equal to number of neurons in probabilistic layer (" << next_layer_neurons_number << ").\n";
996
997 throw logic_error(buffer.str());
998 }
999
1000 const Index step = next_layer_neurons_number*next_layer_neurons_number;
1001
1002 next_back_propagation->biases_derivatives.setZero();
1003
1004 for(Index i = 0; i < samples_number; i++)
1005 {
1006 next_back_propagation->delta_row = next_back_propagation->delta.chip(i,0);
1007
1008 TensorMap< Tensor<type, 2> > activations_derivatives_matrix(next_forward_propagation->activations_derivatives.data() + i*step,
1009 next_layer_neurons_number, next_layer_neurons_number);
1010
1011 next_back_propagation->error_combinations_derivatives.chip(i,0) =
1012 next_back_propagation->delta_row.contract(activations_derivatives_matrix, AT_B);
1013 }
1014
1015 back_propagation->delta.device(*thread_pool_device) =
1016 (next_back_propagation->error_combinations_derivatives).contract(next_synaptic_weights, A_BT);
1017 }
1018}
1019
1020
1021void RecurrentLayer::insert_gradient(LayerBackPropagation* back_propagation, const Index& index, Tensor<type, 1>& gradient) const
1022{
1023 const Index inputs_number = get_inputs_number();
1024 const Index neurons_number = get_neurons_number();
1025
1026 RecurrentLayerBackPropagation* recurrent_layer_back_propagation
1027 = static_cast<RecurrentLayerBackPropagation*>(back_propagation);
1028
1029 // Biases
1030
1031 memcpy(gradient.data() + index,
1032 recurrent_layer_back_propagation->biases_derivatives.data(),
1033 static_cast<size_t>(neurons_number)*sizeof(type));
1034
1035 // Input weights
1036
1037 memcpy(gradient.data() + index + neurons_number,
1038 recurrent_layer_back_propagation->input_weights_derivatives.data(),
1039 static_cast<size_t>(inputs_number*neurons_number)*sizeof(type));
1040
1041 // Recurrent weights
1042
1043 memcpy(gradient.data() + index + neurons_number + inputs_number*neurons_number,
1044 recurrent_layer_back_propagation->recurrent_weights_derivatives.data(),
1045 static_cast<size_t>(neurons_number*neurons_number)*sizeof(type));
1046}
1047
1048
1049void RecurrentLayer::calculate_error_gradient(const Tensor<type, 2>& inputs,
1050 LayerForwardPropagation* forward_propagation,
1051 LayerBackPropagation* back_propagation) const
1052{
1053 RecurrentLayerForwardPropagation* recurrent_layer_forward_propagation =
1054 static_cast<RecurrentLayerForwardPropagation*>(forward_propagation);
1055
1056 RecurrentLayerBackPropagation* recurrent_layer_back_propagation =
1057 static_cast<RecurrentLayerBackPropagation*>(back_propagation);
1058
1059
1060 calculate_biases_error_gradient(inputs, recurrent_layer_forward_propagation, recurrent_layer_back_propagation);
1061
1062 calculate_input_weights_error_gradient(inputs, recurrent_layer_forward_propagation, recurrent_layer_back_propagation);
1063
1064 calculate_recurrent_weights_error_gradient(inputs, recurrent_layer_forward_propagation, recurrent_layer_back_propagation);
1065
1066}
1067
1068
1069void RecurrentLayer::calculate_biases_error_gradient(const Tensor<type, 2>& inputs,
1070 RecurrentLayerForwardPropagation* forward_propagation,
1071 RecurrentLayerBackPropagation* back_propagation) const
1072{
1073 // Derivatives of combinations with respect to biases
1074
1075 const Index samples_number = inputs.dimension(0);
1076 const Index neurons_number = get_neurons_number();
1077 const Index parameters_number = neurons_number;
1078
1079 back_propagation->combinations_biases_derivatives.setZero();
1080
1081 back_propagation->biases_derivatives.setZero();
1082
1083 for(Index sample = 0; sample < samples_number; sample++)
1084 {
1085 back_propagation->current_layer_deltas = back_propagation->delta.chip(sample,0);
1086
1087 if(sample%timesteps == 0)
1088 {
1089 back_propagation->combinations_biases_derivatives.setZero();
1090 }
1091 else
1092 {
1093 multiply_rows(back_propagation->combinations_biases_derivatives, forward_propagation->current_activations_derivatives);
1094
1095 back_propagation->combinations_biases_derivatives
1096 = back_propagation->combinations_biases_derivatives.contract(recurrent_weights, A_B).eval();
1097 }
1098
1099 forward_propagation->current_activations_derivatives
1100 = forward_propagation->activations_derivatives.chip(sample, 0);
1101
1102 for(Index i = 0; i < parameters_number; i++) back_propagation->combinations_biases_derivatives(i,i) += static_cast<type>(1);
1103
1104 back_propagation->biases_derivatives += back_propagation->combinations_biases_derivatives
1105 .contract(back_propagation->current_layer_deltas*forward_propagation->current_activations_derivatives, A_B);
1106 }
1107}
1108
1109
1110void RecurrentLayer::calculate_input_weights_error_gradient(const Tensor<type, 2>& inputs,
1111 RecurrentLayerForwardPropagation* forward_propagation,
1112 RecurrentLayerBackPropagation* back_propagation) const
1113{
1114 // Derivatives of combinations with respect to input weights
1115
1116 const Index samples_number = inputs.dimension(0);
1117 const Index inputs_number = get_inputs_number();
1118 const Index neurons_number = get_neurons_number();
1119 const Index parameters_number = inputs_number*neurons_number;
1120
1121 Index column_index = 0;
1122 Index input_index = 0;
1123
1124 back_propagation->combinations_weights_derivatives.setZero();
1125 back_propagation->input_weights_derivatives.setZero();
1126
1127 for(Index sample = 0; sample < samples_number; sample++)
1128 {
1129 forward_propagation->current_inputs = inputs.chip(sample, 0);
1130
1131 back_propagation->current_layer_deltas = back_propagation->delta.chip(sample, 0);
1132
1133 if(sample%timesteps == 0)
1134 {
1135 back_propagation->combinations_weights_derivatives.setZero();
1136 }
1137 else
1138 {
1139 multiply_rows(back_propagation->combinations_weights_derivatives, forward_propagation->current_activations_derivatives);
1140
1141 back_propagation->combinations_weights_derivatives
1142 = back_propagation->combinations_weights_derivatives.contract(recurrent_weights, A_B).eval();
1143 }
1144
1145 forward_propagation->current_activations_derivatives
1146 = forward_propagation->activations_derivatives.chip(sample, 0);
1147
1148 column_index = 0;
1149 input_index = 0;
1150
1151 for(Index i = 0; i < parameters_number; i++)
1152 {
1153 back_propagation->combinations_weights_derivatives(i, column_index) += forward_propagation->current_inputs(input_index);
1154
1155 input_index++;
1156
1157 if(input_index == inputs_number)
1158 {
1159 input_index = 0;
1160 column_index++;
1161 }
1162 }
1163
1164 back_propagation->input_weights_derivatives += back_propagation->combinations_weights_derivatives
1165 .contract(back_propagation->current_layer_deltas*forward_propagation->current_activations_derivatives, A_B);
1166 }
1167}
1168
1169
1170void RecurrentLayer::calculate_recurrent_weights_error_gradient(const Tensor<type, 2>& inputs,
1171 RecurrentLayerForwardPropagation* forward_propagation,
1172 RecurrentLayerBackPropagation* back_propagation) const
1173{
1174 const Index samples_number = inputs.dimension(0);
1175 const Index neurons_number = get_neurons_number();
1176
1177 const Index parameters_number = neurons_number*neurons_number;
1178
1179 // Derivatives of combinations with respect to recurrent weights
1180
1181 back_propagation->combinations_recurrent_weights_derivatives.setZero();
1182
1183 back_propagation->recurrent_weights_derivatives.setZero();
1184
1185 Index column_index = 0;
1186 Index activation_index = 0;
1187
1188 for(Index sample = 0; sample < samples_number; sample++)
1189 {
1190 back_propagation->current_layer_deltas = back_propagation->delta.chip(sample,0);
1191
1192 if(sample%timesteps == 0)
1193 {
1194 back_propagation->combinations_recurrent_weights_derivatives.setZero();
1195 }
1196 else
1197 {
1198 forward_propagation->previous_activations = forward_propagation->activations.chip(sample-1, 0);
1199
1200 multiply_rows(back_propagation->combinations_recurrent_weights_derivatives, forward_propagation->current_activations_derivatives);
1201
1202 back_propagation->combinations_recurrent_weights_derivatives
1203 = back_propagation->combinations_recurrent_weights_derivatives.contract(recurrent_weights,A_B).eval();
1204
1205 column_index = 0;
1206 activation_index = 0;
1207
1208 for(Index i = 0; i < parameters_number; i++)
1209 {
1210 back_propagation->combinations_recurrent_weights_derivatives(i, column_index)
1211 += forward_propagation->previous_activations(activation_index);
1212
1213 activation_index++;
1214
1215 if(activation_index == neurons_number)
1216 {
1217 activation_index = 0;
1218 column_index++;
1219 }
1220 }
1221 }
1222
1223 forward_propagation->current_activations_derivatives = forward_propagation->activations_derivatives.chip(sample, 0);
1224
1225 back_propagation->recurrent_weights_derivatives += back_propagation->combinations_recurrent_weights_derivatives
1226 .contract(back_propagation->current_layer_deltas*forward_propagation->current_activations_derivatives, A_B);
1227 }
1228
1229}
1230
1231
1236
1237string RecurrentLayer::write_expression(const Tensor<string, 1>& inputs_names,
1238 const Tensor<string, 1>& outputs_names) const
1239{
1240#ifdef OPENNN_DEBUG
1241
1242 const Index neurons_number = get_neurons_number();
1243
1244 const Index inputs_number = get_inputs_number();
1245 const Index inputs_name_size = inputs_names.size();
1246
1247 if(inputs_name_size != inputs_number)
1248 {
1249 ostringstream buffer;
1250
1251 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1252 << "string write_expression(const Tensor<string, 1>&, const Tensor<string, 1>&) const method.\n"
1253 << "Size of inputs name must be equal to number of layer inputs.\n";
1254
1255 throw logic_error(buffer.str());
1256 }
1257
1258 const Index outputs_name_size = outputs_names.size();
1259
1260 if(outputs_name_size != neurons_number)
1261 {
1262 ostringstream buffer;
1263
1264 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1265 << "string write_expression(const Tensor<string, 1>&, const Tensor<string, 1>&) const method.\n"
1266 << "Size of outputs name must be equal to number of neurons.\n";
1267
1268 throw logic_error(buffer.str());
1269 }
1270
1271#endif
1272
1273 ostringstream buffer;
1274
1275 for(Index j = 0; j < outputs_names.size(); j++)
1276 {
1277 const Tensor<type, 1> synaptic_weights_column = recurrent_weights.chip(j,1);
1278
1279
1280 buffer << outputs_names(j) << " = " << write_activation_function_expression() << "( " << biases(j) << " +";
1281
1282 for(Index i = 0; i < inputs_names.size() - 1; i++)
1283 {
1284 buffer << " (" << inputs_names[i] << "*" << synaptic_weights_column(i) << ") +";
1285 }
1286
1287 buffer << " (" << inputs_names[inputs_names.size() - 1] << "*" << synaptic_weights_column[inputs_names.size() - 1] << ") );\n";
1288 }
1289
1290 return buffer.str();
1291}
1292
1293
1294string RecurrentLayer::write_activation_function_expression() const
1295{
1296 switch(activation_function)
1297 {
1298 case ActivationFunction::HyperbolicTangent: return "tanh";
1299
1300 case ActivationFunction::Linear: return string();
1301
1302 default: return write_activation_function();
1303 }
1304}
1305
1306
1307void RecurrentLayer::from_XML(const tinyxml2::XMLDocument& document)
1308{
1309 ostringstream buffer;
1310
1311 // Perceptron layer
1312
1313 const tinyxml2::XMLElement* perceptron_layer_element = document.FirstChildElement("RecurrentLayer");
1314
1315 if(!perceptron_layer_element)
1316 {
1317 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1318 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1319 << "RecurrentLayer element is nullptr.\n";
1320
1321 throw logic_error(buffer.str());
1322 }
1323
1324 // Inputs number
1325
1326 const tinyxml2::XMLElement* inputs_number_element = perceptron_layer_element->FirstChildElement("InputsNumber");
1327
1328 if(!inputs_number_element)
1329 {
1330 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1331 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1332 << "InputsNumber element is nullptr.\n";
1333
1334 throw logic_error(buffer.str());
1335 }
1336
1337 if(inputs_number_element->GetText())
1338 {
1339 set_inputs_number(static_cast<Index>(stoi(inputs_number_element->GetText())));
1340 }
1341
1342 // Neurons number
1343
1344 const tinyxml2::XMLElement* neurons_number_element = perceptron_layer_element->FirstChildElement("NeuronsNumber");
1345
1346 if(!neurons_number_element)
1347 {
1348 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1349 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1350 << "NeuronsNumber element is nullptr.\n";
1351
1352 throw logic_error(buffer.str());
1353 }
1354
1355 if(neurons_number_element->GetText())
1356 {
1357 set_neurons_number(static_cast<Index>(stoi(neurons_number_element->GetText())));
1358 }
1359
1360 // Activation function
1361
1362 const tinyxml2::XMLElement* activation_function_element = perceptron_layer_element->FirstChildElement("ActivationFunction");
1363
1364 if(!activation_function_element)
1365 {
1366 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1367 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1368 << "ActivationFunction element is nullptr.\n";
1369
1370 throw logic_error(buffer.str());
1371 }
1372
1373 if(activation_function_element->GetText())
1374 {
1375 set_activation_function(activation_function_element->GetText());
1376 }
1377
1378 // Parameters
1379
1380 const tinyxml2::XMLElement* parameters_element = perceptron_layer_element->FirstChildElement("Parameters");
1381
1382 if(!parameters_element)
1383 {
1384 buffer << "OpenNN Exception: RecurrentLayer class.\n"
1385 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1386 << "Parameters element is nullptr.\n";
1387
1388 throw logic_error(buffer.str());
1389 }
1390
1391 if(parameters_element->GetText())
1392 {
1393 const string parameters_string = parameters_element->GetText();
1394
1395 set_parameters(to_type_vector(parameters_string, ' '));
1396 }
1397}
1398
1399
1400void RecurrentLayer::write_XML(tinyxml2::XMLPrinter& file_stream) const
1401
1402{
1403 ostringstream buffer;
1404
1405 // Perceptron layer
1406
1407 file_stream.OpenElement("RecurrentLayer");
1408
1409 // Inputs number
1410
1411 file_stream.OpenElement("InputsNumber");
1412
1413 buffer.str("");
1414 buffer << get_inputs_number();
1415
1416 file_stream.PushText(buffer.str().c_str());
1417
1418 file_stream.CloseElement();
1419
1420 // Outputs number
1421
1422 file_stream.OpenElement("NeuronsNumber");
1423
1424 buffer.str("");
1425 buffer << get_neurons_number();
1426
1427 file_stream.PushText(buffer.str().c_str());
1428
1429 file_stream.CloseElement();
1430
1431 // Activation function
1432
1433 file_stream.OpenElement("ActivationFunction");
1434
1435 file_stream.PushText(write_activation_function().c_str());
1436
1437 file_stream.CloseElement();
1438
1439 // Parameters
1440
1441 file_stream.OpenElement("Parameters");
1442
1443 buffer.str("");
1444
1445 const Tensor<type, 1> parameters = get_parameters();
1446 const Index parameters_size = parameters.size();
1447
1448 for(Index i = 0; i < parameters_size; i++)
1449 {
1450 buffer << parameters(i);
1451
1452 if(i != (parameters_size-1)) buffer << " ";
1453 }
1454
1455 file_stream.PushText(buffer.str().c_str());
1456
1457 file_stream.CloseElement();
1458
1459 // Recurrent layer (end tag)
1460
1461 file_stream.CloseElement();
1462}
1463
1464
1465string RecurrentLayer::write_combinations_python() const
1466{
1467 ostringstream buffer;
1468
1469 const Index inputs_number = get_inputs_number();
1470 const Index neurons_number = get_neurons_number();
1471
1472 buffer << "\t\tcombinations = [None] * "<<neurons_number<<"\n" << endl;
1473
1474 for(Index i = 0; i < neurons_number; i++)
1475 {
1476 buffer << "\t\tcombinations[" << i << "] = " << biases(i);
1477
1478 for(Index j = 0; j < neurons_number; j++)
1479 {
1480 buffer << " +" << recurrent_weights(j, i) << "*self.hidden_states[" << j << "]";
1481 }
1482
1483 for(Index j = 0; j < inputs_number; j++)
1484 {
1485 buffer << " +" << input_weights(j, i) << "*inputs[" << j << "]";
1486 }
1487
1488 buffer << " " << endl;
1489 }
1490
1491 buffer << "\t\t" << endl;
1492
1493 return buffer.str();
1494}
1495
1496
1498{
1499 ostringstream buffer;
1500
1501 const Index neurons_number = get_neurons_number();
1502
1503 buffer << "\t\tactivations = [None] * "<<neurons_number<<"\n" << endl;
1504
1505 for(Index i = 0; i < neurons_number; i++)
1506 {
1507 buffer << "\t\tactivations[" << i << "] = ";
1508
1509 switch(activation_function)
1510 {
1511
1512 case ActivationFunction::HyperbolicTangent:
1513 buffer << "np.tanh(combinations[" << i << "])\n";
1514 break;
1515
1516 case ActivationFunction::RectifiedLinear:
1517 buffer << "np.maximum(0.0, combinations[" << i << "])\n";
1518 break;
1519
1520 case ActivationFunction::Logistic:
1521 buffer << "1.0/(1.0 + np.exp(-combinations[" << i << "]))\n";
1522 break;
1523
1524 case ActivationFunction::Threshold:
1525 buffer << "1.0 if combinations[" << i << "] >= 0.0 else 0.0\n";
1526 break;
1527
1528 case ActivationFunction::SymmetricThreshold:
1529 buffer << "1.0 if combinations[" << i << "] >= 0.0 else -1.0\n";
1530 break;
1531
1532 case ActivationFunction::Linear:
1533 buffer << "combinations[" << i << "]\n";
1534 break;
1535
1536 case ActivationFunction::ScaledExponentialLinear:
1537 buffer << "1.0507*1.67326*(np.exp(combinations[" << i << "]) - 1.0) if combinations[" << i << "] < 0.0 else 1.0507*combinations[" << i << "]\n";
1538 break;
1539
1540 case ActivationFunction::SoftPlus:
1541 buffer << "np.log(1.0 + np.exp(combinations[" << i << "]))\n";
1542 break;
1543
1544 case ActivationFunction::SoftSign:
1545 buffer << "combinations[" << i << "]/(1.0 - combinations[" << i << "] ) if combinations[" << i << "] < 0.0 else combinations[" << i << "]/(1.0 + combinations[" << i << "] )\n";
1546 break;
1547
1548 case ActivationFunction::ExponentialLinear:
1549 buffer << "1.0*(np.exp(combinations[" << i << "]) - 1.0) if combinations[" << i << "] < 0.0 else combinations[" << i << "]\n";
1550 break;
1551
1552 case ActivationFunction::HardSigmoid:
1554 break;
1555 }
1556 }
1557
1558 buffer << "\t\tself.hidden_states = activations" << endl;
1559
1560 return buffer.str();
1561}
1562
1563
1564string RecurrentLayer::write_expression_python() const
1565{
1566 ostringstream buffer;
1567
1568 buffer << "\tdef " << layer_name << "(self,inputs):\n" << endl;
1569
1570 buffer << write_combinations_python();
1571
1572 buffer << write_activations_python();
1573
1574 buffer << "\n\t\treturn activations;\n" << endl;
1575
1576 return buffer.str();
1577}
1578
1579}
1580
1581// OpenNN: Open Neural Networks Library.
1582// Copyright(C) 2005-2021 Artificial Intelligence Techniques, SL.
1583//
1584// This library is free software; you can redistribute it and/or
1585// modify it under the terms of the GNU Lesser General Public
1586// License as published by the Free Software Foundation; either
1587// version 2.1 of the License, or any later version.
1588//
1589// This library is distributed in the hope that it will be useful,
1590// but WITHOUT ANY WARRANTY; without even the implied warranty of
1591// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1592// Lesser General Public License for more details.
1593
1594// You should have received a copy of the GNU Lesser General Public
1595// License along with this library; if not, write to the Free Software
1596// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
This abstract class represents the concept of layer of neurons in OpenNN.
Definition: layer.h:53
string layer_name
Layer name.
Definition: layer.h:179
Type layer_type
Layer type.
Definition: layer.h:183
const Tensor< type, 2 > & get_recurrent_weights() const
string write_activation_function() const
void set_parameters_constant(const type &)
string write_activations_python() const
void set_input_weights_constant(const type &)
const bool & get_display() const
Index get_inputs_number() const
Returns the number of inputs to the layer.
void set_biases_constant(const type &)
string write_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
Tensor< type, 2 > recurrent_weights
This matrix containing conection strengths from a recurrent layer inputs to its neurons.
bool display
Display messages to screen.
ActivationFunction
Enumeration of the available activation functions for the recurrent layer.
void set_activation_function(const ActivationFunction &)
void set_recurrent_weights_constant(const type &)
Tensor< type, 1 > biases
const Tensor< type, 2 > & get_input_weights() const
void set_inputs_number(const Index &)
Index get_neurons_number() const
Returns the size of the neurons vector.
const RecurrentLayer::ActivationFunction & get_activation_function() const
Returns the activation function of the layer.
void set_parameters(const Tensor< type, 1 > &, const Index &=0)
void set_display(const bool &)
void set_hidden_states_constant(const type &)
void initialize_input_weights_Glorot(const type &, const type &)
const Tensor< type, 1 > & get_hidden_states() const
Returns the hidden states of the layer.
Index get_parameters_number() const
Returns the number of parameters (biases and weights) of the layer.
ActivationFunction activation_function
Activation function variable.
Tensor< type, 1 > get_biases() const
void set_neurons_number(const Index &)
Tensor< type, 1 > get_parameters() const
void PushText(const char *text, bool cdata=false)
Add a text node.
Definition: tinyxml2.cpp:2878
virtual void CloseElement(bool compactMode=false)
If streaming, close the Element.
Definition: tinyxml2.cpp:2834