CoolFace
Modelpublic

abdelstark/vjepa2-vitl-img16-256-onnx

sourceHugging Facecc-by-nc-4.0updated 5mo agoView on Hugging Face
0likes
Model Card

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.

PropertyValue
ArchitectureViT-L/16
Parameters304M
Embedding dimension1024
Layers / Heads24 / 16
Patch size16 px
Input size256 x 256
Input formatImage: [1, 3, 256, 256] (no frame duplication required)
Internal frames16 (tubelet_size = 2 → 8 temporal groups, collapsed)
Output tokens256 spatial patches
CLS tokenNo
Training dataInternet-scale video
PaperBardes et al. 2024
Original repofacebookresearch/vjepa2
LicenseCC-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_values shape [1, 3, 256, 256] (standard image)
  • —Internal: the graph replicates to 16 frames, builds 8 tubelets, runs the encoder
  • —External output: last_hidden_state shape [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

ParameterValue
Opset17
ProducerPyTorch 2.11.0
Graph nodes10,440
External dataYes (model.onnx_data)

ONNX I/O

DirectionNameShapeType
Inputpixel_values[1, 3, 256, 256]float32
Outputlast_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):

MetricThresholdWorst observed
Patch cosine≥ 0.9990.9999999932
Patch mean abs diff≤ 0.010.000137
Patch max abs diff≤ 0.50.0064
Input-independence cosine< 0.850.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

FileSizeDescription
model.onnx~2.9 MBONNX graph (opset 17, 10,440 nodes)
model.onnx_data~1.16 GBExternal weight data
model.report.json—PyTorch vs ONNX parity report

Usage

With latent-inspector (Rust)

bash
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)

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)

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

VariantInputUse 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

bibtex
@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.