diff --git a/tmva/pymva/src/RModelParser_Keras.cxx b/tmva/pymva/src/RModelParser_Keras.cxx index 47e968b1e1d19..fab76bb2a0e75 100644 --- a/tmva/pymva/src/RModelParser_Keras.cxx +++ b/tmva/pymva/src/RModelParser_Keras.cxx @@ -42,8 +42,10 @@ void AddKerasLayer(RModel &rmodel, PyObject *fLayer); // Declaring Internal Functions for Keras layers which don't have activation as an additional attribute std::unique_ptr MakeKerasActivation(PyObject *fLayer); // For instantiating ROperator for Keras Activation Layer std::unique_ptr MakeKerasReLU(PyObject *fLayer); // For instantiating ROperator for Keras ReLU layer +std::unique_ptr MakeKerasLeakyReLU(PyObject *fLayer); // For instantiating ROperator for Keras Leaky ReLU layer std::unique_ptr MakeKerasSelu(PyObject *fLayer); // For instantiating ROperator for Keras Selu layer std::unique_ptr MakeKerasSigmoid(PyObject *fLayer); // For instantiating ROperator for Keras Sigmoid layer +std::unique_ptr MakeKerasSoftmax(PyObject *fLayer); // For instantiating ROperator for Keras Softmax layer std::unique_ptr MakeKerasPermute(PyObject *fLayer); // For instantiating ROperator for Keras Permute Layer // Declaring Internal function for Keras layers which have additional activation attribute @@ -59,9 +61,11 @@ const KerasMethodMap mapKerasLayer = { // For activation layers {"ReLU", &MakeKerasReLU}, + {"LeakyReLU", &MakeKerasLeakyReLU}, // For layers with activation attributes {"relu", &MakeKerasReLU}, + {"leakyRelu", &MakeKerasLeakyReLU}, {"selu", &MakeKerasSelu}, {"sigmoid", &MakeKerasSigmoid} }; @@ -75,8 +79,9 @@ const KerasMethodMapWithActivation mapKerasLayerWithActivation = { /// \brief Adds equivalent ROperator with respect to Keras model layer /// into the referenced RModel object /// -/// \param[inout] rmodel RModel object, by reference, returned ith the added ROperator +/// \param[in] rmodel RModel object /// \param[in] fLayer Python Keras layer as a Dictionary object +/// \param[out] RModel object with the added ROperator /// /// Function adds equivalent ROperator into the referenced RModel object. /// Keras models can have layers like Dense and Conv which have activation @@ -259,6 +264,35 @@ std::unique_ptr MakeKerasReLU(PyObject* fLayer) } +////////////////////////////////////////////////////////////////////////////////// +/// \brief Prepares a ROperator object for Keras Leaky ReLU activation +/// +/// \param[in] fLayer Python Keras layer as a Dictionary object +/// \return Unique pointer to ROperator object +/// +/// For instantiating a ROperator_Leaky_Relu object, the names of +/// input & output tensors and the deta-type of the layer are extracted. +std::unique_ptr MakeKerasLeakyReLU(PyObject* fLayer) +{ + PyObject* fInputs=PyDict_GetItemString(fLayer,"layerInput"); + PyObject* fOutputs=PyDict_GetItemString(fLayer,"layerOutput"); + + std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType")); + std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0)); + std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0)); + + std::unique_ptr op; + switch(ConvertStringToType(fLayerDType)){ + case ETensorType::FLOAT: + op.reset(new ROperator_Leaky_Relu(fLayerInputName, fLayerOutputName)); + break; + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Leaky Relu does not yet support input type " + fLayerDType); + } + return op; +} + + ////////////////////////////////////////////////////////////////////////////////// /// \brief Prepares a ROperator object for Keras Selu activation /// @@ -315,6 +349,34 @@ std::unique_ptr MakeKerasSigmoid(PyObject* fLayer){ } +////////////////////////////////////////////////////////////////////////////////// +/// \brief Prepares a ROperator object for Keras Softmax activation +/// +/// \param[in] fLayer Python Keras layer as a Dictionary object +/// \return Unique pointer to ROperator object +/// +/// For instantiating a ROperator_Softmax object, the names of +/// input & output tensors and the deta-type of the layer are extracted. +std::unique_ptr MakeKerasSoftmax(PyObject* fLayer){ + PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput"); + PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput"); + + std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType")); + std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0)); + std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0)); + + std::unique_ptr op; + switch(ConvertStringToType(fLayerDType)){ + case ETensorType::FLOAT: + op.reset(new ROperator_Softmax(fLayerInputName, fLayerOutputName)); + break; + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Softmax does not yet support input type " + fLayerDType); + } + return op; +} + + ////////////////////////////////////////////////////////////////////////////////// /// \brief Prepares a ROperator object for Keras Permute layer /// diff --git a/tmva/pymva/src/RModelParser_PyTorch.cxx b/tmva/pymva/src/RModelParser_PyTorch.cxx index cfbc57b08ec13..5f678635270e6 100644 --- a/tmva/pymva/src/RModelParser_PyTorch.cxx +++ b/tmva/pymva/src/RModelParser_PyTorch.cxx @@ -44,12 +44,14 @@ namespace INTERNAL{ // For searching and calling specific preparatory function for PyTorch ONNX Graph's node std::unique_ptr MakePyTorchNode(PyObject* fNode); -std::unique_ptr MakePyTorchGemm(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Gemm operator -std::unique_ptr MakePyTorchConv(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Conv operator -std::unique_ptr MakePyTorchRelu(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Relu operator -std::unique_ptr MakePyTorchSelu(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Selu operator -std::unique_ptr MakePyTorchSigmoid(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Sigmoid operator -std::unique_ptr MakePyTorchTranspose(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Transpose operator +std::unique_ptr MakePyTorchGemm(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Gemm operator +std::unique_ptr MakePyTorchConv(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Conv operator +std::unique_ptr MakePyTorchRelu(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Relu operator +std::unique_ptr MakePyTorchLeakyRelu(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Relu operator +std::unique_ptr MakePyTorchSelu(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Selu operator +std::unique_ptr MakePyTorchSigmoid(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Sigmoid operator +std::unique_ptr MakePyTorchSoftmax(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Softmax operator +std::unique_ptr MakePyTorchTranspose(PyObject* fNode); // For instantiating ROperator for PyTorch ONNX's Transpose operator // For mapping PyTorch ONNX Graph's Node with the preparatory functions for ROperators using PyTorchMethodMap = std::unordered_map (*)(PyObject* fNode)>; @@ -59,8 +61,10 @@ const PyTorchMethodMap mapPyTorchNode = {"onnx::Gemm", &MakePyTorchGemm}, {"onnx::Conv", &MakePyTorchConv}, {"onnx::Relu", &MakePyTorchRelu}, + {"onnx::LeakyRelu",&MakePyTorchLeakyRelu}, {"onnx::Selu", &MakePyTorchSelu}, {"onnx::Sigmoid", &MakePyTorchSigmoid}, + {"onnx::Softmax", &MakePyTorchSoftmax}, {"onnx::Transpose", &MakePyTorchTranspose} }; @@ -175,6 +179,33 @@ std::unique_ptr MakePyTorchRelu(PyObject* fNode){ return op; } +////////////////////////////////////////////////////////////////////////////////// +/// \brief Prepares a ROperator_Leaky_Relu object +/// +/// \param[in] fNode Python PyTorch ONNX Graph node +/// \return Unique pointer to ROperator object +/// +/// For instantiating a ROperator_Leaky_Relu object, the names of +/// input & output tensors and the data-type of the Graph node +/// are extracted. +std::unique_ptr MakePyTorchLeakyRelu(PyObject* fNode){ + PyObject* fInputs = PyDict_GetItemString(fNode,"nodeInputs"); + PyObject* fOutputs = PyDict_GetItemString(fNode,"nodeOutputs"); + std::string fNodeDType = PyStringAsString(PyList_GetItem(PyDict_GetItemString(fNode,"nodeDType"),0)); + std::string fNameX = PyStringAsString(PyList_GetItem(fInputs,0)); + std::string fNameY = PyStringAsString(PyList_GetItem(fOutputs,0)); + std::unique_ptr op; + switch(ConvertStringToType(fNodeDType)){ + case ETensorType::FLOAT: { + op.reset(new ROperator_Leaky_Relu(fNameX,fNameY)); + break; + } + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Leaky Relu does not yet support input type " + fNodeDType); + } + return op; +} + ////////////////////////////////////////////////////////////////////////////////// /// \brief Prepares a ROperator_Selu object /// @@ -227,6 +258,31 @@ std::unique_ptr MakePyTorchSigmoid(PyObject* fNode){ return op; } +////////////////////////////////////////////////////////////////////////////////// +/// \brief Prepares a ROperator_Softmax object +/// +/// \param[in] fNode Python PyTorch ONNX Graph node +/// \return Unique pointer to ROperator object +/// +/// For instantiating a ROperator_Softmax object, the names of +/// input & output tensors and the data-type of the Graph node +/// are extracted. +std::unique_ptr MakePyTorchSoftmax(PyObject* fNode){ + PyObject* fInputs = PyDict_GetItemString(fNode,"nodeInputs"); + PyObject* fOutputs = PyDict_GetItemString(fNode,"nodeOutputs"); + std::string fNodeDType = PyStringAsString(PyList_GetItem(PyDict_GetItemString(fNode,"nodeDType"),0)); + + std::unique_ptr op; + switch(ConvertStringToType(fNodeDType)){ + case ETensorType::FLOAT: { + op.reset(new ROperator_Softmax(PyStringAsString(PyList_GetItem(fInputs,0)), PyStringAsString(PyList_GetItem(fOutputs,0)))); + break; + } + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Softmax does not yet support input type " + fNodeDType); + } + return op; +} ////////////////////////////////////////////////////////////////////////////////// /// \brief Prepares a ROperator_Transpose object @@ -419,7 +475,7 @@ RModel Parse(std::string filename, std::vector> inputShapes, //Getting the ONNX graph from model using the dummy inputs and example outputs PyRunString("_set_onnx_shape_inference(True)",fGlobalNS,fLocalNS); - PyRunString("graph=_model_to_graph(model,dummyInputs,example_outputs=output)",fGlobalNS,fLocalNS); + PyRunString("graph=_model_to_graph(model,dummyInputs)",fGlobalNS,fLocalNS); //Extracting the model information in list modelData @@ -539,7 +595,7 @@ RModel Parse(std::string filename, std::vector> inputShapes, } ////////////////////////////////////////////////////////////////////////////////// -/// \param[in] filepath file location of PyTorch .pt model +/// \param[in] filename file location of PyTorch .pt model /// \param[in] inputShapes vector of input shape vectors /// \return Parsed RModel object /// diff --git a/tmva/sofie/CMakeLists.txt b/tmva/sofie/CMakeLists.txt index ba524b06ea5f7..18d09a367491e 100644 --- a/tmva/sofie/CMakeLists.txt +++ b/tmva/sofie/CMakeLists.txt @@ -21,9 +21,11 @@ ROOT_STANDARD_LIBRARY_PACKAGE(ROOTTMVASofie TMVA/ROperator_Conv.hxx TMVA/ROperator_Gemm.hxx TMVA/ROperator_Relu.hxx + TMVA/ROperator_Leaky_Relu.hxx TMVA/ROperator_Reshape.hxx TMVA/ROperator_Selu.hxx TMVA/ROperator_Sigmoid.hxx + TMVA/ROperater_Softmax.hxx TMVA/ROperator_Slice.hxx TMVA/ROperator_Transpose.hxx TMVA/ROperator_Pool.hxx diff --git a/tmva/sofie/inc/TMVA/ROperater_Softmax.hxx b/tmva/sofie/inc/TMVA/ROperater_Softmax.hxx new file mode 100644 index 0000000000000..f30ba6cc647a1 --- /dev/null +++ b/tmva/sofie/inc/TMVA/ROperater_Softmax.hxx @@ -0,0 +1,76 @@ +#ifndef TMVA_SOFIE_ROPERATOR_Softmax + #define TMVA_SOFIE_ROPERATOR_Softmax + + #include "TMVA/SOFIE_common.hxx" + #include "TMVA/ROperator.hxx" + #include "TMVA/RModel.hxx" + + #include + + namespace TMVA{ + namespace Experimental{ + namespace SOFIE{ + + template + class ROperator_Softmax final : public ROperator + { + + private: + + std::string fNX; + std::string fNY; + std::vector fShape; + + public: + ROperator_Softmax(){} + ROperator_Softmax(std::string nameX, std::string nameY): + fNX(UTILITY::Clean_name(nameX)), fNY(UTILITY::Clean_name(nameY)){} + + std::vector TypeInference(std::vector input){ + return input; + } + + std::vector> ShapeInference(std::vector> input){ + auto ret = input; //suggest copy to compiler + return ret; + } + + void Initialize(RModel& model){ + if (model.CheckIfTensorAlreadyExist(fNX) == false){ //input must be a graph input, or already initialized intermediate tensor + throw std::runtime_error("TMVA SOFIE Softmax Op Input Tensor is not found in model"); + } + fShape = model.GetTensorShape(fNX); + model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShape); + } + + + std::string Generate(std::string OpName){ + OpName = "op_" + OpName; + if (fShape.empty()){ + throw std::runtime_error("TMVA SOFIE Transpose Softmax called to Generate without being initialized first"); + } + std::stringstream out; + int length = 1; + for(auto& i: fShape){ + length *= i; + } + out << "\n//------ SOFTMAX\n"; + out << SP << "double sum = 0.0;\n"; + out << SP << "for (int id = 0; id < " << length << " ; id++){\n"; + out << SP << SP << "tensor_" << fNY << "[id] = std::exp( - tensor_" << fNX << "[id]);\n"; + out << SP << SP << "sum += tensor_" << fNY << "[id];\n"; + out << SP << "}\n"; + out << SP << "for (int id = 0; id < " << length << " ; id++){\n"; + out << SP << SP << "tensor_" << fNY << "[id] /= sum;\n"; + out << SP << "}\n"; + return out.str(); + } + + }; + + }//SOFIE + }//Experimental + }//TMVA + + + #endif //TMVA_SOFIE_ROPERATOR_Softmax diff --git a/tmva/sofie/inc/TMVA/ROperator_Leaky_Relu.hxx b/tmva/sofie/inc/TMVA/ROperator_Leaky_Relu.hxx new file mode 100644 index 0000000000000..f94db4e325672 --- /dev/null +++ b/tmva/sofie/inc/TMVA/ROperator_Leaky_Relu.hxx @@ -0,0 +1,71 @@ +#ifndef TMVA_SOFIE_ROPERATOR_LEAKY_RELU +#define TMVA_SOFIE_ROPERATOR_LEAKY_RELU + +#include "TMVA/SOFIE_common.hxx" +#include "TMVA/ROperator.hxx" +#include "TMVA/RModel.hxx" + +#include + +namespace TMVA{ +namespace Experimental{ +namespace SOFIE{ + +template +class ROperator_Leaky_Relu final : public ROperator +{ + +private: + + std::string fNX; + std::string fNY; + std::vector fShape; + +public: + ROperator_Leaky_Relu(){} + ROperator_Leaky_Relu(std::string nameX, std::string nameY): + fNX(UTILITY::Clean_name(nameX)), fNY(UTILITY::Clean_name(nameY)){} + + std::vector TypeInference(std::vector input){ + return input; + } + + std::vector> ShapeInference(std::vector> input){ + auto ret = input; //suggest copy to compiler + return ret; + } + + void Initialize(RModel& model){ + if (model.CheckIfTensorAlreadyExist(fNX) == false){ //input must be a graph input, or already initialized intermediate tensor + throw std::runtime_error("TMVA SOFIE Leaky Relu Op Input Tensor is not found in model"); + } + fShape = model.GetTensorShape(fNX); + model.AddIntermediateTensor(fNY, model.GetTensorType(fNX), fShape); + } + + + std::string Generate(std::string OpName){ + OpName = "op_" + OpName; + if (fShape.empty()) { + throw std::runtime_error("TMVA SOFIE Transpose Leaky Relu called to Generate without being initialized first"); + } + std::stringstream out; + int length = 1; + for(auto& i: fShape){ + length *= i; + } + out << "\n//------ LEAKY RELU\n"; + out << SP << "for (int id = 0; id < " << length << " ; id++){\n"; + out << SP << SP << "tensor_" << fNY << "[id] = ((tensor_" << fNX << "[id] > 0 )? tensor_" << fNX << "[id] : 0.01 * tensor_"<< fNX<<");\n"; + out << SP << "}\n"; + return out.str(); + } + +}; + +}//SOFIE +}//Experimental +}//TMVA + + +#endif //TMVA_SOFIE_ROPERATOR_RELU diff --git a/tmva/sofie/test/TestCustomModelsFromONNX.cxx b/tmva/sofie/test/TestCustomModelsFromONNX.cxx index 867103a4e051a..4710ca32a079c 100644 --- a/tmva/sofie/test/TestCustomModelsFromONNX.cxx +++ b/tmva/sofie/test/TestCustomModelsFromONNX.cxx @@ -15,6 +15,12 @@ #include "LinearWithSigmoid_FromONNX.hxx" #include "input_models/references/LinearWithSigmoid.ref.hxx" +#include "LinearWithSoftmax_FromONNX.hxx" +#include "input_models/references/LinearWithSoftmax.ref.hxx" + +#include "LinearWithLeakyRelu_FromONNX.hxx" +#include "input_models/references/LinearWithLeakyRelu.ref.hxx" + #include "ConvWithPadding_FromONNX.hxx" #include "input_models/references/ConvWithPadding.ref.hxx" @@ -176,6 +182,28 @@ TEST(ONNX, LinearWithSelu) } +TEST(ONNX, LinearWithLeakyRelu) +{ + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard all-ones input + std::vector input(48); + std::fill_n(input.data(), input.size(), 1.0f); + TMVA_SOFIE_LinearWithLeakyRelu::Session s("LinearWithLeakyRelu_FromONNX.dat"); + std::vector output = s.infer(input.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(LinearWithLeakyRelu_ExpectedOutput::all_ones) / sizeof(float)); + + float *correct = LinearWithLeakyRelu_ExpectedOutput::all_ones; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } +} + + TEST(ONNX, LinearWithSigmoid) { constexpr float TOLERANCE = DEFAULT_TOLERANCE; @@ -198,6 +226,28 @@ TEST(ONNX, LinearWithSigmoid) } +TEST(ONNX, LinearWithSoftmax) +{ + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard all-ones input + std::vector input(48); + std::fill_n(input.data(), input.size(), 1.0f); + TMVA_SOFIE_LinearWithSoftmax::Session s("LinearWithSoftmax_FromONNX.dat"); + std::vector output = s.infer(input.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(LinearWithSoftmax_ExpectedOutput::all_ones) / sizeof(float)); + + float *correct = LinearWithSoftmax_ExpectedOutput::all_ones; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } +} + + TEST(ONNX, ConvWithPadding) { constexpr float TOLERANCE = DEFAULT_TOLERANCE; diff --git a/tmva/sofie/test/TestCustomModelsFromROOT.cxx b/tmva/sofie/test/TestCustomModelsFromROOT.cxx index 75fed247e3efd..ae13db81a1125 100644 --- a/tmva/sofie/test/TestCustomModelsFromROOT.cxx +++ b/tmva/sofie/test/TestCustomModelsFromROOT.cxx @@ -15,6 +15,12 @@ #include "LinearWithSigmoid_FromROOT.hxx" #include "input_models/references/LinearWithSigmoid.ref.hxx" +#include "LinearWithSoftmax_FromONNX.hxx" +#include "input_models/references/LinearWithSoftmax.ref.hxx" + +#include "LinearWithLeakyRelu_FromONNX.hxx" +#include "input_models/references/LinearWithLeakyRelu.ref.hxx" + #include "ConvWithPadding_FromROOT.hxx" #include "input_models/references/ConvWithPadding.ref.hxx" @@ -193,6 +199,48 @@ TEST(ROOT, LinearWithSigmoid) } +TEST(ROOT, LinearWithSoftmax) +{ + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard all-ones input + std::vector input(48); + std::fill_n(input.data(), input.size(), randn(48)); + std::vector output = TMVA_SOFIE_LinearWithSoftmax::infer(input.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(LinearWithSoftmax_ExpectedOutput::all_ones) / sizeof(float)); + + float *correct = LinearWithSoftmax_ExpectedOutput::all_ones; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } +} + + +TEST(ROOT, LinearWithLeakyRelu) +{ + constexpr float TOLERANCE = DEFAULT_TOLERANCE; + + // Preparing the standard all-ones input + std::vector input(48); + std::iota(input.begin(), input.end(), randn(48)); + std::vector output = TMVA_SOFIE_LinearWithLeakyRelu::infer(input.data()); + + // Checking output size + EXPECT_EQ(output.size(), sizeof(LinearWithLeakyRelu_ExpectedOutput::all_ones) / sizeof(float)); + + float *correct = LinearWithLeakyRelu_ExpectedOutput::all_ones; + + // Checking every output value, one by one + for (size_t i = 0; i < output.size(); ++i) { + EXPECT_LE(std::abs(output[i] - correct[i]), TOLERANCE); + } +} + + TEST(ROOT, ConvWithPadding) { constexpr float TOLERANCE = DEFAULT_TOLERANCE; diff --git a/tmva/sofie/test/input_models/LinearWithLeakyRelu.onnx b/tmva/sofie/test/input_models/LinearWithLeakyRelu.onnx new file mode 100644 index 0000000000000..7164c22e99e04 Binary files /dev/null and b/tmva/sofie/test/input_models/LinearWithLeakyRelu.onnx differ diff --git a/tmva/sofie/test/input_models/LinearWithSoftmax.onnx b/tmva/sofie/test/input_models/LinearWithSoftmax.onnx new file mode 100644 index 0000000000000..5872a23748926 Binary files /dev/null and b/tmva/sofie/test/input_models/LinearWithSoftmax.onnx differ diff --git a/tmva/sofie/test/input_models/references/LinearWithLeakyRelu.ref.hxx b/tmva/sofie/test/input_models/references/LinearWithLeakyRelu.ref.hxx new file mode 100644 index 0000000000000..2221c9ae26fa8 --- /dev/null +++ b/tmva/sofie/test/input_models/references/LinearWithLeakyRelu.ref.hxx @@ -0,0 +1,10 @@ +namespace LinearWithLeakyRelu_ExpectedOutput{ + // input: ([ 1.3159, -2.4688, 0.3144, 1.3210, -2.0890, 0.7903, 0.4291, -0.7938, + // -0.5030, 0.7853, -0.4555, 1.4221, -1.6509, 1.3590, -0.4521, -0.0615, + // -1.8761, 0.7038]) + float input[] = { + 1.3159, -0.2469, 0.3144, 1.3210, -0.2089, 0.7903, 0.4291, -0.0794, + -0.0503, 0.7853, -0.0455, 1.4221, -0.1651, 1.3590, -0.0452, -0.0062, + -0.1876, 0.7038 + }; +} // namespace LinearWithLeakyRelu_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie/test/input_models/references/LinearWithSoftmax.ref.hxx b/tmva/sofie/test/input_models/references/LinearWithSoftmax.ref.hxx new file mode 100644 index 0000000000000..bc11a540c4fca --- /dev/null +++ b/tmva/sofie/test/input_models/references/LinearWithSoftmax.ref.hxx @@ -0,0 +1,12 @@ +namespace LinearWithSoftmax_ExpectedOutput{ + // input: ([[ 0.4325, -0.0198, -0.3384, 1.0506, 0.6901], + // [ 0.9477, -0.3371, -0.7476, 0.7868, 0.6242], + // [-0.3339, 2.4288, 0.4182, 0.5997, -1.5545], + // [ 0.5749, -0.5843, -1.2282, 2.0387, -0.7899]]) + float input[] = { + [[0.1905, 0.1212, 0.0881, 0.3535, 0.2465], + [0.3295, 0.0912, 0.0605, 0.2805, 0.2384], + [0.0459, 0.7266, 0.0973, 0.1167, 0.0135], + [0.1651, 0.0518, 0.0272, 0.7137, 0.0422]] + }; +} // namespace LinearWithSoftmax_ExpectedOutput \ No newline at end of file diff --git a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx index 3b0a30cdb04e4..e55597ad98407 100644 --- a/tmva/sofie_parsers/src/RModelParser_ONNX.cxx +++ b/tmva/sofie_parsers/src/RModelParser_ONNX.cxx @@ -131,6 +131,38 @@ std::unique_ptr make_ROperator_Relu(const onnx::NodeProto& nodeproto, return op; } +std::unique_ptr make_ROperator_Leaky_Relu(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /*graphproto */, std::unordered_map& tensor_type){ + + ETensorType input_type; + + auto input_name = nodeproto.input(0); + auto it = tensor_type.find(input_name); + if (it != tensor_type.end()){ + input_type = it->second; + }else{ + throw std::runtime_error("TMVA::SOFIE ONNX Parser leaky relu op has input tensor" + input_name + " but its type is not yet registered"); + } + + std::unique_ptr op; + + + switch(input_type){ + case ETensorType::FLOAT: + op.reset(new ROperator_Leaky_Relu(nodeproto.input(0), nodeproto.output(0))); + break; + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Leaky Relu does not yet support input type " + std::to_string(static_cast(input_type))); + } + + ETensorType output_type = (op->TypeInference({input_type}))[0]; + auto it2 = tensor_type.find(nodeproto.output(0)); + if (it2 == tensor_type.end()){ + tensor_type[nodeproto.output(0)] = output_type; + } + + return op; +} + std::unique_ptr make_ROperator_Selu(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /*graphproto */, std::unordered_map& tensor_type){ ETensorType input_type; @@ -195,6 +227,38 @@ std::unique_ptr make_ROperator_Sigmoid(const onnx::NodeProto& nodepro return op; } +std::unique_ptr make_ROperator_Softmax(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /*graphproto */, std::unordered_map& tensor_type){ + + ETensorType input_type; + + auto input_name = nodeproto.input(0); + auto it = tensor_type.find(input_name); + if (it != tensor_type.end()){ + input_type = it->second; + }else{ + throw std::runtime_error("TMVA::SOFIE ONNX Parser Softmax op has input tensor" + input_name + " but its type is not yet registered"); + } + + std::unique_ptr op; + + + switch(input_type){ + case ETensorType::FLOAT: + op.reset(new ROperator_Softmax(nodeproto.input(0), nodeproto.output(0))); + break; + default: + throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Softmax does not yet support input type " + std::to_string(static_cast(input_type))); + } + + ETensorType output_type = (op->TypeInference({input_type}))[0]; + auto it2 = tensor_type.find(nodeproto.output(0)); + if (it2 == tensor_type.end()){ + tensor_type[nodeproto.output(0)] = output_type; + } + + return op; +} + std::unique_ptr make_ROperator_Gemm(const onnx::NodeProto& nodeproto, const onnx::GraphProto& /* graphproto */, std::unordered_map& tensor_type){ ETensorType input_type; @@ -1009,7 +1073,7 @@ RModel RModelParser_ONNX::Parse(std::string filename){ rmodel.AddBlasRoutines({"Gemm", "Axpy"}); } else if (op_type == "RNN") { rmodel.AddBlasRoutines({"Gemm", "Axpy"}); - } else if (op_type == "Selu" || op_type == "Sigmoid") { + } else if (op_type == "Selu" || op_type == "Sigmoid" || op_type == "LeakyRelu") { rmodel.AddNeededStdLib("cmath"); } else if (op_type == "LSTM") { rmodel.AddBlasRoutines({"Gemm", "Axpy"});