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