Skip to page content
SAVRN
Search Plan a project

Open-weight model · Feature extraction

CoVisco-L-14

by Ernie Research ernie-research/CoVisco-L-14

CoVisco-L-14 is an open-weight model for feature extraction from Ernie Research, released under MIT License. It has 639M parameters. At 16-bit it needs about 1.5 GB of GPU memory, which fits on 1x MI300X from $1.85 an hour, at the lowest prices in the SAVRN Index.

CoVisco-L-14 is the Stage 1 vision encoder of the CoVisco pipeline (Codec-Native Vision Encoder with Native Token Compression for Efficient Unified Visual Understanding).

Parameters639M
Context—
Weights2.6 GB
Licensemit
AccessOpen weights
Monthly Downloads—

Runs On

What it takes to serve CoVisco-L-14 (639M parameters): the memory its weights need at each precision, and the cheapest way to rent enough data-center GPUs to hold them.

PrecisionWeightsMemory neededCheapest setupPer hourAlso fits
16-bit 1.3 GB 1.5 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00
8-bit 0.6 GB 0.8 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00
4-bit 0.3 GB 0.4 GB 1x MI300X (192 GB)
Vultr
$1.85 1x H100 $1.99 · 1x MI325X $2.00

Memory is the weights at that precision plus 20% for the runtime and a short context; a long context needs more. Prices are the lowest on-demand hourly rates in the SAVRN Index, read Oct 18, 2026.

CoVisco-L-14 on every accelerator the SAVRN Index prices, at every precision

Model Card

By Ernie Research, published under mit, revision 3ea4613abe34.

CoVisco-L-14 is the Stage 1 vision encoder of the CoVisco pipeline (Codec-Native Vision Encoder with Native Token Compression for Efficient Unified Visual Understanding). It encodes images and videos in a unified way and is trained by SigLIP-style distillation against two teacher branches. This repository releases only the vision-encoder weights in the safe.safetensors format, together with the minimal model code and a self-contained loader. The model is a transformers.PreTrainedModel and is loadable via frompretrained (see below). - 4D RoPE (ropetheta=10000) for spatio-temporal positions - Three projection heads (each LayerNorm + Linear, output L2-normalized): - toimagecaption → 1536-d…

Read Ernie Research's full model card

CoVisco-L-14 (Vision Encoder)

CoVisco-L-14 is the Stage 1 vision encoder of the CoVisco pipeline (Codec-Native Vision Encoder with Native Token Compression for Efficient Unified Visual Understanding). It encodes images and videos in a unified way and is trained by SigLIP-style distillation against two teacher branches.

This repository releases only the vision-encoder weights in the safe .safetensors format, together with the minimal model code and a self-contained loader. The model is a transformers.PreTrainedModel and is loadable via from_pretrained (see below).

Architecture

  • Backbone: ViT-L/14, image_size=224, patch_size=14, layers=24, width=1024, heads=16
  • Native token compression: num_query_per_seg=100 learnable abstract tokens, segment_t_size=32
  • 4D RoPE (rope_theta=10000) for spatio-temporal positions
  • Three projection heads (each LayerNorm + Linear, output L2-normalized):
  • to_image_caption → 1536-d, SigLIP2 space (image classification / retrieval)
  • to_image → 1536-d
  • to_video_caption → 4096-d, Qwen3-VL-Embedding space (video classification / retrieval)
  • Independent logit_scale / logit_bias per contrast pair

Files

  • model.safetensors — model weights (fp32)
  • config.json — encoder config + head dims + preprocessing config; includes architectures / model_type / auto_map so the repo is HF-loadable
  • covisco_vit.py, covisco_model.py, reconstruction_decoder.py — model code
  • load_covisco.py — self-contained loader + image-embedding example
  • codec_visidx.py — codec-based token filtering (see below)
  • requirements.txt, LICENSE

Load via from_pretrained

The model is a transformers.PreTrainedModel, so you can load it with the standard HF API (needs trust_remote_code=True because the modeling code ships in this repo via auto_map):

from transformers import AutoModel

