ONNX Manager Implementation Summary
See Also:
Overview
This implementation adds support for ONNX (Open Neural Network Exchange) model evaluation to RDFAnalyzerCore, similar to the existing BDTManager functionality. ONNX is an open standard for machine learning models that enables interoperability between different ML frameworks.
What Was Implemented
1. ONNX Runtime Integration (CMake)
- File:
cmake/SetupOnnxRuntime.cmake - Automatically downloads ONNX Runtime binaries from official Microsoft releases during CMake configuration
- Supports Linux (x64, aarch64) and macOS (x64, arm64)
- Version: 1.20.0
- No manual installation or building required
- Sets up proper RPATH for runtime library loading
2. OnnxManager Plugin
- Files:
core/plugins/OnnxManager/OnnxManager.hcore/plugins/OnnxManager/OnnxManager.cccore/plugins/OnnxManager/CMakeLists.txt
Features:
- Inherits from
NamedObjectManager<std::shared_ptr<Ort::Session>> - Loads ONNX models from configuration files
- Applies models to ROOT RDataFrame with conditional execution
- Returns -1.0 when runVar is false (to skip unnecessary computation)
- Thread-safe for use with ROOT’s ImplicitMT
- Follows the same patterns as BDTManager for consistency
- Supports multiple outputs (e.g., ParticleTransformer with bootstrapped models)
- Deferred execution: Models loaded but NOT applied until explicitly invoked
Key Methods:
applyModel(modelName, outputSuffix=""): Apply a specific model to the dataframeapplyAllModels(outputSuffix=""): Apply all configured modelsgetModel(key): Retrieve ONNX session objectgetModelFeatures(key): Get input feature namesgetRunVar(modelName): Get conditional run variable namegetAllModelNames(): List all loaded modelsgetModelInputNames(modelName): Get ONNX input tensor namesgetModelOutputNames(modelName): Get ONNX output tensor names
3. Comprehensive Unit Tests
- File:
core/test/testOnnxManager.cc - Test Configuration:
core/test/cfg/onnx_models.txt - Test Models:
core/test/cfg/test_model.onnx- Single output modelcore/test/cfg/test_model2.onnx- Single output modelcore/test/cfg/test_model_multi_output.onnx- Multi-output model
Test Coverage:
- Constructor and manager creation
- Model retrieval (valid and invalid)
- Feature retrieval (valid and invalid)
- RunVar retrieval (valid and invalid)
- Base class interface methods
- Model application with valid inputs
- Model application with runVar=false
- Multiple model support
- Multi-output model support
- Thread safety with ROOT ImplicitMT
- Const correctness
- Input/output name retrieval
4. Documentation
- File:
README.md
Additions:
- Overview of OnnxManager in the plugins section
- Detailed usage guide with configuration file format
- C++ API examples and code snippets
- Instructions for creating ONNX models from:
- scikit-learn (using skl2onnx)
- PyTorch (using torch.onnx.export)
- TensorFlow/Keras (using tf2onnx)
Configuration Format
Create a configuration file (e.g., cfg/onnx_models.txt):
file=path/to/model.onnx name=model_output inputVariables=var1,var2,var3 runVar=should_run
Add to main config:
onnxConfig=cfg/onnx_models.txt
Input Padding for Fixed-Size Models
ONNX models with fixed-size inputs (e.g., transformer attention mechanisms) now support automatic zero-padding. This is particularly useful for models that require a specific input size but process variable-length sequences.
Configuration:
Add paddingSize parameter to your model configuration:
# onnx_models.txt
file=transformer.onnx name=ParT inputVariables=pt,eta,phi paddingSize=128
Behavior:
- Input vectors shorter than
paddingSizeare zero-padded to the specified size - Input vectors longer than
paddingSizeare truncated to the specified size - Omitting
paddingSizepreserves existing behavior (no padding/truncation)
Example:
// Configuration: paddingSize=128
// Input: RVec<float>{1.0, 2.0, 3.0} (size 3)
// After padding: RVec<float>{1.0, 2.0, 3.0, 0.0, 0.0, ..., 0.0} (size 128)
// Input: RVec<float>{...300 elements...} (size 300)
// After truncation: RVec<float>{...first 128 elements...} (size 128)
This feature enables the use of transformer-based models (ParticleNet, ParT, etc.) that require fixed-size attention masks and positional encodings.
Usage Example
Single Output Model
// Create and configure OnnxManager
auto onnxManager = std::make_unique<OnnxManager>(*configProvider);
ManagerContext ctx{*configProvider, *dataManager, *systematicManager, *logger, *skimSink, *metaSink};
onnxManager->setContext(ctx);
// Define input features first
dataManager->Define("pt", ...);
dataManager->Define("eta", ...);
// Then apply models
onnxManager->applyAllModels();
// Access results in dataframe
auto df = dataManager->getDataFrame();
auto predictions = df.Take<float>("model_output");
Multi-Output Model (ParticleTransformer Style)
// Define inputs
dataManager->Define("jet_pt", ...);
dataManager->Define("jet_eta", ...);
dataManager->Define("jet_phi", ...);
// Apply multi-output model
onnxManager->applyModel("particle_transformer");
// Access individual outputs
auto df = dataManager->getDataFrame();
auto output0 = df.Take<float>("particle_transformer_output0"); // First output
auto output1 = df.Take<float>("particle_transformer_output1"); // Second output
auto output2 = df.Take<float>("particle_transformer_output2"); // Third output
Testing Notes
Build Requirements
- ROOT 6.30.02 or later
- CMake 3.19.0 or later
- C++17 compatible compiler
- Internet connection during build (to download ONNX Runtime)
Running Tests
The implementation includes comprehensive unit tests that can be run with:
source env.sh
source build.sh
cd build
ctest -R OnnxManagerTest
Note: Tests require a ROOT environment with CVMFS or local ROOT installation.
Design Decisions
- Binary Distribution: ONNX Runtime is downloaded as pre-built binaries rather than built from source to:
- Avoid long build times
- Reduce build complexity
- Ensure consistent behavior across platforms
- Follow the problem statement requirement
- Conditional Execution: Like BDTManager, models return -1.0 when runVar is false to:
- Skip expensive inference when not needed
- Maintain compatibility with existing analysis patterns
- Enable clear identification of skipped events
- Code Structure: Follows BDTManager patterns to:
- Maintain consistency across the codebase
- Leverage existing NamedObjectManager infrastructure
- Make the API familiar to existing users
- Thread Safety: Configured with single-threaded ONNX inference per event to:
- Allow ROOT’s ImplicitMT to handle parallelism at the event level
- Avoid thread contention within ONNX Runtime
- Maintain consistent behavior with other managers
- Deferred Execution: Models are loaded during construction but NOT applied automatically to:
- Allow users to define all required input features first
- Prevent errors from missing DataFrame columns
- Give users full control over when inference happens
- Support complex analysis workflows with dependencies
- Multiple Outputs: Full support for models with multiple output tensors to:
- Enable ParticleTransformer-style models with bootstrapped outputs
- Support ensemble models that return multiple predictions
- Create individual DataFrame columns for each output for easy access
Security Considerations
- ONNX Runtime binaries are downloaded from official Microsoft GitHub releases
- Version is pinned to 1.20.0 for reproducibility
- CMake verifies download success before proceeding
- No external code execution during model loading (only model inference)
Future Enhancements
Potential improvements for future work:
- Support for multiple output tensors from models - Implemented ✅
- Support for variable-shaped inputs (dynamic batch sizes, ragged arrays)
- Batch inference for improved performance
- Model caching for faster repeated evaluations
- Support for different ONNX Runtime execution providers (CPU, CUDA, TensorRT)
- Model quantization support
- Integration with ONNX Model Zoo
Compatibility
- Platform Support: Linux (x64, aarch64), macOS (x64, arm64)
- ONNX Opset: Compatible with ONNX opset 12+
- Model Types: Linear models, tree ensembles, neural networks, transformers, etc.
- ML Frameworks: Any framework that can export to ONNX format
- Multiple Outputs: Fully supported (e.g., ParticleTransformer with bootstrapped models)
Limitations
- Float Input: Input features are converted to float32
- Fixed Shape Input: Currently expects (1, N) shaped input tensors for single input models
- CPU Only: ONNX Runtime is configured for CPU inference only
- Single Scalar Output Per Tensor: Each output tensor must produce a single scalar value