1 //   OpenNN: Open Neural Networks Library
2 //   www.opennn.net
3 //
4 //   N O R M A L I Z 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 "normalized_squared_error.h"
10 
11 namespace OpenNN
12 {
13 
14 /// Default constructor.
15 /// It creates a normalized squared error term object 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 
NormalizedSquaredError()19 NormalizedSquaredError::NormalizedSquaredError() : LossIndex()
20 {
21     set_default();
22 }
23 
24 
25 /// Neural network and data set constructor.
26 /// It creates a normalized squared error term associated to a neural network and measured on a data set.
27 /// It also initializes all the rest of class members to their default values.
28 /// @param new_neural_network_pointer Pointer to a neural network object.
29 /// @param new_data_set_pointer Pointer to a data set object.
30 
NormalizedSquaredError(NeuralNetwork * new_neural_network_pointer,DataSet * new_data_set_pointer)31 NormalizedSquaredError::NormalizedSquaredError(NeuralNetwork* new_neural_network_pointer, DataSet* new_data_set_pointer)
32     : LossIndex(new_neural_network_pointer, new_data_set_pointer)
33 {
34     set_default();
35 }
36 
37 
38 /// Destructor.
39 
~NormalizedSquaredError()40 NormalizedSquaredError::~NormalizedSquaredError()
41 {
42 }
43 
44 
45 /// Returns the normalization coefficient.
46 
get_normalization_coefficient() const47 type NormalizedSquaredError::get_normalization_coefficient() const
48 {
49     return normalization_coefficient;
50 }
51 
52 
53 /// Returns the selection normalization coefficient.
54 
get_selection_normalization_coefficient() const55 type NormalizedSquaredError::get_selection_normalization_coefficient() const
56 {
57     return selection_normalization_coefficient;
58 }
59 
60 
61 ///
62 /// \brief set_data_set_pointer
63 /// \param new_data_set_pointer
64 
set_data_set_pointer(DataSet * new_data_set_pointer)65 void NormalizedSquaredError::set_data_set_pointer(DataSet* new_data_set_pointer)
66 {
67     data_set_pointer = new_data_set_pointer;
68 
69     set_normalization_coefficient();
70 }
71 
72 /// Sets the normalization coefficient from training samples.
73 /// This method calculates the normalization coefficient of the dataset.
74 
set_normalization_coefficient()75 void NormalizedSquaredError::set_normalization_coefficient()
76 {
77     // Data set
78 
79     const Tensor<type, 1> targets_mean = data_set_pointer->calculate_used_targets_mean();
80 
81     //Targets matrix
82 
83     const Tensor<type, 2> targets = data_set_pointer->get_target_data();
84 
85     //Normalization coefficient
86 
87     normalization_coefficient = calculate_normalization_coefficient(targets, targets_mean);
88 
89 }
90 
91 /// Sets the normalization coefficient.
92 /// @param new_normalization_coefficient New normalization coefficient to be set.
93 
set_normalization_coefficient(const type & new_normalization_coefficient)94 void NormalizedSquaredError::set_normalization_coefficient(const type& new_normalization_coefficient)
95 {
96     normalization_coefficient = new_normalization_coefficient;
97 }
98 
99 
100 /// Sets the normalization coefficient from selection samples.
101 /// This method calculates the normalization coefficient of the dataset.
102 
set_selection_normalization_coefficient()103 void NormalizedSquaredError::set_selection_normalization_coefficient()
104 {
105     // Data set
106 
107     const Tensor<Index, 1> selection_indices = data_set_pointer->get_selection_samples_indices();
108 
109     const Index selection_samples_number = selection_indices.size();
110 
111     if(selection_samples_number == 0) return;
112 
113     const Tensor<type, 1> selection_targets_mean = data_set_pointer->calculate_selection_targets_mean();
114 
115     const Tensor<type, 2> targets = data_set_pointer->get_selection_target_data();
116 
117     // Normalization coefficient
118 
119     selection_normalization_coefficient = calculate_normalization_coefficient(targets, selection_targets_mean);
120 }
121 
122 
123 /// Sets the normalization coefficient from selection samples.
124 /// @param new_normalization_coefficient New normalization coefficient to be set.
125 
set_selection_normalization_coefficient(const type & new_selection_normalization_coefficient)126 void NormalizedSquaredError::set_selection_normalization_coefficient(const type& new_selection_normalization_coefficient)
127 {
128     selection_normalization_coefficient = new_selection_normalization_coefficient;
129 }
130 
131 
132 /// Sets the default values.
133 
set_default()134 void NormalizedSquaredError::set_default()
135 {
136     if(has_neural_network() && has_data_set() && data_set_pointer->has_data())
137     {
138         set_normalization_coefficient();
139         set_selection_normalization_coefficient();
140     }
141     else
142     {
143         normalization_coefficient = -1;
144         selection_normalization_coefficient = -1;
145     }
146 }
147 
148 /// Returns the normalization coefficient to be used for the loss of the error.
149 /// This is measured on the training samples of the data set.
150 /// @param targets Matrix with the targets values from dataset.
151 /// @param targets_mean Vector with the means of the given targets.
152 
calculate_normalization_coefficient(const Tensor<type,2> & targets,const Tensor<type,1> & targets_mean) const153 type NormalizedSquaredError::calculate_normalization_coefficient(const Tensor<type, 2>& targets, const Tensor<type, 1>& targets_mean) const
154 {
155 
156 #ifdef __OPENNN_DEBUG__
157 
158 //    check();
159 
160     const Index means_number = targets_mean.dimension(0);
161     const Index targets_number = targets.dimension(1);
162 
163     if(targets_number != means_number)
164     {
165         ostringstream buffer;
166 
167         buffer << "OpenNN Exception: NormalizedquaredError function.\n"
168                << "type calculate_normalization_coefficient(const Tensor<type, 2>& targets, const Tensor<type, 1>& targets_mean) function.\n"
169                << " The columns number of targets("<< targets_number <<") must be equal("<< means_number<<").\n";
170 
171         throw logic_error(buffer.str());
172     }
173 #endif
174 
175     const Index size = targets.dimension(0);
176 
177     type normalization_coefficient = 0;
178 
179     for(Index i = 0; i < size; i++)
180     {
181         Tensor<type, 0> norm_1 = (targets.chip(i,0) - targets_mean).square().sum();
182 
183         normalization_coefficient += norm_1(0);
184     }
185 
186     return normalization_coefficient;
187 }
188 
189 
190 
191 ///
192 ////// \brief NormalizedSquaredError::calculate_error
193 ////// \param batch
194 ////// \param forward_propagation
195 ////// \param back_propagation
calculate_error(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,LossIndex::BackPropagation & back_propagation) const196 void NormalizedSquaredError::calculate_error(const DataSet::Batch& batch,
197                      const NeuralNetwork::ForwardPropagation& forward_propagation,
198                      LossIndex::BackPropagation& back_propagation) const
199 {
200     Tensor<type, 0> sum_squared_error;
201 
202     const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
203 
204     const Tensor<type, 2>& outputs = forward_propagation.layers(trainable_layers_number-1).activations_2d;
205     const Tensor<type, 2>& targets = batch.targets_2d;
206 
207     back_propagation.errors.device(*thread_pool_device) = outputs - targets;
208 
209     sum_squared_error.device(*thread_pool_device) =  back_propagation.errors.contract(back_propagation.errors, SSE);
210 
211     const Index batch_samples_number = batch.get_samples_number();
212     const Index total_samples_number = data_set_pointer->get_samples_number();
213 
214     back_propagation.error = sum_squared_error(0)/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
215 
216     return;
217 }
218 
219 
220 ///
221 ////// \brief NormalizedSquaredError::calculate_error_terms
222 ////// \param batch
223 ////// \param forward_propagation
224 ////// \param second_order_loss
calculate_error_terms(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,SecondOrderLoss & second_order_loss) const225 void NormalizedSquaredError::calculate_error_terms(const DataSet::Batch& batch,
226                                                    const NeuralNetwork::ForwardPropagation& forward_propagation,
227                                                    SecondOrderLoss& second_order_loss) const
228 {
229     const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
230 
231     const Index batch_samples_number = batch.get_samples_number();
232     const Index total_samples_number = data_set_pointer->get_samples_number();
233 
234     const Tensor<type, 2>& outputs = forward_propagation.layers(trainable_layers_number-1).activations_2d;
235     const Tensor<type, 2>& targets = batch.targets_2d;
236 
237     second_order_loss.error_terms.resize(outputs.dimension(0));
238     const Eigen::array<int, 1> rows_sum = {Eigen::array<int, 1>({1})};
239 
240     second_order_loss.error_terms.device(*thread_pool_device) = ((outputs - targets).square().sum(rows_sum)).sqrt();
241 
242     Tensor<type, 0> error;
243     error.device(*thread_pool_device) = second_order_loss.error_terms.contract(second_order_loss.error_terms, AT_B);
244 
245     const type coefficient = ((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
246 
247     second_order_loss.error = error()/coefficient;
248 }
249 
250 
calculate_output_gradient(const DataSet::Batch & batch,const NeuralNetwork::ForwardPropagation & forward_propagation,BackPropagation & back_propagation) const251 void NormalizedSquaredError::calculate_output_gradient(const DataSet::Batch& batch,
252                                const NeuralNetwork::ForwardPropagation& forward_propagation,
253                                BackPropagation& back_propagation) const
254 {
255      #ifdef __OPENNN_DEBUG__
256 
257      check();
258 
259      #endif
260 
261      const Index batch_samples_number = batch.get_samples_number();
262      const Index total_samples_number = data_set_pointer->get_samples_number();
263 
264      const Index trainable_layers_number = neural_network_pointer->get_trainable_layers_number();
265 
266      const Tensor<type, 2>& outputs = forward_propagation.layers(trainable_layers_number-1).activations_2d;
267      const Tensor<type, 2>& targets = batch.targets_2d;
268 
269      const type coefficient = static_cast<type>(2.0)/(static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number)*normalization_coefficient);
270 
271      back_propagation.errors.device(*thread_pool_device) = outputs - targets;
272 
273      back_propagation.output_gradient.device(*thread_pool_device) = coefficient*back_propagation.errors;
274 }
275 
276 
calculate_Jacobian_gradient(const DataSet::Batch & batch,LossIndex::SecondOrderLoss & second_order_loss) const277 void NormalizedSquaredError::calculate_Jacobian_gradient(const DataSet::Batch& batch,
278                                     LossIndex::SecondOrderLoss& second_order_loss) const
279 {
280 #ifdef __OPENNN_DEBUG__
281 
282     check();
283 
284 #endif
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 type coefficient = 2/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
290 
291     second_order_loss.gradient.device(*thread_pool_device) = second_order_loss.error_terms_Jacobian.contract(second_order_loss.error_terms, AT_B);
292 
293     second_order_loss.gradient.device(*thread_pool_device) = coefficient*second_order_loss.gradient;
294 }
295 
296 
calculate_hessian_approximation(const DataSet::Batch & batch,LossIndex::SecondOrderLoss & second_order_loss) const297 void NormalizedSquaredError::calculate_hessian_approximation(const DataSet::Batch& batch,
298                                                              LossIndex::SecondOrderLoss& second_order_loss) const
299 {
300 #ifdef __OPENNN_DEBUG__
301 
302     check();
303 
304 #endif
305 
306     const Index batch_samples_number = batch.get_samples_number();
307     const Index total_samples_number = data_set_pointer->get_samples_number();
308 
309     const type coefficient = 2/((static_cast<type>(batch_samples_number)/static_cast<type>(total_samples_number))*normalization_coefficient);
310 
311     second_order_loss.hessian.device(*thread_pool_device) = second_order_loss.error_terms_Jacobian.contract(second_order_loss.error_terms_Jacobian, AT_B);
312 
313     second_order_loss.hessian.device(*thread_pool_device) = coefficient*second_order_loss.hessian;
314 }
315 
316 
317 /// Returns a string with the name of the normalized squared error loss type, "NORMALIZED_SQUARED_ERROR".
318 
get_error_type() const319 string NormalizedSquaredError::get_error_type() const
320 {
321     return "NORMALIZED_SQUARED_ERROR";
322 }
323 
324 
325 /// Returns a string with the name of the normalized squared error loss type in text format.
326 
get_error_type_text() const327 string NormalizedSquaredError::get_error_type_text() const
328 {
329     return "Normalized squared error";
330 }
331 
332 
333 /// Serializes the cross entropy error object into a XML document of the TinyXML library without keep the DOM tree in memory.
334 /// See the OpenNN manual for more information about the format of this document
335 
write_XML(tinyxml2::XMLPrinter & file_stream) const336 void NormalizedSquaredError::write_XML(tinyxml2::XMLPrinter& file_stream) const
337 {
338     // Error type
339 
340     file_stream.OpenElement("NormalizedSquaredError");
341 
342     file_stream.CloseElement();
343 }
344 
345 
346 /// Loads a root mean squared error object from a XML document.
347 /// @param document Pointer to a TinyXML document with the object data.
348 
from_XML(const tinyxml2::XMLDocument & document)349 void NormalizedSquaredError::from_XML(const tinyxml2::XMLDocument& document)
350 {
351     const tinyxml2::XMLElement* root_element = document.FirstChildElement("NormalizedSquaredError");
352 
353     if(!root_element)
354     {
355         ostringstream buffer;
356 
357         buffer << "OpenNN Exception: NormalizedSquaredError class.\n"
358                << "void from_XML(const tinyxml2::XMLDocument&) method.\n"
359                << "Normalized squared element is nullptr.\n";
360 
361         throw logic_error(buffer.str());
362     }
363 }
364 
365 }
366 
367 // OpenNN: Open Neural Networks Library.
368 // Copyright(C) 2005-2020 Artificial Intelligence Techniques, SL.
369 //
370 // This library is free software; you can redistribute it and/or
371 // modify it under the terms of the GNU Lesser General Public
372 // License as published by the Free Software Foundation; either
373 // version 2.1 of the License, or any later version.
374 //
375 // This library is distributed in the hope that it will be useful,
376 // but WITHOUT ANY WARRANTY; without even the implied warranty of
377 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
378 // Lesser General Public License for more details.
379 
380 // You should have received a copy of the GNU Lesser General Public
381 // License along with this library; if not, write to the Free Software
382 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
383