REPO = "ernie-research/CoVisco-L-14"
model = AutoModel.from_pretrained(REPO, trust_remote_code=True).eval()
# or, importing the class directly (no trust_remote_code needed):
# from covisco_model import CoViscoModel
# model = CoViscoModel.from_pretrained(REPO).eval()

The only preprocessing knobs a user adjusts are the resolution and (for video) segment_t_size. The raw parameters (size / mean / std / interpolation) live in model.config.preprocess_cfg, and load_covisco.py ships ready-made helpers so you do not hand-roll any transform. The same per-frame transform serves both images and video — the only extra step for video is sampling frames and stacking them into a (B, C, T, H, W) clip, which encode_video does for you:

  • Per-frame transform (shared): build_preprocess(cfg, size=...) for a fixed/square resolution (see Programmatic usage), or build_native_preprocess(cfg, max_side=...) for native, aspect-preserving resolution (see Native resolution).
  • Image end-to-end: encode_image(model, preprocess, img) → single image.
  • Video end-to-end: read_video_frames(path, num_frames) → encode_video(model, preprocess, frames), with build_model(..., segment_t_size=...) setting the temporal segment size (see Video usage).

Resolution is decided entirely by the preprocessing (the H x W of the pixel_values you feed in); there is no resolution knob to set on the model. The one runtime parameter that is not fixed by preprocessing is segment_t_size (how video frames are split into temporal segments). If you stay on the pure HF API (no helpers), override it directly on the live encoder (no weight shape depends on it):

model = AutoModel.from_pretrained("ernie-research/CoVisco-L-14", trust_remote_code=True)

model.encoder.segment_t_size = 16          # video frame count T must be a multiple of it
model.encoder.config.segment_t_size = 16   # keep config in sync (optional)

The load_covisco.py helpers below wrap this with the torchvision preprocessing and apply such overrides at build time.

Quick start

pip install -r requirements.txt
# smoke test (blank 224x224 image)
python load_covisco.py
# real image, square 224 preprocessing
python load_covisco.py --image your.jpg
# native resolution (keeps aspect ratio, long side <= 1400 px)
python load_covisco.py --native_resolution --image your.jpg
# native resolution with a smaller memory cap
python load_covisco.py --native_resolution --max_side 896 --image your.jpg

CLI flags: --config, --weights, --image, --device, --native_resolution, --max_side, --segment_t_size.

Programmatic usage (image)

from load_covisco import build_model, build_preprocess, encode_image
from PIL import Image

model, cfg = build_model("config.json", "model.safetensors", device="cuda")
preprocess = build_preprocess(cfg, size=224)  # square 224; explicit resolution
emb = encode_image(model, preprocess, Image.open("your.jpg"), device="cuda")
# emb: [1, 1536], L2-normalized (SigLIP2 branch, to_image_caption)

# 4096-d Qwen3-VL-Embedding-space embedding instead. For images this head is
# keyed `to_image_video_caption` (it reuses the video-caption head weights;
# `to_video_caption` only exists under modality="video").
emb = encode_image(model, preprocess, Image.open("your.jpg"), device="cuda",
                   branch="to_image_video_caption")

Native resolution (images)

The encoder is RoPE-only (no learned position table) and derives its patch grid as h = H // 14, w = W // 14 at runtime, so it accepts any H x W that is a multiple of the patch size — including non-square inputs:

from load_covisco import build_model, build_native_preprocess, encode_image
from PIL import Image

model, cfg = build_model("config.json", "model.safetensors", device="cuda")
preprocess = build_native_preprocess(cfg, max_side=1400)

emb = encode_image(model, preprocess, Image.open("doc_page.png"), device="cuda")

build_native_preprocess keeps the original aspect ratio and only scales the long side down to max_side (memory cap) and rounds each side to a multiple of patch_size. This is the recommended mode for text-heavy document pages, where the square 224 center-crop destroys detail.

Because a batch is a single dense tensor (no resolution packing), encode one image at a time when using native resolution.

Video usage

Videos are passed as a (B, C, T, H, W) tensor (channel before time). T must be a positive multiple of segment_t_size; the encoder splits the clip into T // segment_t_size temporal segments. segment_t_size is a pure runtime parameter (RoPE positions are computed from the runtime frame grid; no weight shape depends on it), so override it when loading — no manual config rebuild needed:

