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/// Provide optionally a batch size that can be used to overwrite the one given by the
795/// model. If a batch size is not given 1 is used if the model does not provide a batch size
796///
797/// Example Usage:
798/// ~~~ {.cpp}
799/// using TMVA::Experimental::SOFIE;
800/// RModel model = PyKeras::Parse("trained_model_dense.h5");
801/// ~~~
802RModel Parse(std::string filename, int batch_size){
803
804 char sep = '/';
805 #ifdef _WIN32
806 sep = '\\';
807 #endif
808
809 size_t isep = filename.rfind(sep, filename.length());
810 std::string filename_nodir = filename;
811 if (isep != std::string::npos){
812 filename_nodir = (filename.substr(isep+1, filename.length() - isep));
813 }
814
815 //Check on whether the Keras .h5 file exists
816 if(!std::ifstream(filename).good()){
817 throw std::runtime_error("Model file "+filename_nodir+" not found!");
818 }
819
820
821 std::time_t ttime = std::time(0);
822 std::tm* gmt_time = std::gmtime(&ttime);
823 std::string parsetime (std::asctime(gmt_time));
824
825 RModel rmodel(filename_nodir, parsetime);
826
827 //Intializing Python Interpreter and scope dictionaries
828 Py_Initialize();
829 PyObject* main = PyImport_AddModule("__main__");
830 PyObject* fGlobalNS = PyModule_GetDict(main);
831 PyObject* fLocalNS = PyDict_New();
832 if (!fGlobalNS) {
833 throw std::runtime_error("Can't init global namespace for Python");
834 }
835 if (!fLocalNS) {
836 throw std::runtime_error("Can't init local namespace for Python");
837 }
838
839 // Extracting model information
840 // For each layer: type,name,activation,dtype,input tensor's name,
841 // output tensor's name, kernel's name, bias's name
842 // None object is returned for if property doesn't belong to layer
843 PyRunString("import tensorflow",fGlobalNS,fLocalNS);
844 PyRunString("import tensorflow.keras as keras",fGlobalNS,fLocalNS);
845 PyRunString("from tensorflow.keras.models import load_model",fGlobalNS,fLocalNS);
846 PyRunString("print('TF/Keras Version: '+ tensorflow.__version__)",fGlobalNS,fLocalNS);
847 PyRunString(TString::Format("model=load_model('%s')",filename.c_str()),fGlobalNS,fLocalNS);
848 PyRunString(TString::Format("model.load_weights('%s')",filename.c_str()),fGlobalNS,fLocalNS);
849 PyRunString("globals().update(locals())",fGlobalNS,fLocalNS);
850 PyRunString("modelData=[]",fGlobalNS,fLocalNS);
851 PyRunString("for idx in range(len(model.layers)):\n"
852 " layer=model.get_layer(index=idx)\n"
853 " layerData={}\n"
854 " layerData['layerType']=layer.__class__.__name__\n"
855 " layerData['layerAttributes']=layer.__dict__\n"
856 " layerData['layerInput']=[x.name for x in layer.input] if isinstance(layer.input,list) else [layer.input.name]\n"
857 " layerData['layerOutput']=[x.name for x in layer.output] if isinstance(layer.output,list) else [layer.output.name]\n"
858 " layerData['layerDType']=layer.dtype\n"
859 " layerData['layerWeight']=[x.name for x in layer.weights]\n"
860 " modelData.append(layerData)",fGlobalNS,fLocalNS);
861
862
863 PyObject* fPModel = GetValueFromDict(fLocalNS,"modelData");
864 PyObject *fLayer;
865 Py_ssize_t fModelSize = PyList_Size(fPModel);
866 std::string fLayerType;
867
868 // Traversing through all the layers and passing the Layer object to `AddKerasLayer()`
869 // for adding the equivalent ROperators into the RModel object.
870 for(Py_ssize_t fModelIterator=0;fModelIterator<fModelSize;++fModelIterator){
871 fLayer = PyList_GetItem(fPModel,fModelIterator);
872 fLayerType = PyStringAsString(GetValueFromDict(fLayer,"layerType"));
873
874 // Ignoring the input layer for models built using Keras Functional API
875 if(fLayerType == "InputLayer")
876 continue;
877
878 // Adding any required routines depending on the Layer types for generating
879 // inference code.
880 else if(fLayerType == "Dense")
881 rmodel.AddBlasRoutines({"Gemm", "Gemv"});
882 else if (fLayerType == "BatchNormalization")
883 rmodel.AddBlasRoutines({"Copy", "Axpy"});
884 else if(fLayerType == "Conv1D" || fLayerType == "Conv2D" || fLayerType == "Conv3D")
885 rmodel.AddBlasRoutines({"Gemm", "Axpy"});
886
887 INTERNAL::AddKerasLayer(rmodel,fLayer);
888
889 }
890
891 //Extracting model's weights
892 //For every initialized tensor, weightProp will have its name and dtype in string
893 //and value in numpy array
894 PyRunString("weight=[]",fGlobalNS,fLocalNS);
895 PyRunString("for idx in range(len(model.get_weights())):\n"
896 " weightProp={}\n"
897 " weightProp['name']=model.weights[idx].name\n"
898 " weightProp['dtype']=(model.get_weights())[idx].dtype.name\n"
899 " 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"
900 " weight.append(weightProp)",fGlobalNS,fLocalNS);
901
902 PyObject *fWeightTensor, *fPWeight;
903 PyArrayObject *fWeightTensorValue;
904 std::string fWeightName;
905 ETensorType fWeightDType;
906 fPWeight = GetValueFromDict(fLocalNS,"weight");
907 std::vector<std::size_t> fWeightTensorShape;
908 std::size_t fWeightTensorSize;
909
910 // Traversing through all the Weight tensors
911 for (Py_ssize_t weightIter = 0; weightIter < PyList_Size(fPWeight); weightIter++){
912 fWeightTensor = PyList_GetItem(fPWeight, weightIter);
913 fWeightName = PyStringAsString(GetValueFromDict(fWeightTensor,"name"));
914 fWeightDType = ConvertStringToType(PyStringAsString(GetValueFromDict(fWeightTensor,"dtype")));
915
916 fWeightTensorValue = (PyArrayObject*)GetValueFromDict(fWeightTensor,"value");
917 fWeightTensorSize=1;
918 fWeightTensorShape.clear();
919
920 // Building the shape vector and finding the tensor size
921 for(int j=0; j<PyArray_NDIM(fWeightTensorValue); ++j){
922 fWeightTensorShape.push_back((std::size_t)(PyArray_DIM(fWeightTensorValue,j)));
923 fWeightTensorSize*=(std::size_t)(PyArray_DIM(fWeightTensorValue,j));
924 }
925
926 switch(fWeightDType){
927 case ETensorType::FLOAT : {
928 float* fWeightArray = (float*)PyArray_DATA(fWeightTensorValue);
929 std::shared_ptr<void> fData(malloc(fWeightTensorSize * sizeof(float)), free);
930 std::memcpy(fData.get(),fWeightArray, fWeightTensorSize * sizeof(float));
931 rmodel.AddInitializedTensor(fWeightName,ETensorType::FLOAT,fWeightTensorShape,fData);
932 break;
933 }
934 default:
935 throw std::runtime_error("Type error: TMVA SOFIE does not yet weight data layer type"+ConvertTypeToString(fWeightDType));
936 }
937 }
938
939
940 // Extracting input tensor info
941 // For every input tensor inputNames will have their names as string,
942 // inputShapes will have their shape as Python Tuple, and inputTypes
943 // will have their dtype as string
944 PyRunString("inputNames=model.input_names",fGlobalNS,fLocalNS);
945 PyRunString("inputShapes=model.input_shape if type(model.input_shape)==list else [model.input_shape]",fGlobalNS,fLocalNS);
946 PyRunString("inputTypes=[]",fGlobalNS,fLocalNS);
947 PyRunString("for idx in range(len(model.inputs)):\n"
948 " inputTypes.append(model.inputs[idx].dtype.__str__()[9:-2])",fGlobalNS,fLocalNS);
949
950 PyObject* fPInputs = GetValueFromDict(fLocalNS,"inputNames");
951 PyObject* fPInputShapes = GetValueFromDict(fLocalNS,"inputShapes");
952 PyObject* fPInputTypes = GetValueFromDict(fLocalNS,"inputTypes");
953
954 std::string fInputName;
955 ETensorType fInputDType;
956
957 // For single input models, the model.input_shape will return a tuple
958 // describing the input tensor shape. For multiple inputs models,
959 // the model.input_shape will return a list of tuple, each describing
960 // the input tensor shape.
961 if(PyTuple_Check(fPInputShapes)){
962 fInputName = PyStringAsString(PyList_GetItem(fPInputs,0));
963 fInputDType = ConvertStringToType(PyStringAsString(PyList_GetItem(fPInputTypes,0)));
964
965 switch(fInputDType){
966
967 case ETensorType::FLOAT : {
968
969 // Getting the shape vector from the Tuple object
970 std::vector<size_t>fInputShape = GetDataFromTuple(fPInputShapes);
971 if (static_cast<int>(fInputShape[0]) <= 0){
972 fInputShape[0] = std::max(batch_size,1);
973 std::cout << "Model has not a defined batch size ";
974 if (batch_size <=0) std::cout << " assume is 1 ";
975 else std::cout << " use given value of " << batch_size;
976 std::cout << " - input shape for tensor " << fInputName << " : "
977 << TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
978 }
979 rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
980 rmodel.AddInputTensorName(fInputName);
981 break;
982 }
983
984 default:
985 throw std::runtime_error("Type error: TMVA SOFIE does not yet support data type"+ConvertTypeToString(fInputDType));
986 }
987
988 }
989
990 else{
991
992 // Iterating through multiple input tensors
993 for(Py_ssize_t inputIter = 0; inputIter < PyList_Size(fPInputs);++inputIter){
994
995 fInputName = PyStringAsString(PyList_GetItem(fPInputs,inputIter));
996 fInputDType = ConvertStringToType(PyStringAsString(PyList_GetItem(fPInputTypes,inputIter)));
997
998 switch(fInputDType){
999 case ETensorType::FLOAT : {
1000 PyObject* fInputShapeTuple=PyList_GetItem(fPInputShapes,inputIter);
1001
1002 std::vector<size_t>fInputShape = GetDataFromTuple(fInputShapeTuple);
1003 if (static_cast<int>(fInputShape[0]) <= 0){
1004 fInputShape[0] = std::max(batch_size,1);
1005 std::cout << "Model has not a defined batch size ";
1006 if (batch_size <=0) std::cout << " assume is 1 ";
1007 else std::cout << " use given value of " << batch_size;
1008 std::cout << " - input shape for tensor "
1009 << fInputName << " : " << TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
1010 }
1011 rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
1012 rmodel.AddInputTensorName(fInputName);
1013 break;
1014 }
1015
1016 default:
1017 throw std::runtime_error("Type error: TMVA SOFIE does not yet support data type"+ConvertTypeToString(fInputDType));
1018
1019 }
1020 }
1021 }
1022
1023
1024 // For adding OutputTensorInfos, the names of the output
1025 // tensors are extracted from the Keras model
1026 PyRunString("outputNames=[]",fGlobalNS,fLocalNS);
1027 PyRunString("for layerName in model.output_names:\n"
1028 " outputNames.append(model.get_layer(layerName).output.name)",fGlobalNS,fLocalNS);
1029 PyObject* fPOutputs = GetValueFromDict(fLocalNS,"outputNames");
1030 std::vector<std::string> fOutputNames;
1031 for(Py_ssize_t outputIter = 0; outputIter < PyList_Size(fPOutputs);++outputIter){
1032 fOutputNames.push_back(PyStringAsString(PyList_GetItem(fPOutputs,outputIter)));
1033 }
1034 rmodel.AddOutputTensorNameList(fOutputNames);
1035
1036 return rmodel;
1037}
1038}//PyKeras
1039}//SOFIE
1040}//Experimental
1041}//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 AddInputTensorInfo(std::string input_name, ETensorType type, std::vector< Dim > shape)
Definition RModel.cxx:125
void AddOutputTensorNameList(std::vector< std::string > output_tensor_names)
Definition RModel.cxx:225
void AddInitializedTensor(std::string tensor_name, ETensorType type, std::vector< std::size_t > shape, std::shared_ptr< void > data)
Definition RModel.cxx:161
void AddInputTensorName(std::string name)
Definition RModel.cxx:144
void AddOperator(std::unique_ptr< ROperator > op, int order_execution=-1)
Definition RModel.cxx:148
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:2378
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.
RModel Parse(std::string filename, int batch_size=-1)
Parser function for translatng Keras .h5 model into a RModel object.
static void(&) PyRunString(TString, PyObject *, PyObject *)
static PyObject *(&) GetValueFromDict(PyObject *, const char *)
static const char *(&) PyStringAsString(PyObject *)
std::string ConvertShapeToString(std::vector< size_t > shape)
std::string ConvertTypeToString(ETensorType type)
ETensorType ConvertStringToType(std::string type)
create variable transformations
static void output()