From 97f49c198d43cf4121cbf06205f0946eb6b99194 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 30 Nov 2025 15:52:35 +0100 Subject: [PATCH 1/2] [tmva][pymva] Deprecate the `PyKeras` method The new Keras 3 API broke the existing TMVA `PyKeras` method. Re-implementing the method for Keras 3 turned out to be difficult, as it is not possible anymore to disable eager execution and have a decent speed in evaluating the model for single events. So large-scale refactoring would be necessary to implement `PyKeras` again with good performance (see also https://github.com/root-project/root/pull/15790). Nowadays, we also have the RBatchGenerator to train Keras models directly with batches that are provided by ROOT. Therefore, the TMVA `PyKeras` method is not the only way anymore to train a Keras model with ROOT data without 3rd party libraries for the IO. That means it's not an essential feature anymore, and deprecating it will even make the situation clearer for the user, as there are not two different ways anymore to train Keras models on ROOT data. --- README/ReleaseNotes/v640/index.md | 1 + tmva/pymva/CMakeLists.txt | 3 +++ tmva/pymva/src/MethodPyKeras.cxx | 4 ++++ tmva/tmva/inc/TMVA/Types.h | 7 +++++++ 4 files changed, 15 insertions(+) diff --git a/README/ReleaseNotes/v640/index.md b/README/ReleaseNotes/v640/index.md index cb523d2794bd9..1b0498692bd5e 100644 --- a/README/ReleaseNotes/v640/index.md +++ b/README/ReleaseNotes/v640/index.md @@ -39,6 +39,7 @@ The following people have contributed to this new version: * Comparing C++ `nullptr` objects with `None` in Python now raises a `TypeError`, as announced in the ROOT 6.38 release notes. Use truth-value checks like `if not x` or `x is None` instead. * The `TGLIncludes.h` and `TGLWSIncludes.h` that were deprecated in ROOT 6.38 and scheduled for removal are gone now. Please include your required headers like `` or `` directly. * The GLEW headers (`GL/eglew.h`, `GL/glew.h`, `GL/glxew.h`, and `GL/wglew.h`) that were installed when building ROOT with `builtin_glew=ON` are no longer installed. This is done because ROOT is moving away from GLEW for loading OpenGL extensions. +* The TMVA `PyKeras` method is deprecated. It was broken by the API changes in Keras 3, released in November 2023 and part of TensorFlow 2.16 or newer. The `PyKeras` method will be removed in ROOT 6.42 (unless an updated implementation for Keras 3 that matches usage, performance and stability requirements will be found unexpectedly). ## Build System diff --git a/tmva/pymva/CMakeLists.txt b/tmva/pymva/CMakeLists.txt index 89ed2de8f754f..fda34f0910c06 100644 --- a/tmva/pymva/CMakeLists.txt +++ b/tmva/pymva/CMakeLists.txt @@ -36,4 +36,7 @@ ROOT_STANDARD_LIBRARY_PACKAGE(PyMVA TMVA ) +# To avoid deprecation warnings during build of ROOT. +target_compile_definitions(PyMVA PUBLIC PYMVA_BUILDS_ITSELF) + ROOT_ADD_TEST_SUBDIRECTORY(test) diff --git a/tmva/pymva/src/MethodPyKeras.cxx b/tmva/pymva/src/MethodPyKeras.cxx index 38ee5d5185953..731bf8119ba05 100644 --- a/tmva/pymva/src/MethodPyKeras.cxx +++ b/tmva/pymva/src/MethodPyKeras.cxx @@ -182,6 +182,10 @@ void MethodPyKeras::InitKeras() { // initialize first Keras. This is done only here when class has // all state variable set from options or read from XML file // Import Keras + Log() << kWARNING + << "The PyKeras TMVA method was deprecated in ROOT 6.40 and will be removed in ROOT 6.42, since it was broken " + "by the API changes in Keras 3, released in November 2023 and part of TensorFlow 2.16 or newer." + << Endl; if (fUseTFKeras) Log() << kINFO << "Setting up tf.keras" << Endl; diff --git a/tmva/tmva/inc/TMVA/Types.h b/tmva/tmva/inc/TMVA/Types.h index 6f019e808a6cd..3428543520b22 100644 --- a/tmva/tmva/inc/TMVA/Types.h +++ b/tmva/tmva/inc/TMVA/Types.h @@ -42,6 +42,8 @@ #include "RtypesCore.h" +#include // for R__DEPRECATED + #include "TString.h" namespace TMVA { @@ -100,7 +102,12 @@ namespace TMVA { kPyRandomForest , kPyAdaBoost , kPyGTB , +#ifdef PYMVA_BUILDS_ITSELF kPyKeras , +#else + kPyKeras + R__DEPRECATED(6, 42, "the PyKeras method is dropped since it didn't support Keras 3 (TensorFlow 2.16+)"), +#endif kPyTorch , kC50 , kRSNNS , From 1853ede165e0b7fbc176b452a6a87d1080f4b165 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Sun, 4 Jan 2026 12:08:40 +0100 Subject: [PATCH 2/2] [tutorials] Remove tutorials for deprecated TMVA PyKeras method Don't keep tutorials for deprecated functionality around, to make it explicit to the users that they should not use TMVA PyKeras anymore. We don't lose any test coverage when removing these tutorials, since `tmva-pymva` is globally disabled in the CI anyway, as Keras 3 is not supported by PyMVA. --- tutorials/CMakeLists.txt | 12 +-- .../keras/ApplicationClassificationKeras.py | 47 ----------- .../keras/ApplicationRegressionKeras.py | 40 --------- .../keras/ClassificationKeras.py | 76 ----------------- .../machine_learning/keras/GenerateModel.py | 62 -------------- .../machine_learning/keras/MulticlassKeras.py | 84 ------------------- .../machine_learning/keras/RegressionKeras.py | 77 ----------------- tutorials/machine_learning/keras/index.md | 3 - 8 files changed, 1 insertion(+), 400 deletions(-) delete mode 100755 tutorials/machine_learning/keras/ApplicationClassificationKeras.py delete mode 100755 tutorials/machine_learning/keras/ApplicationRegressionKeras.py delete mode 100755 tutorials/machine_learning/keras/ClassificationKeras.py delete mode 100755 tutorials/machine_learning/keras/GenerateModel.py delete mode 100755 tutorials/machine_learning/keras/MulticlassKeras.py delete mode 100755 tutorials/machine_learning/keras/RegressionKeras.py delete mode 100644 tutorials/machine_learning/keras/index.md diff --git a/tutorials/CMakeLists.txt b/tutorials/CMakeLists.txt index 59ea6e78103be..146f1fe7bffa9 100644 --- a/tutorials/CMakeLists.txt +++ b/tutorials/CMakeLists.txt @@ -326,7 +326,7 @@ if(NOT TARGET Gui) endif() if (NOT ROOT_tmva_FOUND) - list(APPEND tmva_veto machine_learning/*.C machine_learning/*.py machine_learning/envelope/*.C machine_learning/keras/*.C machine_learning/keras/*.py machine_learning/pytorch/*.py ) + list(APPEND tmva_veto machine_learning/*.C machine_learning/*.py machine_learning/envelope/*.C machine_learning/pytorch/*.py ) else() #copy input data files configure_file(${CMAKE_CURRENT_SOURCE_DIR}/machine_learning/data/tmva_class_example.root ${CMAKE_CURRENT_BINARY_DIR}/machine_learning/data COPYONLY) @@ -650,10 +650,6 @@ set (machine_learning-TMVA_SOFIE_RDataFrame_JIT-depends tutorial-machine_learnin set (machine_learning-TMVA_SOFIE_Keras_HiggsModel-depends tutorial-machine_learning-TMVA_SOFIE_RDataFrame_JIT) set (machine_learning-TMVA_SOFIE_RDataFrame-depends tutorial-machine_learning-TMVA_SOFIE_Keras_HiggsModel) set (machine_learning-TMVA_SOFIE_Inference-depends tutorial-machine_learning-TMVA_SOFIE_RDataFrame) -set (machine_learning-keras-RegressionKeras-depends tutorial-machine_learning-pytorch-RegressionPyTorch-py) -set (machine_learning-keras-ClassificationKeras-depends tutorial-machine_learning-pytorch-ClassificationPyTorch-py) -set (machine_learning-keras-ApplicationRegressionKeras-depends tutorial-machine_learning-keras-RegressionKeras-py) -set (machine_learning-keras-ApplicationClassificationKeras-depends tutorial-machine_learning-keras-ClassificationKeras-py) #--List long-running tutorials to label them as "longtest" set (long_running @@ -846,11 +842,6 @@ if(ROOT_pyroot_FOUND) # Disable tutorial showing connection to the HTCondor service at CERN list(APPEND pyveto analysis/dataframe/distrdf004_dask_lxbatch.py) - if(NOT tmva-pymva) - file(GLOB tmva_veto_py RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} machine_learning/keras/*.py) - list(APPEND pyveto ${tmva_veto_py}) - endif() - if (ROOT_KERAS_FOUND) set (machine_learning-TMVA_SOFIE_RDataFrame-py-depends tutorial-machine_learning-TMVA_SOFIE_Keras_HiggsModel) endif() @@ -953,7 +944,6 @@ if(ROOT_pyroot_FOUND) machine_learning/TMVA_SOFIE_Inference.py machine_learning/TMVA_SOFIE_Models.py machine_learning/TMVA_SOFIE_RDataFrame.py - machine_learning/keras/*.py ) file(GLOB requires_torch RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} machine_learning/pytorch/*.py diff --git a/tutorials/machine_learning/keras/ApplicationClassificationKeras.py b/tutorials/machine_learning/keras/ApplicationClassificationKeras.py deleted file mode 100755 index 85c7882fb75e4..0000000000000 --- a/tutorials/machine_learning/keras/ApplicationClassificationKeras.py +++ /dev/null @@ -1,47 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to apply a trained model to new data. -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from ROOT import TMVA, TFile, TString, gROOT -from array import array -from subprocess import call -from os.path import isfile - -# Setup TMVA -TMVA.Tools.Instance() -TMVA.PyMethodBase.PyInitialize() -reader = TMVA.Reader("Color:!Silent") - -# Load data -data = TFile.Open(str(gROOT.GetTutorialDir()) + "/machine_learning/data/tmva_class_example.root") -signal = data.Get('TreeS') -background = data.Get('TreeB') - -branches = {} -for branch in signal.GetListOfBranches(): - branchName = branch.GetName() - branches[branchName] = array('f', [-999]) - reader.AddVariable(branchName, branches[branchName]) - signal.SetBranchAddress(branchName, branches[branchName]) - background.SetBranchAddress(branchName, branches[branchName]) - -# Book methods -reader.BookMVA('PyKeras', TString('dataset/weights/TMVAClassification_PyKeras.weights.xml')) - -# Print some example classifications -print('Some signal example classifications:') -for i in range(20): - signal.GetEntry(i) - print(reader.EvaluateMVA('PyKeras')) -print('') - -print('Some background example classifications:') -for i in range(20): - background.GetEntry(i) - print(reader.EvaluateMVA('PyKeras')) diff --git a/tutorials/machine_learning/keras/ApplicationRegressionKeras.py b/tutorials/machine_learning/keras/ApplicationRegressionKeras.py deleted file mode 100755 index 7055236e23047..0000000000000 --- a/tutorials/machine_learning/keras/ApplicationRegressionKeras.py +++ /dev/null @@ -1,40 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to apply a trained model to new data (regression). -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from ROOT import TMVA, TFile, TString, gROOT -from array import array -from subprocess import call -from os.path import isfile - -# Setup TMVA -TMVA.Tools.Instance() -TMVA.PyMethodBase.PyInitialize() -reader = TMVA.Reader("Color:!Silent") - -# Load data -data = TFile.Open(str(gROOT.GetTutorialDir()) + '/machine_learning/data/tmva_reg_example.root') -tree = data.Get('TreeR') - -branches = {} -for branch in tree.GetListOfBranches(): - branchName = branch.GetName() - branches[branchName] = array('f', [-999]) - tree.SetBranchAddress(branchName, branches[branchName]) - if branchName != 'fvalue': - reader.AddVariable(branchName, branches[branchName]) - -# Book methods -reader.BookMVA('PyKeras', TString('dataset/weights/TMVARegression_PyKeras.weights.xml')) - -# Print some example regressions -print('Some example regressions:') -for i in range(20): - tree.GetEntry(i) - print('True/MVA value: {}/{}'.format(branches['fvalue'][0],reader.EvaluateMVA('PyKeras'))) diff --git a/tutorials/machine_learning/keras/ClassificationKeras.py b/tutorials/machine_learning/keras/ClassificationKeras.py deleted file mode 100755 index 3baad783676e9..0000000000000 --- a/tutorials/machine_learning/keras/ClassificationKeras.py +++ /dev/null @@ -1,76 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to do classification in TMVA with neural networks -## trained with keras. -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from ROOT import TMVA, TFile, TCut, gROOT -from subprocess import call -from os.path import isfile - -from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense -from tensorflow.keras.optimizers import SGD - - -def create_model(): - # Generate model - - # Define model - model = Sequential() - model.add(Dense(64, activation='relu', input_dim=4)) - model.add(Dense(2, activation='softmax')) - - # Set loss and optimizer - model.compile(loss='categorical_crossentropy', - optimizer=SGD(learning_rate=0.01), weighted_metrics=['accuracy', ]) - - # Store model to file - model.save('modelClassification.h5') - model.summary() - - -def run(): - with TFile.Open('TMVA_Classification_Keras.root', 'RECREATE') as output, TFile.Open(str(gROOT.GetTutorialDir()) + '/machine_learning/data/tmva_class_example.root') as data: - factory = TMVA.Factory('TMVAClassification', output, - '!V:!Silent:Color:DrawProgressBar:Transformations=D,G:AnalysisType=Classification') - - signal = data.Get('TreeS') - background = data.Get('TreeB') - - dataloader = TMVA.DataLoader('dataset') - for branch in signal.GetListOfBranches(): - dataloader.AddVariable(branch.GetName()) - - dataloader.AddSignalTree(signal, 1.0) - dataloader.AddBackgroundTree(background, 1.0) - dataloader.PrepareTrainingAndTestTree(TCut(''), - 'nTrain_Signal=4000:nTrain_Background=4000:SplitMode=Random:NormMode=NumEvents:!V') - - # Book methods - factory.BookMethod(dataloader, TMVA.Types.kFisher, 'Fisher', - '!H:!V:Fisher:VarTransform=D,G') - factory.BookMethod(dataloader, TMVA.Types.kPyKeras, 'PyKeras', - 'H:!V:VarTransform=D,G:FilenameModel=modelClassification.h5:FilenameTrainedModel=trainedModelClassification.h5:NumEpochs=20:BatchSize=32:LearningRateSchedule=10,0.01;20,0.005') - - # Run training, test and evaluation - factory.TrainAllMethods() - factory.TestAllMethods() - factory.EvaluateAllMethods() - - -if __name__ == "__main__": - # Setup TMVA - TMVA.Tools.Instance() - TMVA.PyMethodBase.PyInitialize() - - # Create and store the ML model - create_model() - - # Run TMVA - run() diff --git a/tutorials/machine_learning/keras/GenerateModel.py b/tutorials/machine_learning/keras/GenerateModel.py deleted file mode 100755 index 832d54e410dc2..0000000000000 --- a/tutorials/machine_learning/keras/GenerateModel.py +++ /dev/null @@ -1,62 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to define and generate a keras model for use with -## TMVA. -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense, Activation -from tensorflow.keras.regularizers import l2 -from tensorflow.keras.optimizers import SGD -from tensorflow.keras.utils import plot_model - -# Setup the model here -num_input_nodes = 4 -num_output_nodes = 2 -num_hidden_layers = 1 -nodes_hidden_layer = 64 -l2_val = 1e-5 - -model = Sequential() - -# Hidden layer 1 -# NOTE: Number of input nodes need to be defined in this layer -model.add(Dense(nodes_hidden_layer, activation='relu', kernel_regularizer=l2(l2_val), input_dim=num_input_nodes)) - -# Hidden layer 2 to num_hidden_layers -# NOTE: Here, you can do what you want -for k in range(num_hidden_layers-1): - model.add(Dense(nodes_hidden_layer, activation='relu', kernel_regularizer=l2(l2_val))) - -# Output layer -# NOTE: Use following output types for the different tasks -# Binary classification: 2 output nodes with 'softmax' activation -# Regression: 1 output with any activation ('linear' recommended) -# Multiclass classification: (number of classes) output nodes with 'softmax' activation -model.add(Dense(num_output_nodes, activation='softmax')) - -# Compile model -# NOTE: Use following settings for the different tasks -# Any classification: 'categorical_crossentropy' is recommended loss function -# Regression: 'mean_squared_error' is recommended loss function -model.compile(loss='categorical_crossentropy', optimizer=SGD(learning_rate=0.01), weighted_metrics=['accuracy',]) - -# Save model -model.save('model.h5') - -# Additional information about the model -# NOTE: This is not needed to run the model - -# Print summary -model.summary() - -# Visualize model as graph -try: - plot_model(model, to_file='model.png', show_shapes=True) -except: - print('[INFO] Failed to make model plot') diff --git a/tutorials/machine_learning/keras/MulticlassKeras.py b/tutorials/machine_learning/keras/MulticlassKeras.py deleted file mode 100755 index 358b8197f9bd2..0000000000000 --- a/tutorials/machine_learning/keras/MulticlassKeras.py +++ /dev/null @@ -1,84 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to do multiclass classification in TMVA with neural -## networks trained with keras. -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from ROOT import TMVA, TFile, TCut, gROOT -from os.path import isfile - -from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense -from tensorflow.keras.optimizers import SGD - - -def create_model(): - # Define model - model = Sequential() - model.add(Dense(32, activation='relu', input_dim=4)) - model.add(Dense(4, activation='softmax')) - - # Set loss and optimizer - model.compile(loss='categorical_crossentropy', optimizer=SGD( - learning_rate=0.01), weighted_metrics=['accuracy',]) - - # Store model to file - model.save('modelMultiClass.h5') - model.summary() - - -def run(): - with TFile.Open('TMVA.root', 'RECREATE') as output, TFile.Open('tmva_example_multiple_background.root') as data: - factory = TMVA.Factory('TMVAClassification', output, - '!V:!Silent:Color:DrawProgressBar:Transformations=D,G:AnalysisType=multiclass') - - signal = data.Get('TreeS') - background0 = data.Get('TreeB0') - background1 = data.Get('TreeB1') - background2 = data.Get('TreeB2') - - dataloader = TMVA.DataLoader('dataset') - for branch in signal.GetListOfBranches(): - dataloader.AddVariable(branch.GetName()) - - dataloader.AddTree(signal, 'Signal') - dataloader.AddTree(background0, 'Background_0') - dataloader.AddTree(background1, 'Background_1') - dataloader.AddTree(background2, 'Background_2') - dataloader.PrepareTrainingAndTestTree(TCut(''), - 'SplitMode=Random:NormMode=NumEvents:!V') - - # Book methods - factory.BookMethod(dataloader, TMVA.Types.kFisher, 'Fisher', - '!H:!V:Fisher:VarTransform=D,G') - factory.BookMethod(dataloader, TMVA.Types.kPyKeras, 'PyKeras', - 'H:!V:VarTransform=D,G:FilenameModel=modelMultiClass.h5:FilenameTrainedModel=trainedModelMultiClass.h5:NumEpochs=20:BatchSize=32') - - # Run TMVA - factory.TrainAllMethods() - factory.TestAllMethods() - factory.EvaluateAllMethods() - - -if __name__ == "__main__": - # Generate model - create_model() - - # Setup TMVA - TMVA.Tools.Instance() - TMVA.PyMethodBase.PyInitialize() - - # Load data - if not isfile('tmva_example_multiple_background.root'): - createDataMacro = str(gROOT.GetTutorialDir()) + '/machine_learning/createData.C' - print(createDataMacro) - gROOT.ProcessLine('.L {}'.format(createDataMacro)) - gROOT.ProcessLine('create_MultipleBackground(4000)') - - # Run TMVA - run() diff --git a/tutorials/machine_learning/keras/RegressionKeras.py b/tutorials/machine_learning/keras/RegressionKeras.py deleted file mode 100755 index dce84773d8b7f..0000000000000 --- a/tutorials/machine_learning/keras/RegressionKeras.py +++ /dev/null @@ -1,77 +0,0 @@ -## \file -## \ingroup tutorial_tmva_keras -## \notebook -nodraw -## This tutorial shows how to do regression in TMVA with neural networks -## trained with keras. -## -## \macro_code -## -## \date 2017 -## \author TMVA Team - -from ROOT import TMVA, TFile, TCut, gROOT -from subprocess import call -from os.path import isfile - -from tensorflow.keras.models import Sequential -from tensorflow.keras.layers import Dense -from tensorflow.keras.optimizers import SGD - - -def create_model(): - # Define model - model = Sequential() - model.add(Dense(64, activation='tanh', input_dim=2)) - model.add(Dense(1, activation='linear')) - - # Set loss and optimizer - model.compile(loss='mean_squared_error', optimizer=SGD( - learning_rate=0.01), weighted_metrics=[]) - - # Store model to file - model.save('modelRegression.h5') - model.summary() - - -def run(): - - with TFile.Open('TMVA_Regression_Keras.root', 'RECREATE') as output, TFile.Open(str(gROOT.GetTutorialDir()) + '/machine_learning/data/tmva_reg_example.root') as data: - factory = TMVA.Factory('TMVARegression', output, - '!V:!Silent:Color:DrawProgressBar:Transformations=D,G:AnalysisType=Regression') - - tree = data.Get('TreeR') - - dataloader = TMVA.DataLoader('dataset') - for branch in tree.GetListOfBranches(): - name = branch.GetName() - if name != 'fvalue': - dataloader.AddVariable(name) - dataloader.AddTarget('fvalue') - - dataloader.AddRegressionTree(tree, 1.0) - # use only 1000 events since evaluation is very slow (especially on MacOS). Increase it to get meaningful results - dataloader.PrepareTrainingAndTestTree(TCut(''), - 'nTrain_Regression=1000:SplitMode=Random:NormMode=NumEvents:!V') - - # Book methods - factory.BookMethod(dataloader, TMVA.Types.kPyKeras, 'PyKeras', - 'H:!V:VarTransform=D,G:FilenameModel=modelRegression.h5:FilenameTrainedModel=trainedModelRegression.h5:NumEpochs=20:BatchSize=32') - factory.BookMethod(dataloader, TMVA.Types.kBDT, 'BDTG', - '!H:!V:VarTransform=D,G:NTrees=1000:BoostType=Grad:Shrinkage=0.1:UseBaggedBoost:BaggedSampleFraction=0.5:nCuts=20:MaxDepth=4') - - # Run TMVA - factory.TrainAllMethods() - factory.TestAllMethods() - factory.EvaluateAllMethods() - - -if __name__ == "__main__": - # Setup TMVA - TMVA.Tools.Instance() - TMVA.PyMethodBase.PyInitialize() - - # Generate model - create_model() - - # Run TMVA - run() diff --git a/tutorials/machine_learning/keras/index.md b/tutorials/machine_learning/keras/index.md deleted file mode 100644 index 3f5b7d4e01462..0000000000000 --- a/tutorials/machine_learning/keras/index.md +++ /dev/null @@ -1,3 +0,0 @@ -\defgroup tutorial_tmva_keras TMVA Keras tutorials -\ingroup tutorial_ml -\brief Example code which illustrates how to use keras with the python interface of TMVA