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:

Summary

  • ONNXNetwork encapsulates ONNX models in VERONA, providing lazy loading via load_onnx_model() and shape introspection via get_input_shape().

  • The class converts ONNX to PyTorch on demand using onnx-to-torch, wrapped in a TorchModelWrapper for attack estimation modules.

  • VerificationContext stores the network and handles serialization through to_dict() and from_dict(), enabling reproducible verification runs.

  • The AutoVerifyModule accesses network.path to pass the original ONNX file to external verifiers, while property generators use network.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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →