[tmva][sofie] Restructure emitted code to be differentiable with Clad#18332
[tmva][sofie] Restructure emitted code to be differentiable with Clad#18332guitargeek wants to merge 5 commits intoroot-project:masterfrom
Conversation
tmva/sofie/inc/TMVA/SOFIE_common.hxx
Outdated
| return out; | ||
| } | ||
|
|
||
| inline void Copy(float const *b, float const *e, float *o) |
There was a problem hiding this comment.
Does providing a pullback for std::copy not work?
There was a problem hiding this comment.
No, I tried a bit but then gave up. This was my approach:
#include <Math/CladDerivator.h>
namespace std {
void copy_pullback(double const *first, double const *last, double *out_first, double *_d_out, double *_d_first,
double *_d_last, double *_d_out_first)
{
// Implementation doesn't matter yet, it doesn't compile anyway
}
} // namespace std
void fooImpl(double const *x, double *y)
{
std::copy(x, x + 1, y);
}
void foo(double const *x, double *y)
{
fooImpl(x, y);
}
double g(double *variables)
{
double out;
foo(variables, &out);
return out * variables[1];
}
void clademo()
{
// Call clad to generate the gradient of g.
auto g_grad = clad::gradient(g, "variables");
// Execute the generated gradient function.
double variables[]{3., 4.};
double grad_output[]{0., 0.};
g_grad.execute(variables, grad_output);
std::cout << "grad_output[0]: " << grad_output[0] << std::endl;
std::cout << "grad_output[1]: " << grad_output[1] << std::endl;
// Dump the generated gradient code to standard output.
g_grad.dump();
}It segfaults. I think Clad just doesn't play well with the STL algos that take iterators, it's better to avoid this, no?
In any case, supporting this is not crucial for this PR. I was refactoring things to avoid this copy call in the generated code anyway.
Once this PR is functional for our usecase (actually now, but I also want to make the ROOT CI pass again), I'll write up what was not perfect in Clad for this and open issues
There was a problem hiding this comment.
Ah, I see. Probably worth opening an issue in clad…
| }); | ||
|
|
||
| TMVA_SOFIE_Equal::Session s("Equal_FromONNX.dat"); | ||
| std::vector<bool> output = s.infer(input1.data(),input2.data()); |
There was a problem hiding this comment.
Did that fail to differentiate?
There was a problem hiding this comment.
No, I didn't even try to differentiate the models in the test. I'm solely focusing on the SBI usecase that we implement with LHCb. The reason why I changed this is because std::vector<bool> is not a good output type parameter. See:
Test Results 22 files 22 suites 3d 3h 39m 5s ⏱️ Results for commit 728e5ad. ♻️ This comment has been updated with latest results. |
6b90cb6 to
87597cd
Compare
Proof of concept test for this PRTake this ONNX file (remove the VRlL_real_500k_evts_model.onnx.txt Here are the scripts to convert the model to C++ and then to differentiate it with Clad: // onnx_to_cpp.C
void onnx_to_cpp()
{
using namespace TMVA::Experimental;
SOFIE::RModelParser_ONNX parser;
SOFIE::RModel model = parser.Parse("./VRlL_real_500k_evts_model.onnx");
model.SetOptimizationLevel(SOFIE::OptimizationLevel::kBasic);
model.Generate();
model.PrintRequiredInputTensors();
model.OutputGenerated("./VRlL_real_500k_evts_model.hxx");
}// sofie_ad.C
#include "VRlL_real_500k_evts_model.hxx"
#include <Math/CladDerivator.h>
float my_func(TMVA_SOFIE_VRlL_real_500k_evts_model::Session const *session, float const *tensor_x,
float *tensor_theory_params)
{
float out = 0.;
TMVA_SOFIE_VRlL_real_500k_evts_model::doInfer(session, tensor_x, tensor_theory_params, &out);
return out;
}
void sofie_ad()
{
std::vector<float> input1{5.0, 2.0, 1.0, -1.0, 1.0};
std::vector<float> input2{0.0};
// Generated header file shall contain a Session class which requires
// initialization to load the corresponding weights.
TMVA_SOFIE_VRlL_real_500k_evts_model::Session s("VRlL_real_500k_evts_model.dat");
// Once instantiated the session object's infer method can be used
// std::vector<float> out = s.infer(input1.data(), input2.data());
auto func = [&](std::span<float> params) { return s.infer(input1.data(), params.data())[0]; };
auto numDiff = [&](int i) {
const float eps = 1e-4;
std::vector<float> p{input2};
p[i] = input2[i] - eps;
float funcValDown = func(p);
p[i] = input2[i] + eps;
float funcValUp = func(p);
return (funcValUp - funcValDown) / (2 * eps);
};
for (std::size_t i = 0; i < input2.size(); ++i) {
std::cout << i << ":" << std::endl;
std::cout << " numr : " << numDiff(i) << std::endl;
}
float grad_output[]{0., 0., 0., 0., 0.};
auto g_grad = clad::gradient<clad::opts::disable_tbr>(my_func, "tensor_theory_params");
g_grad.execute(&s, input1.data(), input2.data(), grad_output);
std::fill(std::begin(grad_output), std::end(grad_output), 0);
g_grad.execute(&s, input1.data(), input2.data(), grad_output);
std::cout << " clad : " << grad_output[0] << std::endl;
g_grad.dump();
}Note that Usage with expected output (replace |
89b638c to
a3d545f
Compare
3f40542 to
78fcc20
Compare
4c9920f to
97903fa
Compare
|
Why did we decide to not pursue this? |
|
@vgvassilev, sorry that was totally an accident. Maybe I confused it with another PR, or I wanted to close and re-open the PR to run the tests, but apparently I missed the "reopen" button. |
9873d07 to
4f822ad
Compare
7e5f4aa to
575f9e0
Compare
It's possible to call `gemm` without a `C` parameter (constant offset), so the custom derivative can't assume that `C` and `_d_C` are always set. This avoids segfaults when `C` is `nullptr`.
It simplifies the code a bit if we don't have separate treatment of larger and smaller (n < 100) constant vectors. It's also not that meaningful to have a magic size threshold per vector that also doesn't help avoiding large stack allocations and emitted code footprint for the initializer list. The mechanism is mute when there are many small constant tensors.
The idea of this commit is to refactor the `doInfer()` function that implements the inference from a member function of the `Session` struct to a free function that takes the `Session` by `const`-reference.
This is covered by the tests that differentiate code emitted by SOFIE.
Restructure emitted code to be differentiable with Clad.