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