Loading ONNX Networks in the VERONA Verification Pipeline: A Complete Guide
VERONA treats ONNX models as first-class ONNXNetwork objects that handle lazy protobuf loading, input shape extraction, PyTorch conversion, and JSON serialization to streamline neural network verification workflows.
The VERONA framework (ada-research/verona) standardizes how verification pipelines ingest machine learning models. When loading ONNX networks in VERONA verification pipeline implementations, the system wraps each ONNX file in an ONNXNetwork instance located in ada_verona/database/machine_learning_model/onnx_network.py. This class abstracts format-specific complexities while exposing the metadata and interfaces required by both formal verifiers and gradient-based attack estimators.
Core Architecture of the ONNXNetwork Class
The ONNXNetwork class serves as the concrete implementation for machine learning models within VERONA's database layer. It encapsulates five critical responsibilities that enable seamless integration with the verification ecosystem.
Storage and Path Management
The constructor (__init__) receives a Path object and stores it in the private attribute _path, exposing it via the path property. This design ensures that downstream components—particularly the AutoVerifyModule—can always locate the original ONNX file for external verifier invocation.
Lazy Loading of ONNX Protobufs
The load_onnx_model() method implements lazy initialization by calling onnx.load(str(self.path)) only when first accessed. The resulting protobuf object is cached in self.onnx_model, preventing redundant disk I/O during repeated verification attempts.
Input Shape Extraction
Dynamic neural network inputs are handled by get_input_shape(), which reads the first input tensor from the ONNX protobuf. The method converts dynamic dimensions (represented as 0 in ONNX) to -1 and returns a list of integers, providing the static shape metadata required by property generators.
PyTorch Conversion for Attack Estimation
To support gradient-based attacks, load_pytorch_model() leverages the onnx-to-torch library via convert(self.path). This returns a torch.nn.Module wrapped in a TorchModelWrapper that also stores the input shape, enabling seamless use by PGD and FGSM implementations in ada_verona/verification_module/attacks/.
Serialization Support
The class implements to_dict(), from_dict(), and from_file() methods to make networks JSON-serializable. This allows complete verification runs to be persisted and reconstructed, with VerificationContext.from_dict() invoking ONNXNetwork.from_dict() to restore model objects from saved state.
Integration Flow Through the Verification Pipeline
Understanding how ONNXNetwork instances flow through VERONA explains why the abstraction matters for robustness verification.
1. Verification Context Creation
When configuring a verification job, the system constructs a VerificationContext (defined in ada_verona/database/verification_context.py). This context stores the network attribute as an ONNXNetwork instance, alongside the data point and property generator. During deserialization, the context uses ONNXNetwork.from_dict() to reconstruct the model object from JSON.
2. Property Generation
The PropertyGenerator (such as One2OnePropertyGenerator in ada_verona/verification_module/property_generator/one2one_property_generator.py) queries verification_context.network.get_input_shape() to determine the tensor dimensions. This shape information shapes the VNNLib constraints that model the robustness property, ensuring the specification matches the network's input layer.
3. Verification Execution
The AutoVerifyModule (located in ada_verona/verification_module/auto_verify_module.py) receives the context and writes the VNNLib file based on the property generator's output. It then invokes the selected external verifier using verification_context.network.path, passing the original ONNX file path rather than an intermediate representation.
4. Attack Estimation (Optional)
When the pipeline includes adversarial robustness estimation, the attack_estimation_module calls verification_context.network.load_pytorch_model(). This returns a TorchModelWrapper compatible with the PGD and FGSM wrappers in ada_verona/verification_module/attacks/pgd_attack.py, enabling gradient-based perturbation generation without manual model conversion.
Practical Code Examples
Instantiating and Inspecting an ONNXNetwork
from pathlib import Path
from ada_verona.database.machine_learning_model.onnx_network import ONNXNetwork
# Path to an exported ONNX model
onnx_path = Path("examples/example_experiment/data/networks/mnist-net_256x2.onnx")
# Create the network object
network = ONNXNetwork(onnx_path)
# Load the raw ONNX protobuf (cached on subsequent calls)
onnx_proto = network.load_onnx_model()
print("ONNX opset:", onnx_proto.opset_import[0].version)
# Query the expected input shape (e.g. [1, 1, 28, 28] for MNIST)
print("Input shape:", network.get_input_shape())
Converting ONNX to PyTorch for Attack Estimators
import torch
# Convert ONNX → PyTorch and obtain a wrapper that knows the input shape
torch_wrapper = network.load_pytorch_model()
# The underlying torch.nn.Module can be used like any regular model
torch_model = torch_wrapper.model
torch_model.eval()
# Run a forward pass on a dummy tensor with the correct shape
dummy = torch.randn(network.get_input_shape())
logits = torch_model(dummy)
print("Logits shape:", logits.shape)
Building a VerificationContext for a Single Image
import pandas as pd
from ada_verona.database.verification_context import VerificationContext
from ada_verona.database.dataset.data_point import DataPoint
from ada_verona.verification_module.property_generator.one2one_property_generator import One2OnePropertyGenerator
# Example data point (replace with a real torch tensor)
image_tensor = torch.randn(*network.get_input_shape())
data_point = DataPoint(id="img_001", label=3, data=image_tensor)
# Property generator that creates a VNNLib file for a given epsilon
prop_gen = One2OnePropertyGenerator()
# Temporary folder where VERONA will write VNNLib & CSV files
tmp_dir = Path("./tmp_verification")
tmp_dir.mkdir(parents=True, exist_ok=True)
# Assemble the context
ctx = VerificationContext(
network=network,
data_point=data_point,
tmp_path=tmp_dir,
property_generator=prop_gen,
)
# The context now holds everything needed for the verifier:
print("Network name:", ctx.network.name)
print("Temporary folder:", ctx.tmp_path)
Running the Automatic Verifier
from autoverify.verifier.verifier import Verifier
from ada_verona.verification_module.auto_verify_module import AutoVerifyModule
# Assume an installed verifier (e.g., "sdpcrown")
verifier = Verifier(name="sdpcrown") # placeholder – see autoverify docs
module = AutoVerifyModule(verifier=verifier, timeout=300.0)
epsilon = 0.03
result = module.verify(ctx, epsilon)
if isinstance(result, str):
print("Verification failed:", result)
else:
print("Verification outcome:", result.result) # "SAT" or "UNSAT"
Key Implementation Files
Several source files collaborate to enable ONNX support in VERONA:
-
ada_verona/database/machine_learning_model/onnx_network.py: Contains the coreONNXNetworkimplementation, including lazy loading, shape extraction, and ONNX-to-PyTorch conversion. -
ada_verona/database/verification_context.py: Defines theVerificationContextclass that stores the network alongside data points and property generators, handling reconstruction viafrom_dict(). -
ada_verona/verification_module/auto_verify_module.py: Consumes the context and invokes external verifiers using the ONNX file path stored in the network object. -
ada_verona/verification_module/attack_estimation_module.py: Demonstrates consumption of the PyTorch wrapper for gradient-based attacks. -
ada_verona/verification_module/attacks/pgd_attack.py: Uses theTorchModelWrapperreturned byload_pytorch_model()to craft adversarial examples. -
ada_verona/verification_module/property_generator/one2one_property_generator.py: Generates VNNLib constraints based on the network's input shape obtained viaget_input_shape().
Summary
-
ONNXNetwork encapsulates ONNX models in VERONA, providing lazy loading via
load_onnx_model()and shape introspection viaget_input_shape(). -
The class converts ONNX to PyTorch on demand using onnx-to-torch, wrapped in a
TorchModelWrapperfor attack estimation modules. -
VerificationContext stores the network and handles serialization through
to_dict()andfrom_dict(), enabling reproducible verification runs. -
The
AutoVerifyModuleaccessesnetwork.pathto pass the original ONNX file to external verifiers, while property generators usenetwork.get_input_shape()to construct valid VNNLib specifications.
Frequently Asked Questions
How does VERONA handle dynamic input dimensions in ONNX models?
The get_input_shape() method in ONNXNetwork automatically converts dynamic dimensions (represented as 0 in the ONNX protobuf) to -1. This ensures that property generators receive a consistent list of integers representing the input tensor shape, even when the original model supports variable batch sizes or spatial dimensions.
Can VERONA serialize verification runs that use ONNX models?
Yes. The ONNXNetwork class implements to_dict() and from_dict() methods that serialize the model's file path and metadata to JSON. When a VerificationContext is saved and later reconstructed, it calls ONNXNetwork.from_dict() to restore the network object, enabling full reproducibility of verification experiments without reloading raw model data.
What conversion library does VERONA use for ONNX to PyTorch translation?
VERONA uses the onnx-to-torch library via the convert() function inside load_pytorch_model(). This conversion creates a torch.nn.Module that is wrapped in a TorchModelWrapper along with the network's input shape, making it available to gradient-based attack implementations like PGD and FGSM.
How does the verification context pass ONNX models to external verifiers?
The AutoVerifyModule extracts the file path via verification_context.network.path and passes this path directly to the external verification tool. This approach avoids intermediate representation issues by ensuring the verifier receives the original ONNX file, while VERONA manages all preprocessing and property specification generation internally.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →