This is the model card of a transformers model that has been pushed on the Hub. Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. Use the code below to get started with the model. Carbon emissions can be estimated using the Machine Learning Impact calculator presented in Lacoste et al. (2019).
Corpus-ViT is an open-weight model for feature extraction from Gugu8, released under Apache License 2.0. It has 25M parameters. At 16-bit it needs about 0.1 GB of GPU memory, which fits on 1x MI300X from $1.85 an hour, at the lowest prices in the SAVRN Index. It draws 14 downloads a month.
A vision encoder with a runtime-selectable token budget, built to sit between an image and an LLM. A coarse 32px grid covers the whole image; a router spends extra 16px detail only on the regions that need it.
Runs On
What it takes to serve Corpus-ViT (25M parameters): the memory its weights need at each precision, and the cheapest way to rent enough data-center GPUs to hold them.
| Precision | Weights | Memory needed | Cheapest setup | Per hour | Also fits |
|---|---|---|---|---|---|
| 16-bit | 0.0 GB | 0.1 GB | 1x MI300X (192 GB) Vultr |
$1.85 | 1x H100 $1.99 · 1x MI325X $2.00 |
| 8-bit | 0.0 GB | 0.0 GB | 1x MI300X (192 GB) Vultr |
$1.85 | 1x H100 $1.99 · 1x MI325X $2.00 |
| 4-bit | 0.0 GB | 0.0 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 12, 2026.
Corpus-ViT on every accelerator the SAVRN Index prices, at every precision
Model Card
By Gugu8, published under apache-2.0, revision fabd2cd9c5c1.
A vision encoder with a runtime-selectable token budget, built to sit between an image and an LLM. A coarse 32px grid covers the whole image; a router spends extra 16px detail only on the regions that need it. Output length is 49 + 3k tokens — 49 at k=0, 121 at k=24 — so you choose the tokens/fidelity trade-off per image, at inference, with no retraining. 24.94M parameters. Trained in 6000 steps (~30 min) on 2× Kaggle T4 by distillation from google/siglip-base-patch16-224 (86M). This is a working, honest baseline, not a competitive encoder. On the Flickr30k official test split it reaches 8.95 mean R@1 against the teacher's 83.25 — roughly 10% of its teacher. Chance on 1000 images is 0.10.…
Read Gugu8's full model card
A vision encoder with a runtime-selectable token budget, built to sit between
an image and an LLM. A coarse 32px grid covers the whole image; a router spends
extra 16px detail only on the regions that need it. Output length is
49 + 3k tokens — 49 at k=0, 121 at k=24 — so you choose the
tokens/fidelity trade-off per image, at inference, with no retraining.
24.94M parameters. Trained in 6000 steps (~30 min) on 2× Kaggle T4 by
distillation from google/siglip-base-patch16-224 (86M).
Read this first: the quality caveat
This is a working, honest baseline, not a competitive encoder. On the Flickr30k official test split it reaches 8.95 mean R@1 against the teacher's 83.25 — roughly 10% of its teacher. Chance on 1000 images is 0.10.
Two specific things did not work, and are stated rather than buried:
- The token budget barely pays. Going from 49 to 121 tokens buys 0.5 mean
R@1. The router fits the teacher's busyness signal (loss 1.52 → 0.45), but
that does not yet convert into retrieval. Treat the
kdial as working infrastructure with an unproven payoff, not a demonstrated win. - An in-batch contrastive objective made things worse (9.00 → 7.35 mean
R@1, and Imagenette kNN 64.0 → 59.2). The 48-way task saturates early, so it
competes with the distillation gradient rather than adding signal. It is kept
behind
--clip-w, default0.
If you need competitive absolute numbers, the lever is data and training scale, not architecture: this saw 31k images for 6000 steps.
Measured results
Kaggle kernel gugu888/foveavit v24, status COMPLETE, T4 x2, 6000 steps,
batch 48, fp16. Raw JSON: benchmarks_kaggle_v24.json. Training curve:
train_log_v24.jsonl.
Flickr30k official test split (1000 images, one caption each)
| model | k | tokens | i2t R@1 | i2t R@5 | t2i R@1 | mean R@1 |
|---|---|---|---|---|---|---|
| teacher (SigLIP-B/16) | – | 196 | 82.60 | 95.40 | 83.90 | 83.25 |
| Corpus-ViT | 0 | 49 | 6.20 | 21.20 | 9.40 | 7.80 |
| Corpus-ViT | 8 | 73 | 6.00 | 20.90 | 9.50 | 7.75 |
| Corpus-ViT | 16 | 97 | 6.60 | 20.80 | 9.30 | 7.95 |
| Corpus-ViT | 24 | 121 | 7.60 | 20.30 | 10.30 | 8.95 |
Detail subset (captions ≥12 words or containing a digit; 920/1000)
| model | k | tokens | i2t R@1 | mean R@1 |
|---|---|---|---|---|
| teacher | – | 196 | 83.59 | 84.08 |
| Corpus-ViT | 0 | 49 | 7.17 | 8.47 |
| Corpus-ViT | 24 | 121 | 7.83 | 9.08 |
Imagenette (12044 images, 9 classes)
| model | zero-shot | kNN | linear probe |
|---|---|---|---|
| teacher | 99.20 | 99.36 | 99.42 |
| Corpus-ViT k=0 | 36.87 | 64.01 | 73.15 |
| Corpus-ViT k=24 | 36.37 | 63.90 | 73.46 |
Probes tell a better story than retrieval (73% linear vs 99% teacher), which is the expected pattern for a model that learns representations faster than it learns the metric head.
Test hygiene: training used the official split and held out the 1000 official test images — 30783 train / 1000 held out. Benchmark images were never trained on.
Usage
import torch
from transformers import AutoModel
model = AutoModel.from_pretrained("Gugu8/Corpus-ViT", trust_remote_code=True).eval()
# ImageNet-style normalisation to [-1, 1], 224x224.
px = torch.randn(1, 3, 224, 224)
# 1) Spatial tokens for an LLM connector: (B, 49 + 3k, 768)
tokens = model(px, k=16).last_hidden_state # (1, 97, 768)
# 2) One retrieval/classification vector in SigLIP's joint space: (B, 768)
emb, n_tokens = model.embed(px, k=16) # emb is L2-normalised
The embedding is directly comparable to SigLIP text embeddings:
from transformers import AutoModel, AutoTokenizer
siglip = AutoModel.from_pretrained("google/siglip-base-patch16-224").eval()
tok = AutoTokenizer.from_pretrained("google/siglip-base-patch16-224")
with torch.no_grad():
t = siglip.get_text_features(**tok(["a dog on a beach"], return_tensors="pt",
padding="max_length", max_length=64,
truncation=True))
t = torch.nn.functional.normalize(t.float(), dim=-1)
score = (emb @ t.T).squeeze() # higher = better match
Content-driven sharpening
k uses the learned router. To pick cells by image content instead (e.g. detail
that matters for a document, a chart, or a face), use sharpen with a budget
fraction. Cells are ranked by how much a coarse cell disagrees with its four
fine children:
emb, n = model.sharpen(px, budget=0.25) # ~25% of cells sharpened, content-driven
Token budget
k |
0 | 4 | 8 | 16 | 24 |
|---|---|---|---|---|---|
| tokens out | 49 | 61 | 73 | 97 | 121 |
tokens_out(k) returns the count without running the model.
How it works
input 224x224
├─ coarse conv, stride 32 -> 7x7 = 49 tokens (always)
└─ fine conv, stride 16 -> 14x14 = 196 tokens (used where needed)
|
router scores all 49 cells for "busyness"
|
top-k cells are REPLACED by their 4 fine children each
|
output = (49 - k) + 4k = 49 + 3k spatial tokens (+4 internal registers)
- Registers are learned tokens kept out of the LLM-facing output.
- RoPE is relative and 2-D; a parent cell sits at
2i+1, the geometric centre of its four children, so mixed grids share one consistent frame. embed()uses a learned pool head (LayerNorm + Linear over the mean final token state), trained against the teacher's projected image embedding. This matters: mean-pooling the raw token features is not in SigLIP's text space, and retrieving with it scores at exactly chance (0.10 R@1). Getting this wrong is the single biggest trap in this repo.
Distillation setup
- Teacher
google/siglip-base-patch16-224, frozen, fp16. - SigLIP's 14x14 token grid groups exactly into 7x7 coarse blocks, so targets need no resampling.
- Per-token targets: mean of each coarse block's four child tokens.
- Pooled target: the teacher's joint-space image embedding, one tower forward.
- Router target:
detail= cosine disagreement between a coarse cell and its four children, i.e. the teacher's own busyness. - Loss =
1.0*token + 0.5*router + 1.0*pool.
Reproduce
KAGGLE_USERNAME=<user> KAGGLE_KEY=<key> \
KAGGLE_BIN=/Users/everybody/Downloads/foveal-env/bin/kaggle \
PYTHON=/Users/everybody/Downloads/foveavit-env/bin/python \
./scripts/push_foveavit.sh --push
The Kaggle notebook is self-contained — it writes the package inline and pulls Flickr30k from HuggingFace, so no dataset attachment is required.
Limitations
- ~10% of teacher retrieval; not competitive with CLIP/SigLIP at scale.
- The token-budget payoff is unproven (0.5 R@1 for 2.5x tokens).
- Trained at 224px on one GPU pair; multi-crop and higher resolution untested.
- Eval uses one caption per image (the first). Scoring against all five inflates i2t R@1 and is not comparable to published numbers.
- The detail subset threshold (12 words) is a heuristic.
Provenance
Weights come from Kaggle kernel gugu888/foveavit v24. model.safetensors was
verified bit-identical to the trained checkpoint, and the modeling code in this
repo reproduces the training implementation to 0.00e+00 max absolute difference
across k = 0, 4, 8, 16, 24.
Note for contributors: loading with trust_remote_code
transformers' dynamic-module loader inspects each uploaded file for top-level
imports and treats every non-relative from X import Y as a third-party
package it must be able to importlib.import_module. Sibling imports therefore
have to be relative:
from .configuration_corpusvit import CorpusVitConfig # correct
from configuration_corpusvit import CorpusVitConfig # breaks loading
The try/except ImportError fallback does not help — the loader's regex
strips such blocks unreliably across lines, so the absolute import is still seen
and the load fails with
This modeling file requires the following packages that were not found in your
environment: configuration_corpusvit.
Verified by loading this repo from the Hub and diffing against the training
checkpoint: max|delta| = 0.00e+00.
Configuration
- Architecture
- CorpusViT
- Stored precision
- float32
- Model type
- corpus_vit
Identity and Version
- Repository
- Gugu8/Corpus-ViT
- Publisher
- Gugu8
- Task
- Feature extraction
- Modality
- Text
- Library
- transformers
- Parameters
- 25M parameters
- Languages
- Not stated by the source
- Revision
- fabd2cd9c5c1a4fd089a267c3eeed84b26a57467
- First published
- 2026-10-04
- Last updated
- 2026-10-05
Files and Weights
11 files, 99.8 MB in total. The weights are 1 file totalling 99.8 MB in safetensors.
Every file
| File | Type | Size | SHA-256 |
|---|---|---|---|
| model.safetensors | Weights | 99.8 MB | 72236f32209e |
| benchmarks_kaggle_v15.json | Configuration | 2.6 KB | — |
| benchmarks_kaggle_v24.json | Configuration | 4.3 KB | — |
| config.json | Configuration | 577 B | — |
| configuration_corpusvit.py | Configuration | 1.4 KB | — |
| modeling_corpusvit.py | Configuration | 12.0 KB | — |
| train_corpusvit.py | Configuration | 2.2 KB | — |
| README.md | Documentation | 8.5 KB | — |
| train_log_v15.jsonl | Other | 20.7 KB | — |
| train_log_v24.jsonl | Other | 20.7 KB | — |
| .gitattributes | Repository | 1.5 KB | — |
License and Download
- License
- apache-2.0
- Access
- Open weights, no gate
- Download size
- 99.8 MB
Released by Gugu8 through its official repository on Hugging Face. Read the license.
Memory Requirements
| Precision | Weights in memory |
|---|---|
| As published | 99.8 MB |
| 16-bit | 0.0 GB |
| 8-bit | 0.0 GB |
| 4-bit | 0.0 GB |
Weights only, from the published parameter count; the key-value cache and runtime add to this.
Questions About Corpus-ViT
How much GPU memory does Corpus-ViT need?
About 0.1 GB at 16-bit and 0 GB at 4-bit: the weights (25M parameters) plus a working margin. A long context needs more.
What is the cheapest GPU to run Corpus-ViT 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 Corpus-ViT commercially?
Yes. Corpus-ViT is released under Apache License 2.0. The Apache License 2.0 is a permissive open-source license. It permits commercial use, modification and redistribution. It requires keeping the license and copyright notices and any NOTICE file, stating significant changes, and it includes an express patent grant from contributors.
Similar Models
Starwell is a generative model for stellarator design. Given a number of field periods, an aspect ratio and a target magnetic-gradient scale length LgradB, it samples a plasma boundary for the mhdstable problem of the ConStellaration benchmark, predicts its benchmark metrics, and lays out modular coils for it. Boundaries are SurfaceRZFourier dicts (rcos, zsin) that constellaration evaluates directly; sample also takes tau, guidance, steps and seed, and model.config.sampling holds the integrator's defaults. Coils are simsopt CurveXYZFourier coefficients for one half period with their currents, and simsopt.field.coilsviasymmetries builds the full set; limits (in metres and per metre) sets the…
More details please refer to our Github: FlagEmbedding. FlagEmbedding can map any text to a low-dimensional dense vector which can be used for tasks like retrieval, classification, clustering, or semantic search. And it also can be used in vector databases for LLMs. Updates - 10/12/2023: Release LLM-Embedder, a unified embedding model to support diverse retrieval augmentation needs for LLMs. Paper:fire: - 09/15/2023: The technical report of BGE has been released - 09/15/2023: The masive training data of BGE has been released - 09/12/2023: New models: - 09/07/2023: Update fine-tune code: Add script to mine hard negatives and support adding instruction during fine-tuning. - 08/09/2023: BGE…
The easiest way to starting using jina-embeddings-v2-small-en is to use Jina AI's Embedding API. jina-embeddings-v2-small-en is an English, monolingual embedding model supporting 8192 sequence length. It is based on a BERT architecture (JinaBERT) that supports the symmetric bidirectional variant of ALiBi to allow longer sequence length. The backbone jina-bert-v2-small-en is pretrained on the C4 dataset. The model is further trained on Jina AI's collection of more than 400 millions of sentence pairs and hard negatives. These pairs were obtained from various domains and were carefully selected through a thorough cleaning process. The embedding model was trained using 512 sequence length, but…
More details please refer to our Github: FlagEmbedding. If you are looking for a model that supports more languages, longer texts, and other retrieval methods, you can try using bge-m3. FlagEmbedding focuses on retrieval-augmented LLMs, consisting of the following projects currently: - 1/30/2024: Release BGE-M3, a new member to BGE model series! M3 stands for Multi-linguality (100+ languages), Multi-granularities (input length up to 8192), Multi-Functionality (unification of dense, lexical, multi-vec/colbert retrieval). It is the first embedding model which supports all three retrieval methods, achieving new SOTA on multi-lingual (MIRACL) and cross-lingual (MKQA) benchmarks. Technical…
This is the model card of a transformers model that has been pushed on the Hub. Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. Use the code below to get started with the model. Carbon emissions can be estimated using the Machine Learning Impact calculator presented in Lacoste et al. (2019).