from load_covisco import (build_model, build_preprocess,
                          read_video_frames, encode_video)

NUM_FRAMES = 16      # frames uniformly sampled per clip
SEGMENT_T_SIZE = 16   # frames per temporal segment (must divide NUM_FRAMES;
                      # 16 -> 1 segment here; config.json default is 32)
IMAGE_SIZE = 224      # square resolution per frame (any multiple of 14 works)

# segment_t_size is runtime-only (RoPE, no weight shape depends on it):
# same weights, override the config.json default (32).
model, cfg = build_model("config.json", "model.safetensors", device="cuda",
                         segment_t_size=SEGMENT_T_SIZE)

# Same per-frame transform as images. For aspect-preserving native-resolution
# frames use build_native_preprocess(cfg, max_side=1400) instead.
preprocess = build_preprocess(cfg, size=IMAGE_SIZE)

# read_video_frames needs PyAV (optional): pip install av
frames = read_video_frames("video.mp4", NUM_FRAMES)   # list of PIL images
video_emb = encode_video(model, preprocess, frames, device="cuda")
# video_emb: (1, 4096), L2-normalized (to_video_caption, Qwen3-VL-Embedding space)

encode_video applies the shared per-frame preprocess, stacks the frames into the (B, C, T, H, W) tensor the encoder expects, and runs the forward pass. If you already have a (T, C, H, W) clip tensor you can pass it directly instead of a list of PIL frames.

On load, the effective segment_t_size is printed (e.g. [INFO] effective segment_t_size = 16 (video frame count T must be a multiple of it)). A frame count that is not a multiple of the effective segment_t_size raises an AssertionError at forward time; a clip shorter than segment_t_size is also accepted (single segment, T frames) — positions stay consistent because RoPE is computed from the runtime grid.

Codec-based token filtering (video)

The encoder natively supports sparse patch selection: passing visible_indices (visidx) makes get_segments compute only the selected patches and derive 4D-RoPE from them — a codec-style token budget without any mosaic packing. codec_visidx.py generates these indices from video codec signals, with the same scoring math as the training-time precompute_codec_visidx pipeline:

  • Backend 1 — cv_reader (exact, matches training): install Compressed-Video-Reader (C++ extension + patched FFmpeg) to read H.264/HEVC macroblock motion vectors and DCT residuals. The scoring functions are embedded verbatim, so the visidx is numerically identical to the training-time precompute.
  • Backend 2 — PyAV (fallback, no extra deps): FFmpeg AV_FRAME_DATA_MOTION_VECTORS side data for motion; luminance frame differences approximate the residual.
  • Selection (both backends): per-segment equal top-K (keep_ratio of segment_t_size * patches_per_frame), first frame of each segment always kept in full, indices sorted ascending — the layout get_segments requires.

Two usage modes:

End-to-end (scoring runs inline, right before the forward pass):

from codec_visidx import compute_codec_visidx, sample_frame_ids, _decode_frames
from load_covisco import build_model, build_preprocess, encode_video

NUM_FRAMES, SEGMENT_T_SIZE, KEEP_RATIO = 64, 16, 0.57

visidx, meta = compute_codec_visidx("video.mp4", num_frames=NUM_FRAMES,
                                    segment_t_size=SEGMENT_T_SIZE,
                                    keep_ratio=KEEP_RATIO)

model, cfg = build_model("config.json", "model.safetensors", device="cuda",
                         segment_t_size=SEGMENT_T_SIZE)
preprocess = build_preprocess(cfg, size=224)   # codec mode needs square preprocessing

# Decode the SAME frames the scoring used. sample_frame_ids samples over
# meta["total_frames"] (the probed stream length), so this guarantees patch
# index i still refers to the same patch — don't substitute read_video_frames
# here, it samples over the decoded-frame count instead.
_, rgbs, *_ = _decode_frames("video.mp4",
                             sample_frame_ids(meta["total_frames"], NUM_FRAMES))

# Pass visidx into encode_video to run in codec (sparse-patch) mode.
video_emb = encode_video(model, preprocess, rgbs, device="cuda",
                         visible_indices=visidx)   # (1, 4096)

