probabilistic_layer.cpp
1// OpenNN: Open Neural Networks Library
2// www.opennn.net
3//
4// P R O B A B I L I S T I C L A Y E R C L A S S
5//
6// Artificial Intelligence Techniques SL
7// artelnics@artelnics.com
8
9#include "probabilistic_layer.h"
10
11namespace OpenNN
12{
13
17
19{
20 set();
21}
22
23
27
28ProbabilisticLayer::ProbabilisticLayer(const Index& new_inputs_number, const Index& new_neurons_number)
29{
30 set(new_inputs_number, new_neurons_number);
31
32 if(new_neurons_number > 1)
33 {
34 activation_function = ActivationFunction::Softmax;
35 }
36}
37
38
41
43{
44}
45
46
48{
49 return synaptic_weights.dimension(0);
50}
51
52
53Index ProbabilisticLayer::get_neurons_number() const
54{
55 return biases.size();
56}
57
58
59Index ProbabilisticLayer::get_biases_number() const
60{
61 return biases.size();
62}
63
64
66
68{
69 return synaptic_weights.size();
70}
71
72
74
76{
77 return decision_threshold;
78}
79
80
83
85{
87}
88
89
92
94{
95 if(activation_function == ActivationFunction::Binary)
96 {
97 return "Binary";
98 }
99 else if(activation_function == ActivationFunction::Logistic)
100 {
101 return "Logistic";
102 }
103 else if(activation_function == ActivationFunction::Competitive)
104 {
105 return "Competitive";
106 }
107 else if(activation_function == ActivationFunction::Softmax)
108 {
109 return "Softmax";
110 }
111 else
112 {
113 ostringstream buffer;
114
115 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
116 << "string write_activation_function() const method.\n"
117 << "Unknown probabilistic method.\n";
118
119 throw logic_error(buffer.str());
120 }
121}
122
123
126
128{
129 if(activation_function == ActivationFunction::Binary)
130 {
131 return "binary";
132 }
133 else if(activation_function == ActivationFunction::Logistic)
134 {
135 return "logistic";
136 }
137 else if(activation_function == ActivationFunction::Competitive)
138 {
139 return "competitive";
140 }
141 else if(activation_function == ActivationFunction::Softmax)
142 {
143 return "softmax";
144 }
145 else
146 {
147 ostringstream buffer;
148
149 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
150 << "string write_activation_function_text() const method.\n"
151 << "Unknown probabilistic method.\n";
152
153 throw logic_error(buffer.str());
154 }
155}
156
157
160
162{
163 return display;
164}
165
166
168
169const Tensor<type, 2>& ProbabilisticLayer::get_biases() const
170{
171 return biases;
172}
173
174
176
177const Tensor<type, 2>& ProbabilisticLayer::get_synaptic_weights() const
178{
179 return synaptic_weights;
180}
181
182
185
186Tensor<type, 2> ProbabilisticLayer::get_biases(Tensor<type, 1>& parameters) const
187{
188 const Index neurons_number = get_neurons_number();
189
190 const TensorMap < Tensor<type, 2> > bias_tensor(parameters.data(), 1, neurons_number);
191
192 return bias_tensor;
193}
194
195
198
199Tensor<type, 2> ProbabilisticLayer::get_synaptic_weights(Tensor<type, 1>& parameters) const
200{
201 const Index inputs_number = get_inputs_number();
202 const Index neurons_number = get_neurons_number();
203 const Index biases_number = get_biases_number();
204
205 const TensorMap< Tensor<type, 2> > synaptic_weights_tensor(parameters.data()+biases_number, inputs_number, neurons_number);
206
207 return synaptic_weights_tensor;
208}
209
210
212
214{
215 return biases.size() + synaptic_weights.size();
216}
217
218
222
224{
225 Tensor<type, 1> parameters(synaptic_weights.size() + biases.size());
226
227 for(Index i = 0; i < biases.size(); i++)
228 {
229 fill_n(parameters.data()+i, 1, biases(i));
230 }
231
232 for(Index i = 0; i < synaptic_weights.size(); i++)
233 {
234 fill_n(parameters.data()+ biases.size() +i, 1, synaptic_weights(i));
235 }
236
237 return parameters;
238
239}
240
241
244
246{
247 biases.resize(0, 0);
248
249 synaptic_weights.resize(0,0);
250
251 set_default();
252}
253
254
258
259void ProbabilisticLayer::set(const Index& new_inputs_number, const Index& new_neurons_number)
260{
261 biases.resize(1, new_neurons_number);
262
263 synaptic_weights.resize(new_inputs_number, new_neurons_number);
264
266
267 set_default();
268}
269
270
273
274void ProbabilisticLayer::set(const ProbabilisticLayer& other_probabilistic_layer)
275{
276 set_default();
277
278 activation_function = other_probabilistic_layer.activation_function;
279
280 decision_threshold = other_probabilistic_layer.decision_threshold;
281
282 display = other_probabilistic_layer.display;
283}
284
285
286void ProbabilisticLayer::set_inputs_number(const Index& new_inputs_number)
287{
288 const Index neurons_number = get_neurons_number();
289
290 biases.resize(1, neurons_number);
291
292 synaptic_weights.resize(new_inputs_number, neurons_number);
293}
294
295
296void ProbabilisticLayer::set_neurons_number(const Index& new_neurons_number)
297{
298 const Index inputs_number = get_inputs_number();
299
300 biases.resize(1, new_neurons_number);
301
302 synaptic_weights.resize(inputs_number, new_neurons_number);
303}
304
305
306void ProbabilisticLayer::set_biases(const Tensor<type, 2>& new_biases)
307{
308 biases = new_biases;
309}
310
311
312void ProbabilisticLayer::set_synaptic_weights(const Tensor<type, 2>& new_synaptic_weights)
313{
314 synaptic_weights = new_synaptic_weights;
315}
316
317
318void ProbabilisticLayer::set_parameters(const Tensor<type, 1>& new_parameters, const Index& index)
319{
320 const Index biases_number = biases.size();
321 const Index synaptic_weights_number = synaptic_weights.size();
322
323 memcpy(biases.data(), new_parameters.data() + index, static_cast<size_t>(biases_number)*sizeof(type));
324 memcpy(synaptic_weights.data(), new_parameters.data() + biases_number + index, static_cast<size_t>(synaptic_weights_number)*sizeof(type));
325}
326
327
330
331void ProbabilisticLayer::set_decision_threshold(const type& new_decision_threshold)
332{
333#ifdef OPENNN_DEBUG
334
335 if(new_decision_threshold <= 0)
336 {
337 ostringstream buffer;
338
339 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
340 << "void set_decision_threshold(const type&) method.\n"
341 << "Decision threshold(" << decision_threshold << ") must be greater than zero.\n";
342
343 throw logic_error(buffer.str());
344 }
345 else if(new_decision_threshold >= 1)
346 {
347 ostringstream buffer;
348
349 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
350 << "void set_decision_threshold(const type&) method.\n"
351 << "Decision threshold(" << decision_threshold << ") must be less than one.\n";
352
353 throw logic_error(buffer.str());
354 }
355
356#endif
357
358 decision_threshold = new_decision_threshold;
359}
360
361
367
369{
370 layer_name = "probabilistic_layer";
371
372 layer_type = Layer::Type::Probabilistic;
373
374 const Index neurons_number = get_neurons_number();
375
376 if(neurons_number == 1)
377 {
378 activation_function = ActivationFunction::Logistic;
379 }
380 else
381 {
382 activation_function = ActivationFunction::Softmax;
383 }
384
385 decision_threshold = type(0.5);
386
387 display = true;
388}
389
390
394
396{
397#ifdef OPENNN_DEBUG
398
399 const Index neurons_number = get_neurons_number();
400
401 if(neurons_number == 1 && new_activation_function == Competitive)
402 {
403 ostringstream buffer;
404
405 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
406 << "void set_activation_function(const ActivationFunction&) method.\n"
407 << "Activation function cannot be Competitive when the number of neurons is 1.\n";
408
409 throw logic_error(buffer.str());
410 }
411
412 if(neurons_number == 1 && new_activation_function == Softmax)
413 {
414 ostringstream buffer;
415
416 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
417 << "void set_activation_function(const ActivationFunction&) method.\n"
418 << "Activation function cannot be Softmax when the number of neurons is 1.\n";
419
420 throw logic_error(buffer.str());
421 }
422
423 if(neurons_number != 1 && new_activation_function == Binary)
424 {
425 ostringstream buffer;
426
427 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
428 << "void set_activation_function(const ActivationFunction&) method.\n"
429 << "Activation function cannot be Binary when the number of neurons is greater than 1.\n";
430
431 throw logic_error(buffer.str());
432 }
433
434 if(neurons_number != 1 && new_activation_function == Logistic)
435 {
436 ostringstream buffer;
437
438 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
439 << "void set_activation_function(const ActivationFunction&) method.\n"
440 << "Activation function cannot be Logistic when the number of neurons is greater than 1.\n";
441
442 throw logic_error(buffer.str());
443 }
444
445#endif
446
447 activation_function = new_activation_function;
448}
449
450
454
455void ProbabilisticLayer::set_activation_function(const string& new_activation_function)
456{
457 if(new_activation_function == "Binary")
458 {
459 set_activation_function(ActivationFunction::Binary);
460 }
461 else if(new_activation_function == "Logistic")
462 {
463 set_activation_function(ActivationFunction::Logistic);
464 }
465 else if(new_activation_function == "Competitive")
466 {
467 set_activation_function(ActivationFunction::Competitive);
468 }
469 else if(new_activation_function == "Softmax")
470 {
471 set_activation_function(ActivationFunction::Softmax);
472 }
473 else
474 {
475 ostringstream buffer;
476
477 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
478 << "void set_activation_function(const string&) method.\n"
479 << "Unknown probabilistic method: " << new_activation_function << ".\n";
480
481 throw logic_error(buffer.str());
482 }
483}
484
485
490
491void ProbabilisticLayer::set_display(const bool& new_display)
492{
493 display = new_display;
494}
495
496
499
501{
502 biases.setConstant(value);
503}
504
505
508
510{
511 synaptic_weights.setConstant(value);
512}
513
514
515void ProbabilisticLayer::set_synaptic_weights_constant_Glorot()
516{
517 synaptic_weights.setRandom();
518}
519
520
523
525{
526 biases.setConstant(value);
527
528 synaptic_weights.setConstant(value);
529}
530
531
534
536{
537 const type minimum = type(-0.2);
538 const type maximum = type(0.2);
539
540 for(Index i = 0; i < biases.size(); i++)
541 {
542 const type random = static_cast<type>(rand()/(RAND_MAX+1.0));
543
544 biases(i) = minimum + (maximum - minimum)*random;
545 }
546
547 for(Index i = 0; i < synaptic_weights.size(); i++)
548 {
549 const type random = static_cast<type>(rand()/(RAND_MAX+1.0));
550
551 synaptic_weights(i) = minimum + (maximum - minimum)*random;
552 }
553}
554
555
556void ProbabilisticLayer::insert_parameters(const Tensor<type, 1>& parameters, const Index& )
557{
558 const Index biases_number = get_biases_number();
559 const Index synaptic_weights_number = get_synaptic_weights_number();
560
561 memcpy(biases.data() , parameters.data(), static_cast<size_t>(biases_number)*sizeof(type));
562 memcpy(synaptic_weights.data(), parameters.data() + biases_number, static_cast<size_t>(synaptic_weights_number)*sizeof(type));
563}
564
565
566void ProbabilisticLayer::calculate_combinations(const Tensor<type, 2>& inputs,
567 const Tensor<type, 2>& biases,
568 const Tensor<type, 2>& synaptic_weights,
569 Tensor<type, 2>& combinations) const
570{
571 const Index batch_samples_number = inputs.dimension(0);
572 const Index biases_number = get_neurons_number();
573
574 for(Index i = 0; i < biases_number; i++)
575 {
576 fill_n(combinations.data()+i*batch_samples_number, batch_samples_number, biases(i));
577 }
578
579 combinations.device(*thread_pool_device) += inputs.contract(synaptic_weights, A_B);
580}
581
582
583// Activations
584
585void ProbabilisticLayer::calculate_activations(const Tensor<type, 2>& combinations, Tensor<type, 2>& activations) const
586{
587#ifdef OPENNN_DEBUG
588
589 const Index dimensions_number = combinations.rank();
590
591 if(dimensions_number != 2)
592 {
593 ostringstream buffer;
594
595 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
596 << "void calculate_activations(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
597 << "Dimensions of combinations (" << dimensions_number << ") must be 2.\n";
598
599 throw logic_error(buffer.str());
600 }
601
602 const Index neurons_number = get_neurons_number();
603
604 const Index combinations_columns_number = combinations.dimension(1);
605
606 if(combinations_columns_number != neurons_number)
607 {
608 ostringstream buffer;
609
610 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
611 << "void calculate_activations(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
612 << "Number of combinations columns (" << combinations_columns_number << ") must be equal to number of neurons (" << neurons_number << ").\n";
613
614 throw logic_error(buffer.str());
615 }
616
617#endif
618
619 switch(activation_function)
620 {
621 case ActivationFunction::Binary: binary(combinations, activations); return;
622
623 case ActivationFunction::Logistic: logistic(combinations, activations); return;
624
625 case ActivationFunction::Competitive: competitive(combinations, activations); return;
626
627 case ActivationFunction::Softmax: softmax(combinations, activations); return;
628 }
629
630 ostringstream buffer;
631
632 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
633 << "void calculate_activations(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
634 << "Unknown probabilistic method.\n";
635
636 throw logic_error(buffer.str());
637}
638
639
640void ProbabilisticLayer::calculate_activations_derivatives(const Tensor<type, 2>& combinations,
641 Tensor<type, 2>& activations,
642 Tensor<type, 3>& activations_derivatives) const
643{
644#ifdef OPENNN_DEBUG
645
646 const Index neurons_number = get_neurons_number();
647
648 const Index combinations_columns_number = combinations.dimension(1);
649
650 if(combinations_columns_number != neurons_number)
651 {
652 ostringstream buffer;
653
654 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
655 << "void calculate_activations_derivatives(const Tensor<type, 2>&, Tensor<type, 2>&) const method.\n"
656 << "Number of combinations columns (" << combinations_columns_number
657 << ") must be equal to number of neurons (" << neurons_number << ").\n";
658
659 throw logic_error(buffer.str());
660 }
661
662#endif
663
664 switch(activation_function)
665 {
666 case ActivationFunction::Logistic: logistic_derivatives(combinations, activations, activations_derivatives); return;
667
668 case ActivationFunction::Softmax: softmax_derivatives(combinations, activations, activations_derivatives); return;
669
670 default: return;
671 }
672}
673
674
679
680Tensor<type, 2> ProbabilisticLayer::calculate_outputs(const Tensor<type, 2>& inputs)
681{
682 const Index batch_size = inputs.dimension(0);
683 const Index outputs_number = get_neurons_number();
684
685 Tensor<type, 2> outputs(batch_size, outputs_number);
686
687 calculate_combinations(inputs, biases, synaptic_weights, outputs);
688
689 calculate_activations(outputs, outputs);
690
691 return outputs;
692}
693
694
695void ProbabilisticLayer::forward_propagate(const Tensor<type, 2>& inputs, LayerForwardPropagation* forward_propagation)
696{
697 ProbabilisticLayerForwardPropagation* probabilistic_layer_forward_propagation
698 = static_cast<ProbabilisticLayerForwardPropagation*>(forward_propagation);
699
700 calculate_combinations(inputs, biases, synaptic_weights, probabilistic_layer_forward_propagation->combinations);
701
702 calculate_activations_derivatives(probabilistic_layer_forward_propagation->combinations,
703 probabilistic_layer_forward_propagation->activations,
704 probabilistic_layer_forward_propagation->activations_derivatives);
705}
706
707
708void ProbabilisticLayer::forward_propagate(const Tensor<type, 2>& inputs,
709 Tensor<type, 1> potential_parameters,
710 LayerForwardPropagation* forward_propagation)
711{
712 const Index neurons_number = get_neurons_number();
713 const Index inputs_number = get_inputs_number();
714
715#ifdef OPENNN_DEBUG
716
717 if(inputs_number != inputs.dimension(1))
718 {
719 ostringstream buffer;
720
721 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
722 << "void forward_propagate(const Tensor<type, 2>&, Tensor<type, 1>&, ForwardPropagation&) method.\n"
723 << "Number of inputs columns (" << inputs.dimension(1) << ") must be equal to number of inputs ("
724 << inputs_number << ").\n";
725
726 throw logic_error(buffer.str());
727 }
728
729#endif
730
731 ProbabilisticLayerForwardPropagation* probabilistic_layer_forward_propagation
732 = static_cast<ProbabilisticLayerForwardPropagation*>(forward_propagation);
733
734 const TensorMap<Tensor<type, 2>> potential_biases(potential_parameters.data(), neurons_number, 1);
735
736 const TensorMap<Tensor<type, 2>> potential_synaptic_weights(potential_parameters.data()+neurons_number,
737 inputs_number, neurons_number);
738
739 calculate_combinations(inputs, potential_biases, potential_synaptic_weights, probabilistic_layer_forward_propagation->combinations);
740
741 calculate_activations_derivatives(probabilistic_layer_forward_propagation->combinations,
742 probabilistic_layer_forward_propagation->activations,
743 probabilistic_layer_forward_propagation->activations_derivatives);
744}
745
746
747// Gradient methods
748
749void ProbabilisticLayer::calculate_error_gradient(const Tensor<type, 2>& inputs,
750 LayerForwardPropagation* forward_propagation,
751 LayerBackPropagation* back_propagation) const
752{
753 ProbabilisticLayerForwardPropagation* probabilistic_layer_forward_propagation =
754 static_cast<ProbabilisticLayerForwardPropagation*>(forward_propagation);
755
756 ProbabilisticLayerBackPropagation* probabilistic_layer_back_propagation =
757 static_cast<ProbabilisticLayerBackPropagation*>(back_propagation);
758
759 const Index samples_number = inputs.dimension(0);
760 const Index neurons_number = get_neurons_number();
761
762 if(neurons_number == 1) // Binary gradient
763 {
764 TensorMap< Tensor<type, 2> > activations_derivatives(probabilistic_layer_forward_propagation->activations_derivatives.data(), samples_number, neurons_number);
765
766 probabilistic_layer_back_propagation->biases_derivatives.device(*thread_pool_device) =
767 (probabilistic_layer_back_propagation->delta * activations_derivatives).sum(Eigen::array<Index, 1>({0}));
768
769 probabilistic_layer_back_propagation->synaptic_weights_derivatives.device(*thread_pool_device) =
770 inputs.contract((probabilistic_layer_back_propagation->delta * activations_derivatives), AT_B);
771 }
772 else // Multiple gradient
773 {
774 if(activation_function == ActivationFunction::Softmax)
775 {
776 const Index step = neurons_number * neurons_number;
777
778 for(Index i = 0; i < samples_number; i++)
779 {
780 probabilistic_layer_back_propagation->delta_row = probabilistic_layer_back_propagation->delta.chip(i,0);
781
782 TensorMap< Tensor<type, 2> > activations_derivatives_matrix(probabilistic_layer_forward_propagation->activations_derivatives.data() + i*step,
783 neurons_number, neurons_number);
784
785 probabilistic_layer_back_propagation->error_combinations_derivatives.chip(i,0) =
786 probabilistic_layer_back_propagation->delta_row.contract(activations_derivatives_matrix, AT_B);
787 }
788
789 probabilistic_layer_back_propagation->biases_derivatives.device(*thread_pool_device) =
790 (probabilistic_layer_back_propagation->error_combinations_derivatives).sum(Eigen::array<Index, 1>({0}));
791
792 probabilistic_layer_back_propagation->synaptic_weights_derivatives.device(*thread_pool_device) =
793 inputs.contract(probabilistic_layer_back_propagation->error_combinations_derivatives, AT_B);
794 }
795 else
796 {
797 TensorMap< Tensor<type, 2> > activations_derivatives(probabilistic_layer_forward_propagation->activations_derivatives.data(), samples_number, neurons_number);
798
799 probabilistic_layer_back_propagation->biases_derivatives.device(*thread_pool_device) =
800 (probabilistic_layer_back_propagation->delta*activations_derivatives).sum(Eigen::array<Index, 1>({0}));
801
802 probabilistic_layer_back_propagation->synaptic_weights_derivatives.device(*thread_pool_device) =
803 inputs.contract((probabilistic_layer_back_propagation->delta*activations_derivatives), AT_B);
804 }
805 }
806}
807
808
809void ProbabilisticLayer::insert_gradient(LayerBackPropagation* back_propagation, const Index& index, Tensor<type, 1>& gradient) const
810{
811 const Index biases_number = get_biases_number();
812 const Index synaptic_weights_number = get_synaptic_weights_number();
813
814 const ProbabilisticLayerBackPropagation* probabilistic_layer_back_propagation =
815 static_cast<ProbabilisticLayerBackPropagation*>(back_propagation);
816
817 memcpy(gradient.data() + index,
818 probabilistic_layer_back_propagation->biases_derivatives.data(),
819 static_cast<size_t>(biases_number)*sizeof(type));
820
821 memcpy(gradient.data() + index + biases_number,
822 probabilistic_layer_back_propagation->synaptic_weights_derivatives.data(),
823 static_cast<size_t>(synaptic_weights_number)*sizeof(type));
824}
825
826
827void ProbabilisticLayer::calculate_squared_errors_Jacobian_lm(const Tensor<type, 2>& inputs,
828 LayerForwardPropagation* forward_propagation,
829 LayerBackPropagationLM* back_propagation)
830{
831 ProbabilisticLayerForwardPropagation* probabilistic_layer_forward_propagation =
832 static_cast<ProbabilisticLayerForwardPropagation*>(forward_propagation);
833
834 ProbabilisticLayerBackPropagationLM* probabilistic_layer_back_propagation_lm =
835 static_cast<ProbabilisticLayerBackPropagationLM*>(back_propagation);
836
837 const Index samples_number = inputs.dimension(0);
838
839 const Index inputs_number = get_inputs_number();
840 const Index neurons_number = get_neurons_number();
841
842 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian.setZero();
843
844 if(activation_function == ActivationFunction::Softmax)
845 {
846 Index parameter_index = 0;
847
848 for(Index sample = 0; sample < samples_number; sample++)
849 {
850 parameter_index = 0;
851
852 for(Index neuron = 0; neuron < neurons_number; neuron++)
853 {
854 for(Index input = 0; input < inputs_number; input++)
855 {
856 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian(sample, neurons_number+parameter_index) =
857 probabilistic_layer_back_propagation_lm->error_combinations_derivatives(sample, neuron) *
858 inputs(sample, input);
859
860 parameter_index++;
861 }
862
863 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian(sample, neuron) =
864 probabilistic_layer_back_propagation_lm->error_combinations_derivatives(sample, neuron);
865 }
866 }
867 }
868 else
869 {
870 Index parameter_index = 0;
871
872 for(Index sample = 0; sample < samples_number; sample++)
873 {
874 parameter_index = 0;
875
876 for(Index neuron = 0; neuron < neurons_number; neuron++)
877 {
878 for(Index input = 0; input < inputs_number; input++)
879 {
880 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian(sample, neurons_number+parameter_index) =
881 probabilistic_layer_back_propagation_lm->delta(sample, neuron) *
882 probabilistic_layer_forward_propagation->activations_derivatives(sample, neuron, 0) *
883 inputs(sample, input);
884
885 parameter_index++;
886 }
887
888 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian(sample, neuron) =
889 probabilistic_layer_back_propagation_lm->delta(sample, neuron) *
890 probabilistic_layer_forward_propagation->activations_derivatives(sample, neuron, 0);
891 }
892 }
893 }
894}
895
896
897void ProbabilisticLayer::insert_squared_errors_Jacobian_lm(LayerBackPropagationLM * back_propagation ,
898 const Index & index,
899 Tensor<type, 2>& squared_errors_Jacobian) const
900{
901 ProbabilisticLayerBackPropagationLM* probabilistic_layer_back_propagation_lm =
902 static_cast<ProbabilisticLayerBackPropagationLM*>(back_propagation);
903
904 const Index batch_samples_number = probabilistic_layer_back_propagation_lm->squared_errors_Jacobian.dimension(0);
905 const Index layer_parameters_number = get_parameters_number();
906
907 memcpy(squared_errors_Jacobian.data() + index,
908 probabilistic_layer_back_propagation_lm->squared_errors_Jacobian.data(),
909 static_cast<size_t>(layer_parameters_number*batch_samples_number)*sizeof(type));
910}
911
912
915
917{
918 ostringstream buffer;
919
920 // Probabilistic layer
921
922 file_stream.OpenElement("ProbabilisticLayer");
923
924 // Inputs number
925
926 file_stream.OpenElement("InputsNumber");
927
928 buffer.str("");
929 buffer << get_inputs_number();
930
931 file_stream.PushText(buffer.str().c_str());
932
933 file_stream.CloseElement();
934
935 // Neurons number
936
937 file_stream.OpenElement("NeuronsNumber");
938
939 buffer.str("");
940 buffer << get_neurons_number();
941
942 file_stream.PushText(buffer.str().c_str());
943
944 file_stream.CloseElement();
945
946 // Activation function
947
948 file_stream.OpenElement("ActivationFunction");
949
950 file_stream.PushText(write_activation_function().c_str());
951
952 file_stream.CloseElement();
953
954 // Parameters
955
956 file_stream.OpenElement("Parameters");
957
958 buffer.str("");
959
960 const Tensor<type, 1> parameters = get_parameters();
961 const Index parameters_size = parameters.size();
962
963 for(Index i = 0; i < parameters_size; i++)
964 {
965 buffer << parameters(i);
966
967 if(i != (parameters_size-1)) buffer << " ";
968 }
969
970 file_stream.PushText(buffer.str().c_str());
971
972 file_stream.CloseElement();
973
974 // Decision threshold
975
976 file_stream.OpenElement("DecisionThreshold");
977
978 buffer.str("");
979 buffer << decision_threshold;
980
981 file_stream.PushText(buffer.str().c_str());
982
983 file_stream.CloseElement();
984
985 // Probabilistic layer (end tag)
986
987 file_stream.CloseElement();
988}
989
990
993
995{
996 ostringstream buffer;
997
998 // Probabilistic layer
999
1000 const tinyxml2::XMLElement* probabilistic_layer_element = document.FirstChildElement("ProbabilisticLayer");
1001
1002 if(!probabilistic_layer_element)
1003 {
1004 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1005 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1006 << "Probabilistic layer element is nullptr.\n";
1007
1008 throw logic_error(buffer.str());
1009 }
1010
1011 // Inputs number
1012
1013 const tinyxml2::XMLElement* inputs_number_element = probabilistic_layer_element->FirstChildElement("InputsNumber");
1014
1015 if(!inputs_number_element)
1016 {
1017 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1018 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1019 << "Inputs number element is nullptr.\n";
1020
1021 throw logic_error(buffer.str());
1022 }
1023
1024 Index new_inputs_number;
1025
1026 if(inputs_number_element->GetText())
1027 {
1028 new_inputs_number = static_cast<Index>(stoi(inputs_number_element->GetText()));
1029 }
1030
1031 // Neurons number
1032
1033 const tinyxml2::XMLElement* neurons_number_element = probabilistic_layer_element->FirstChildElement("NeuronsNumber");
1034
1035 if(!inputs_number_element)
1036 {
1037 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1038 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1039 << "Neurons number element is nullptr.\n";
1040
1041 throw logic_error(buffer.str());
1042 }
1043
1044 Index new_neurons_number;
1045
1046 if(neurons_number_element->GetText())
1047 {
1048 new_neurons_number = static_cast<Index>(stoi(neurons_number_element->GetText()));
1049 }
1050
1051 set(new_inputs_number, new_neurons_number);
1052
1053 // Activation function
1054
1055 const tinyxml2::XMLElement* activation_function_element = probabilistic_layer_element->FirstChildElement("ActivationFunction");
1056
1057 if(!activation_function_element)
1058 {
1059 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1060 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1061 << "Activation function element is nullptr.\n";
1062
1063 throw logic_error(buffer.str());
1064 }
1065
1066 if(activation_function_element->GetText())
1067 {
1068 set_activation_function(activation_function_element->GetText());
1069 }
1070
1071 // Parameters
1072
1073 const tinyxml2::XMLElement* parameters_element = probabilistic_layer_element->FirstChildElement("Parameters");
1074
1075 if(!parameters_element)
1076 {
1077 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1078 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1079 << "Parameters element is nullptr.\n";
1080
1081 throw logic_error(buffer.str());
1082 }
1083
1084 if(parameters_element->GetText())
1085 {
1086 const string parameters_string = parameters_element->GetText();
1087
1088 set_parameters(to_type_vector(parameters_string, ' '));
1089 }
1090
1091 // Decision threshold
1092
1093 const tinyxml2::XMLElement* decision_threshold_element = probabilistic_layer_element->FirstChildElement("DecisionThreshold");
1094
1095 if(!decision_threshold_element)
1096 {
1097 buffer << "OpenNN Exception: ProbabilisticLayer class.\n"
1098 << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
1099 << "Decision threshold element is nullptr.\n";
1100
1101 throw logic_error(buffer.str());
1102 }
1103
1104 if(decision_threshold_element->GetText())
1105 {
1106 set_decision_threshold(static_cast<type>(atof(decision_threshold_element->GetText())));
1107 }
1108
1109 // Display
1110
1111 const tinyxml2::XMLElement* display_element = probabilistic_layer_element->FirstChildElement("Display");
1112
1113 if(display_element)
1114 {
1115 const string new_display_string = display_element->GetText();
1116
1117 try
1118 {
1119 set_display(new_display_string != "0");
1120 }
1121 catch(const logic_error& e)
1122 {
1123 cerr << e.what() << endl;
1124 }
1125 }
1126}
1127
1128
1132
1133string ProbabilisticLayer::write_binary_expression(const Tensor<string, 1>& inputs_names, const Tensor<string, 1>& outputs_names) const
1134{
1135 ostringstream buffer;
1136
1137 buffer.str("");
1138
1139 for(Index j = 0; j < outputs_names.size(); j++)
1140 {
1141 buffer << outputs_names(j) << " = binary(" << inputs_names(j) << ");\n";
1142 }
1143 return buffer.str();
1144}
1145
1146
1150
1151string ProbabilisticLayer::write_logistic_expression(const Tensor<string, 1>& inputs_names,
1152 const Tensor<string, 1>& outputs_names) const
1153{
1154 ostringstream buffer;
1155
1156 for(Index j = 0; j < outputs_names.size(); j++)
1157 {
1158 buffer << outputs_names(j) << " = logistic(" << inputs_names(j) << ");\n";
1159 }
1160 return buffer.str();
1161}
1162
1163
1167
1168string ProbabilisticLayer::write_competitive_expression(const Tensor<string, 1>& inputs_names, const Tensor<string, 1>& outputs_names) const
1169{
1170 ostringstream buffer;
1171
1172 for(Index j = 0; j < outputs_names.size(); j++)
1173 {
1174 buffer << outputs_names(j) << " = competitive(" << inputs_names(j) << ");\n";
1175 }
1176 return buffer.str();
1177}
1178
1179
1183
1184string ProbabilisticLayer::write_softmax_expression(const Tensor<string, 1>& inputs_names, const Tensor<string, 1>& outputs_names) const
1185{
1186 ostringstream buffer;
1187
1188 for(Index j = 0; j < outputs_names.size(); j++)
1189 {
1190 buffer << outputs_names(j) << " = softmax(" << inputs_names(j) << ");\n";
1191 }
1192
1193 return buffer.str();
1194}
1195
1196
1200
1201string ProbabilisticLayer::write_no_probabilistic_expression(const Tensor<string, 1>& inputs_names,
1202 const Tensor<string, 1>& outputs_names) const
1203{
1204 ostringstream buffer;
1205
1206 for(Index j = 0; j < outputs_names.size(); j++)
1207 {
1208 buffer << outputs_names(j) << " = (" << inputs_names(j) << ");\n";
1209 }
1210 return buffer.str();
1211}
1212
1213
1214string ProbabilisticLayer::write_combinations_c() const
1215{
1216 ostringstream buffer;
1217
1218 const Index inputs_number = get_inputs_number();
1219 const Index neurons_number = get_neurons_number();
1220
1221 buffer << "\tvector<float> combinations(" << neurons_number << ");\n" << endl;
1222
1223 for(Index i = 0; i < neurons_number; i++)
1224 {
1225 buffer << "\tcombinations[" << i << "] = " << biases(i);
1226
1227 for(Index j = 0; j < inputs_number; j++)
1228 {
1229 buffer << " +" << synaptic_weights(j, i) << "*inputs[" << j << "]";
1230 }
1231
1232 buffer << ";" << endl;
1233 }
1234
1235 return buffer.str();
1236}
1237
1238
1240{
1241 ostringstream buffer;
1242
1243 const Index neurons_number = get_neurons_number();
1244
1245 buffer << "\n\tvector<float> activations(" << neurons_number << ");\n" << endl;
1246
1247 for(Index i = 0; i < neurons_number; i++)
1248 {
1249 switch(activation_function)
1250 {
1251 case ActivationFunction::Binary:
1252 buffer << "\tactivations[" << i << "] = combinations[" << i << "] < 0.5 ? 0.0 : 1.0;\n";
1253 break;
1254
1255 case ActivationFunction::Logistic:
1256 buffer << "\tactivations[" << i << "] = 1.0/(1.0 + exp(-combinations[" << i << "]));\n";
1257 break;
1258
1259 case ActivationFunction::Competitive:
1261
1262 buffer << "";
1263
1264 break;
1265
1266 case ActivationFunction::Softmax:
1267
1268 if(i == 0)
1269 {
1270 buffer << "\tfloat sum = 0;\n" << endl;
1271
1272 buffer << "\tsum = ";
1273
1274 for(Index i = 0; i < neurons_number; i++)
1275 {
1276 buffer << "exp(combinations[" << i << "])";
1277
1278 if(i != neurons_number-1) buffer << " + ";
1279 }
1280
1281 buffer << ";\n" << endl;
1282
1283 for(Index i = 0; i < neurons_number; i++)
1284 {
1285 buffer << "\tactivations[" << i << "] = exp(combinations[" << i << "])/sum;\n";
1286 }
1287 }
1288 break;
1289 }
1290 }
1291
1292 return buffer.str();
1293}
1294
1295
1296string ProbabilisticLayer::write_combinations_python() const
1297{
1298 ostringstream buffer;
1299
1300 const Index inputs_number = get_inputs_number();
1301 const Index neurons_number = get_neurons_number();
1302
1303 buffer << "\t\tcombinations = [None] * "<<neurons_number<<"\n" << endl;
1304
1305 for(Index i = 0; i < neurons_number; i++)
1306 {
1307 buffer << "\t\tcombinations[" << i << "] = " << biases(i);
1308
1309 for(Index j = 0; j < inputs_number; j++)
1310 {
1311 buffer << " +" << synaptic_weights(j, i) << "*inputs[" << j << "]";
1312 }
1313
1314 buffer << " " << endl;
1315 }
1316
1317 buffer << "\t\t" << endl;
1318
1319 return buffer.str();
1320}
1321
1322
1323string ProbabilisticLayer::write_activations_python() const
1324{
1325 ostringstream buffer;
1326
1327 const Index neurons_number = get_neurons_number();
1328
1329 buffer << "\t\tactivations = [None] * "<<neurons_number<<"\n" << endl;
1330
1331 for(Index i = 0; i < neurons_number; i++)
1332 {
1333 switch(activation_function)
1334 {
1335 case ActivationFunction::Binary:
1336 buffer << "\t\tactivations[" << i << "] = 0.0 if combinations[" << i << "] < 0.5 else 1.0\n";
1337 break;
1338
1339 case ActivationFunction::Logistic:
1340 buffer << "\t\tactivations[" << i << "] = 1.0/(1.0 + np.exp(-combinations[" << i << "]));\n";
1341 break;
1342
1343 case ActivationFunction::Competitive:
1344
1345 if(i == 0)
1346 {
1347 buffer << "\t\tfor i, value in enumerate(combinations):"<<endl;
1348
1349 buffer <<"\t\t\tif(max(combinations) == value):"<<endl;
1350
1351 buffer <<"\t\t\t\tactivations[i] = 1"<<endl;
1352
1353 buffer <<"\t\t\telse:"<<endl;
1354
1355 buffer <<"\t\t\t\tactivations[i] = 0"<<endl;
1356 }
1357
1358 break;
1359
1360 case ActivationFunction::Softmax:
1361
1362 if(i == 0)
1363 {
1364 buffer << "\t\tsum_ = 0;\n" << endl;
1365
1366 buffer << "\t\tsum_ = ";
1367
1368 for(Index i = 0; i < neurons_number; i++)
1369 {
1370 buffer << "\tnp.exp(combinations[" << i << "])";
1371
1372 if(i != neurons_number-1) buffer << " + ";
1373 }
1374
1375 buffer << ";\n" << endl;
1376
1377 for(Index i = 0; i < neurons_number; i++)
1378 {
1379 buffer << "\t\tactivations[" << i << "] = np.exp(combinations[" << i << "])/sum_;\n";
1380 }
1381
1382 }
1383 break;
1384 }
1385 }
1386
1387 return buffer.str();
1388}
1389
1390
1391string ProbabilisticLayer::write_combinations(const Tensor<string, 1>& inputs_names) const
1392{
1393 ostringstream buffer;
1394
1395 const Index inputs_number = get_inputs_number();
1396 const Index neurons_number = get_neurons_number();
1397
1398 for(Index i = 0; i < neurons_number; i++)
1399 {
1400 buffer << "probabilistic_layer_combinations_" << to_string(i) << " = " << biases(i);
1401
1402 for(Index j = 0; j < inputs_number; j++)
1403 {
1404 buffer << " +" << synaptic_weights(j, i) << "*" << inputs_names(j) << "";
1405 }
1406
1407 buffer << " " << endl;
1408 }
1409
1410 buffer << "\t" << endl;
1411
1412 return buffer.str();
1413}
1414
1415
1416string ProbabilisticLayer::write_activations(const Tensor<string, 1>& outputs_names) const
1417{
1418 ostringstream buffer;
1419
1420 const Index neurons_number = get_neurons_number();
1421
1422 for(Index i = 0; i < neurons_number; i++)
1423 {
1424 switch(activation_function)
1425 {
1426 case ActivationFunction::Binary:
1427 {
1428 buffer << "\tif" << "probabilistic_layer_combinations_" << to_string(i) << " < 0.5, " << outputs_names(i) << "= 0.0. Else " << outputs_names(i) << " = 1.0\n";
1429 }
1430 break;
1431
1432 case ActivationFunction::Logistic:
1433 {
1434 buffer << outputs_names(i) << " = 1.0/(1.0 + exp(-" << "probabilistic_layer_combinations_" << to_string(i) << ");\n";
1435 }
1436 break;
1437
1438 case ActivationFunction::Competitive:
1439 if(i == 0)
1440 {
1441 buffer << "\tfor each probabilistic_layer_combinations_i:"<<endl;
1442
1443 buffer <<"\t\tif probabilistic_layer_combinations_i is equal to max(probabilistic_layer_combinations_i):"<<endl;
1444
1445 buffer <<"\t\t\tactivations[i] = 1"<<endl;
1446
1447 buffer <<"\t\telse:"<<endl;
1448
1449 buffer <<"\t\t\tactivations[i] = 0"<<endl;
1450 }
1451
1452 break;
1453
1454 case ActivationFunction::Softmax:
1455
1456 if(i == 0)
1457 {
1458 buffer << "sum = ";
1459
1460 for(Index i = 0; i < neurons_number; i++)
1461 {
1462 buffer << "exp(probabilistic_layer_combinations_" << to_string(i) << ")";
1463
1464 if(i != neurons_number-1) buffer << " + ";
1465 }
1466
1467 buffer << ";\n" << endl;
1468
1469 for(Index i = 0; i < neurons_number; i++)
1470 {
1471 buffer << outputs_names(i) << " = exp(probabilistic_layer_combinations_" << to_string(i) <<")/sum;\n";
1472 }
1473
1474 }
1475 break;
1476 }
1477 }
1478
1479 return buffer.str();
1480}
1481
1482
1483string ProbabilisticLayer::write_expression_c() const
1484{
1485 ostringstream buffer;
1486
1487 buffer << "vector<float> " << layer_name << "(const vector<float>& inputs)\n{" << endl;
1488
1489 buffer << write_combinations_c();
1490
1491 buffer << write_activations_c();
1492
1493 buffer << "\n\treturn activations;\n}" << endl;
1494
1495 return buffer.str();
1496}
1497
1498
1499string ProbabilisticLayer::write_expression_python() const
1500{
1501 ostringstream buffer;
1502
1503 buffer << "\tdef " << layer_name << "(self, inputs):\n" << endl;
1504
1505 buffer << write_combinations_python();
1506
1507 buffer << write_activations_python();
1508
1509 buffer << "\n\t\treturn activations;\n" << endl;
1510
1511 return buffer.str();
1512}
1513
1514
1515string ProbabilisticLayer::write_expression(const Tensor<string, 1>& inputs_names,
1516 const Tensor<string, 1>& outputs_names) const
1517{
1518 ostringstream buffer;
1519
1520 buffer << write_combinations(inputs_names);
1521
1522 buffer << write_activations(outputs_names);
1523
1524 return buffer.str();
1525}
1526
1527
1528
1529}
1530
1531// OpenNN: Open Neural Networks Library.
1532// Copyright(C) 2005-2021 Artificial Intelligence Techniques, SL.
1533//
1534// This library is free software; you can redistribute it and/or
1535// modify it under the terms of the GNU Lesser General Public
1536// License as published by the Free Software Foundation; either
1537// version 2.1 of the License, or any later version.
1538//
1539// This library is distributed in the hope that it will be useful,
1540// but WITHOUT ANY WARRANTY; without even the implied warranty of
1541// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1542// Lesser General Public License for more details.
1543
1544// You should have received a copy of the GNU Lesser General Public
1545// License along with this library; if not, write to the Free Software
1546// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
string layer_name
Layer name.
Definition: layer.h:179
Type layer_type
Layer type.
Definition: layer.h:183
This class represents a layer of probabilistic neurons.
void set_parameters_constant(const type &)
string write_softmax_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
Index get_synaptic_weights_number() const
Returns the number of layer's synaptic weights.
const bool & get_display() const
Index get_inputs_number() const
Returns the number of inputs.
const ActivationFunction & get_activation_function() const
string write_activation_function_text() const
void set_biases_constant(const type &)
string write_logistic_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
void from_XML(const tinyxml2::XMLDocument &)
bool display
Display messages to screen.
ActivationFunction
Enumeration of available methods for interpreting variables as probabilities.
void set_activation_function(const ActivationFunction &)
const Tensor< type, 2 > & get_biases() const
Returns the biases of the layer.
const type & get_decision_threshold() const
Returns the decision threshold.
string write_no_probabilistic_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
Tensor< type, 2 > calculate_outputs(const Tensor< type, 2 > &)
string write_competitive_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
string write_binary_expression(const Tensor< string, 1 > &, const Tensor< string, 1 > &) const
void set_decision_threshold(const type &)
void set_synaptic_weights_constant(const type &)
Tensor< type, 2 > synaptic_weights
This matrix containing conection strengths from a layer's inputs to its neurons.
void write_XML(tinyxml2::XMLPrinter &) const
const Tensor< type, 2 > & get_synaptic_weights() const
Returns the synaptic weights of the layer.
Index get_parameters_number() const
Returns the number of parameters(biases and synaptic weights) of the layer.
ActivationFunction activation_function
Activation function variable.
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