1 //   OpenNN: Open Neural Networks Library
2 //   www.opennn.net
3 //
4 //   W E I G T H E D   S Q U A R E D   E R R O R   C L A S S
5 //
6 //   Artificial Intelligence Techniques SL
7 //   artelnics@artelnics.com
8 
9 #include "weighted_squared_error.h"
10 
11 namespace OpenNN
12 {
13 
14 /// Default constructor.
15 /// It creates a weighted squared error term not associated to any
16 /// neural network and not measured on any data set.
17 /// It also initializes all the rest of class members to their default values.
18 
WeightedSquaredError()19 WeightedSquaredError::WeightedSquaredError() : LossIndex()
20 {
21     set_default();
22 }
23 
24 
25 /// Neural network and data set constructor.
26 /// It creates a weighted squared error term object associated to a
27 /// neural network and measured on a data set.
28 /// It also initializes all the rest of class members to their default values.
29 /// @param new_neural_network_pointer Pointer to a neural network object.
30 /// @param new_data_set_pointer Pointer to a data set object.
31 
WeightedSquaredError(NeuralNetwork * new_neural_network_pointer,DataSet * new_data_set_pointer)32 WeightedSquaredError::WeightedSquaredError(NeuralNetwork* new_neural_network_pointer, DataSet* new_data_set_pointer)
33     : LossIndex(new_neural_network_pointer, new_data_set_pointer)
34 {
35     set_default();
36 }
37 
38 
39 /// Destructor.
40 
~WeightedSquaredError()41 WeightedSquaredError::~WeightedSquaredError()
42 {
43 }
44 
45 
46 /// Returns the weight of the positives.
47 
get_positives_weight() const48 type WeightedSquaredError::get_positives_weight() const
49 {
50     return positives_weight;
51 }
52 
53 
54 /// Returns the weight of the negatives.
55 
get_negatives_weight() const56 type WeightedSquaredError::get_negatives_weight() const
57 {
58     return negatives_weight;
59 }
60 
61 
get_normalizaton_coefficient() const62 type WeightedSquaredError::get_normalizaton_coefficient() const
63 {
64     return normalization_coefficient;
65 }
66 
67 
68 /// Set the default values for the object.
69 
set_default()70 void WeightedSquaredError::set_default()
71 {
72     if(has_data_set() && data_set_pointer->has_data())
73     {
74         set_weights();
75 
76         set_normalization_coefficient();
77     }
78     else
79     {
80         negatives_weight = -1.0;
81         positives_weight = -1.0;
82 
83         normalization_coefficient = -1.0;
84     }
85 }
86 
87 
88 /// Set a new weight for the positives values.
89 /// @param new_positives_weight New weight for the positives.
90 
set_positives_weight(const type & new_positives_weight)91 void WeightedSquaredError::set_positives_weight(const type& new_positives_weight)
92 {
93     positives_weight = new_positives_weight;
94 }
95 
96 
97 /// Set a new weight for the negatives values.
98 /// @param new_negatives_weight New weight for the negatives.
99 
set_negatives_weight(const type & new_negatives_weight)100 void WeightedSquaredError::set_negatives_weight(const type& new_negatives_weight)
101 {
102     negatives_weight = new_negatives_weight;
103 }
104 
105 
106 /// Set new weights for the positives and negatives values.
107 /// @param new_positives_weight New weight for the positives.
108 /// @param new_negatives_weight New weight for the negatives.
109 
set_weights(const type & new_positives_weight,const type & new_negatives_weight)110 void WeightedSquaredError::set_weights(const type& new_positives_weight, const type& new_negatives_weight)
111 {
112     positives_weight = new_positives_weight;
113     negatives_weight = new_negatives_weight;
114 }
115 
116 
117 /// Calculates of the weights for the positives and negatives values with the data of the data set.
118 
set_weights()119 void WeightedSquaredError::set_weights()
120 {
121     // Control sentence
122 
123 #ifdef __OPENNN_DEBUG__
124 
125 //    check();
126 
127 #endif
128 
129     if(data_set_pointer->get_target_variables_number() == 0)
130     {
131         positives_weight = 1.0;
132         negatives_weight = 1.0;
133     }
134     else if(data_set_pointer && data_set_pointer->get_target_columns()(0).type == DataSet::Binary)
135     {
136         const Tensor<Index, 1> target_distribution = data_set_pointer->calculate_target_distribution();
137 
138         const Index negatives = target_distribution[0];
139         const Index positives = target_distribution[1];
140 
141         if(positives == 0 || negatives == 0)
142         {
143             positives_weight = 1.0;
144             negatives_weight = 1.0;
145 
146             return;
147         }
148 
149         negatives_weight = 1.0;
150         positives_weight = static_cast<type>(negatives)/static_cast<type>(positives);
151     }
152     else
153     {
154         positives_weight = 1.0;
155         negatives_weight = 1.0;
156     }
157 
158 }
159 
160 
161 /// Calculates of the normalization coefficient with the data of the data set.
162 
set_normalization_coefficient()163 void WeightedSquaredError::set_normalization_coefficient()
164 {
165     // Control sentence
166 
167 #ifdef __OPENNN_DEBUG__
168 
169     check();
170 
171 #endif
172 
173     if(data_set_pointer->get_target_columns().size()==0)
174     {
175         normalization_coefficient = static_cast<type>(1);
176     }
177     else if(data_set_pointer && data_set_pointer->get_target_columns()(0).type == DataSet::Binary)
178     {
179         const Tensor<Index, 1> target_variables_indices = data_set_pointer->get_target_variables_indices();
180 
181         const Index negatives = data_set_pointer->calculate_used_negatives(target_variables_indices[0]);
182 
183         normalization_coefficient = negatives*negatives_weight*static_cast<type>(0.5);
184     }
185     else
186     {
187         normalization_coefficient = static_cast<type>(1);
188     }
189 }
190 
191 
192 ///
193 /// \brief set_data_set_pointer
194 /// \param new_data_set_pointer
195 
set_data_set_pointer(DataSet * new_data_set_pointer)196 void WeightedSquaredError::set_data_set_pointer(DataSet* new_data_set_pointer)
197 {
198     data_set_pointer = new_data_set_pointer;
199 
200     set_weights();
201 
202     set_normalization_coefficient();
203 }
204 
205 
weighted_sum_squared_error(const Tensor<type,2> & x,const Tensor<type,2> & y) const206 type WeightedSquaredError::weighted_sum_squared_error(const Tensor<type, 2> & x, const Tensor<type, 2> & y) const
207 {
208 #ifdef __OPENNN_DEBUG__
209 
210     const Index rows_number = x.dimension(0);
211     const Index columns_number = x.dimension(1);
212 
213     const Index other_rows_number = y.dimension(0);
214 
215     if(other_rows_number != rows_number)
216     {
217         ostringstream buffer;
218 
219         buffer << "OpenNN Exception: Metrics functions.\n"
220                << "double minkowski_error(const Matrix<double>&, const double&) method.\n"
221                << "Other number of rows must be equal to this number of rows.\n";
222 
223         throw logic_error(buffer.str());
224     }
225 
226     const Index other_columns_number = y.dimension(1);
227 
228     if(other_columns_number != columns_number)
229     {
230         ostringstream buffer;
231 
232         buffer << "OpenNN Exception: Metrics functions.\n"
233                << "double minkowski_error(const Matrix<double>&, const double&) method.\n"
234                << "Other number of columns must be equal to this number of columns.\n";
235 
236         throw logic_error(buffer.str());
237     }
238 
239 #endif
240 
241     const Tensor<bool, 2> if_sentence = y == y.constant(1);
242     const Tensor<bool, 2> else_sentence = y == y.constant(0);
243 
244     Tensor<type, 2> f_1(x.dimension(0), x.dimension(1));
245 
246     Tensor<type, 2> f_2(x.dimension(0), x.dimension(1));
247 
248     Tensor<type, 2> f_3(x.dimension(0), x.dimension(1));
249 
250     f_1 = (x - y).square()*positives_weight;
251 
252     f_2 = (x - y).square()*negatives_weight;
253 
254     f_3 = x.constant(0);
255 
256     Tensor<type, 0> weighted_sum_squared_error = (if_sentence.select(f_1, else_sentence.select(f_2, f_3))).sum();
257 
258     return weighted_sum_squared_error(0);
259 }
260 
261 
calculate_error(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,LossIndex::BackPropagation & back_propagation) const262 void WeightedSquaredError::calculate_error(const DataSet::Batch& batch,
263                      const NeuralNetwork::ForwardPropagation& forward_propagation,
264                      LossIndex::BackPropagation& back_propagation) const
265 {
266     const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
267 
268     const type error = weighted_sum_squared_error(forward_propagation.layers[trainable_layers_number-1].activations_2d,
269                                                                  batch.targets_2d);
270 
271     const Index batch_samples_number = batch.samples_number;
272     const Index total_samples_number = data_set_pointer->get_samples_number();
273 
274     back_propagation.error = error/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
275 
276     return;
277 }
278 
279 
calculate_error_terms(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,SecondOrderLoss & second_order_loss) const280 void WeightedSquaredError::calculate_error_terms(const DataSet::Batch& batch,
281                                                  const NeuralNetwork::ForwardPropagation& forward_propagation,
282                                                  SecondOrderLoss& second_order_loss) const
283 {
284     const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
285 
286     const Index batch_samples_number = batch.get_samples_number();
287     const Index total_samples_number = data_set_pointer->get_samples_number();
288 
289     const Tensor<type, 2>& outputs = forward_propagation.layers(trainable_layers_number-1).activations_2d;
290     const Tensor<type, 2>& targets = batch.targets_2d;
291 
292     const Eigen::array<int, 1> rows_sum = {Eigen::array<int, 1>({1})};
293 
294     const Tensor<bool, 2> if_sentence = outputs == outputs.constant(1);
295 
296     Tensor<type, 2> f_1(outputs.dimension(0), outputs.dimension(1));
297 
298     Tensor<type, 2> f_2(outputs.dimension(0), outputs.dimension(1));
299 
300     f_1 = ((outputs - targets))*positives_weight;
301 
302     f_2 = ((outputs - targets))*negatives_weight;
303 
304     second_order_loss.error_terms = ((if_sentence.select(f_1, f_2)).sum(rows_sum).square()).sqrt();
305 
306     Tensor<type, 0> error;
307     error.device(*thread_pool_device) = second_order_loss.error_terms.contract(second_order_loss.error_terms, AT_B);
308 
309     const type coefficient = ((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
310 
311     second_order_loss.error = error()/coefficient;
312 }
313 
314 
315 // Gradient methods
316 
calculate_output_gradient(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,BackPropagation & back_propagation) const317 void WeightedSquaredError::calculate_output_gradient(const DataSet::Batch& batch,
318                                const NeuralNetwork::ForwardPropagation& forward_propagation,
319                                BackPropagation& back_propagation) const
320 {
321      #ifdef __OPENNN_DEBUG__
322 
323      check();
324 
325      #endif
326 
327 
328      const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
329 
330      const Tensor<type, 2>& outputs = forward_propagation.layers(trainable_layers_number-1).activations_2d;
331      const Tensor<type, 2>& targets = batch.targets_2d;
332 
333      const Index batch_samples_number = batch.targets_2d.size();
334      const Index total_samples_number = data_set_pointer->get_samples_number();
335 
336      const type coefficient = static_cast<type>(2.0)/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
337 
338 //     cout << "+ w " << positives_weight <<endl;
339 //     cout << "- w " << negatives_weight <<endl;
340 //     cout << "batch samples " << batch_samples_number <<endl;
341 //     cout << "total samples " << total_samples_number<<endl;
342 //     cout << "output " << outputs<<endl;
343 //     cout << "target " << targets<<endl;
344 //     cout << "norm  " << normalization_coefficient<<endl;
345 //     cout << "error " << back_propagation.error<<endl;
346 
347 
348      const Tensor<bool, 2> if_sentence = targets == targets.constant(1);
349      const Tensor<bool, 2> else_sentence = targets == targets.constant(0);
350 
351      Tensor<type, 2> f_1(outputs.dimension(0), outputs.dimension(1));
352 
353      Tensor<type, 2> f_2(outputs.dimension(0), outputs.dimension(1));
354 
355      Tensor<type, 2> f_3(outputs.dimension(0), outputs.dimension(1));
356 
357      f_1 = coefficient*(outputs - targets)*positives_weight;
358 
359      f_2 = coefficient*(outputs - targets)*negatives_weight;
360 
361      f_3 = outputs.constant(0);
362 
363 //     cout << f_2;
364 //     back_propagation.output_gradient = (if_sentence.select(f_1, else_sentence.select(f_2, f_3)));
365      back_propagation.output_gradient.device(*thread_pool_device) = (if_sentence.select(f_1, else_sentence.select(f_2, f_3)));
366 
367 //     cout<<back_propagation.output_gradient<<endl;
368 
369 //     system("pause");
370 
371 }
372 
373 
calculate_Jacobian_gradient(const DataSet::Batch & batch,LossIndex::SecondOrderLoss & second_order_loss) const374 void WeightedSquaredError::calculate_Jacobian_gradient(const DataSet::Batch& batch,
375                                     LossIndex::SecondOrderLoss& second_order_loss) const
376 {
377 #ifdef __OPENNN_DEBUG__
378 
379     check();
380 
381 #endif
382 
383     const Index batch_samples_number = batch.get_samples_number();
384     const Index total_samples_number = data_set_pointer->get_samples_number();
385 
386     const type coefficient = 2/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
387 
388     second_order_loss.gradient.device(*thread_pool_device) = second_order_loss.error_terms_Jacobian.contract(second_order_loss.error_terms, AT_B);
389 
390     second_order_loss.gradient.device(*thread_pool_device) = coefficient*second_order_loss.gradient;
391 }
392 
393 // Hessian method
394 
calculate_hessian_approximation(const DataSet::Batch & batch,LossIndex::SecondOrderLoss & second_order_loss) const395 void WeightedSquaredError::calculate_hessian_approximation(const DataSet::Batch& batch,
396                                                            LossIndex::SecondOrderLoss& second_order_loss) const
397 {
398 #ifdef __OPENNN_DEBUG__
399 
400     check();
401 
402 #endif
403 
404     const Index batch_samples_number = batch.get_samples_number();
405     const Index total_samples_number = data_set_pointer->get_samples_number();
406 
407     const type coefficient = 2/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
408 
409     second_order_loss.hessian.device(*thread_pool_device) = second_order_loss.error_terms_Jacobian.contract(second_order_loss.error_terms_Jacobian, AT_B);
410 
411     second_order_loss.hessian.device(*thread_pool_device) = coefficient*second_order_loss.hessian;
412 }
413 
414 
415 /// Returns a string with the name of the weighted squared error loss type, "WEIGHTED_SQUARED_ERROR".
416 
get_error_type() const417 string WeightedSquaredError::get_error_type() const
418 {
419     return "WEIGHTED_SQUARED_ERROR";
420 }
421 
422 
423 /// Returns a string with the name of the weighted squared error loss type in text format.
424 
get_error_type_text() const425 string WeightedSquaredError::get_error_type_text() const
426 {
427     return "Weighted squared error";
428 }
429 
430 
431 /// Serializes the cross entropy error object into a XML document of the TinyXML library without keep the DOM tree in memory.
432 /// See the OpenNN manual for more information about the format of this document.
433 /// @param file_stream
434 
write_XML(tinyxml2::XMLPrinter & file_stream) const435 void WeightedSquaredError::write_XML(tinyxml2::XMLPrinter& file_stream) const
436 {
437     ostringstream buffer;
438 
439     // Error type
440 
441     file_stream.OpenElement("WeightedSquaredError");
442 
443     // Positives weight
444 
445     file_stream.OpenElement("PositivesWeight");
446 
447     buffer.str("");
448     buffer << positives_weight;
449 
450     file_stream.PushText(buffer.str().c_str());
451 
452     file_stream.CloseElement();
453 
454     // Negatives weight
455 
456     file_stream.OpenElement("NegativesWeight");
457 
458     buffer.str("");
459     buffer << negatives_weight;
460 
461     file_stream.PushText(buffer.str().c_str());
462 
463     file_stream.CloseElement();
464 
465     // Close error
466 
467     file_stream.CloseElement();
468 }
469 
470 
471 /// Loads a weighted squared error object from a XML document.
472 /// @param document Pointer to a TinyXML document with the object data.
473 
from_XML(const tinyxml2::XMLDocument & document)474 void WeightedSquaredError::from_XML(const tinyxml2::XMLDocument& document)
475 {
476     const tinyxml2::XMLElement* root_element = document.FirstChildElement("WeightedSquaredError");
477 
478     if(!root_element)
479     {
480         ostringstream buffer;
481 
482         buffer << "OpenNN Exception: WeightedSquaredError class.\n"
483                << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
484                << "Weighted squared element is nullptr.\n";
485 
486         throw logic_error(buffer.str());
487     }
488 
489     // Positives weight
490 
491     const tinyxml2::XMLElement* positives_weight_element = root_element->FirstChildElement("PositivesWeight");
492 
493     if(positives_weight_element)
494     {
495         const string string = positives_weight_element->GetText();
496 
497         try
498         {
499             set_positives_weight(static_cast<type>(atof(string.c_str())));
500         }
501         catch(const logic_error& e)
502         {
503             cerr << e.what() << endl;
504         }
505     }
506 
507     // Negatives weight
508 
509     const tinyxml2::XMLElement* negatives_weight_element = root_element->FirstChildElement("NegativesWeight");
510 
511     if(negatives_weight_element)
512     {
513         const string string = negatives_weight_element->GetText();
514 
515         try
516         {
517             set_negatives_weight(static_cast<type>(atof(string.c_str())));
518         }
519         catch(const logic_error& e)
520         {
521             cerr << e.what() << endl;
522         }
523     }
524 }
525 
526 }
527 
528 // OpenNN: Open Neural Networks Library.
529 // Copyright(C) 2005-2020 Artificial Intelligence Techniques, SL.
530 //
531 // This library is free software; you can redistribute it and/or
532 // modify it under the terms of the GNU Lesser General Public
533 // License as published by the Free Software Foundation; either
534 // version 2.1 of the License, or any later version.
535 //
536 // This library is distributed in the hope that it will be useful,
537 // but WITHOUT ANY WARRANTY; without even the implied warranty of
538 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
539 // Lesser General Public License for more details.
540 
541 // You should have received a copy of the GNU Lesser General Public
542 // License along with this library; if not, write to the Free Software
543 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
544