Pre-extracted (batch datasets; output layout matches the SFT precompute_codec_visidx pipeline):

# videos.jsonl: one {"video": "/path.mp4", "key": "unique_id"} per line
python codec_visidx.py --jsonl videos.jsonl --out_root cache/ \
    --num_frames 64 --segment_t_size 16 --keep_ratio 0.57
# writes cache//{visidx.npy, frame_ids.npy, meta.json}
import numpy as np
from codec_visidx import load_visidx, _decode_frames
from load_covisco import encode_video

# Reuse the exact frame ids saved alongside the visidx.
frame_ids = np.load("cache/<key>/frame_ids.npy").tolist()
_, rgbs, *_ = _decode_frames("video.mp4", frame_ids)
video_emb = encode_video(model, preprocess, rgbs, device="cuda",
                         visible_indices=load_visidx("cache/<key>"))

Constraints: the clip fed to encode_video must use the same num_frames linspace sampling, the same square image_size preprocessing, and the same segment_t_size as the visidx computation (meta.json records these for verification). With keep_ratio=0.57 and a 224² grid, each 16-frame segment keeps ~2329 of its 4096 patches (~57%), cutting encoder FLOPs correspondingly.

Model interface

model(pixel_values, modality=..., run_decoder=False) returns a dict:

key shape meaning
to_image_caption (B, 1536) L2-normalized, SigLIP2 text space (modality image/None)
to_image (B, 1536) L2-normalized, SigLIP2 image space (modality image/None)
to_image_video_caption (B, 4096) L2-normalized, Qwen3-VL-Embedding space for images — reuses the video-caption head weights (modality image/None)
to_video_caption (B, 4096) L2-normalized, Qwen3-VL-Embedding space for video (modality video/None)
pooled_output (B, 1024) raw pooled representation (what the heads consume)
query_tokens (B, S, 100, 1024) pre-pooling abstract/query tokens (return_intermediates=True)
vit_tokens (B, S, N-100, 1024) pre-pooling ViT patch tokens (return_intermediates=True)

modality selects which projection heads are computed: "image" → to_image / to_image_caption / to_image_video_caption, "video" → to_video_caption only, None → all. The 4096-d image head is keyed to_image_video_caption (not to_video_caption) even though it shares the same projection weights.

Extracting token-level representations

Pass return_intermediates=True to also get the pre-pooling token outputs. This is what the CoVisco SFT stage consumes as visual inputs (e.g. as the vision tower of an LLM):

from load_covisco import build_model, build_preprocess
from PIL import Image

model, cfg = build_model("config.json", "model.safetensors", device="cuda")
preprocess = build_preprocess(cfg, size=224)
px = preprocess(Image.open("your.jpg")).unsqueeze(0).cuda()   # (B, C, H, W)

out = model(px, modality="image", run_decoder=False,
            return_intermediates=True)

out["pooled_output"]      # (B, 1024)          pooled representation
out["query_tokens"]       # (B, 1, 100, 1024)  per-segment abstract/query tokens
out["vit_tokens"]         # (B, 1, 256, 1024)  per-segment ViT patch tokens
# 224x224 / patch 14 -> 16x16 = 256 patches per frame/segment
# (the first 100 tokens of every segment are the learnable query tokens)

For video the layout is (B, S, ...) with S = T // segment_t_size segments, e.g. 16 frames at segment_t_size=16 (one segment of 16 frames) → query_tokens (B, 1, 100, 1024) and vit_tokens (B, 1, 16*256, 1024) (256 patches per frame, all frames of the segment).

Notes:

  • The projection heads consume pooled_output; query_tokens / vit_tokens are the uncompressed sequence outputs if you need token-level features.
  • return_attentions=True additionally returns per-layer attention weights (requires eager attention, not flash-attn).

Classification / retrieval needs a teacher text encoder

This repo ships only the vision tower. To run zero-shot classification or text↔image/video retrieval you must match embeddings against the frozen teacher text encoder of the corresponding branch:

  • to_image_caption (1536-d) ↔ SigLIP2 text tower (timm/ViT-gopt-16-SigLIP2-384, pretrained=webli)
  • to_video_caption (4096-d) ↔ Qwen/Qwen3-VL-Embedding-8B text encoder

