Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tmva][sofie] Fix when specifying input batch size in Model #15819

Merged
merged 3 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion tmva/pymva/inc/TMVA/RModelParser_Keras.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ namespace PyKeras{
/// Parser function for translatng Keras .h5 model into a RModel object.
/// Accepts the file location of a Keras model and returns the
/// equivalent RModel object.
RModel Parse(std::string filename);
/// One can specify as option a batch size that can be used when the input Keras model
/// has not a defined input batch size : e.g. for input = (input_dim,)
RModel Parse(std::string filename, int batch_size = -1);

}//PyKeras
}//SOFIE
Expand Down
19 changes: 14 additions & 5 deletions tmva/pymva/src/RModelParser_Keras.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -791,12 +791,15 @@ std::unique_ptr<ROperator> MakeKerasIdentity(PyObject* fLayer)
/// For adding the Output Tensor infos, only the names of the model's output
/// tensors are extracted and are then passed into `AddOutputTensorNameList()`.
///
/// Provide optionally a batch size that can be used to overwrite the one given by the
/// model. If a batch size is not given 1 is used if the model does not provide a batch size
///
/// Example Usage:
/// ~~~ {.cpp}
/// using TMVA::Experimental::SOFIE;
/// RModel model = PyKeras::Parse("trained_model_dense.h5");
/// ~~~
RModel Parse(std::string filename){
RModel Parse(std::string filename, int batch_size){

char sep = '/';
#ifdef _WIN32
Expand Down Expand Up @@ -966,8 +969,11 @@ RModel Parse(std::string filename){
// Getting the shape vector from the Tuple object
std::vector<size_t>fInputShape = GetDataFromTuple(fPInputShapes);
if (static_cast<int>(fInputShape[0]) <= 0){
fInputShape[0] = 1;
std::cout << "Model has not a defined batch size, assume is 1 - input shape : "
fInputShape[0] = std::max(batch_size,1);
std::cout << "Model has not a defined batch size ";
if (batch_size <=0) std::cout << " assume is 1 ";
else std::cout << " use given value of " << batch_size;
std::cout << " - input shape for tensor " << fInputName << " : "
<< TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
}
rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
Expand Down Expand Up @@ -995,8 +1001,11 @@ RModel Parse(std::string filename){

std::vector<size_t>fInputShape = GetDataFromTuple(fInputShapeTuple);
if (static_cast<int>(fInputShape[0]) <= 0){
fInputShape[0] = 1;
std::cout << "Model has not a defined batch size, assume is 1 - input shape for tensor "
fInputShape[0] = std::max(batch_size,1);
std::cout << "Model has not a defined batch size ";
if (batch_size <=0) std::cout << " assume is 1 ";
else std::cout << " use given value of " << batch_size;
std::cout << " - input shape for tensor "
<< fInputName << " : " << TMVA::Experimental::SOFIE::ConvertShapeToString(fInputShape) << std::endl;
}
rmodel.AddInputTensorInfo(fInputName, ETensorType::FLOAT, fInputShape);
Expand Down
3 changes: 2 additions & 1 deletion tmva/pymva/test/TestRModelParserKeras.C
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void GenerateModels() {
TEST(RModelParser_Keras, SEQUENTIAL)
{
constexpr float TOLERANCE = DEFAULT_TOLERANCE;
// input is 8 x batch size that is fixed to be 4
std::vector<float> inputSequential = { 0.12107884, 0.89718615, 0.89123899, 0.32197549,
0.17891638, 0.83555135, 0.98680066, 0.14496809,
0.07255503, 0.55386989, 0.6628149 , 0.29843291,
Expand All @@ -38,7 +39,7 @@ TEST(RModelParser_Keras, SEQUENTIAL)
if (gSystem->AccessPathName("KerasModelSequential.h5",kFileExists))
GenerateModels();

TMVA::Experimental:: RSofieReader r("KerasModelSequential.h5");
TMVA::Experimental:: RSofieReader r("KerasModelSequential.h5",{{4,8}});
std::vector<float> outputSequential = r.Compute(inputSequential);


Expand Down
3 changes: 1 addition & 2 deletions tmva/sofie/inc/TMVA/RFunction.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ public:
std::shared_ptr<RModel> GetFunctionBlock() {
return function_block;
}

std::string GenerateModel(const std::string& filename, long read_pos=0, long block_size=1);
std::string GenerateModel(const std::string& filename, long read_pos = 0, long block_size = -1);
std::string Generate(const std::vector<std::string>& inputPtrs);
FunctionTarget GetFunctionTarget() {
return fTarget;
Expand Down
31 changes: 22 additions & 9 deletions tmva/sofie/src/RModel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -262,24 +262,37 @@ void RModel::Initialize(int batchSize, bool verbose) {
// loop on inputs and see if shape can be full specified
// if the batch size is provided it can be used to specify the full shape
// Add the full specified tensors in fReadyInputTensors collection
for (auto &input : fInputTensorInfos) {
auto originalInputTensorInfos = fInputTensorInfos; // need to copy because we may delete elements
for (auto &input : originalInputTensorInfos) {
if (verbose) std::cout << "looking at the tensor " << input.first << std::endl;
// if a batch size is provided convert batch size
// assume is parametrized as "bs" or "batch_size"
if (batchSize > 0) {
// std::vector<Dim> shape;
// shape.reserve(input.second.shape.size());
for (auto &d : input.second.shape) {
if (d.isParam && (d.param == "bs" || d.param == "batch_size")) {
d = Dim{static_cast<size_t>(batchSize)};
// assume first parameter is teh batch size
if (!input.second.shape.empty()) {
auto & d0 = input.second.shape[0];
if (d0.isParam) {
if (verbose) std::cout << "Fix the batch size to " << batchSize << std::endl;
d0 = Dim{static_cast<size_t>(batchSize)};
}
else { // look for cases that a bs or bath_size is specified in tensor shape
for (auto &d : input.second.shape) {
if (d.isParam && (d.param == "bs" || d.param == "batch_size")) {
d = Dim{static_cast<size_t>(batchSize)};
if (verbose) std::cout << "Input shape has bs or batch_size as names. Fix the batch size to " << batchSize << std::endl;
}
}
}
}
}
auto shape = ConvertShapeToInt(input.second.shape);
if (!shape.empty()) {
// add to the ready input tensor informations
AddInputTensorInfo(input.first, input.second.type, shape);
// remove from the tensor info
// remove from the tensor info old dynamic shape
fInputTensorInfos.erase(input.first);
// add to the ready input tensor information the new fixed shape
AddInputTensorInfo(input.first, input.second.type, shape);
}
// store the parameters of the input tensors
else {
Expand Down Expand Up @@ -633,7 +646,7 @@ void RModel::ReadInitializedTensorsFromFile(long pos) {
fGC += " std::ifstream f;\n";
fGC += " f.open(filename);\n";
fGC += " if (!f.is_open()) {\n";
fGC += " throw std::runtime_error(\"tmva-sofie failed to open file for input weights\");\n";
fGC += " throw std::runtime_error(\"tmva-sofie failed to open file \" + filename + \" for input weights\");\n";
fGC += " }\n";

if(fIsGNNComponent) {
Expand Down Expand Up @@ -769,7 +782,7 @@ long RModel::WriteInitializedTensorsToFile(std::string filename) {
}
if (!f.is_open())
throw
std::runtime_error("tmva-sofie failed to open file for tensor weight data");
std::runtime_error("tmva-sofie failed to open file " + filename + " for tensor weight data");
for (auto& i: fInitializedTensors) {
if (i.second.type() == ETensorType::FLOAT) {
size_t length = 1;
Expand Down
7 changes: 6 additions & 1 deletion tmva/tmva/inc/TMVA/RSofieReader.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ public:
if (gSystem->Load("libPyMVA") < 0) {
throw std::runtime_error("RSofieReader: cannot use SOFIE with Keras since libPyMVA is missing");
}
parserCode += "{\nTMVA::Experimental::SOFIE::RModel model = TMVA::Experimental::SOFIE::PyKeras::Parse(\"" + path + "\"); \n";
// assume batch size is first entry in first input !
std::string batch_size = "-1";
if (!inputShapes.empty() && ! inputShapes[0].empty())
batch_size = std::to_string(inputShapes[0][0]);
parserCode += "{\nTMVA::Experimental::SOFIE::RModel model = TMVA::Experimental::SOFIE::PyKeras::Parse(\"" + path +
"\"," + batch_size + "); \n";
}
else if (type == kPt) {
// use PyTorch direct parser
Expand Down
Loading