OTB  9.0.0
Orfeo Toolbox
otbAutoencoderModel.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2022 Centre National d'Etudes Spatiales (CNES)
3  *
4  * This file is part of Orfeo Toolbox
5  *
6  * https://www.orfeo-toolbox.org/
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 #ifndef otbAutoencoderModel_hxx
21 #define otbAutoencoderModel_hxx
22 
23 #include "otbAutoencoderModel.h"
24 #include "otbMacro.h"
25 
26 #include <fstream>
27 
28 #if defined(__GNUC__) || defined(__clang__)
29 #pragma GCC diagnostic push
30 
31 #if (defined (__GNUC__) && (__GNUC__ >= 9)) || (defined (__clang__) && (__clang_major__ >= 10))
32 #pragma GCC diagnostic ignored "-Wdeprecated-copy"
33 #endif
34 
35 #pragma GCC diagnostic ignored "-Wshadow"
36 #pragma GCC diagnostic ignored "-Wunused-parameter"
37 #pragma GCC diagnostic ignored "-Woverloaded-virtual"
38 #endif
39 #include "otbSharkUtils.h"
40 // include train function
41 #include <shark/ObjectiveFunctions/ErrorFunction.h>
42 //~ #include <shark/ObjectiveFunctions/SparseAutoencoderError.h>//the error function performing the regularisation of the hidden neurons
43 
44 #include <shark/Algorithms/GradientDescent/Rprop.h> // the RProp optimization algorithm
45 #include <shark/ObjectiveFunctions/Loss/SquaredLoss.h> // squared loss used for regression
46 #include <shark/ObjectiveFunctions/Regularizer.h> //L2 regulariziation
47 //~ #include <shark/Models/ImpulseNoiseModel.h> //noise source to corrupt the inputs
48 
49 #include <shark/Algorithms/StoppingCriteria/MaxIterations.h> //A simple stopping criterion that stops after a fixed number of iterations
50 #include <shark/Algorithms/StoppingCriteria/TrainingProgress.h> //Stops when the algorithm seems to converge, Tracks the progress of the training error over a period of time
51 
52 #include <shark/Algorithms/GradientDescent/Adam.h>
53 #if defined(__GNUC__) || defined(__clang__)
54 #pragma GCC diagnostic pop
55 #endif
56 
57 namespace otb
58 {
59 
60 template <class TInputValue, class NeuronType>
62 {
63  this->m_IsDoPredictBatchMultiThreaded = true;
64  this->m_WriteLearningCurve = false;
65 }
66 
67 template <class TInputValue, class NeuronType>
69 {
70 }
71 
72 template <class TInputValue, class NeuronType>
74 {
75  std::vector<shark::RealVector> features;
76  Shark::ListSampleToSharkVector(this->GetInputListSample(), features);
77  shark::Data<shark::RealVector> inputSamples = shark::createDataFromRange(features);
78  shark::Data<shark::RealVector> inputSamples_copy = inputSamples;
79 
80  std::ofstream ofs;
81  if (this->m_WriteLearningCurve == true)
82  {
83  ofs.open(m_LearningCurveFileName);
84  ofs << "learning curve" << std::endl;
85  }
86 
87  // Initialization of the feed forward neural network
88  m_Encoder = ModelType();
89  m_InLayers.clear();
90  size_t previousShape = shark::dataDimension(inputSamples);
91  for (unsigned int i = 0; i < m_NumberOfHiddenNeurons.Size(); ++i)
92  {
93  m_InLayers.push_back(LayerType(previousShape, m_NumberOfHiddenNeurons[i]));
94  previousShape = m_NumberOfHiddenNeurons[i];
95  m_Encoder.add(&(m_InLayers.back()), true);
96  }
97  for (unsigned int i = std::max(0, static_cast<int>(m_NumberOfHiddenNeurons.Size() - 1)); i > 0; --i)
98  {
99  m_InLayers.push_back(LayerType(previousShape, m_NumberOfHiddenNeurons[i - 1]));
100  previousShape = m_NumberOfHiddenNeurons[i - 1];
101  }
102  m_OutLayer = OutLayerType(previousShape, shark::dataDimension(inputSamples));
103 
104  // Training of the autoencoders pairwise, starting from the first and last layers
105  for (unsigned int i = 0; i < m_NumberOfHiddenNeurons.Size(); ++i)
106  {
107  if (m_Epsilon > 0)
108  {
109  shark::TrainingProgress<> criterion(5, m_Epsilon);
110  // Shark doesn't allow to train a layer using a sparsity term AND a noisy input.
111  if (m_Noise[i] != 0)
112  {
113  TrainOneLayer(criterion, i, inputSamples, ofs);
114  }
115  else
116  {
117  TrainOneSparseLayer(criterion, i, inputSamples, ofs);
118  }
119  criterion.reset();
120  }
121  else
122  {
123  shark::MaxIterations<> criterion(m_NumberOfIterations);
124  // Shark doesn't allow to train a layer using a sparsity term AND a noisy input.
125  if (m_Noise[i] != 0)
126  {
127  TrainOneLayer(criterion, i, inputSamples, ofs);
128  otbMsgDevMacro(<< "m_Noise " << m_Noise[0]);
129  }
130  else
131  {
132  TrainOneSparseLayer(criterion, i, inputSamples, ofs);
133  }
134  criterion.reset();
135  }
136  // encode the samples with the last encoder trained
137  inputSamples = m_InLayers[i](inputSamples);
138  }
139  if (m_NumberOfIterationsFineTuning > 0)
140  {
141  shark::MaxIterations<> criterion(m_NumberOfIterationsFineTuning);
142  TrainNetwork(criterion, inputSamples_copy, ofs);
143  }
144  this->SetDimension(m_NumberOfHiddenNeurons[m_NumberOfHiddenNeurons.Size() - 1]);
145 }
146 
147 template <class TInputValue, class NeuronType>
148 template <class T>
149 void AutoencoderModel<TInputValue, NeuronType>::TrainOneLayer(shark::AbstractStoppingCriterion<T>& criterion, unsigned int layer_index,
150  shark::Data<shark::RealVector>& samples, std::ostream& File)
151 {
152  typedef shark::AbstractModel<shark::RealVector, shark::RealVector> BaseModelType;
153  ModelType net;
154  net.add(&(m_InLayers[layer_index]), true);
155  net.add((layer_index ? (BaseModelType*)&(m_InLayers[m_NumberOfHiddenNeurons.Size() * 2 - 1 - layer_index]) : (BaseModelType*)&m_OutLayer), true);
156 
157  otbMsgDevMacro(<< "Noise " << m_Noise[layer_index]);
158  std::size_t inputs = dataDimension(samples);
159  initRandomUniform(net, -m_InitFactor * std::sqrt(1.0 / inputs), m_InitFactor * std::sqrt(1.0 / inputs));
160 
161  //~ shark::ImpulseNoiseModel noise(inputs,m_Noise[layer_index],1.0); //set an input pixel with probability m_Noise to 0
162  //~ shark::ConcatenatedModel<shark::RealVector,shark::RealVector> model = noise>> net;
163  shark::LabeledData<shark::RealVector, shark::RealVector> trainSet(samples, samples); // labels identical to inputs
164  shark::SquaredLoss<shark::RealVector> loss;
165  //~ shark::ErrorFunction error(trainSet, &model, &loss);
166  shark::ErrorFunction<> error(trainSet, &net, &loss);
167 
168  shark::TwoNormRegularizer<> regularizer(error.numberOfVariables());
169  error.setRegularizer(m_Regularization[layer_index], &regularizer);
170 
171  shark::Adam<> optimizer;
172  error.init();
173  optimizer.init(error);
174 
175  otbMsgDevMacro(<< "Error before training : " << optimizer.solution().value);
176  if (this->m_WriteLearningCurve == true)
177  {
178  File << "end layer" << std::endl;
179  }
180 
181  unsigned int i = 0;
182  do
183  {
184  i++;
185  optimizer.step(error);
186  if (this->m_WriteLearningCurve == true)
187  {
188  File << optimizer.solution().value << std::endl;
189  }
190  otbMsgDevMacro(<< "Error after " << i << " iterations : " << optimizer.solution().value);
191  } while (!criterion.stop(optimizer.solution()));
192 
193  net.setParameterVector(optimizer.solution().point);
194 }
195 
196 template <class TInputValue, class NeuronType>
197 template <class T>
198 void AutoencoderModel<TInputValue, NeuronType>::TrainOneSparseLayer(shark::AbstractStoppingCriterion<T>& criterion, unsigned int layer_index,
199  shark::Data<shark::RealVector>& samples, std::ostream& File)
200 {
201  typedef shark::AbstractModel<shark::RealVector, shark::RealVector> BaseModelType;
202  ModelType net;
203  net.add(&(m_InLayers[layer_index]), true);
204  net.add((layer_index ? (BaseModelType*)&(m_InLayers[m_NumberOfHiddenNeurons.Size() * 2 - 1 - layer_index]) : (BaseModelType*)&m_OutLayer), true);
205 
206  std::size_t inputs = dataDimension(samples);
207  shark::initRandomUniform(net, -m_InitFactor * std::sqrt(1.0 / inputs), m_InitFactor * std::sqrt(1.0 / inputs));
208 
209  // Idea : set the initials value for the output weights higher than the input weights
210 
211  shark::LabeledData<shark::RealVector, shark::RealVector> trainSet(samples, samples); // labels identical to inputs
212  shark::SquaredLoss<shark::RealVector> loss;
213  //~ shark::SparseAutoencoderError error(trainSet,&net, &loss, m_Rho[layer_index], m_Beta[layer_index]);
214  // SparseAutoencoderError doesn't exist anymore, for now use a plain ErrorFunction
215  shark::ErrorFunction<> error(trainSet, &net, &loss);
216 
217  shark::TwoNormRegularizer<> regularizer(error.numberOfVariables());
218  error.setRegularizer(m_Regularization[layer_index], &regularizer);
219  shark::Adam<> optimizer;
220  error.init();
221  optimizer.init(error);
222 
223  otbMsgDevMacro(<< "Error before training : " << optimizer.solution().value);
224  unsigned int i = 0;
225  do
226  {
227  i++;
228  optimizer.step(error);
229  otbMsgDevMacro(<< "Error after " << i << " iterations : " << optimizer.solution().value);
230  if (this->m_WriteLearningCurve == true)
231  {
232  File << optimizer.solution().value << std::endl;
233  }
234  } while (!criterion.stop(optimizer.solution()));
235  if (this->m_WriteLearningCurve == true)
236  {
237  File << "end layer" << std::endl;
238  }
239  net.setParameterVector(optimizer.solution().point);
240 }
241 
242 template <class TInputValue, class NeuronType>
243 template <class T>
244 void AutoencoderModel<TInputValue, NeuronType>::TrainNetwork(shark::AbstractStoppingCriterion<T>& criterion, shark::Data<shark::RealVector>& samples,
245  std::ostream& File)
246 {
247  // create full network
248  ModelType net;
249  for (auto& layer : m_InLayers)
250  {
251  net.add(&layer, true);
252  }
253  net.add(&m_OutLayer, true);
254 
255  // labels identical to inputs
256  shark::LabeledData<shark::RealVector, shark::RealVector> trainSet(samples, samples);
257  shark::SquaredLoss<shark::RealVector> loss;
258 
259  shark::ErrorFunction<> error(trainSet, &net, &loss);
260  shark::TwoNormRegularizer<> regularizer(error.numberOfVariables());
261  error.setRegularizer(m_Regularization[0], &regularizer);
262 
263  shark::Adam<> optimizer;
264  error.init();
265  optimizer.init(error);
266  otbMsgDevMacro(<< "Error before training : " << optimizer.solution().value);
267  unsigned int i = 0;
268  while (!criterion.stop(optimizer.solution()))
269  {
270  i++;
271  optimizer.step(error);
272  otbMsgDevMacro(<< "Error after " << i << " iterations : " << optimizer.solution().value);
273  if (this->m_WriteLearningCurve == true)
274  {
275  File << optimizer.solution().value << std::endl;
276  }
277  }
278 }
279 
280 template <class TInputValue, class NeuronType>
282 {
283  try
284  {
285  this->Load(filename);
286  }
287  catch (...)
288  {
289  return false;
290  }
291  return true;
292 }
293 
294 template <class TInputValue, class NeuronType>
295 bool AutoencoderModel<TInputValue, NeuronType>::CanWriteFile(const std::string& /*filename*/)
296 {
297  return true;
298 }
299 
300 template <class TInputValue, class NeuronType>
301 void AutoencoderModel<TInputValue, NeuronType>::Save(const std::string& filename, const std::string& /*name*/)
302 {
303  otbMsgDevMacro(<< "saving model ...");
304  std::ofstream ofs(filename);
305  ofs << "Autoencoder" << std::endl; // the first line of the model file contains a key
306  ofs << (m_InLayers.size() + 1) << std::endl; // second line is the number of encoders/decoders
307  shark::TextOutArchive oa(ofs);
308  for (const auto& layer : m_InLayers)
309  {
310  oa << layer;
311  }
312  oa << m_OutLayer;
313  ofs.close();
314 }
315 
316 template <class TInputValue, class NeuronType>
317 void AutoencoderModel<TInputValue, NeuronType>::Load(const std::string& filename, const std::string& /*name*/)
318 {
319  std::ifstream ifs(filename);
320  char buffer[256];
321  // check first line
322  ifs.getline(buffer, 256);
323  std::string bufferStr(buffer);
324  if (bufferStr != "Autoencoder")
325  {
326  itkExceptionMacro(<< "Error opening " << filename.c_str());
327  }
328  // check second line
329  ifs.getline(buffer, 256);
330  int nbLevels = boost::lexical_cast<int>(buffer);
331  if (nbLevels < 2 || nbLevels % 2 == 1)
332  {
333  itkExceptionMacro(<< "Unexpected number of levels : " << buffer);
334  }
335  m_InLayers.clear();
336  m_Encoder = ModelType();
337  shark::TextInArchive ia(ifs);
338  for (int i = 0; (i + 1) < nbLevels; i++)
339  {
340  LayerType layer;
341  ia >> layer;
342  m_InLayers.push_back(layer);
343  }
344  ia >> m_OutLayer;
345  ifs.close();
346 
347  for (int i = 0; i < nbLevels / 2; i++)
348  {
349  m_Encoder.add(&(m_InLayers[i]), true);
350  }
351 
352  this->SetDimension(m_Encoder.outputShape()[0]);
353 }
354 
355 template <class TInputValue, class NeuronType>
358 {
359  shark::RealVector samples(value.Size());
360  for (size_t i = 0; i < value.Size(); i++)
361  {
362  samples[i] = value[i];
363  }
364 
365  std::vector<shark::RealVector> features;
366  features.push_back(samples);
367 
368  shark::Data<shark::RealVector> data = shark::createDataFromRange(features);
369 
370  // features layer for a network containing the encoder and decoder part
371  data = m_Encoder(data);
372  TargetSampleType target;
373  target.SetSize(this->m_Dimension);
374 
375  for (unsigned int a = 0; a < this->m_Dimension; ++a)
376  {
377  target[a] = data.element(0)[a];
378  }
379  return target;
380 }
381 
382 template <class TInputValue, class NeuronType>
383 void AutoencoderModel<TInputValue, NeuronType>::DoPredictBatch(const InputListSampleType* input, const unsigned int& startIndex, const unsigned int& size,
384  TargetListSampleType* targets, ConfidenceListSampleType* /*quality*/,
385  ProbaListSampleType* /*proba*/) const
386 {
387  std::vector<shark::RealVector> features;
388  Shark::ListSampleRangeToSharkVector(input, features, startIndex, size);
389  shark::Data<shark::RealVector> data = shark::createDataFromRange(features);
390  TargetSampleType target;
391  // features layer for a network containing the encoder and decoder part
392  data = m_Encoder(data);
393 
394  unsigned int id = startIndex;
395  target.SetSize(this->m_Dimension);
396 
397  for (const auto& p : data.elements())
398  {
399  for (unsigned int a = 0; a < this->m_Dimension; ++a)
400  {
401  target[a] = p[a];
402  }
403  targets->SetMeasurementVector(id, target);
404  ++id;
405  }
406 }
407 
408 } // namespace otb
409 
410 #endif
otb::AutoencoderModel::CanWriteFile
bool CanWriteFile(const std::string &filename) override
Definition: otbAutoencoderModel.hxx:295
otb::AutoencoderModel::DoPredictBatch
virtual void DoPredictBatch(const InputListSampleType *, const unsigned int &startIndex, const unsigned int &size, TargetListSampleType *, ConfidenceListSampleType *quality=nullptr, ProbaListSampleType *proba=nullptr) const override
Definition: otbAutoencoderModel.hxx:383
otb::AutoencoderModel::ModelType
shark::ConcatenatedModel< shark::RealVector > ModelType
Neural network related typedefs.
Definition: otbAutoencoderModel.h:102
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otbAutoencoderModel.h
otb::AutoencoderModel::CanReadFile
bool CanReadFile(const std::string &filename) override
Definition: otbAutoencoderModel.hxx:281
otbMacro.h
otb::AutoencoderModel::InputListSampleType
Superclass::InputListSampleType InputListSampleType
Definition: otbAutoencoderModel.h:88
otb::AutoencoderModel::TargetSampleType
Superclass::TargetSampleType TargetSampleType
Definition: otbAutoencoderModel.h:91
otb::AutoencoderModel::TrainOneSparseLayer
void TrainOneSparseLayer(shark::AbstractStoppingCriterion< T > &criterion, unsigned int, shark::Data< shark::RealVector > &, std::ostream &)
Definition: otbAutoencoderModel.hxx:198
otb::AutoencoderModel::TrainNetwork
void TrainNetwork(shark::AbstractStoppingCriterion< T > &criterion, shark::Data< shark::RealVector > &, std::ostream &)
Definition: otbAutoencoderModel.hxx:244
otb::AutoencoderModel::LayerType
shark::LinearModel< shark::RealVector, NeuronType > LayerType
Definition: otbAutoencoderModel.h:103
otb::AutoencoderModel::DoPredict
virtual TargetSampleType DoPredict(const InputSampleType &input, ConfidenceValueType *quality=nullptr, ProbaSampleType *proba=nullptr) const override
Definition: otbAutoencoderModel.hxx:357
otb::AutoencoderModel::ProbaSampleType
Superclass::ProbaSampleType ProbaSampleType
Definition: otbAutoencoderModel.h:99
otb::AutoencoderModel::ConfidenceListSampleType
Superclass::ConfidenceListSampleType ConfidenceListSampleType
Definition: otbAutoencoderModel.h:97
otb::AutoencoderModel::ProbaListSampleType
Superclass::ProbaListSampleType ProbaListSampleType
Definition: otbAutoencoderModel.h:100
otb::AutoencoderModel::Train
void Train() override
Definition: otbAutoencoderModel.hxx:73
otb::AutoencoderModel::Save
void Save(const std::string &filename, const std::string &name="") override
Definition: otbAutoencoderModel.hxx:301
otb::AutoencoderModel::ConfidenceValueType
Superclass::ConfidenceValueType ConfidenceValueType
Confidence map related typedefs.
Definition: otbAutoencoderModel.h:95
otb::AutoencoderModel::TrainOneLayer
void TrainOneLayer(shark::AbstractStoppingCriterion< T > &criterion, unsigned int, shark::Data< shark::RealVector > &, std::ostream &)
Definition: otbAutoencoderModel.hxx:149
otb::AutoencoderModel::~AutoencoderModel
~AutoencoderModel() override
Definition: otbAutoencoderModel.hxx:68
otb::AutoencoderModel::AutoencoderModel
AutoencoderModel()
Definition: otbAutoencoderModel.hxx:61
otbMsgDevMacro
#define otbMsgDevMacro(x)
Definition: otbMacro.h:64
otb::AutoencoderModel::OutLayerType
shark::LinearModel< shark::RealVector, shark::LinearNeuron > OutLayerType
Definition: otbAutoencoderModel.h:104
otb::AutoencoderModel::InputSampleType
Superclass::InputSampleType InputSampleType
Definition: otbAutoencoderModel.h:87
otb::AutoencoderModel::Load
void Load(const std::string &filename, const std::string &name="") override
Definition: otbAutoencoderModel.hxx:317
otb::AutoencoderModel::TargetListSampleType
Superclass::TargetListSampleType TargetListSampleType
Definition: otbAutoencoderModel.h:92