The embedding spaces are branch-specific and not interchangeable.

Notes

  • The encoder is stored twice in the state dict, under encoder.* and its alias visual.* (the model sets self.visual = self.encoder). Both resolve to the same module; a clean load reports no missing/unexpected keys. This is why the file is ~2.4 GB for a ~320 M-parameter encoder.
  • image_size in config.json is informational only — it does not affect any weight shape or forward computation (patch embedding is a conv and positions are RoPE), so the same weights run at any resolution.
  • flash_attn is optional; the code falls back to standard attention if it is not installed.
  • use_reconstruction=False for the released weights (no decoder parameters); pass run_decoder=False to skip the decoder path entirely.
  • The training-time data-augmentation paths (uniform_sample_frames / frame_concat) have been removed from this release; inference always uses the get_segments path with the frame count and segment_t_size you configure.

License

MIT License.

Citation

@article{liu2026covisco,
  title={CoVisco: Codec-Native Vision Encoder with Native Token Compression for Unified Image-Video Understanding},
  author={Liu, Yulong and Han, Xiaotian and Shang, Junyuan and Ding, Yuchen and Zhang, Zhenyu and Wang, Shuohuan and Zhu, Guibo and Han, Sirui and Yu, Dianhai},
  journal={arXiv preprint arXiv:2609.39924},
  year={2026}
}

Configuration

Architecture
CoViscoModel
Model type
covisco

Identity and Version

Repository
ernie-research/CoVisco-L-14
Publisher
Ernie Research
Task
Feature extraction
Modality
Text
Library
pytorch
Parameters
639M parameters
Languages
Not stated by the source
Revision
3ea4613abe342c2d4cdf3991c0edfad16db6962d
First published
2026-10-07
Last updated
2026-10-09

Files and Weights

11 files, 2.6 GB in total. The weights are 1 file totalling 2.6 GB in safetensors.

Weights1 file · 2.6 GB
Configuration6 files · 108.8 KB
Documentation2 files · 18.5 KB
Other1 file · 273 B
Repository1 file · 1.5 KB
Every file
FileTypeSizeSHA-256
model.safetensorsWeights2.6 GB afe6b17348df
codec_visidx.pyConfiguration24.9 KB —
config.jsonConfiguration1.4 KB —
covisco_model.pyConfiguration12.5 KB —
covisco_vit.pyConfiguration48.2 KB —
load_covisco.pyConfiguration13.5 KB —
reconstruction_decoder.pyConfiguration8.4 KB —
LICENSEDocumentation1.2 KB —
README.mdDocumentation17.2 KB —
requirements.txtOther273 B —
.gitattributesRepository1.5 KB —

License and Download

License
mit
Access
Open weights, no gate
Download size
2.6 GB
Download from Ernie Research

Released by Ernie Research through its official repository on Hugging Face. Read the license.

Built From

  • Described by arXiv:2609.39924

Memory Requirements

PrecisionWeights in memory
As published2.6 GB
16-bit1.3 GB
8-bit0.6 GB
4-bit0.3 GB

Weights only, from the published parameter count; the key-value cache and runtime add to this.

Questions About CoVisco-L-14

How much GPU memory does CoVisco-L-14 need?

About 1.5 GB at 16-bit and 0.4 GB at 4-bit: the weights (639M parameters) plus a working margin. A long context needs more.

What is the cheapest GPU to run CoVisco-L-14 on?

At 16-bit, 1x MI300X from $1.85 an hour; at 4-bit, 1x MI300X from $1.85 an hour, at the lowest on-demand prices the SAVRN Index lists.

Can I use CoVisco-L-14 commercially?

Yes. CoVisco-L-14 is released under MIT License. The MIT License is a short permissive license. It permits commercial use, modification and redistribution, provided the copyright notice and permission notice are included.

Similar Models

Model · Feature extraction

voxtral-mini-audio-extractor

Vxltxr Llc

