https://mooseframework.inl.gov
Loading...
Searching...
No Matches
LibtorchArtificialNeuralNet.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 MOOSE_LIBTORCH_ENABLED
11
13#include "MooseError.h"
14
15namespace Moose
16{
17
19 const std::string name,
20 const unsigned int num_inputs,
21 const unsigned int num_outputs,
22 const std::vector<unsigned int> & num_neurons_per_layer,
23 const std::vector<std::string> & activation_function,
24 const torch::DeviceType device_type,
25 const torch::ScalarType data_type)
26 : _name(name),
27 _num_inputs(num_inputs),
28 _num_outputs(num_outputs),
29 _num_neurons_per_layer(num_neurons_per_layer),
30 _activation_function(MultiMooseEnum("relu sigmoid elu gelu linear", "relu")),
31 _device_type(device_type),
32 _data_type(data_type)
33{
34 _activation_function = activation_function;
35
36 // Check if the number of activation functions matches the number of hidden layers
37 if ((_activation_function.size() != 1) &&
39 mooseError("The number of activation functions should be either one or the same as the number "
40 "of hidden layers");
42}
43
46 : torch::nn::Module(),
47 _name(nn.name()),
48 _num_inputs(nn.numInputs()),
49 _num_outputs(nn.numOutputs()),
50 _num_neurons_per_layer(nn.numNeuronsPerLayer()),
51 _activation_function(nn.activationFunctions()),
52 _device_type(nn.deviceType()),
53 _data_type(nn.dataType())
54{
55
56 // We construct the NN architecture
58 // We fill it up with the current parameter values
59 const auto & from_params = nn.named_parameters();
60 auto to_params = this->named_parameters();
61 for (unsigned int param_i : make_range(from_params.size()))
62 to_params[param_i].value().data() = from_params[param_i].value().data().clone();
63}
64
65void
67{
68 // Adding hidden layers
69 unsigned int inp_neurons = _num_inputs;
70 for (unsigned int i = 0; i < numHiddenLayers(); ++i)
71 {
72 std::unordered_map<std::string, unsigned int> parameters = {
73 {"inp_neurons", inp_neurons}, {"out_neurons", _num_neurons_per_layer[i]}};
74 addLayer("hidden_layer_" + std::to_string(i + 1), parameters);
75
76 // Necessary to retain double precision (and error-free runs)
78 inp_neurons = _num_neurons_per_layer[i];
79 }
80 // Adding output layer
81 std::unordered_map<std::string, unsigned int> parameters = {{"inp_neurons", inp_neurons},
82 {"out_neurons", _num_outputs}};
83 addLayer("output_layer_", parameters);
84 _weights.back()->to(_device_type, _data_type);
85}
86
87torch::Tensor
89{
90 torch::Tensor output(x);
91 if (_data_type != output.scalar_type())
92 output.to(_data_type);
93 if (_device_type != output.device().type())
94 output.to(_device_type);
95
96 for (unsigned int i = 0; i < _weights.size() - 1; ++i)
97 {
98 std::string activation =
100 if (activation == "relu")
101 output = torch::relu(_weights[i]->forward(output));
102 else if (activation == "sigmoid")
103 output = torch::sigmoid(_weights[i]->forward(output));
104 else if (activation == "elu")
105 output = torch::elu(_weights[i]->forward(output));
106 else if (activation == "gelu")
107 output = torch::gelu(_weights[i]->forward(output));
108 else if (activation == "linear")
109 output = _weights[i]->forward(output);
110 }
111
112 output = _weights[_weights.size() - 1]->forward(output);
113
114 return output;
115}
116
117void
119 const std::string & layer_name,
120 const std::unordered_map<std::string, unsigned int> & parameters)
121{
122 auto it = parameters.find("inp_neurons");
123 if (it == parameters.end())
124 ::mooseError("Number of input neurons not found during the construction of "
125 "LibtorchArtificialNeuralNet!");
126 unsigned int inp_neurons = it->second;
127
128 it = parameters.find("out_neurons");
129 if (it == parameters.end())
130 ::mooseError("Number of output neurons not found during the construction of "
131 "LibtorchArtificialNeuralNet!");
132 unsigned int out_neurons = it->second;
133
134 _weights.push_back(register_module(layer_name, torch::nn::Linear(inp_neurons, out_neurons)));
135}
136
137void
138LibtorchArtificialNeuralNet::store(nlohmann::json & json) const
139{
140 const auto & named_params = this->named_parameters();
141 for (const auto & param_i : make_range(named_params.size()))
142 {
143 // We cast the parameters into a 1D vector
144 json[named_params[param_i].key()] = std::vector<Real>(
145 named_params[param_i].value().data_ptr<Real>(),
146 named_params[param_i].value().data_ptr<Real>() + named_params[param_i].value().numel());
147 }
148}
149
150void
151to_json(nlohmann::json & json, const Moose::LibtorchArtificialNeuralNet * const & network)
152{
153 if (network)
154 network->store(json);
155}
156
157}
158
159template <>
160void
162 std::ostream & stream, std::shared_ptr<Moose::LibtorchArtificialNeuralNet> & nn, void * context)
163{
164 std::string n(nn->name());
165 dataStore(stream, n, context);
166
167 unsigned int ni(nn->numInputs());
168 dataStore(stream, ni, context);
169
170 unsigned int no(nn->numOutputs());
171 dataStore(stream, no, context);
172
173 unsigned int nhl(nn->numHiddenLayers());
174 dataStore(stream, nhl, context);
175
176 std::vector<unsigned int> nnpl(nn->numNeuronsPerLayer());
177 dataStore(stream, nnpl, context);
178
179 unsigned int afs(nn->activationFunctions().size());
180 dataStore(stream, afs, context);
181
182 std::vector<std::string> items(afs);
183 for (unsigned int i = 0; i < afs; ++i)
184 items[i] = nn->activationFunctions()[i];
185
186 dataStore(stream, items, context);
187
188 auto device_type = static_cast<std::underlying_type<torch::DeviceType>::type>(nn->deviceType());
189 dataStore(stream, device_type, context);
190
191 auto data_type = static_cast<std::underlying_type<torch::ScalarType>::type>(nn->dataType());
192 dataStore(stream, data_type, context);
193
194 torch::save(nn, nn->name());
195}
196
197template <>
198void
200 std::istream & stream, std::shared_ptr<Moose::LibtorchArtificialNeuralNet> & nn, void * context)
201{
202 std::string name;
203 dataLoad(stream, name, context);
204
205 unsigned int num_inputs;
206 dataLoad(stream, num_inputs, context);
207
208 unsigned int num_outputs;
209 dataLoad(stream, num_outputs, context);
210
211 unsigned int num_hidden_layers;
212 dataLoad(stream, num_hidden_layers, context);
213
214 std::vector<unsigned int> num_neurons_per_layer;
215 num_neurons_per_layer.resize(num_hidden_layers);
216 dataLoad(stream, num_neurons_per_layer, context);
217
218 unsigned int num_activation_items;
219 dataLoad(stream, num_activation_items, context);
220
221 std::vector<std::string> activation_functions;
222 activation_functions.resize(num_activation_items);
223 dataLoad(stream, activation_functions, context);
224
225 std::underlying_type<torch::DeviceType>::type device_type;
226 dataLoad(stream, device_type, context);
227 const torch::DeviceType divt(static_cast<torch::DeviceType>(device_type));
228
229 std::underlying_type<torch::ScalarType>::type data_type;
230 dataLoad(stream, data_type, context);
231 const torch::ScalarType datt(static_cast<torch::ScalarType>(data_type));
232
233 nn = std::make_shared<Moose::LibtorchArtificialNeuralNet>(
234 name, num_inputs, num_outputs, num_neurons_per_layer, activation_functions, divt, datt);
235
236 torch::load(nn, name);
237}
238
239template <>
240void
242 std::ostream & /*stream*/,
244 void * /*context*/)
245{
246}
247
248template <>
249void
251 std::istream & /*stream*/,
253 void * /*context*/)
254{
255}
256
257#endif
void dataStore< Moose::LibtorchArtificialNeuralNet >(std::ostream &stream, std::shared_ptr< Moose::LibtorchArtificialNeuralNet > &nn, void *context)
void dataLoad< Moose::LibtorchArtificialNeuralNet >(std::istream &stream, std::shared_ptr< Moose::LibtorchArtificialNeuralNet > &nn, void *context)
void dataLoad< Moose::LibtorchArtificialNeuralNet const >(std::istream &, Moose::LibtorchArtificialNeuralNet const *&, void *)
void dataStore< Moose::LibtorchArtificialNeuralNet const >(std::ostream &, Moose::LibtorchArtificialNeuralNet const *&, void *)
void dataLoad(std::istream &stream, LineSegment &l, void *context)
void dataStore(std::ostream &stream, LineSegment &l, void *context)
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
virtual torch::Tensor forward(const torch::Tensor &x) override
Overriding the forward substitution function for the neural network, unfortunately this cannot be con...
const torch::ScalarType _data_type
The data type used in this neural network.
void store(nlohmann::json &json) const
Store the network architecture in a json file (for debugging, visualization)
MultiMooseEnum _activation_function
Activation functions (either one for all hidden layers or one for every layer separately)
std::vector< torch::nn::Linear > _weights
Submodules that hold linear operations and the corresponding weights and biases (y = W * x + b)
LibtorchArtificialNeuralNet(const std::string name, const unsigned int num_inputs, const unsigned int num_outputs, const std::vector< unsigned int > &num_neurons_per_layer, const std::vector< std::string > &activation_function={"relu"}, const torch::DeviceType device_type=torch::kCPU, const torch::ScalarType scalar_type=torch::kDouble)
Construct using input parameters.
virtual void addLayer(const std::string &layer_name, const std::unordered_map< std::string, unsigned int > &parameters)
Add layers to the neural network.
const unsigned int _num_outputs
Number of neurons on the output layer.
const torch::DeviceType _device_type
The device type used for this neural network.
unsigned int numHiddenLayers() const
Return the number of hidden layers.
const std::vector< unsigned int > _num_neurons_per_layer
Hidden layer architecture.
void constructNeuralNetwork()
Construct the neural network.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type.
unsigned int size() const
Return the number of active items in the MultiMooseEnum.
MOOSE now contains C++17 code, so give a reasonable error message stating what the user can do to add...
void to_json(nlohmann::json &json, const Moose::LibtorchArtificialNeuralNet *const &network)