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