A lightweight standalone audio feature extractor derived from Voxtral-Mini-3B-2507. This repository isolates the Whisper-based audio encoder and multi-modal projector from the original 3B language model. The extracted module is intended for offline audio preprocessing, dataset preparation, feature caching, and downstream multimodal training pipelines. The language-model decoder is not included. It does not contain the 3B LLaMA language-model decoder. For offline preprocessing, loading the complete multimodal language model is unnecessary when the only required output is the projected audio representation. This standalone checkpoint can therefore be used as a dedicated audio feature…

Open weights apache-2.0 662M parameters

Model · Feature extraction

Qwen3-Embedding-0.6B

Qwen

The Qwen3 Embedding model series is the latest proprietary model of the Qwen family, specifically designed for text embedding and ranking tasks. Building upon the dense foundational models of the Qwen3 series, it provides a comprehensive range of text embeddings and reranking models in various sizes (0.6B, 4B, and 8B). This series inherits the exceptional multilingual capabilities, long-text understanding, and reasoning skills of its foundational model. The Qwen3 Embedding series represents significant advancements in multiple text embedding and ranking tasks, including text retrieval, code retrieval, text classification, text clustering, and bitext mining. Exceptional Versatility: The…

Open weights apache-2.0 596M parameters 32,768 tokens sentence-transformers

Model · Feature extraction

d1-600m-text2sql

Jirameth

Fine-tuned from LiquidAI/d1-omni-600M to select one of four supplied SQL queries using a schema, question, and conversation history. Your application supplies and executes the queries. On 1,000 synthetic development turns, v1 scored 45.6% versus 24.9% for the unchanged base, a gain of 20.7 percentage points. Both used FP32, complete inputs, original query order, batch size 4, and highest-score selection. The set contains 250 four-turn conversations across 10 synthetic clinical task families excluded from training. Each turn receives a fixed supplied history, independent of earlier predictions. This development set was reused for model selection. Results do not establish independent-test or…

Open weights other 587M parameters 128,000 tokens transformers

Model · Feature extraction

w2v-bert-2.0

AI at Meta

We are open-sourcing our Conformer-based W2v-BERT 2.0 speech encoder as described in Section 3.2.1 of the paper, which is at the core of our Seamless models. This model was pre-trained on 4.5M hours of unlabeled audio data covering more than 143 languages. It requires finetuning to be used for downstream tasks such as Automatic Speech Recognition (ASR), or Audio Classification. This model and its training are supported by Transformers, more on it in the docs. This is a bare checkpoint without any modeling head, and thus requires finetuning to be used for downstream tasks such as ASR. You can however use it to extract audio embeddings from the top layer with this code snippet: To learn more…

Open weights mit 580M parameters transformers

Model · Feature extraction

jina-embeddings-v3

Jina AI

jina-embeddings-v3 is a multilingual multi-task text embedding model designed for a variety of NLP applications. Based on the Jina-XLM-RoBERTa architecture, this model supports Rotary Position Embeddings to handle long input sequences up to 8192 tokens. Additionally, it features 5 LoRA adapters to generate task-specific embeddings efficiently. - retrieval.query: Used for query embeddings in asymmetric retrieval tasks - retrieval.passage: Used for passage embeddings in asymmetric retrieval tasks - separation: Used for embeddings in clustering and re-ranking applications - classification: Used for embeddings in classification tasks - text-matching: Used for embeddings in tasks that quantify…

Open weights cc-by-nc-4.0 572M parameters 8,194 tokens transformers

We have updated the new reranker, supporting larger lengths, more languages, and achieving better performance. More details please refer to our Github: FlagEmbedding. FlagEmbedding focuses on retrieval-augmented LLMs, consisting of the following projects currently: - 3/18/2024: Release new rerankers, built upon powerful M3 and LLM (GEMMA and MiniCPM, not so large actually) backbones, supporitng multi-lingual processing and larger inputs, massive improvements of ranking performances on BEIR, C-MTEB/Retrieval, MIRACL, LlamaIndex Evaluation. - 3/18/2024: Release Visualized-BGE, equipping BGE with visual capabilities. Visualized-BGE can be utilized to generate embeddings for hybrid image-text…

Open weights mit 560M parameters 514 tokens transformers