Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
ROperator_RNN.hxx
Go to the documentation of this file.
1#ifndef TMVA_SOFIE_ROPERATOR_RNN
2#define TMVA_SOFIE_ROPERATOR_RNN
3
4#include "TMVA/RModel.hxx"
5#include "TMVA/ROperator.hxx"
7
8#include <memory>
9#include <sstream>
10#include <vector>
11
13
14/*! \brief Recurrent Neural Network operator
15 *
16 * Inference code generation for one-layer vanilla RNN. Supports forward, reverse and bidirectional RNNs.
17 * See the <a href="https://github.com/onnx/onnx/blob/master/docs/Operators.md#RNN">ONNX documentation</a>
18 * for details about the supported RNN architectures.
19 */
20template <typename T> class ROperator_RNN final : public ROperator {
21 private:
22 std::vector<float> fAttrActivationAlpha; ///< Scaling values used by some activation functions
23 std::vector<float> fAttrActivationBeta; ///< Scaling values used by some activation functions
24 std::vector<std::string> fAttrActivations; ///< Activation functions
25 float fAttrClip; ///< Clip threshold
26 std::string fAttrDirection; ///< Direction of processing
27 size_t fAttrHiddenSize; ///< Number of the hidden layers
28 size_t fAttrLayout; ///< Data layout
29
30 std::string fNX; ///< Name of the input
31 std::string fNW; ///< Name of the weights
32 std::string fNR; ///< Name of the recurrence
33 std::string fNB; ///< Name of the bias
34 std::string fNSequence_lens; ///< Name of the length of the sequences
35 std::string fNInitial_h; ///< Name of the initial value of the hidden states
36 std::string fNY; ///< Name of the output
37 std::string fNY_h; ///< Name of the last sequence of the output
38
39 std::vector<size_t> fShapeX; ///< Shape of the input
40 std::vector<size_t> fShapeW; ///< Shape of the weights
41 std::vector<size_t> fShapeR; ///< Shape of the recurrence
42 std::vector<size_t> fShapeB; ///< Shape of the bias
43 std::vector<size_t> fShapeSequence_lens; ///< Shape of the length of the sequences
44 std::vector<size_t> fShapeInitial_h; ///< Shape of the initial value of the hidden states
45 std::vector<size_t> fShapeY; ///< Shape of the output
46 std::vector<size_t> fShapeY_h; ///< Shape of the last sequence of the output
47
48 std::string fType; ///< Type of the tensors
49
50 public:
51 /*! Default constructor of ROperator_RNN */
53
54 /*! \brief Constructor of ROperator_RNN from the attributes
55 *
56 * \param activation_alpha scaling values used by some activation functions
57 * \param activation_beta scaling values used by some activation functions
58 * \param activations activation functions
59 * \param clip clip threshold
60 * \param direction direction of processing of the sequneces
61 * \param hidden_size number of hidden layers
62 * \param layout data layout
63 * \param nameX name of the input tensor
64 * \param nameW name of the weight tensor
65 * \param nameR name of the recurrence tensor
66 * \param nameB name of the bias tensor
67 * \param nameSequence_lens name of the length of the sequences
68 * \param nameInitial_h name of the initial value of the hidden states
69 * \param nameY name of the output
70 * \param nameY_h name of the last sequence of the output
71 */
72 ROperator_RNN(std::vector<float> activation_alpha,
73 std::vector<float> activation_beta,
74 std::vector<std::string> activations, float clip,
75 std::string direction, size_t hidden_size, size_t layout,
76 std::string nameX, std::string nameW, std::string nameR,
77 std::string nameB, std::string nameSequence_lens,
78 std::string nameInitial_h, std::string nameY,
79 std::string nameY_h)
84 fNX(UTILITY::Clean_name(nameX)), fNW(UTILITY::Clean_name(nameW)),
85 fNR(UTILITY::Clean_name(nameR)), fNB(UTILITY::Clean_name(nameB)),
86 fNSequence_lens(UTILITY::Clean_name(nameSequence_lens)),
87 fNInitial_h(UTILITY::Clean_name(nameInitial_h)),
88 fNY(UTILITY::Clean_name(nameY)), fNY_h(UTILITY::Clean_name(nameY_h)) {
89 if (std::is_same<T, float>::value) {
90 fType = "float";
91 } else {
92 throw std::runtime_error(
93 "TMVA SOFIE Encountered unsupported type parsing a RNN operator");
94 }
95
97 if(!fNB.empty()){
98 fInputTensorNames.emplace_back(fNB);
99 }
100 if(!fNSequence_lens.empty()){
102 }
103 if(!fNInitial_h.empty()){
104 fInputTensorNames.emplace_back(fNInitial_h);
105 }
106
107 fOutputTensorNames = { };
108 if(!fNY.empty()){
109 fOutputTensorNames.emplace_back(fNY);
110 }
111 if(!fNY_h.empty()){
112 fOutputTensorNames.emplace_back(fNY_h);
113 }
114 }
115
116 /*! \brief Infers the type of the output tensors
117 *
118 * \param input type of the input tensors
119 */
120 std::vector<ETensorType> TypeInference(std::vector<ETensorType> input) override;
121
122 /*! \brief Infers the shape of the output tensors
123 *
124 * \param input shape of the input tensors
125 */
126 std::vector<std::vector<size_t>>
127 ShapeInference(std::vector<std::vector<size_t>> input) override;
128
129 /*! \brief Initialize the model
130 *
131 * \param model Model
132 */
133 void Initialize(RModel &) override;
134
135 /*! \brief Generates the inference code
136 *
137 * \param OpName name of the operator
138 */
139 std::string Generate(std::string OpName) override;
140
141 // generate code for Session data members (e.g. internal vectors)
142 std::string GenerateSessionMembersCode(std::string opName) override;
143
144 /*! \brief Returns the blas routines needed to compile the generated code
145 */
146 std::vector<std::string> GetBlasRoutines() override { return { std::string("Gemm"), std::string("Axpy") }; }
147};
148
149template <typename T>
150auto ROperator_RNN<T>::TypeInference(std::vector<ETensorType> input) -> std::vector<ETensorType>
151{
152 ETensorType out = input[0];
153 return {out, out};
154}
155
156template <typename T>
157auto ROperator_RNN<T>::ShapeInference(std::vector<std::vector<size_t>> input) -> std::vector<std::vector<size_t>>
158{
159 size_t num_directions = input[1][0];
160 size_t hidden_size = input[1][1];
161 if (fAttrLayout == 0) {
162 size_t seq_length = input[0][0];
163 size_t batch_size = input[0][1];
164 std::vector<std::vector<size_t>> ret(
166 return ret;
167 } else {
168 size_t batch_size = input[0][0];
169 size_t seq_length = input[0][1];
170 std::vector<std::vector<size_t>> ret(
172 return ret;
173 }
174}
175
176template <typename T>
178{
179 fUseSession = model.UseSession();
180 // Check the input and output tensors
181 if (!model.CheckIfTensorAlreadyExist(fNX)) {
182 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNX + " is not found in model.");
183 }
184 fShapeX = model.GetTensorShape(fNX);
185 if (fShapeX.size() != 3) {
186 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNX + " is not of 3 dimensions.");
187 }
188 if (!model.CheckIfTensorAlreadyExist(fNW)) {
189 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNW + " is not found in model.");
190 }
191 fShapeW = model.GetTensorShape(fNW);
192 if (fShapeW.size() != 3) {
193 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNW + " is not of 3 dimensions.");
194 }
195 if (!model.CheckIfTensorAlreadyExist(fNR)) {
196 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNR + " is not found in model.");
197 }
198 fShapeR = model.GetTensorShape(fNR);
199 if (fShapeR.size() != 3) {
200 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNR + " is not of 3 dimensions.");
201 }
202 if (!fNB.empty()) {
203 if (!model.CheckIfTensorAlreadyExist(fNB)) {
204 throw std::runtime_error("TMVA SOFIE RNN op input tensor " + fNB + " is not found in model.");
205 }
206 fShapeB = model.GetTensorShape(fNB);
207 if (fShapeB.size() != 2 && fShapeB.size() != 4) {
208 throw std::runtime_error("TMVA SOFIE RNN op input tensor " + fNB + " is not of 2 or 4 dimensions.");
209 }
210 if (fShapeB.size() == 2) {
211 // Broadcasting the bias
212 auto original_data = model.GetInitializedTensorData(fNB);
213 size_t num_directions = fShapeW[0];
214 size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
215 size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
216 if (fType == "float") {
217 float *original_bias = static_cast<float *>(original_data.get());
218 float *new_bias = new float[num_directions * seq_length * batch_size * fAttrHiddenSize];
219 std::vector<float> sum(fAttrHiddenSize);
220 for (size_t direction = 0; direction < num_directions; direction++) {
221 for (size_t h = 0; h < fAttrHiddenSize; h++) {
222 sum[h] = original_bias[direction * 2 * fAttrHiddenSize + h] +
223 original_bias[(2 * direction + 1) * fAttrHiddenSize + h];
224 }
225 for (size_t seq = 0; seq < seq_length; seq++) {
226 for (size_t batch = 0; batch < batch_size; batch++) {
227 size_t bias_offset = direction * seq_length * batch_size * fAttrHiddenSize +
228 seq * batch_size * fAttrHiddenSize + batch * fAttrHiddenSize;
229 std::copy(sum.begin(), sum.end(), new_bias + bias_offset);
230 }
231 }
232 }
233 std::vector<size_t> new_bias_shape = {num_directions, seq_length, batch_size, fAttrHiddenSize};
234 std::shared_ptr<void> new_bias_ptr(new_bias, std::default_delete<float[]>());
235 model.UpdateInitializedTensor(fNB, model.GetTensorType(fNB), new_bias_shape, new_bias_ptr);
236 fShapeB = model.GetTensorShape(fNB);
237 }
238 }
239 }
240 if (!fNSequence_lens.empty()) {
241 if (!model.CheckIfTensorAlreadyExist(fNSequence_lens)) {
242 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNSequence_lens + "is not found in model.");
243 }
244 fShapeSequence_lens = model.GetTensorShape(fNSequence_lens);
245 if (fShapeSequence_lens.size() != 1) {
246 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNSequence_lens + " is not of 1 dimension.");
247 }
248 }
249 if (!fNInitial_h.empty()) {
250 if (!model.CheckIfTensorAlreadyExist(fNInitial_h)) {
251 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNInitial_h + " is not found in model.");
252 }
253 fShapeInitial_h = model.GetTensorShape(fNInitial_h);
254 if (fShapeInitial_h.size() != 3) {
255 throw std::runtime_error("TMVA SOFIE RNN Op input tensor " + fNInitial_h + " is not of 3 dimensions.");
256 }
257 }
258 if (!fNY.empty()) {
259 fShapeY = ShapeInference({fShapeX, fShapeW})[0];
260 if (!model.CheckIfTensorAlreadyExist(fNY)) {
261 model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShapeY);
262 }
263 }
264 if (!fNY_h.empty()) {
265 fShapeY_h = ShapeInference({fShapeX, fShapeW})[1];
266 if (!model.CheckIfTensorAlreadyExist(fNY_h)) {
267 model.AddIntermediateTensor(fNY_h, model.GetTensorType(fNX), fShapeY_h);
268 }
269 }
270 // Check the attributes
271 for (auto &activation : fAttrActivations) {
272 if (activation != "Relu" && activation != "Tanh" && activation != "Sigmoid" && activation != "Affine" &&
273 activation != "LeakyRelu" && activation != "ThresholdRelu" && activation != "ScaledTanh" &&
274 activation != "HardSigmoid" && activation != "Elu" && activation != "Softsign" && activation != "Softplus") {
275 throw std::runtime_error("TMVA SOFIE - Activation function " + activation + " not implemented");
276 }
277 }
278 if (fAttrDirection != "forward" && fAttrDirection != "backward" && fAttrDirection != "bidirectional") {
279 throw std::runtime_error("TMVA SOFIE - Invalid RNN direction fAttrDirection = " + fAttrDirection);
280 }
281 if (fAttrHiddenSize != fShapeW[1]) {
282 throw std::runtime_error("TMVA SOFIE - fAttrHiddenSize must be equal to " + std::to_string(fShapeW[1]));
283 }
284 if (fAttrLayout > 1) {
285 throw std::runtime_error("TMVA SOFIE - Layout fAttrLayout = " + std::to_string(fAttrLayout) +
286 " must be 0 (timewise) or 1 (batchwise)");
287 }
288 if (fAttrActivations.empty()) {
289 if (fAttrDirection == "bidirectional") {
290 fAttrActivations = {"Tanh", "Tanh"};
291 } else {
292 fAttrActivations = {"Tanh"};
293 }
294 }
295 // Add needed standard library headers
296 model.AddNeededStdLib("cmath");
297}
298
299// generate code for Session data members (e.g. internal vectors)
300template <typename T>
302{
303 opName = "op_" + opName;
304 std::stringstream out;
305
306 size_t num_directions = fShapeW[0];
307 size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
308 size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
309 size_t input_size = fShapeX[2];
310
311 struct Block {
312 std::string name;
313 size_t size;
314 };
315
316 std::vector<Block> blocks;
317
318 if (fAttrLayout != 0) {
319 blocks.push_back({"input", seq_length * batch_size * input_size});
320 blocks.push_back({"initial_hidden_state", num_directions * batch_size * fAttrHiddenSize});
321 }
322 blocks.push_back({"feedforward", seq_length * batch_size * fAttrHiddenSize});
323 if (fAttrLayout != 0 || fNY.empty()) {
324 blocks.push_back({"hidden_state", seq_length * num_directions * batch_size * fAttrHiddenSize});
325 }
326
327 // Compute total size
328 size_t total_size = 0;
329 for (const auto &b : blocks) {
330 total_size += b.size;
331 }
332
333 // Emit backing storage
334 out << "std::vector<" << fType << "> fVec_" << opName << "_buffer = std::vector<" << fType << ">(" << total_size
335 << ");\n";
336
337 // Emit pointers
338 std::size_t offset = 0;
339 for (const auto &b : blocks) {
340 out << fType << "* fVec_" << opName << "_" << b.name << " = fVec_" << opName << "_buffer.data() + " << offset
341 << ";\n";
342 offset += b.size;
343 }
344
345 out << "\n";
346
347 return out.str();
348}
349
350//////////////////////////////////////////////////////////////////////////////////////////////////
351template <typename T>
352auto ROperator_RNN<T>::Generate(std::string OpName) -> std::string
353{
354 OpName = "op_" + OpName;
355 std::stringstream out;
356
357 size_t seq_length = (fAttrLayout == 0) ? fShapeX[0] : fShapeX[1];
358 size_t batch_size = (fAttrLayout == 0) ? fShapeX[1] : fShapeX[0];
359 size_t input_size = fShapeX[2];
360 size_t num_directions = fShapeW[0];
361
362 // set the input
363 if (fAttrLayout == 0) {
364 if (fType == "float") {
365 out << SP << "float const*" << OpName << "_input = tensor_" << fNX << ";\n";
366 }
367 } else {
368 if (fUseSession)
369 out << SP << fType << " * " << OpName << "_input = this->fVec_" << OpName << "_input;\n";
370 else
371 out << SP << fType << " " << OpName << "_input[" << seq_length * batch_size * input_size << "];\n";
372 out << SP << "for(size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
373 out << SP << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
374 out << SP << SP << SP << "for(size_t i = 0; i < " << input_size << "; i++) {\n";
375 out << SP << SP << SP << SP << OpName << "_input[seq * " << batch_size * input_size << " + batch * " << input_size
376 << " + i] = " << "tensor_" << fNX << "[batch * " << seq_length * input_size << " + seq * " << input_size
377 << " + i];\n";
378 out << SP << SP << SP << "}\n";
379 out << SP << SP << "}\n";
380 out << SP << "}\n";
381 }
382
383 // Set the initial hidden state
384 if (!fNInitial_h.empty()) {
385 if (fAttrLayout == 0) {
386 out << SP << fType << " *" << OpName << "_initial_hidden_state = " << " tensor_" << fNInitial_h << ";\n";
387 } else {
388 if (fUseSession)
389 out << SP << fType << " * " << OpName << "_initial_hidden_state = this->fVec_" << OpName
390 << "_initial_hidden_state;\n";
391 else
392 out << fType << " " << OpName << "_initial_hidden_state[" << num_directions * batch_size * fAttrHiddenSize
393 << "] = {0};\n";
394
395 for (size_t direction = 0; direction < num_directions; direction++) {
396 out << SP << "for(size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
397 out << SP << SP << "for(size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
398 out << SP << SP << SP << OpName << "_initial_hidden_state[" << direction * batch_size * fAttrHiddenSize
399 << " + batch * " << fAttrHiddenSize << " + h] = tensor_" << fNInitial_h << "[batch * "
400 << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << " + h];\n";
401 out << SP << SP << "}\n";
402 out << SP << "}\n";
403 }
404 }
405 }
406
407 if (fUseSession)
408 out << SP << fType << " * " << OpName << "_feedforward = this->fVec_" << OpName << "_feedforward;\n";
409 else
410 out << SP << fType << " " << OpName << "_feedforward[" << seq_length * batch_size * fAttrHiddenSize
411 << "] = {0};\n";
412
413 // Set the hidden state
414 if (fAttrLayout == 0 && !fNY.empty()) {
415 out << SP << fType << " *" << OpName << "_hidden_state = tensor_" << fNY << ";\n";
416 } else {
417 if (fUseSession)
418 out << SP << fType << " * " << OpName << "_hidden_state = this->fVec_" << OpName << "_hidden_state;\n";
419 else
420 out << SP << fType << " " << OpName << "_hidden_state["
421 << seq_length * num_directions * batch_size * fAttrHiddenSize << "] = {0};\n";
422 }
423
424 out << SP << "char " << OpName << "_transA = 'N';\n";
425 out << SP << "char " << OpName << "_transB = 'T';\n";
426 out << SP << "int " << OpName << "_m = " << seq_length * batch_size << ";\n";
427 out << SP << "int " << OpName << "_n = " << fAttrHiddenSize << ";\n";
428 out << SP << "int " << OpName << "_k = " << input_size << ";\n";
429 if (fType == "float") {
430 out << SP << "float " << OpName << "_alpha = 1.;\n";
431 out << SP << "float " << OpName << "_beta = .0;\n";
432 }
433 if (!fNB.empty()) {
434 out << SP << "int " << OpName << "_bias_size = " << seq_length * batch_size * fAttrHiddenSize << ";\n";
435 out << SP << "int " << OpName << "_incx = 1;\n";
436 out << SP << "int " << OpName << "_incy = 1;\n";
437 }
438
439 for (size_t direction = 0; direction < num_directions; direction++) {
440 // feedforward = input * W^T + bias
441 if (fType == "float") {
442 if (direction == 0) {
443 out << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName << "_n, &"
444 << OpName << "_m, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNW << ", &" << OpName
445 << "_k, " << OpName << "_input, &" << OpName << "_k, &" << OpName << "_beta, " << OpName
446 << "_feedforward, &" << OpName << "_n);\n";
447 } else {
448 out << SP << "size_t " << OpName << "_w_offset = " << fAttrHiddenSize * input_size << ";\n";
449 out << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName << "_n, &"
450 << OpName << "_m, &" << OpName << "_k, &" << OpName << "_alpha, tensor_" << fNW << " + " << OpName
451 << "_w_offset, &" << OpName << "_k, " << OpName << "_input, &" << OpName << "_k, &" << OpName
452 << "_beta, " << OpName << "_feedforward, &" << OpName << "_n);\n";
453 }
454 }
455 // Add the bias
456 if (!fNB.empty()) {
457 if (fType == "float") {
458 if (direction == 0) {
459 out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << ", &"
460 << OpName << "_incx, " << OpName << "_feedforward, &" << OpName << "_incy);\n";
461 } else {
462 out << SP << "size_t " << OpName << "_bias_offset = " << seq_length * batch_size * fAttrHiddenSize
463 << ";\n";
464 out << SP << "BLAS::saxpy_(&" << OpName << "_bias_size, &" << OpName << "_alpha, tensor_" << fNB << " + "
465 << OpName << "_bias_offset, &" << OpName << "_incx, " << OpName << "_feedforward, &" << OpName
466 << "_incy);\n";
467 }
468 }
469 }
470
471 // Copy feedforward into hidden state
472 out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
473 out << SP << SP << "size_t offset = seq * " << batch_size * fAttrHiddenSize << ";\n";
474 out << SP << SP << "size_t size = " << batch_size * fAttrHiddenSize << ";\n";
475 out << SP << SP << "size_t h_offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
476 << direction * batch_size * fAttrHiddenSize << ";\n";
477 out << SP << SP << "std::copy(" << OpName << "_feedforward + offset, " << OpName
478 << "_feedforward + offset + size, " << OpName << "_hidden_state + h_offset);\n";
479 out << SP << "}\n";
480
481 out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
482 if (fAttrDirection == "backward" || direction == 1) {
483 out << SP << SP << "size_t index = " << seq_length - 1 << " - seq;\n";
484 } else {
485 out << SP << SP << "size_t index = seq;\n";
486 }
487
488 out << SP << SP << "int m2 = " << batch_size << ";\n";
489 out << SP << SP << "size_t offset = index * " << num_directions * batch_size * fAttrHiddenSize << " + "
490 << direction * batch_size * fAttrHiddenSize << ";\n";
491 out << SP << SP << "size_t size = " << batch_size * fAttrHiddenSize << ";\n";
492 out << SP << SP << "if (seq == 0) {\n";
493 if (!fNInitial_h.empty()) {
494 // hidden_state = hidden_state + initial_hidden_state * R^T
495 out << SP << SP << SP << "size_t r_offset = " << direction * fAttrHiddenSize * fAttrHiddenSize << ";\n";
496 out << SP << SP << SP << "size_t initial_hidden_state_offset = " << direction * batch_size * fAttrHiddenSize
497 << ";\n";
498 if (fType == "float") {
499 out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
500 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + r_offset, &" << OpName
501 << "_n, " << OpName << "_initial_hidden_state + initial_hidden_state_offset, &" << OpName << "_n, &"
502 << OpName << "_alpha, " << OpName << "_hidden_state + offset, &" << OpName << "_n);\n";
503 }
504 }
505 out << SP << SP << "} else {\n";
506 // hidden_state = hidden_state + previous_hidden_state * R^T
507 out << SP << SP << SP << "size_t r_offset = " << direction * fAttrHiddenSize * fAttrHiddenSize << ";\n";
508 if (fAttrDirection == "backward" || direction == 1) {
509 out << SP << SP << SP << "size_t previous_offset = (index + 1) * "
510 << num_directions * batch_size * fAttrHiddenSize << " + " << direction * batch_size * fAttrHiddenSize
511 << ";\n";
512 } else {
513 out << SP << SP << SP << "size_t previous_offset = (seq - 1) * "
514 << num_directions * batch_size * fAttrHiddenSize << " + " << direction * batch_size * fAttrHiddenSize
515 << ";\n";
516 }
517 if (fType == "float") {
518 out << SP << SP << SP << "BLAS::sgemm_(&" << OpName << "_transB, &" << OpName << "_transA, &" << OpName
519 << "_n, &m2, &" << OpName << "_n, &" << OpName << "_alpha, tensor_" << fNR << " + r_offset, &" << OpName
520 << "_n, " << OpName << "_hidden_state + previous_offset, &" << OpName << "_n, &" << OpName << "_alpha, "
521 << OpName << "_hidden_state + offset, &" << OpName << "_n);\n";
522 }
523 out << SP << SP << "}\n";
524
525 // Clip the elements of the hidden state into the range [-fAttrClip, fAttrClip]
526 if (fAttrClip > .0) {
527 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
528 if (fType == "float") {
529 out << SP << SP << SP << "float x = (" << OpName << "_hidden_state[i] > " << -fAttrClip << ") ? " << OpName
530 << "_hidden_state[i] : " << -fAttrClip << ";\n";
531 }
532 out << SP << SP << SP << OpName << "_hidden_state[i] = (x < " << fAttrClip << ") ? x : " << fAttrClip << ";\n";
533 out << SP << SP << "}\n";
534 }
535
536 // Apply the activation function to the hidden state
537 if (fAttrActivations[direction] == "Relu") {
538 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
539 out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
540 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 0.;\n";
541 out << SP << SP << "}\n";
542 } else if (fAttrActivations[direction] == "Tanh") {
543 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
544 if (fType == "float") {
545 out << SP << SP << SP << "float ex = std::exp(-2 * " << OpName << "_hidden_state[i]);\n";
546 }
547 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = (1. - ex) / (1. + ex);\n";
548 out << SP << SP << "}\n";
549 } else if (fAttrActivations[direction] == "Sigmoid") {
550 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
551 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 1. / (1. + std::exp(-" << OpName
552 << "_hidden_state[i]));\n";
553 out << SP << SP << "}\n";
554 } else if (fAttrActivations[direction] == "Affine") {
555 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
556 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction] << " * "
557 << OpName << "_hidden_state[i] + " << fAttrActivationBeta[direction] << ";\n";
558 out << SP << SP << "}\n";
559 } else if (fAttrActivations[direction] == "ScaledTanh") {
560 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
561 if (fType == "float") {
562 out << SP << SP << SP << "float ex = std::exp(-2 * " << fAttrActivationBeta[direction] << " * " << OpName
563 << "_hidden_state[i]);\n";
564 }
565 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction]
566 << " * (1. - ex) / (1. + ex);\n";
567 out << SP << SP << "}\n";
568 } else if (fAttrActivations[direction] == "HardSigmoid") {
569 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
570 if (fType == "float") {
571 out << SP << SP << SP << "float a = " << fAttrActivationAlpha[direction] << " * " << OpName
572 << "_hidden_state[i] + " << fAttrActivationBeta[direction] << ";\n";
573 out << SP << SP << SP << "float b = (a > 0.) ? a : 0.;\n";
574 }
575 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = (b < 1.) ? b : 1.;\n";
576 out << SP << SP << "}\n";
577 } else if (fAttrActivations[direction] == "LeakyRelu") {
578 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
579 out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
580 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction] << " * "
581 << OpName << "_hidden_state[i];\n";
582 out << SP << SP << "}\n";
583 } else if (fAttrActivations[direction] == "ThresholdRelu") {
584 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
585 out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < " << fAttrActivationAlpha[direction] << ")\n";
586 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = 0.;\n";
587 out << SP << SP << "}";
588 } else if (fAttrActivations[direction] == "Elu") {
589 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
590 out << SP << SP << SP << "if (" << OpName << "_hidden_state[i] < 0.)\n";
591 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << fAttrActivationAlpha[direction]
592 << " * std::exp(" << OpName << "_hidden_state[i] - 1.);\n";
593 out << SP << SP << "}\n";
594 } else if (fAttrActivations[direction] == "Softsign") {
595 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
596 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = " << OpName << "_hidden_state[i] / (1. + abs("
597 << OpName << "_hidden_state[i]));\n";
598 out << SP << SP << "}\n";
599 } else { // fAttrActivations[direction] = Softplus
600 out << SP << SP << "for (size_t i = offset; i < offset + size; i++) {\n";
601 out << SP << SP << SP << SP << OpName << "_hidden_state[i] = log(1. + std::exp(" << OpName
602 << "_hidden_state[i]));\n";
603 out << SP << SP << "}\n";
604 out << SP << "}\n";
605 }
606 out << SP << "}\n";
607 }
608
609 // Padding the hidden state for RNN with different sequence lengths
610 if (!fNSequence_lens.empty()) {
611 out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
612 out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
613 out << SP << SP << SP << "if (seq >= tensor_" << fNSequence_lens << "[batch]) {\n";
614 out << SP << SP << SP << SP << "for (size_t h = 0; h < " << fAttrHiddenSize << "; h++) {\n";
615 if (num_directions == 1) {
616 out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
617 << num_directions * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << " + h] = 0.;\n";
618 } else {
619 out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
620 << num_directions * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << " + h] = 0.;\n";
621 out << SP << SP << SP << SP << SP << OpName << "_hidden_state[seq * "
622 << num_directions * batch_size * fAttrHiddenSize << " + " << batch_size * fAttrHiddenSize << " + batch * "
623 << fAttrHiddenSize << " + h] = 0.;\n";
624 }
625 out << SP << SP << SP << SP << "}\n";
626 out << SP << SP << SP << "}\n";
627 out << SP << SP << "}\n";
628 out << SP << "}\n";
629 }
630
631 // Copy the hidden state into y and y_h
632 if (fAttrLayout == 0) {
633 if (!fNY_h.empty()) {
634 if (fNSequence_lens.empty()) {
635 size_t yh_size = batch_size * fAttrHiddenSize;
636 if (fAttrDirection == "backward") {
637 out << SP << "std::copy(" << OpName << "_hidden_state, " << OpName << "_hidden_state + " << yh_size
638 << ", tensor_" << fNY_h << ");\n";
639 } else {
640 size_t offset = (seq_length - 1) * num_directions * batch_size * fAttrHiddenSize;
641 out << SP << "std::copy(" << OpName << "_hidden_state + " << offset << ", " << OpName
642 << "_hidden_state + " << offset << " + " << yh_size << ", tensor_" << fNY_h << ");\n";
643 }
644 if (num_directions == 2) {
645 out << SP << "std::copy(" << OpName << "_hidden_state + " << yh_size << ", " << OpName
646 << "_hidden_state + " << 2 * yh_size << ", tensor_" << fNY_h << " + " << yh_size << ");\n";
647 }
648 } else { // RNN with different sequence lengths
649 if (fAttrDirection == "backward") {
650 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
651 out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
652 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
653 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + offset);\n";
654 out << SP << "}\n";
655 } else {
656 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
657 out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
658 out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
659 << " + batch * " << fAttrHiddenSize << ";\n";
660 out << SP << SP << "size_t yh_offset = batch * " << fAttrHiddenSize << ";\n";
661 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
662 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
663 out << SP << "}\n";
664 }
665 if (num_directions == 2) {
666 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
667 out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
668 << ";\n";
669 out << SP << SP << "size_t yh_offset = " << batch_size * fAttrHiddenSize << " + batch * "
670 << fAttrHiddenSize << ";\n";
671 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
672 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
673 out << SP << "}\n";
674 }
675 }
676 }
677 } else { // fAttrLayout=1
678 if (!fNY.empty()) {
679 for (size_t direction = 0; direction < num_directions; direction++) {
680 out << SP << "for (size_t seq = 0; seq < " << seq_length << "; seq++) {\n";
681 out << SP << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
682 out << SP << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize << " + "
683 << direction * batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize << ";\n";
684 out << SP << SP << SP << "size_t y_offset = batch * " << seq_length * num_directions * fAttrHiddenSize
685 << " + seq * " << num_directions * fAttrHiddenSize << " + " << direction * fAttrHiddenSize << ";\n";
686 out << SP << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
687 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY << " + y_offset);\n";
688 out << SP << SP << "}\n";
689 out << SP << "}\n";
690 }
691 }
692 if (!fNY_h.empty()) {
693 if (fAttrDirection == "backward") {
694 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
695 out << SP << SP << "size_t offset = batch * " << fAttrHiddenSize << ";\n";
696 out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
697 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
698 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
699 out << SP << "}\n";
700 } else {
701 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
702 if (fNSequence_lens.empty()) {
703 out << SP << SP << "size_t seq = " << seq_length - 1 << ";\n";
704 } else {
705 out << SP << SP << "size_t seq = " << "tensor_" << fNSequence_lens << "[batch] - 1;\n";
706 }
707 out << SP << SP << "size_t offset = seq * " << num_directions * batch_size * fAttrHiddenSize
708 << " + batch * " << fAttrHiddenSize << ";\n";
709 out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << ";\n";
710 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
711 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
712 out << SP << "}\n";
713 }
714 if (num_directions == 2) {
715 out << SP << "for (size_t batch = 0; batch < " << batch_size << "; batch++) {\n";
716 out << SP << SP << "size_t offset = " << batch_size * fAttrHiddenSize << " + batch * " << fAttrHiddenSize
717 << ";\n";
718 out << SP << SP << "size_t yh_offset = batch * " << num_directions * fAttrHiddenSize << " + "
719 << fAttrHiddenSize << ";\n";
720 out << SP << SP << "std::copy(" << OpName << "_hidden_state + offset, " << OpName
721 << "_hidden_state + offset + " << fAttrHiddenSize << ", tensor_" << fNY_h << " + yh_offset);\n";
722 out << SP << "}\n";
723 }
724 }
725 }
726
727 return out.str();
728}
729
730} // namespace TMVA::Experimental::SOFIE
731
732#endif
#define b(i)
Definition RSha256.hxx:100
#define h(i)
Definition RSha256.hxx:106
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
char name[80]
Definition TGX11.cxx:146
const_iterator begin() const
const_iterator end() const
Recurrent Neural Network operator.
std::vector< size_t > fShapeB
Shape of the bias.
std::vector< float > fAttrActivationBeta
Scaling values used by some activation functions.
size_t fAttrHiddenSize
Number of the hidden layers.
std::string fNInitial_h
Name of the initial value of the hidden states.
ROperator_RNN(std::vector< float > activation_alpha, std::vector< float > activation_beta, std::vector< std::string > activations, float clip, std::string direction, size_t hidden_size, size_t layout, std::string nameX, std::string nameW, std::string nameR, std::string nameB, std::string nameSequence_lens, std::string nameInitial_h, std::string nameY, std::string nameY_h)
Constructor of ROperator_RNN from the attributes.
void Initialize(RModel &) override
Initialize the model.
std::vector< size_t > fShapeR
Shape of the recurrence.
std::string fNW
Name of the weights.
std::vector< ETensorType > TypeInference(std::vector< ETensorType > input) override
Infers the type of the output tensors.
std::vector< size_t > fShapeY
Shape of the output.
std::string fType
Type of the tensors.
std::string fNY
Name of the output.
std::string fNSequence_lens
Name of the length of the sequences.
std::vector< size_t > fShapeSequence_lens
Shape of the length of the sequences.
std::vector< std::string > GetBlasRoutines() override
Returns the blas routines needed to compile the generated code.
std::string fNR
Name of the recurrence.
std::string GenerateSessionMembersCode(std::string opName) override
std::vector< float > fAttrActivationAlpha
Scaling values used by some activation functions.
ROperator_RNN()
Default constructor of ROperator_RNN.
std::vector< size_t > fShapeX
Shape of the input.
std::string fAttrDirection
Direction of processing.
std::vector< std::vector< size_t > > ShapeInference(std::vector< std::vector< size_t > > input) override
Infers the shape of the output tensors.
std::string fNX
Name of the input.
std::string Generate(std::string OpName) override
Generates the inference code.
std::string fNY_h
Name of the last sequence of the output.
std::vector< size_t > fShapeInitial_h
Shape of the initial value of the hidden states.
std::vector< size_t > fShapeW
Shape of the weights.
std::vector< std::string > fAttrActivations
Activation functions.
std::vector< size_t > fShapeY_h
Shape of the last sequence of the output.
std::vector< std::string_view > fInputTensorNames
Definition ROperator.hxx:47
std::vector< std::string_view > fOutputTensorNames
Definition ROperator.hxx:48
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338