https://mooseframework.inl.gov
LibtorchANNTrainer.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #ifdef LIBTORCH_ENABLED
11 
12 #include "LibtorchANNTrainer.h"
13 #include "LibtorchDataset.h"
14 #include "Sampler.h"
15 
16 registerMooseObject("StochasticToolsApp", LibtorchANNTrainer);
17 
20 {
22 
23  params.addClassDescription("Trains a simple neural network using libtorch.");
24 
25  params.addRangeCheckedParam<unsigned int>(
26  "num_batches", 1, "1<=num_batches", "Number of batches.");
27  params.addRangeCheckedParam<unsigned int>(
28  "num_epochs", 1, "0<num_epochs", "Number of training epochs.");
29  params.addRangeCheckedParam<Real>(
30  "rel_loss_tol",
31  0,
32  "0<=rel_loss_tol<=1",
33  "The relative loss where we stop the training of the neural net.");
34  params.addParam<std::vector<unsigned int>>(
35  "num_neurons_per_layer", std::vector<unsigned int>(), "Number of neurons per layer.");
36  params.addParam<std::vector<std::string>>(
37  "activation_function",
38  std::vector<std::string>({"relu"}),
39  "The type of activation functions to use. It is either one value "
40  "or one value per hidden layer.");
41  params.addParam<std::string>(
42  "nn_filename", "net.pt", "Filename used to output the neural net parameters.");
43  params.addParam<bool>("read_from_file",
44  false,
45  "Switch to allow reading old trained neural nets for further training.");
46  params.addParam<Real>("learning_rate", 0.001, "Learning rate (relaxation).");
47  params.addRangeCheckedParam<unsigned int>(
48  "print_epoch_loss",
49  0,
50  "0<=print_epoch_loss",
51  "Epoch training loss printing. 0 - no printing, 1 - every epoch, 10 - every 10th epoch.");
52  params.addParam<unsigned int>(
53  "seed", 11, "Random number generator seed for stochastic optimizers.");
54  params.addParam<unsigned int>(
55  "max_processes", 1, "The maximum number of parallel processes that the trainer will use.");
56 
57  params.addParam<bool>(
58  "standardize_input", true, "Standardize (center and scale) training inputs (x values)");
59  params.addParam<bool>(
60  "standardize_output", true, "Standardize (center and scale) training outputs (y values)");
61 
62  params.suppressParameter<MooseEnum>("response_type");
63  return params;
64 }
65 
67  : SurrogateTrainer(parameters),
68  _predictor_row(getPredictorData()),
69  _num_neurons_per_layer(declareModelData<std::vector<unsigned int>>(
70  "num_neurons_per_layer", getParam<std::vector<unsigned int>>("num_neurons_per_layer"))),
71  _activation_function(declareModelData<std::vector<std::string>>(
72  "activation_function", getParam<std::vector<std::string>>("activation_function"))),
73  _nn_filename(getParam<std::string>("nn_filename")),
74  _read_from_file(getParam<bool>("read_from_file")),
75  _nn(declareModelData<std::shared_ptr<Moose::LibtorchArtificialNeuralNet>>("nn")),
76  _standardize_input(getParam<bool>("standardize_input")),
77  _standardize_output(getParam<bool>("standardize_output")),
78  _input_standardizer(declareModelData<StochasticTools::Standardizer>("input_standardizer")),
79  _output_standardizer(declareModelData<StochasticTools::Standardizer>("output_standardizer"))
80 {
81  // Fixing the RNG seed to make sure every experiment is the same.
82  // Otherwise sampling / stochastic gradient descent would be different.
83  torch::manual_seed(getParam<unsigned int>("seed"));
84 
86  _optim_options.learning_rate = getParam<Real>("learning_rate");
87  _optim_options.num_epochs = getParam<unsigned int>("num_epochs");
88  _optim_options.num_batches = getParam<unsigned int>("num_batches");
89  _optim_options.rel_loss_tol = getParam<Real>("rel_loss_tol");
90  _optim_options.print_loss = getParam<unsigned int>("print_epoch_loss") > 0;
91  _optim_options.print_epoch_loss = getParam<unsigned int>("print_epoch_loss");
92  _optim_options.parallel_processes = getParam<unsigned int>("max_processes");
93 }
94 
95 void
97 {
98  // Resize to number of sample points
99  _flattened_data.clear();
100  _flattened_response.clear();
103 }
104 
105 void
107 {
108  for (auto & p : _predictor_row)
109  _flattened_data.push_back(p);
110 
111  _flattened_response.push_back(*_rval);
112 }
113 
114 void
116 {
119 
120  // Then, we create and load our Tensors
121  unsigned int num_samples = _flattened_response.size();
122  unsigned int num_inputs = _n_dims;
123 
124  // We create a neural net (for the definition of the net see the header file)
125  _nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(
127 
128  if (_read_from_file)
129  try
130  {
131  torch::load(_nn, _nn_filename);
132  _console << "Loaded requested .pt file." << std::endl;
133  }
134  catch (const c10::Error & e)
135  {
136  mooseError("The requested pytorch file could not be loaded.\n", e.msg());
137  }
138 
139  // The default data type in pytorch is float, while we use double in MOOSE.
140  // Therefore, in some cases we have to convert Tensors to double.
141  auto options = torch::TensorOptions().dtype(at::kDouble);
142  torch::Tensor data_tensor =
143  torch::from_blob(_flattened_data.data(), {num_samples, num_inputs}, options).to(at::kDouble);
144  torch::Tensor response_tensor =
145  torch::from_blob(_flattened_response.data(), {num_samples, 1}, options).to(at::kDouble);
146 
147  // We standardize the input/output pairs if the user requested it
148  if (_standardize_input)
149  {
150  auto data_std_mean = torch::std_mean(data_tensor, 0);
151  auto & data_std = std::get<0>(data_std_mean);
152  auto & data_mean = std::get<1>(data_std_mean);
153 
154  data_tensor = (data_tensor - data_mean) / data_std;
155 
156  std::vector<Real> converted_data_mean;
157  LibtorchUtils::tensorToVector(data_mean, converted_data_mean);
158  std::vector<Real> converted_data_std;
159  LibtorchUtils::tensorToVector(data_std, converted_data_std);
160  _input_standardizer.set(converted_data_mean, converted_data_std);
161  }
162  else
164 
166  {
167  auto response_std_mean = torch::std_mean(response_tensor, 0);
168  auto & response_std = std::get<0>(response_std_mean);
169  auto & response_mean = std::get<1>(response_std_mean);
170 
171  response_tensor = (response_tensor - response_mean) / response_std;
172 
173  std::vector<Real> converted_response_mean;
174  LibtorchUtils::tensorToVector(response_mean, converted_response_mean);
175  std::vector<Real> converted_response_std;
176  LibtorchUtils::tensorToVector(response_std, converted_response_std);
177  _output_standardizer.set(converted_response_mean, converted_response_std);
178  }
179  else
181 
182  // We create a custom data set from our converted data
183  Moose::LibtorchDataset my_data(data_tensor, response_tensor);
184 
185  // We create atrainer for our neral net and train it with the dataset
187  trainer.train(my_data, _optim_options);
188 }
189 
190 #endif
LibtorchANNTrainer(const InputParameters &parameters)
Construct using input parameters.
std::vector< unsigned int > & _num_neurons_per_layer
Number of neurons within the hidden layers (the length of this vector should be the same as _num_hidd...
void allgather(const T &send_data, std::vector< T, A > &recv_data) const
virtual void train() override
Contains processes which are executed for every sample in the training loop.
const Real * _rval
Response value.
unsigned int _n_dims
Dimension of predictor data - either _sampler.getNumberOfCols() or _pvals.size() + _pcols...
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const bool _standardize_output
If the training output should be standardized (scaled and shifted)
void tensorToVector(torch::Tensor &tensor, std::vector< DataType > &vector)
const Parallel::Communicator & comm() const
const Parallel::Communicator & _communicator
void suppressParameter(const std::string &name)
std::vector< Real > _flattened_response
The gathered response in a flattened form to be able to convert easily to torch::Tensor.
virtual void preTrain() override
Contains processes which are executed before the training loop.
Enum for batch type in stochastic tools MultiApp.
const std::vector< Real > & _predictor_row
Data from the current predictor row.
const bool _standardize_input
If the training output should be standardized (scaled and shifted)
Trainer responsible of fitting a neural network on predefined data.
const std::string _nn_filename
Name of the pytorch output file.
Moose::LibtorchTrainingOptions _optim_options
The struct which contains the information for the training of the neural net.
const bool _read_from_file
Switch indicating if an already existing neural net should be read from a file or not...
unsigned int getLocalSampleSize() const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
This is the main trainer base class.
std::vector< std::string > & _activation_function
Activation functions for each hidden layer.
StochasticTools::Standardizer & _output_standardizer
Standardizer for use with output response (y)
virtual void postTrain() override
Contains processes which are executed after the training loop.
void set(const Real &n)
Methods for setting mean and standard deviation directly Sets mean=0, std=1 for n variables...
Definition: Standardizer.C:16
registerMooseObject("StochasticToolsApp", LibtorchANNTrainer)
void mooseError(Args &&... args) const
virtual void train(LibtorchDataset &dataset, const LibtorchTrainingOptions &options)
void addClassDescription(const std::string &doc_string)
std::shared_ptr< Moose::LibtorchArtificialNeuralNet > & _nn
Pointer to the neural net object (initialized as null)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
const ConsoleStream _console
static InputParameters validParams()
StochasticTools::Standardizer & _input_standardizer
Standardizer for use with input (x)
std::vector< Real > _flattened_data
The gathered data in a flattened form to be able to convert easily to torch::Tensor.
static InputParameters validParams()
void ErrorVector unsigned int