V-JEPA 2 ViT-L/16 β ONNX Export (Image-Native, 16-Frame Internal Tubelet)
Encoder-only ONNX export of facebook/vjepa2-vitl-fpc64-256 with image-native I/O for drop-in use with ONNX Runtime and latent-inspector.
Unlike the 2-frame variant (abdelstark/vjepa2-vitl-fpc2-256-onnx) which requires the caller to duplicate frames to form a video tensor, this export accepts a plain image tensor ([1, 3, 256, 256]) and handles the 16-frame tubelet construction internally, producing the same [1, 256, 1024] patch-embedding output.
Model
V-JEPA 2 is a self-supervised video encoder from Meta FAIR that learns spatiotemporal representations by predicting future frame representations from past frames. This ONNX artifact contains only the encoder (predictor head stripped) and wraps it so the external signature looks like a standard image encoder while preserving V-JEPA 2's temporal prior.
| Property | Value |
|---|---|
| Architecture | ViT-L/16 |
| Parameters | 304M |
| Embedding dimension | 1024 |
| Layers / Heads | 24 / 16 |
| Patch size | 16 px |
| Input size | 256 x 256 |
| Input format | Image: [1, 3, 256, 256] (no frame duplication required) |
| Internal frames | 16 (tubelet_size = 2 β 8 temporal groups, collapsed) |
| Output tokens | 256 spatial patches |
| CLS token | No |
| Training data | Internet-scale video |
| Paper | Bardes et al. 2024 |
| Original repo | facebookresearch/vjepa2 |
| License | CC-BY-NC-4.0 |
Why this variant?
V-JEPA 2 is natively a video model with a pixel_values_videos input. For image-only workflows β latent inspection, similarity search, cross-model CKA β the standard pattern is to duplicate the frame and feed a [1, T, 3, H, W] tensor. That works but pushes the temporal plumbing onto every caller.
This export bakes the 16-frame replication into the ONNX graph itself:
- External input:
pixel_valuesshape[1, 3, 256, 256](standard image) - Internal: the graph replicates to 16 frames, builds 8 tubelets, runs the encoder
- External output:
last_hidden_stateshape[1, 256, 1024](same layout as DINOv2 / I-JEPA ViT-L)
The output is shape-compatible with abdelstark/vjepa2-vitl-fpc2-256-onnx (same 256Γ1024 patch grid), so any downstream tool consuming the 2-frame variant can swap to this one by dropping the duplication step.
ONNX export parameters
| Parameter | Value |
|---|---|
| Opset | 17 |
| Producer | PyTorch 2.11.0 |
| Graph nodes | 10,440 |
| External data | Yes (model.onnx_data) |
ONNX I/O
| Direction | Name | Shape | Type |
|---|---|---|---|
| Input | pixel_values |
[1, 3, 256, 256] |
float32 |
| Output | last_hidden_state |
[1, 256, 1024] |
float32 |
Input: batch of 1 image, 3 channels, 256Γ256 pixels, ImageNet-normalized.
Output: 256 spatial patch tokens of dimension 1024. No CLS token.
Validation
Parity against the upstream PyTorch encoder across 5 sample images (buffalo, cat, elephant, rhino, zebra):
| Metric | Threshold | Worst observed |
|---|---|---|
| Patch cosine | β₯ 0.999 | 0.9999999932 |
| Patch mean abs diff | β€ 0.01 | 0.000137 |
| Patch max abs diff | β€ 0.5 | 0.0064 |
| Input-independence cosine | < 0.85 | 0.317 |
All 5 images pass the parity gate. The input-independence check (running random-noise input and verifying the output is not near-identical to a real image's output) rules out the "export collapsed to a constant" failure mode.
Full report: model.report.json.
Files
| File | Size | Description |
|---|---|---|
model.onnx |
~2.9 MB | ONNX graph (opset 17, 10,440 nodes) |
model.onnx_data |
~1.16 GB | External weight data |
model.report.json |
β | PyTorch vs ONNX parity report |
Usage
With latent-inspector (Rust)
latent-inspector inspect photo.jpg --model vjepa2-vitl-img16-256
latent-inspector compare photo.jpg --models dinov2-vit-l14,vjepa2-vitl-img16-256
With ONNX Runtime (Python)
import onnxruntime as ort
import numpy as np
from PIL import Image
from torchvision import transforms
transform = transforms.Compose([
transforms.Resize(256, interpolation=transforms.InterpolationMode.LANCZOS),
transforms.CenterCrop(256),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
])
image = Image.open("photo.jpg").convert("RGB")
pixel_values = transform(image).unsqueeze(0).numpy() # [1, 3, 256, 256]
session = ort.InferenceSession("model.onnx")
output = session.run(None, {"pixel_values": pixel_values.astype(np.float32)})[0]
# output shape: [1, 256, 1024]
patch_tokens = output[0] # [256, 1024]
image_embedding = patch_tokens.mean(axis=0) # [1024] mean-pool for global embedding
With ONNX Runtime (Rust)
let session = ort::session::Session::builder()?
.with_intra_threads(4)?
.commit_from_file("model.onnx")?;
let pixel_values = ndarray::Array4::<f32>::zeros((1, 3, 256, 256));
// ... fill with preprocessed image ...
let outputs = session.run(ort::inputs!["pixel_values" => pixel_values])?;
let hidden = outputs["last_hidden_state"].try_extract_tensor::<f32>()?;
// shape: [1, 256, 1024]
When to use which V-JEPA 2 export
| Variant | Input | Use when |
|---|---|---|
vjepa2-vitl-fpc2-256-onnx |
[1, 2, 3, 256, 256] |
You already handle video tensors and want the minimal 2-frame footprint |
vjepa2-vitl-img16-256-onnx (this repo) |
[1, 3, 256, 256] |
You want an image-encoder-shaped API and are comparing against DINOv2 / I-JEPA / EUPE |
Both produce [1, 256, 1024] patch outputs and are drop-in compatible with latent-inspector fingerprint analysis.
Citation
@article{bardes2024vjepa2,
title={V-JEPA 2: Self-Supervised Video Models Enable Understanding
of Complex Real-World Interactions},
author={Bardes, Adrien and others},
journal={arXiv preprint arXiv:2506.09985},
year={2024}
}
Acknowledgments
Original weights by Meta FAIR under CC-BY-NC-4.0. Image-native ONNX export and hosting by @AbdelStark for the latent-inspector project.
Model tree for abdelstark/vjepa2-vitl-img16-256-onnx
Base model
facebook/vjepa2-vitl-fpc64-256