Logo ROOT  
Reference Guide
RModelParser_Keras.cxx
Go to the documentation of this file.
1// @(#)root/tmva/pymva $Id$
2// Author: Sanjiban Sengupta 2021
3
4/**********************************************************************************
5 * Project : TMVA - a Root-integrated toolkit for multivariate data analysis *
6 * Package : TMVA *
7 * Function: TMVA::Experimental::SOFIE::PyKeras::Parse *
8 * *
9 * Description: *
10 * Parser function for translating Keras .h5 model to RModel object *
11 * *
12 * Example Usage: *
13 * ~~~ {.cpp} *
14 * using TMVA::Experimental::SOFIE; *
15 * RModel model = PyKeras::Parse("trained_model_dense.h5"); *
16 * ~~~ *
17 * *
18 **********************************************************************************/
19
21
22#include <Python.h>
23
24#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
25#include <numpy/arrayobject.h>
26
27
28namespace TMVA{
29namespace Experimental{
30namespace SOFIE{
31namespace PyKeras{
32
33// Referencing Python utility functions present in PyMethodBase
36static std::vector<size_t>(& GetDataFromTuple)(PyObject*) = PyMethodBase::GetDataFromTuple;
37
38namespace INTERNAL{
39
40// For adding Keras layer into RModel object
41void AddKerasLayer(RModel &rmodel, PyObject *fLayer);
42
43// Declaring Internal Functions for Keras layers which don't have activation as an additional attribute
44std::unique_ptr<ROperator> MakeKerasActivation(PyObject *fLayer); // For instantiating ROperator for Keras Activation Layer
45std::unique_ptr<ROperator> MakeKerasReLU(PyObject *fLayer); // For instantiating ROperator for Keras ReLU layer
46std::unique_ptr<ROperator> MakeKerasSelu(PyObject *fLayer); // For instantiating ROperator for Keras Selu layer
47std::unique_ptr<ROperator> MakeKerasSigmoid(PyObject *fLayer); // For instantiating ROperator for Keras Sigmoid layer
48std::unique_ptr<ROperator> MakeKerasPermute(PyObject *fLayer); // For instantiating ROperator for Keras Permute Layer
49std::unique_ptr<ROperator> MakeKerasBatchNorm(PyObject *fLayer); // For instantiating ROperator for Keras Batch Normalization Layer
50std::unique_ptr<ROperator> MakeKerasReshape(PyObject *fLayer); // For instantiating ROperator for Keras Reshape Layer
51std::unique_ptr<ROperator> MakeKerasConcat(PyObject *fLayer); // For instantiating ROperator for Keras Concat Layer
52std::unique_ptr<ROperator> MakeKerasBinary(PyObject *fLayer); // For instantiating ROperator for Keras binary operations: Add, Subtract & Multiply.
53std::unique_ptr<ROperator> MakeKerasSoftmax(PyObject *fLayer); // For instantiating ROperator for Keras Softmax Layer
54std::unique_ptr<ROperator> MakeKerasTanh(PyObject *fLayer); // For instantiating ROperator for Keras Tanh Layer
55std::unique_ptr<ROperator> MakeKerasLeakyRelu(PyObject *fLayer); // For instantiating ROperator for Keras LeakyRelu Layer
56
57
58// Declaring Internal function for Keras layers which have additional activation attribute
59std::unique_ptr<ROperator> MakeKerasDense(PyObject *fLayer); // For instantiating ROperator for Keras Dense Layer
60std::unique_ptr<ROperator> MakeKerasConv(PyObject *fLayer); // For instantiating ROperator for Keras Conv Layer
61
62// For mapping Keras layer with the preparatory functions for ROperators
63using KerasMethodMap = std::unordered_map<std::string, std::unique_ptr<ROperator> (*)(PyObject *fLayer)>;
64using KerasMethodMapWithActivation = std::unordered_map<std::string, std::unique_ptr<ROperator> (*)(PyObject *fLayer)>;
65
67 {"Activation", &MakeKerasActivation},
68 {"Permute", &MakeKerasPermute},
69 {"BatchNormalization", &MakeKerasBatchNorm},
70 {"Reshape", &MakeKerasReshape},
71 {"Concatenate", &MakeKerasConcat},
72 {"Add", &MakeKerasBinary},
73 {"Subtract", &MakeKerasBinary},
74 {"Multiply", &MakeKerasBinary},
75 {"Softmax", &MakeKerasSoftmax},
76 {"tanh", &MakeKerasTanh},
77 {"LeakyReLU", &MakeKerasLeakyRelu},
78
79 // For activation layers
80 {"ReLU", &MakeKerasReLU},
81
82 // For layers with activation attributes
83 {"relu", &MakeKerasReLU},
84 {"selu", &MakeKerasSelu},
85 {"sigmoid", &MakeKerasSigmoid},
86 {"softmax", &MakeKerasSoftmax}
87};
88
90 {"Dense", &MakeKerasDense},
91 {"Conv2D", &MakeKerasConv},
92 };
93
94
95//////////////////////////////////////////////////////////////////////////////////
96/// \brief Adds equivalent ROperator with respect to Keras model layer
97/// into the referenced RModel object
98///
99/// \param[in] rmodel RModel object
100/// \param[in] fLayer Python Keras layer as a Dictionary object
101/// \param[out] RModel object with the added ROperator
102///
103/// Function adds equivalent ROperator into the referenced RModel object.
104/// Keras models can have layers like Dense and Conv which have activation
105/// function as an attribute. Function first searches if layer object is among
106/// the ones which don't have activation attribute and then calls the respective
107/// preparation function to get the ROperator object, which is then added
108/// into the RModel object. If passed layer is among the ones which may have activation
109/// attribute, then it checks for the activation attribute, if present then first adds
110/// the primary operator into the RModel object, and then adds the operator for the
111/// activation function with appropriate changes in the names of input and output
112/// tensors for both of them.
113/// Example of such layers is the Dense Layer. For a dense layer with input tensor name
114/// dense2BiasAdd0 and output tensor name dense3Relu0 with relu as activation attribute
115/// will be transformed into a ROperator_Gemm with input tensor name dense2BiasAdd0
116/// & output tensor name dense3Dense (layerName+layerType), and a subsequent
117/// ROperator_Relu with input tensor name as dense3Dense and output tensor name
118/// as dense3Relu0.
119///
120/// For developing new preparatory functions for supporting Keras layers in future,
121/// all one needs is to extract the required properties and attributes from the fLayer
122/// dictionary which contains all the information about any Keras layer and after
123/// any required transformations, these are passed for instantiating the ROperator
124/// object.
125///
126/// The fLayer dictionary which holds all the information about a Keras layer has
127/// following structure:-
128///
129/// dict fLayer { 'layerType' : Type of the Keras layer
130/// 'layerAttributes' : Attributes of the keras layer as returned by layer.get_config()
131/// 'layerInput' : List of names of input tensors
132/// 'layerOutput' : List of names of output tensors
133/// 'layerDType' : Data-type of the Keras layer
134/// 'layerWeight' : List of weight tensor names of Keras layers
135/// }
136void AddKerasLayer(RModel& rmodel, PyObject* fLayer){
137 std::string fLayerType = PyStringAsString(PyDict_GetItemString(fLayer,"layerType"));
138
139 if(fLayerType == "Reshape"){
140 PyObject* fAttributes=PyDict_GetItemString(fLayer,"layerAttributes");
141 std::string fLayerName = PyStringAsString(PyDict_GetItemString(fAttributes,"_name"));
142 PyObject* fPTargetShape = PyDict_GetItemString(fAttributes,"target_shape");
143 std::vector<size_t>fTargetShape = GetDataFromTuple(fPTargetShape);
144 std::shared_ptr<void> fData(malloc(fTargetShape.size() * sizeof(int64_t)), free);
145 std::copy(fTargetShape.begin(),fTargetShape.end(),(int64_t*)fData.get());
146 rmodel.AddInitializedTensor(fLayerName+"ReshapeAxes",ETensorType::INT64,{fTargetShape.size()},fData);
147 }
148
149 //For layers without additional activation attribute
150 auto findLayer = mapKerasLayer.find(fLayerType);
151 if(findLayer != mapKerasLayer.end()){
152 rmodel.AddOperator((findLayer->second)(fLayer));
153 return;
154 }
155
156 //For layers like Dense & Conv which has additional activation attribute
157 else if(mapKerasLayerWithActivation.find(fLayerType) != mapKerasLayerWithActivation.end()){
158 findLayer = mapKerasLayerWithActivation.find(fLayerType);
159 PyObject* fAttributes=PyDict_GetItemString(fLayer,"layerAttributes");
160
161 std::string fLayerName = PyStringAsString(PyDict_GetItemString(fAttributes,"_name"));
162
163 PyObject* fPActivation = PyDict_GetItemString(fAttributes,"activation");
164 std::string fLayerActivation = PyStringAsString(PyObject_GetAttrString(fPActivation,"__name__"));
165
166 if(fLayerActivation == "selu" || fLayerActivation == "sigmoid")
167 rmodel.AddNeededStdLib("cmath");
168
169
170 //Checking if additional attribute exixts
171 if(fLayerActivation != "linear"){
172 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
173 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
174 std::string fActivationLayerOutput = PyStringAsString(PyList_GetItem(fOutputs,0));
175
176 if(fLayerType == "Conv2D"){
177 std::unique_ptr<ROperator> op_pre_transpose;
178 op_pre_transpose.reset(new ROperator_Transpose<float>({0,3,1,2}, PyStringAsString(PyList_GetItem(fInputs,0)), fLayerName+"PreTrans"));
179 rmodel.AddOperator(std::move(op_pre_transpose));
180
181 PyList_SetItem(fInputs,0,PyUnicode_FromString((fLayerName+"PreTrans").c_str()));
182 PyDict_SetItemString(fLayer,"layerInput",fInputs);
183 }
184
185 // Making changes in the names of the input and output tensor names
186 PyList_SetItem(fOutputs,0,PyUnicode_FromString((fLayerName+fLayerType).c_str()));
187 PyDict_SetItemString(fLayer,"layerOutput",fOutputs);
188 rmodel.AddOperator((findLayer->second)(fLayer));
189
190 std::string fActivationLayerInput = fLayerName+fLayerType;
191 if(fLayerType == "Conv2D"){
192 std::unique_ptr<ROperator> op_post_transpose;
193 op_post_transpose.reset(new ROperator_Transpose<float>({0,2,3,1}, fLayerName+fLayerType, fLayerName+"PostTrans"));
194 rmodel.AddOperator(std::move(op_post_transpose));
195 fActivationLayerInput = fLayerName+"PostTrans";
196 }
197
198 PyList_SetItem(fInputs,0,PyUnicode_FromString(fActivationLayerInput.c_str()));
199 PyList_SetItem(fOutputs,0,PyUnicode_FromString(fActivationLayerOutput.c_str()));
200 PyDict_SetItemString(fLayer,"layerInput",fInputs);
201 PyDict_SetItemString(fLayer,"layerOutput",fOutputs);
202
203 auto findActivationLayer = mapKerasLayer.find(fLayerActivation);
204 if(findActivationLayer == mapKerasLayer.end()){
205 throw std::runtime_error("TMVA::SOFIE - Parsing Keras Activation layer " + fLayerActivation + " is not yet supported");
206 }
207 rmodel.AddOperator((findActivationLayer->second)(fLayer));
208
209 }
210 else{
211 rmodel.AddOperator((findLayer->second)(fLayer));
212 }
213 return;
214 }
215
216 else{
217 throw std::runtime_error("TMVA::SOFIE - Parsing Keras layer " + fLayerType + " is not yet supported");
218 }
219
220}
221
222//////////////////////////////////////////////////////////////////////////////////
223/// \brief Prepares a ROperator object for Keras Dense Layer
224///
225/// \param[in] fLayer Python Keras layer as a Dictionary object
226/// \return Unique pointer to ROperator object
227///
228/// For Keras's Dense layer, the names of the input tensor, output tensor, and
229/// weight tensors are extracted, and then are passed to instantiate a
230/// ROperator_Gemm object using the required attributes.
231std::unique_ptr<ROperator> MakeKerasDense(PyObject* fLayer){
232 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
233 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
234 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
235
236 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
237 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
238
239 // Extracting names of weight tensors
240 // The names of Kernel weights and bias weights are found in the list
241 // of weight tensors from fLayer.
242 PyObject* fWeightNames = PyDict_GetItemString(fLayer,"layerWeight");
243 std::string fKernelName = PyStringAsString(PyList_GetItem(fWeightNames,0));
244 std::string fBiasName = PyStringAsString(PyList_GetItem(fWeightNames,1));
245
246 std::unique_ptr<ROperator> op;
247
248 float attr_alpha = 1.0;
249 float attr_beta = 1.0;
250 int_t attr_transA = 0;
251 int_t attr_transB = 0;
252
253 switch(ConvertStringToType(fLayerDType)){
255 op.reset(new ROperator_Gemm<float>(attr_alpha, attr_beta, attr_transA, attr_transB, fLayerInputName, fKernelName, fBiasName, fLayerOutputName));
256 break;
257
258 default:
259 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Gemm does not yet support input type " + fLayerDType);
260 }
261 return op;
262}
263
264
265
266//////////////////////////////////////////////////////////////////////////////////
267/// \brief Prepares a ROperator object for Keras Conv Layer
268///
269/// \param[in] fLayer Python Keras layer as a Dictionary object
270/// \return Unique pointer to ROperator object
271///
272/// For Keras's Conv layer, the names of the input tensor, output tensor, and
273/// weight tensors are extracted, along with attributes like dilation_rate,
274/// groups, kernel size, padding, strides. Padding attribute is then
275/// computed for ROperator depending on Keras' attribute parameter.
276std::unique_ptr<ROperator> MakeKerasConv(PyObject* fLayer){
277 PyObject* fAttributes = PyDict_GetItemWithError(fLayer,PyUnicode_FromString("layerAttributes"));
278 PyObject* fInputs = PyDict_GetItemWithError(fLayer,PyUnicode_FromString("layerInput"));
279 PyObject* fOutputs = PyDict_GetItemWithError(fLayer,PyUnicode_FromString("layerOutput"));
280 std::string fLayerDType = PyStringAsString(PyDict_GetItemWithError(fLayer,PyUnicode_FromString("layerDType")));
281
282 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
283 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
284
285 // Extracting names of weight tensors
286 // The names of Kernel weights and bias weights are found in the list
287 // of weight tensors from fLayer.
288 PyObject* fWeightNames = PyDict_GetItemWithError(fLayer,PyUnicode_FromString("layerWeight"));
289 std::string fKernelName = PyStringAsString(PyList_GetItem(fWeightNames,0));
290 std::string fBiasName = PyStringAsString(PyList_GetItem(fWeightNames,1));
291
292 // Extracting the Conv Node Attributes
293 PyObject* fDilations = PyDict_GetItemWithError(fAttributes,PyUnicode_FromString("dilation_rate"));
294 PyObject* fGroup = PyDict_GetItemWithError(fAttributes,PyUnicode_FromString("groups"));
295 PyObject* fKernelShape = PyDict_GetItemWithError(fAttributes,PyUnicode_FromString("kernel_size"));
296 PyObject* fPads = PyDict_GetItemWithError(fAttributes,PyUnicode_FromString("padding"));
297 PyObject* fStrides = PyDict_GetItemWithError(fAttributes,PyUnicode_FromString("strides"));
298
299 std::vector<size_t> fAttrDilations = GetDataFromTuple(fDilations);
300
301
302 size_t fAttrGroup = PyLong_AsLong(fGroup);
303 std::vector<size_t> fAttrKernelShape = GetDataFromTuple(fKernelShape);
304 std::vector<size_t> fAttrStrides = GetDataFromTuple(fStrides);
305 std::string fAttrAutopad;
306 std::vector<size_t>fAttrPads;
307
308 //Seting the layer padding
309 std::string fKerasPadding = PyStringAsString(fPads);
310 if(fKerasPadding == "valid"){
311 fAttrAutopad = "VALID";
312 }
313 else if(fKerasPadding == "same"){
314 fAttrAutopad="NOTSET";
315 PyObject* fInputShape = PyDict_GetItemString(fAttributes,"_batch_input_shape");
316 long inputHeight = PyLong_AsLong(PyTuple_GetItem(fInputShape,1));
317 long inputWidth = PyLong_AsLong(PyTuple_GetItem(fInputShape,2));
318
319 long outputHeight = std::ceil(float(inputHeight) / float(fAttrStrides[0]));
320 long outputWidth = std::ceil(float(inputWidth) / float(fAttrStrides[1]));
321
322 long padding_height = std::max(long((outputHeight - 1) * fAttrStrides[0] + fAttrKernelShape[0] - inputHeight),0L);
323 long padding_width = std::max(long((outputWidth - 1) * fAttrStrides[1] + fAttrKernelShape[1] - inputWidth),0L);
324
325 size_t padding_top = std::floor(padding_height/2);
326 size_t padding_bottom = padding_height - padding_top;
327 size_t padding_left = std::floor(padding_width/2);
328 size_t padding_right = padding_width - padding_left;
329 fAttrPads = {padding_top,padding_bottom,padding_left,padding_right};
330 }
331 else{
332 throw std::runtime_error("TMVA::SOFIE - RModel Keras Parser doesn't yet supports Convolution layer with padding " + fKerasPadding);
333 }
334
335 std::unique_ptr<ROperator> op;
336
337 switch(ConvertStringToType(fLayerDType)){
339 op.reset(new ROperator_Conv<float>(fAttrAutopad, fAttrDilations, fAttrGroup, fAttrKernelShape, fAttrPads, fAttrStrides, fLayerInputName, fKernelName, fBiasName, fLayerOutputName));
340 break;
341
342 default:
343 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Conv does not yet support input type " + fLayerDType);
344 }
345 return op;
346}
347
348
349//////////////////////////////////////////////////////////////////////////////////
350/// \brief Prepares a ROperator object for Keras activation layer
351///
352/// \param[in] fLayer Python Keras layer as a Dictionary object
353/// \return Unique pointer to ROperator object
354///
355/// For Keras's keras.layers.Activation layer, the activation attribute is
356/// extracted and appropriate function for adding the function is called.
357std::unique_ptr<ROperator> MakeKerasActivation(PyObject* fLayer){
358 PyObject* fAttributes=PyDict_GetItemString(fLayer,"layerAttributes");
359 PyObject* fPActivation = PyDict_GetItemString(fAttributes,"activation");
360 std::string fLayerActivation = PyStringAsString(PyObject_GetAttrString(fPActivation,"__name__"));
361
362 auto findLayer = mapKerasLayer.find(fLayerActivation);
363 if(findLayer == mapKerasLayer.end()){
364 throw std::runtime_error("TMVA::SOFIE - Parsing Keras Activation layer " + fLayerActivation + " is not yet supported");
365 }
366 return (findLayer->second)(fLayer);
367}
368
369
370//////////////////////////////////////////////////////////////////////////////////
371/// \brief Prepares a ROperator object for Keras ReLU activation
372///
373/// \param[in] fLayer Python Keras layer as a Dictionary object
374/// \return Unique pointer to ROperator object
375///
376/// For instantiating a ROperator_Relu object, the names of
377/// input & output tensors and the data-type of the layer are extracted.
378std::unique_ptr<ROperator> MakeKerasReLU(PyObject* fLayer)
379{
380 PyObject* fInputs=PyDict_GetItemString(fLayer,"layerInput");
381 PyObject* fOutputs=PyDict_GetItemString(fLayer,"layerOutput");
382
383 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
384 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
385 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
386
387 std::unique_ptr<ROperator> op;
388 switch(ConvertStringToType(fLayerDType)){
390 op.reset(new ROperator_Relu<float>(fLayerInputName, fLayerOutputName));
391 break;
392 default:
393 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Relu does not yet support input type " + fLayerDType);
394 }
395 return op;
396}
397
398
399//////////////////////////////////////////////////////////////////////////////////
400/// \brief Prepares a ROperator object for Keras Selu activation
401///
402/// \param[in] fLayer Python Keras layer as a Dictionary object
403/// \return Unique pointer to ROperator object
404///
405/// For instantiating a ROperator_Selu object, the names of
406/// input & output tensors and the data-type of the layer are extracted.
407std::unique_ptr<ROperator> MakeKerasSelu(PyObject* fLayer){
408 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
409 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
410
411 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
412 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
413 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
414
415 std::unique_ptr<ROperator> op;
416 switch(ConvertStringToType(fLayerDType)){
418 op.reset(new ROperator_Selu<float>(fLayerInputName, fLayerOutputName));
419 break;
420 default:
421 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Selu does not yet support input type " + fLayerDType);
422 }
423 return op;
424}
425
426
427//////////////////////////////////////////////////////////////////////////////////
428/// \brief Prepares a ROperator object for Keras Sigmoid activation
429///
430/// \param[in] fLayer Python Keras layer as a Dictionary object
431/// \return Unique pointer to ROperator object
432///
433/// For instantiating a ROperator_Sigmoid object, the names of
434/// input & output tensors and the data-type of the layer are extracted.
435std::unique_ptr<ROperator> MakeKerasSigmoid(PyObject* fLayer){
436 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
437 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
438
439 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
440 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
441 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
442
443 std::unique_ptr<ROperator> op;
444 switch(ConvertStringToType(fLayerDType)){
446 op.reset(new ROperator_Sigmoid<float>(fLayerInputName, fLayerOutputName));
447 break;
448 default:
449 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Sigmoid does not yet support input type " + fLayerDType);
450 }
451 return op;
452}
453
454//////////////////////////////////////////////////////////////////////////////////
455/// \brief Prepares a ROperator object for Keras Softmax activation
456///
457/// \param[in] fLayer Python Keras layer as a Dictionary object
458/// \return Unique pointer to ROperator object
459///
460/// For instantiating a ROperator_Softmax object, the names of
461/// input & output tensors and the data-type of the layer are extracted.
462std::unique_ptr<ROperator> MakeKerasSoftmax(PyObject* fLayer){
463 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
464 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
465
466 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
467 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
468 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
469
470 std::unique_ptr<ROperator> op;
471 switch(ConvertStringToType(fLayerDType)){
473 op.reset(new ROperator_Softmax<float>(/*default axis is -1*/-1,fLayerInputName, fLayerOutputName));
474 break;
475 default:
476 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Sigmoid does not yet support input type " + fLayerDType);
477 }
478 return op;
479}
480
481//////////////////////////////////////////////////////////////////////////////////
482/// \brief Prepares a ROperator object for Keras Leaky Relu activation
483///
484/// \param[in] fLayer Python Keras layer as a Dictionary object
485/// \return Unique pointer to ROperator object
486///
487/// For instantiating a ROperator_LeakyRelu object, the names of
488/// input & output tensors, the data-type and the alpha attribute of the layer
489/// are extracted.
490std::unique_ptr<ROperator> MakeKerasLeakyRelu(PyObject* fLayer){
491 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
492 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
493 PyObject* fAttributes=PyDict_GetItemString(fLayer,"layerAttributes");
494
495 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
496 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
497 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
498 float fAlpha = (float)PyFloat_AsDouble(PyDict_GetItemString(fAttributes,"alpha"));
499 std::unique_ptr<ROperator> op;
500 switch(ConvertStringToType(fLayerDType)){
502 op.reset(new ROperator_LeakyRelu<float>(fAlpha, fLayerInputName, fLayerOutputName));
503 break;
504 default:
505 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Sigmoid does not yet support input type " + fLayerDType);
506 }
507 return op;
508}
509
510//////////////////////////////////////////////////////////////////////////////////
511/// \brief Prepares a ROperator object for Keras Tanh activation
512///
513/// \param[in] fLayer Python Keras layer as a Dictionary object
514/// \return Unique pointer to ROperator object
515///
516/// For instantiating a ROperator_Tanh object, the names of
517/// input & output tensors and the data-type of the layer are extracted.
518std::unique_ptr<ROperator> MakeKerasTanh(PyObject* fLayer){
519 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
520 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
521
522 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
523 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
524 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
525
526 std::unique_ptr<ROperator> op;
527 switch(ConvertStringToType(fLayerDType)){
529 op.reset(new ROperator_Tanh<float>(fLayerInputName, fLayerOutputName));
530 break;
531 default:
532 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Sigmoid does not yet support input type " + fLayerDType);
533 }
534 return op;
535}
536
537
538//////////////////////////////////////////////////////////////////////////////////
539/// \brief Prepares a ROperator object for Keras Permute layer
540///
541/// \param[in] fLayer Python Keras layer as a Dictionary object
542/// \return Unique pointer to ROperator object
543///
544/// The Permute layer in Keras has an equivalent Tranpose operator in ONNX.
545/// For adding a Transpose operator, the permute dimensions are found, if they
546/// exist are passed in instantiating the ROperator, else default values are used.
547std::unique_ptr<ROperator> MakeKerasPermute(PyObject* fLayer)
548{
549 // Extracting required layer information
550 PyObject* fAttributes=PyDict_GetItemString(fLayer,"layerAttributes");
551 PyObject* fInputs=PyDict_GetItemString(fLayer,"layerInput");
552 PyObject* fOutputs=PyDict_GetItemString(fLayer,"layerOutput");
553
554 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
555 std::string fLayerInputName = PyStringAsString(PyList_GetItem(fInputs,0));
556 std::string fLayerOutputName = PyStringAsString(PyList_GetItem(fOutputs,0));
557
558 // Extracting the permute dimensions present in Attributes of the Keras layer
559 PyObject* fAttributePermute = PyDict_GetItemString(fAttributes,"dims");
560 std::vector<int_t>fPermuteDims;
561
562 // Building vector of permute dimensions from the Tuple object.
563 for(Py_ssize_t tupleIter=0;tupleIter<PyTuple_Size(fAttributePermute);++tupleIter){
564
565 fPermuteDims.push_back((int_t)PyLong_AsLong(PyTuple_GetItem(fAttributePermute,tupleIter)));
566 }
567 std::unique_ptr<ROperator> op;
568 switch(ConvertStringToType(fLayerDType)){
569 case ETensorType::FLOAT:{
570
571 // Adding the permute dimensions if present, else are avoided to use default values.
572 if (!fPermuteDims.empty()){
573 op.reset(new ROperator_Transpose<float>(fPermuteDims, fLayerInputName, fLayerOutputName));
574 }
575 else{
576 op.reset(new ROperator_Transpose<float> (fLayerInputName, fLayerOutputName));
577 }
578 break;
579 }
580 default:
581 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Transpose does not yet support input type " + fLayerDType);
582 }
583 return op;
584}
585
586//////////////////////////////////////////////////////////////////////////////////
587/// \brief Prepares a ROperator object for Keras BatchNorm layer
588///
589/// \param[in] fLayer Python Keras layer as a Dictionary object
590/// \return Unique pointer to ROperator object
591std::unique_ptr<ROperator> MakeKerasBatchNorm(PyObject* fLayer)
592{
593 // Extracting required layer information
594 PyObject* fAttributes = PyDict_GetItemString(fLayer,"layerAttributes");
595 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
596 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
597 PyObject* fGamma = PyDict_GetItemString(fAttributes,"gamma");
598 PyObject* fBeta = PyDict_GetItemString(fAttributes,"beta");
599 PyObject* fMoving_Mean = PyDict_GetItemString(fAttributes,"moving_mean");
600 PyObject* fMoving_Var = PyDict_GetItemString(fAttributes,"moving_variance");
601
602 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
603 std::string fNX = PyStringAsString(PyList_GetItem(fInputs,0));
604 std::string fNY = PyStringAsString(PyList_GetItem(fOutputs,0));
605 std::string fNScale = PyStringAsString(PyObject_GetAttrString(fGamma,"name"));
606 std::string fNB = PyStringAsString(PyObject_GetAttrString(fBeta,"name"));
607 std::string fNMean = PyStringAsString(PyObject_GetAttrString(fMoving_Mean,"name"));
608 std::string fNVar = PyStringAsString(PyObject_GetAttrString(fMoving_Var,"name"));
609 float fEpsilon = (float)PyFloat_AsDouble(PyDict_GetItemString(fAttributes,"epsilon"));
610 float fMomentum = (float)PyFloat_AsDouble(PyDict_GetItemString(fAttributes,"momentum"));
611
612 std::unique_ptr<ROperator> op;
613 op.reset(new ROperator_BatchNormalization<float>(fEpsilon, fMomentum, /* training mode */ 0, fNX, fNScale, fNB, fNMean, fNVar, fNY));
614 return op;
615}
616
617//////////////////////////////////////////////////////////////////////////////////
618/// \brief Prepares a ROperator object for Keras Reshape layer
619///
620/// \param[in] fLayer Python Keras layer as a Dictionary object
621/// \return Unique pointer to ROperator object
622std::unique_ptr<ROperator> MakeKerasReshape(PyObject* fLayer)
623{
624 // Extracting required layer information
625 PyObject* fAttributes = PyDict_GetItemString(fLayer,"layerAttributes");
626 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
627 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
628
629 std::string fLayerName = PyStringAsString(PyDict_GetItemString(fAttributes,"_name"));
630
631 ReshapeOpMode fOpMode = Reshape;
632 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
633 std::string fNameData = PyStringAsString(PyList_GetItem(fInputs,0));
634 std::string fNameOutput = PyStringAsString(PyList_GetItem(fOutputs,0));
635 std::string fNameShape = fLayerName + "ReshapeAxes";
636 std::unique_ptr<ROperator> op;
637 op.reset(new ROperator_Reshape<float>(fOpMode, /*allow zero*/0, fNameData, fNameShape, fNameOutput));
638 return op;
639}
640
641//////////////////////////////////////////////////////////////////////////////////
642/// \brief Prepares a ROperator object for Keras Concat layer
643///
644/// \param[in] fLayer Python Keras layer as a Dictionary object
645/// \return Unique pointer to ROperator object
646std::unique_ptr<ROperator> MakeKerasConcat(PyObject* fLayer)
647{
648 PyObject* fAttributes = PyDict_GetItemString(fLayer,"layerAttributes");
649 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
650 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
651
652 std::vector<std::string> inputs;
653 for(Py_ssize_t i=0; i<PyList_Size(fInputs); ++i){
654 inputs.emplace_back(PyStringAsString(PyList_GetItem(fInputs,i)));
655 }
656 std::string output = PyStringAsString(PyList_GetItem(fOutputs,0));
657
658 int axis = (int)PyLong_AsLong(PyDict_GetItemString(fAttributes,"axis"));
659 std::unique_ptr<ROperator> op;
660 op.reset(new ROperator_Concat<float>(inputs, axis, 0, output));
661 return op;
662}
663
664//////////////////////////////////////////////////////////////////////////////////
665/// \brief Prepares a ROperator object for Keras binary operations like Add,
666/// subtract, and multiply.
667///
668/// \param[in] fLayer Python Keras layer as a Dictionary object
669/// \return Unique pointer to ROperator object
670///
671/// For instantiating a ROperator_BasicBinary object, the names of
672/// input & output tensors, the data-type of the layer and the operation type
673/// are extracted.
674std::unique_ptr<ROperator> MakeKerasBinary(PyObject* fLayer){
675 PyObject* fInputs = PyDict_GetItemString(fLayer,"layerInput");
676 PyObject* fOutputs = PyDict_GetItemString(fLayer,"layerOutput");
677
678 std::string fLayerType = PyStringAsString(PyDict_GetItemString(fLayer,"layerType"));
679 std::string fLayerDType = PyStringAsString(PyDict_GetItemString(fLayer,"layerDType"));
680 std::string fX1 = PyStringAsString(PyList_GetItem(fInputs,0));
681 std::string fX2 = PyStringAsString(PyList_GetItem(fInputs,1));
682 std::string fY = PyStringAsString(PyList_GetItem(fOutputs,0));
683
684 std::unique_ptr<ROperator> op;
685 switch(ConvertStringToType(fLayerDType)){
686 case ETensorType::FLOAT:{
687 if(fLayerType == "Add")
688 op.reset(new ROperator_BasicBinary<float, Add> (fX1, fX2, fY));
689 else if(fLayerType == "Subtract")
690 op.reset(new ROperator_BasicBinary<float, Sub> (fX1, fX2, fY));
691 else
692 op.reset(new ROperator_BasicBinary<float, Mul> (fX1, fX2, fY));
693 break;
694 }
695 default:
696 throw std::runtime_error("TMVA::SOFIE - Unsupported - Operator Sigmoid does not yet support input type " + fLayerDType);
697 }
698 return op;
699}
700
701}//INTERNAL
702
703
704//////////////////////////////////////////////////////////////////////////////////
705/// \param[in] filename file location of Keras .h5
706/// \return Parsed RModel object
707///
708/// The `Parse()` function defined in `TMVA::Experimental::SOFIE::PyKeras` will
709/// parse a trained Keras .h5 model into a RModel Object. After loading the model
710/// in a Python Session, the included layers are extracted with properties
711/// like Layer type, Attributes, Input tensor names, Output tensor names, data-type
712/// and names of the weight/initialized tensors.
713/// The extracted layers from the model are then passed into `AddKerasLayer()`
714/// which prepares the specific ROperator and adds them into the RModel object.
715/// The layers are also checked for adding any required routines for executing
716/// the generated Inference code.
717///
718/// For adding the Initialized tensors into the RModel object, the weights are
719/// extracted from the Keras model in the form of NumPy arrays, which are then
720/// passed into `AddInitializedTensor()` after appropriate casting.
721///
722/// Input tensor infos are required to be added which will contain their names,
723/// shapes and data-types. For keras models with single input tensors, the tensor
724/// shape is returned as a Tuple object, whereas for multi-input models,
725/// the tensor shape is returned as a List of Tuple object containing the shape
726/// of the individual input tensors. SOFIE's RModel also requires that the Keras
727/// models are initialized with Batch Size. The `GetDataFromTuple()` are called
728/// on the Tuple objects, which then returns the shape vector required to call
729/// the `AddInputTensorInfo()`.
730///
731/// For adding the Output Tensor infos, only the names of the model's output
732/// tensors are extracted and are then passed into `AddOutputTensorNameList()`.
733///
734/// Example Usage:
735/// ~~~ {.cpp}
736/// using TMVA::Experimental::SOFIE;
737/// RModel model = PyKeras::Parse("trained_model_dense.h5");
738/// ~~~
739RModel Parse(std::string filename){
740
741 char sep = '/';
742 #ifdef _WIN32
743 sep = '\\';
744 #endif
745
746 size_t isep = filename.rfind(sep, filename.length());
747 std::string filename_nodir = filename;
748 if (isep != std::string::npos){
749 filename_nodir = (filename.substr(isep+1, filename.length() - isep));
750 }
751
752 //Check on whether the Keras .h5 file exists
753 if(!std::ifstream(filename).good()){
754 throw std::runtime_error("Model file "+filename_nodir+" not found!");
755 }
756
757
758 std::time_t ttime = std::time(0);
759 std::tm* gmt_time = std::gmtime(&ttime);
760 std::string parsetime (std::asctime(gmt_time));
761
762 RModel rmodel(filename_nodir, parsetime);
763
764 //Intializing Python Interpreter and scope dictionaries
765 Py_Initialize();
766 PyObject* main = PyImport_AddModule("__main__");
767 PyObject* fGlobalNS = PyModule_GetDict(main);
768 PyObject* fLocalNS = PyDict_New();
769 if (!fGlobalNS) {
770 throw std::runtime_error("Can't init global namespace for Python");
771 }
772 if (!fLocalNS) {
773 throw std::runtime_error("Can't init local namespace for Python");
774 }
775
776 // Extracting model information
777 // For each layer: type,name,activation,dtype,input tensor's name,
778 // output tensor's name, kernel's name, bias's name
779 // None object is returned for if property doesn't belong to layer
780 PyRunString("import tensorflow.keras as keras",fGlobalNS,fLocalNS);
781 PyRunString("from tensorflow.keras.models import load_model",fGlobalNS,fLocalNS);
782 PyRunString("print('Keras Version: '+ keras.__version__)",fGlobalNS,fLocalNS);
783 PyRunString(TString::Format("model=load_model('%s')",filename.c_str()),fGlobalNS,fLocalNS);
784 PyRunString(TString::Format("model.load_weights('%s')",filename.c_str()),fGlobalNS,fLocalNS);
785 PyRunString("globals().update(locals())",fGlobalNS,fLocalNS);
786 PyRunString("modelData=[]",fGlobalNS,fLocalNS);
787 PyRunString("for idx in range(len(model.layers)):\n"
788 " layer=model.get_layer(index=idx)\n"
789 " layerData={}\n"
790 " layerData['layerType']=layer.__class__.__name__\n"
791 " layerData['layerAttributes']=layer.__dict__\n"
792 " layerData['layerInput']=[x.name for x in layer.input] if isinstance(layer.input,list) else [layer.input.name]\n"
793 " layerData['layerOutput']=[x.name for x in layer.output] if isinstance(layer.output,list) else [layer.output.name]\n"
794 " layerData['layerDType']=layer.dtype\n"
795 " layerData['layerWeight']=[x.name for x in layer.weights]\n"
796 " modelData.append(layerData)",fGlobalNS,fLocalNS);
797
798
799 PyObject* fPModel = PyDict_GetItemString(fLocalNS,"modelData");
800 PyObject *fLayer;
801 Py_ssize_t fModelSize = PyList_Size(fPModel);
802 std::string fLayerType;
803
804 // Traversing through all the layers and passing the Layer object to `AddKerasLayer()`
805 // for adding the equivalent ROperators into the RModel object.
806 for(Py_ssize_t fModelIterator=0;fModelIterator<fModelSize;++fModelIterator){
807 fLayer = PyList_GetItem(fPModel,fModelIterator);
808 fLayerType = PyStringAsString(PyDict_GetItemString(fLayer,"layerType"));
809
810 // Ignoring the input layer for models built using Keras Functional API
811 if(fLayerType == "InputLayer")
812 continue;
813
814 // Adding any required routines depending on the Layer types for generating
815 // inference code.
816 else if(fLayerType == "Dense")
817 rmodel.AddBlasRoutines({"Gemm", "Gemv"});
818 else if (fLayerType == "BatchNormalization")
819 rmodel.AddBlasRoutines({"Copy", "Axpy"});
820
821 else if(fLayerType == "Conv1D" || fLayerType == "Conv2D" || fLayerType == "Conv3D")
822 rmodel.AddBlasRoutines({"Gemm", "Axpy"});
823 INTERNAL::AddKerasLayer(rmodel,fLayer);
824
825 }
826
827 //Extracting model's weights
828 //For every initialized tensor, weightProp will have its name and dtype in string
829 //and value in numpy array
830 PyRunString("weight=[]",fGlobalNS,fLocalNS);
831 PyRunString("for idx in range(len(model.get_weights())):\n"
832 " weightProp={}\n"
833 " weightProp['name']=model.weights[idx].name\n"
834 " weightProp['dtype']=(model.get_weights())[idx].dtype.name\n"
835 " weightProp['value']=(model.get_weights())[idx].transpose((3,2,0,1)).copy() if ('conv' in model.weights[idx].name and model.weights[idx].shape.ndims == 4) else (model.get_weights())[idx]\n"
836 " weight.append(weightProp)",fGlobalNS,fLocalNS);
837
838 PyObject *fWeightTensor, *fPWeight;
839 PyArrayObject *fWeightTensorValue;
840 std::string fWeightName;
841 ETensorType fWeightDType;
842 fPWeight = PyDict_GetItemString(fLocalNS,"weight");
843 std::vector<std::size_t> fWeightTensorShape;
844 std::size_t fWeightTensorSize;
845
846 // Traversing through all the Weight tensors
847 for (Py_ssize_t weightIter = 0; weightIter < PyList_Size(fPWeight); weightIter++){
848 fWeightTensor = PyList_GetItem(fPWeight, weightIter);
849 fWeightName = PyStringAsString(PyDict_GetItemString(fWeightTensor,"name"));
850 fWeightDType = ConvertStringToType(PyStringAsString(PyDict_GetItemString(fWeightTensor,"dtype")));
851
852 fWeightTensorValue = (PyArrayObject*)PyDict_GetItemString(fWeightTensor,"value");
853 fWeightTensorSize=1;
854 fWeightTensorShape.clear();
855
856 // Building the shape vector and finding the tensor size
857 for(int j=0; j<PyArray_NDIM(fWeightTensorValue); ++j){
858 fWeightTensorShape.push_back((std::size_t)(PyArray_DIM(fWeightTensorValue,j)));
859 fWeightTensorSize*=(std::size_t)(PyArray_DIM(fWeightTensorValue,j));
860 }
861
862 switch(fWeightDType){
863 case ETensorType::FLOAT : {
864 float* fWeightArray = (float*)PyArray_DATA(fWeightTensorValue);
865 std::shared_ptr<void> fData(malloc(fWeightTensorSize * sizeof(float)), free);
866 std::memcpy(fData.get(),fWeightArray, fWeightTensorSize * sizeof(float));
867 rmodel.AddInitializedTensor(fWeightName,ETensorType::FLOAT,fWeightTensorShape,fData);
868 break;
869 }
870 default:
871 throw std::runtime_error("Type error: TMVA SOFIE does not yet weight data layer type"+ConvertTypeToString(fWeightDType));
872 }
873 }
874
875
876 // Extracting input tensor info
877 // For every input tensor inputNames will have their names as string,
878 // inputShapes will have their shape as Python Tuple, and inputTypes
879 // will have their dtype as string
880 PyRunString("inputNames=model.input_names",fGlobalNS,fLocalNS);
881 PyRunString("inputShapes=model.input_shape if type(model.input_shape)==list else [model.input_shape]",fGlobalNS,fLocalNS);
882 PyRunString("inputTypes=[]",fGlobalNS,fLocalNS);
883 PyRunString("for idx in range(len(model.inputs)):\n"
884 " inputTypes.append(model.inputs[idx].dtype.__str__()[9:-2])",fGlobalNS,fLocalNS);
885
886 PyObject* fPInputs = PyDict_GetItemString(fLocalNS,"inputNames");
887 PyObject* fPInputShapes = PyDict_GetItemString(fLocalNS,"inputShapes");
888 PyObject* fPInputTypes = PyDict_GetItemString(fLocalNS,"inputTypes");
889
890 std::string fInputName;
891 ETensorType fInputDType;
892
893 // For single input models, the model.input_shape will return a tuple
894 // describing the input tensor shape. For multiple inputs models,
895 // the model.input_shape will return a list of tuple, each describing
896 // the input tensor shape.
897 if(PyTuple_Check(fPInputShapes)){
898 fInputName = PyStringAsString(PyList_GetItem(fPInputs,0));
899 fInputDType = ConvertStringToType(PyStringAsString(PyList_GetItem(fPInputTypes,0)));
900
901 switch(fInputDType){
902
903 case ETensorType::FLOAT : {
904
905 // Getting the shape vector from the Tuple object
906 std::vector<size_t>fInputShape = GetDataFromTuple(fPInputShapes);
907 if (static_cast<int>(fInputShape[0]) <= 0){
908 fInputShape[0] = 1;
909 std::cout << "Model has not a defined batch size, assume is 1 - input shape : "
910 << TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
911 }
912 rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
913 rmodel.AddInputTensorName(fInputName);
914 break;
915 }
916
917 default:
918 throw std::runtime_error("Type error: TMVA SOFIE does not yet support data type"+ConvertTypeToString(fInputDType));
919 }
920
921 }
922
923 else{
924
925 // Iterating through multiple input tensors
926 for(Py_ssize_t inputIter = 0; inputIter < PyList_Size(fPInputs);++inputIter){
927
928 fInputName = PyStringAsString(PyList_GetItem(fPInputs,inputIter));
929 fInputDType = ConvertStringToType(PyStringAsString(PyList_GetItem(fPInputTypes,inputIter)));
930
931 switch(fInputDType){
932 case ETensorType::FLOAT : {
933 PyObject* fInputShapeTuple=PyList_GetItem(fPInputShapes,inputIter);
934
935 std::vector<size_t>fInputShape = GetDataFromTuple(fInputShapeTuple);
936 if (static_cast<int>(fInputShape[0]) <= 0){
937 fInputShape[0] = 1;
938 std::cout << "Model has not a defined batch size, assume is 1 - input shape for tensor "
939 << fInputName << " : " << TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
940 }
941 rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
942 rmodel.AddInputTensorName(fInputName);
943 break;
944 }
945
946 default:
947 throw std::runtime_error("Type error: TMVA SOFIE does not yet support data type"+ConvertTypeToString(fInputDType));
948
949 }
950 }
951 }
952
953
954 // For adding OutputTensorInfos, the names of the output
955 // tensors are extracted from the Keras model
956 PyRunString("outputNames=[]",fGlobalNS,fLocalNS);
957 PyRunString("for layerName in model.output_names:\n"
958 " outputNames.append(model.get_layer(layerName).output.name)",fGlobalNS,fLocalNS);
959 PyObject* fPOutputs = PyDict_GetItemString(fLocalNS,"outputNames");
960 std::vector<std::string> fOutputNames;
961 for(Py_ssize_t outputIter = 0; outputIter < PyList_Size(fPOutputs);++outputIter){
962 fOutputNames.push_back(PyStringAsString(PyList_GetItem(fPOutputs,outputIter)));
963 }
964 rmodel.AddOutputTensorNameList(fOutputNames);
965
966 return rmodel;
967}
968}//PyKeras
969}//SOFIE
970}//Experimental
971}//TMVA
_object PyObject
Definition: PyMethodBase.h:43
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 filename
int main(int argc, char *argv[])
Definition: cef_main.cxx:54
#define free
Definition: civetweb.c:1539
#define malloc
Definition: civetweb.c:1536
void AddOutputTensorNameList(std::vector< std::string > outputtensornames)
Definition: RModel.cxx:168
void AddInputTensorInfo(std::string input_name, ETensorType type, std::vector< Dim > shape)
Definition: RModel.cxx:108
void AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition: RModel.cxx:143
void AddBlasRoutines(std::vector< std::string > routines)
Definition: RModel.hxx:83
void AddInputTensorName(std::string name)
Definition: RModel.cxx:127
void AddNeededStdLib(std::string libname)
Definition: RModel.hxx:88
void AddOperator(std::unique_ptr< ROperator > op, int order_execution=-1)
Definition: RModel.cxx:131
static std::vector< size_t > GetDataFromTuple(PyObject *tupleObject)
Utility function which retrieves and returns the values of the Tuple object as a vector of size_t.
static const char * PyStringAsString(PyObject *string)
Returns const char* from Python string in PyObject.
void PyRunString(TString code, TString errorMessage="Failed to run python code", int start=256)
Execute Python code from string.
Basic string class.
Definition: TString.h:136
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition: TString.cxx:2345
RVec< PromoteType< T > > floor(const RVec< T > &v)
Definition: RVec.hxx:1812
RVec< PromoteType< T > > ceil(const RVec< T > &v)
Definition: RVec.hxx:1813
void(off) SmallVectorTemplateBase< T
static constexpr double L
std::unique_ptr< ROperator > MakeKerasConv(PyObject *fLayer)
Prepares a ROperator object for Keras Conv Layer.
std::unique_ptr< ROperator > MakeKerasPermute(PyObject *fLayer)
Prepares a ROperator object for Keras Permute layer.
std::unordered_map< std::string, std::unique_ptr< ROperator >(*)(PyObject *fLayer)> KerasMethodMap
std::unique_ptr< ROperator > MakeKerasBatchNorm(PyObject *fLayer)
Prepares a ROperator object for Keras BatchNorm layer.
void AddKerasLayer(RModel &rmodel, PyObject *fLayer)
Adds equivalent ROperator with respect to Keras model layer into the referenced RModel object.
std::unique_ptr< ROperator > MakeKerasConcat(PyObject *fLayer)
Prepares a ROperator object for Keras Concat layer.
std::unique_ptr< ROperator > MakeKerasLeakyRelu(PyObject *fLayer)
Prepares a ROperator object for Keras Leaky Relu activation.
std::unique_ptr< ROperator > MakeKerasDense(PyObject *fLayer)
Prepares a ROperator object for Keras Dense Layer.
std::unique_ptr< ROperator > MakeKerasBinary(PyObject *fLayer)
Prepares a ROperator object for Keras binary operations like Add, subtract, and multiply.
std::unique_ptr< ROperator > MakeKerasTanh(PyObject *fLayer)
Prepares a ROperator object for Keras Tanh activation.
std::unique_ptr< ROperator > MakeKerasSoftmax(PyObject *fLayer)
Prepares a ROperator object for Keras Softmax activation.
std::unique_ptr< ROperator > MakeKerasReshape(PyObject *fLayer)
Prepares a ROperator object for Keras Reshape layer.
std::unique_ptr< ROperator > MakeKerasReLU(PyObject *fLayer)
Prepares a ROperator object for Keras ReLU activation.
const KerasMethodMapWithActivation mapKerasLayerWithActivation
std::unordered_map< std::string, std::unique_ptr< ROperator >(*)(PyObject *fLayer)> KerasMethodMapWithActivation
std::unique_ptr< ROperator > MakeKerasSigmoid(PyObject *fLayer)
Prepares a ROperator object for Keras Sigmoid activation.
std::unique_ptr< ROperator > MakeKerasSelu(PyObject *fLayer)
Prepares a ROperator object for Keras Selu activation.
std::unique_ptr< ROperator > MakeKerasActivation(PyObject *fLayer)
Prepares a ROperator object for Keras activation layer.
static void(&) PyRunString(TString, PyObject *, PyObject *)
static const char *(&) PyStringAsString(PyObject *)
RModel Parse(std::string filename)
Parser function for translatng Keras .h5 model into a RModel object.
std::string ConvertShapeToString(std::vector< size_t > shape)
std::string ConvertTypeToString(ETensorType type)
ETensorType ConvertStringToType(std::string type)
create variable transformations
static void output()