How to use from the
Use from the
sentence-transformers library
from sentence_transformers import CrossEncoder

model = CrossEncoder("lightonai/LightOn-rerank-PW-0.8B")

query = "Which planet is known as the Red Planet?"
passages = [
	"Venus is often called Earth's twin because of its similar size and proximity.",
	"Mars, known for its reddish appearance, is often referred to as the Red Planet.",
	"Jupiter, the largest planet in our solar system, has a prominent red spot.",
	"Saturn, famous for its rings, is sometimes mistaken for the Red Planet."
]

scores = model.predict([(query, passage) for passage in passages])
print(scores)
Reranking: the second stage that puts the best text and image candidates on top

Website LinkedIn X

📝 Blog post

LightOn-rerank-PW-0.8B

Unified Text + Visual Document Reranker by LightOn

PW-0.8B | LW-0.8B | PW-2B | LW-2B | PW-4B | LW-4B


About the LightOn-rerank family

Production retrieval pipelines usually need two rerankers: one for text passages and one for visual documents (PDF pages, slides, scans). LightOn-rerank models are unified cross-encoder rerankers: a single model scores both text passages and document page images against a query, on top of any first-stage retriever (BM25, dense embeddings, or ColPali-family late-interaction models).

The models are built on Qwen3.5 backbone (hybrid linear + full attention) and jointly fine-tuned on text and visual reranking data with mixed-modality batches (LoRA, merged into the released weights). Training data is English-only; French performance transfers zero-shot from the multilingual backbone.

The family comes in two scoring flavours × three sizes (0.8B / 2B / 4B):

  • PW (pointwise): each candidate is scored independently. The model judges whether the document answers the query, and the score is logit("Yes") − logit("No"). One forward pass per candidate and no generation.
  • LW (listwise): generative listwise ranking, where 4 candidates are placed in a single prompt and the model generates a permutation ([2] > [4] > [1] > [3]). Larger candidate pools are ranked with a sliding window (window 4, stride 2, bottom-to-top). Cross-document attention makes LW markedly stronger on hard visual reranking, and unlike pointwise scoring it keeps improving with backbone size.

LightOn-rerank-PW-0.8B is the smallest pointwise member of the family, released primarily as a size/recipe reference point: it shows that independent Yes/No scoring degrades sharply below 2B parameters. If you want a 0.8B reranker, use LightOn-rerank-LW-0.8B instead (+10.0 nDCG@10 on ViDoRe V3 with the same backbone).

Results

ViDoRe V3 (visual document reranking, 8 domains × EN/FR queries), overall nDCG@10, ColQwen2.5-v0.2 first stage, retrieve 100 / rerank 100. All models, including baselines, were re-evaluated under this same two-stage protocol, so numbers are mutually comparable but not comparable to vendor-reported end-to-end results.

Model Params Scoring ViDoRe V3 overall nDCG@10
LightOn-rerank-LW-4B 4.5B listwise 64.69
Qwen3-VL-Reranker-8B 8B pointwise (pooling) 64.23
LightOn-rerank-LW-2B 2.2B listwise 62.66
LightOn-rerank-PW-2B 2.2B pointwise 59.87
LightOn-rerank-PW-4B 4.5B pointwise 59.80
jina-reranker-m0 2.4B pointwise 59.40
Qwen3-VL-Reranker-2B 2B pointwise (pooling) 59.18
LightOn-rerank-LW-0.8B 0.85B listwise 58.25
MonoQwen2-VL-v0.1 2B pointwise 57.76
First-stage only (ColQwen2.5, no rerank) 55.60
LightOn-rerank-PW-0.8B (this model) 0.85B pointwise 48.20

Note that this model lands below the first-stage retriever: at 0.8B, independent pointwise scoring makes the ranking worse than not reranking at all on several splits (see limitations).

ViDoRe V3 detail (nDCG@10, ColQwen2.5 first stage, rerank-100)

Domain EN FR
finance_en 57.40 31.08
finance_fr 32.28 30.27
computer_science 73.96 50.86
hr 56.41 26.12
energy 64.90 63.97
industrial 54.88 25.99
pharmaceuticals 64.59 46.40
physics 47.84 44.22
mean 56.53 39.86

Overall nDCG@10: 48.20 (EN 56.53 / FR 39.86).

BEIR results (text reranking)

nDCG@10, BM25 first stage, retrieve 100 / rerank 100. ⚠️ marks datasets in the text training mix (NQ, MSMARCO); the decontaminated mean excludes them.

Dataset nDCG@10
fever 79.92
scifact 75.23
trec-covid 82.11
hotpotqa 67.45
nq ⚠️ 54.58
dbpedia 39.87
arguana 38.69
fiqa 34.75
msmarco ⚠️ 36.07
nfcorpus 34.38
touche-2020 37.04
climate-fever 22.22
scidocs 16.10
Mean (13) 47.57
Decontaminated mean (11, excl. ⚠️) 47.98

On text, pointwise holds up at 0.8B (47.98 decontaminated mean, slightly ahead of LW-0.8B at 47.30): the collapse described above is specific to visual reranking, where independent scoring of hard candidates fails at this size.

Model Details

  • Model type: multimodal cross-encoder reranker (pointwise: each candidate is scored independently as logit("Yes") − logit("No"))
  • Base model: Qwen/Qwen3.5-0.8B (Qwen3.5 hybrid linear + full attention VLM)
  • Parameters: ≈0.85B (bfloat16, 1.7 GB)
  • Inputs: query (text) + candidate document(s): text passage or page image
  • Fine-tuning: joint text+vision LoRA (r=32, α=32, rsLoRA, merged into the released weights), mixed-modality batches (2 text + 2 vision groups per micro-batch), vision loss weight 1.3, lr 1e-4, 1 epoch (465 steps)
  • Data: same 213k groups as the listwise models — 107k text groups (NQ, TriviaQA, MS MARCO; each a [pos, neg_0, neg_1, neg_2] 4-list with hard negatives mined via the NV-Retriever approach with GTE-ModernBERT) + 106k vision groups (ColPali train set with negatives mined by Nomic). For pointwise training each group is flattened into (query, document, Yes/No) triples.
  • Languages: English (training), French (zero-shot transfer)
  • Requirements: transformers >= 5.4.0 (qwen3_5 architecture); sentence-transformers >= 5.4.0 for the CrossEncoder usage

Usage: pointwise reranking

Each candidate is scored independently as logit("Yes") − logit("No") at the first generated position, so you can sort candidates by descending score. The model was trained with a fixed system prompt and user template: use them verbatim for best results.

Using Sentence Transformers

Install Sentence Transformers (>=5.4.0):

pip install "sentence-transformers[image]"

The trained system prompt and user templates are baked into the bundled reranker chat template, so query-document pairs are formatted correctly out of the box:

from sentence_transformers import CrossEncoder

model = CrossEncoder("lightonai/LightOn-rerank-PW-0.8B")

query = "What is late interaction in neural information retrieval?"
documents = [
    "ColBERT computes token-level query-document interactions at search time...",
    "The Eiffel Tower is located on the Champ de Mars in Paris.",
]

pairs = [(query, doc) for doc in documents]
scores = model.predict(pairs)
print(scores)
# [-4.125  -7.1875]

rankings = model.rank(query, documents)
print(rankings)
# [{'corpus_id': 0, 'score': -4.125}, {'corpus_id': 1, 'score': -7.1875}]

To rerank page images, pass a PIL.Image (or an image URL or file path string) as the document. Text and image candidates can be mixed in the same call:

from PIL import Image

pairs = [
    (query, Image.open("page_1.png")),
    (query, "https://example.com/page_2.png"),
    (query, "A text passage candidate for the same query."),
]
scores = model.predict(pairs)

Scores are raw logit("Yes") − logit("No") differences. You can map them to 0...1 probabilities with model.predict(pairs, activation_fn=torch.nn.Sigmoid()).

Using Transformers

import torch
from transformers import AutoModelForImageTextToText, AutoProcessor

model_id = "lightonai/LightOn-rerank-PW-0.8B"
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    dtype=torch.bfloat16,
    attn_implementation="flash_attention_2",  # optional, remove if flash-attn is not installed
    device_map="cuda",
).eval()
processor = AutoProcessor.from_pretrained(model_id)
processor.tokenizer.padding_side = "left"  # scores are read at the last position

YES_TOKEN_ID = 9175  # "Yes"
NO_TOKEN_ID = 2665   # "No"

SYSTEM_PROMPT = "Judge whether the document is relevant to the query. Answer Yes or No."
USER_TEMPLATE = (
    "Given a query, determine if the document is relevant. "
    "The query is: {query}\n\nDocument: {doc}"
)

query = "What is late interaction in neural information retrieval?"
documents = [
    "ColBERT computes token-level query-document interactions at search time...",
    "The Eiffel Tower is located on the Champ de Mars in Paris.",
]

texts = [
    processor.apply_chat_template(
        [
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": USER_TEMPLATE.format(query=query, doc=doc)},
        ],
        tokenize=False,
        add_generation_prompt=True,
    )
    for doc in documents
]
inputs = processor(
    text=texts, return_tensors="pt", padding=True, truncation=True, max_length=2048
).to(model.device)
with torch.inference_mode():
    logits = model(**inputs).logits[:, -1]
scores = (logits[:, YES_TOKEN_ID] - logits[:, NO_TOKEN_ID]).tolist()
# scores: [-4.125, -7.1875]

ranked = sorted(zip(scores, documents), reverse=True)

To score a page image instead of a text passage, replace the user message with:

VISION_TEMPLATE = "Given a query, determine if the document image is relevant. The query is: {query}"

{"role": "user", "content": [
    {"type": "image", "image": page_image},  # PIL.Image
    {"type": "text", "text": VISION_TEMPLATE.format(query=query)},
]}

and pass images=[page_image, ...] to the processor call (keep the same system prompt).

Serving with vLLM

vllm serve lightonai/LightOn-rerank-PW-0.8B --trust-remote-code --max-model-len 16384
resp = client.chat.completions.create(
    model="lightonai/LightOn-rerank-PW-0.8B",
    messages=messages,          # same system + user messages as above
    max_tokens=1,
    logprobs=True,
    top_logprobs=20,
    temperature=0.0,
    extra_body={"chat_template_kwargs": {"enable_thinking": False}},
)
top = resp.choices[0].logprobs.content[0].top_logprobs
lp = {t.token: t.logprob for t in top}
score = lp.get("Yes", -100.0) - lp.get("No", -100.0)

Full-page document images can exceed 8k tokens, so keep --max-model-len at 16384 or higher when reranking page images.

Notes & limitations

  • At 0.8B, pointwise Yes/No scoring cannot discriminate hard visual candidates: −10.0 nDCG@10 vs the listwise sibling on the same backbone, with French hit hardest. Prefer LightOn-rerank-LW-0.8B at this size.
  • On ViDoRe V3 this model lands below the first-stage retriever (48.20 vs 55.60 for ColQwen2.5 with no reranking): on several splits you would get better rankings, faster, by not reranking at all. It is released as a cautionary reference point, not a recommended operating point.
  • Training data is English-only. French works zero-shot (the backbone is multilingual) but is slightly behind English on average.
  • BEIR contamination flag: NQ and MSMARCO are part of the text training data; headline text figures use decontaminated means that exclude them.

The LightOn-rerank family

Model Backbone Scoring ViDoRe V3 overall nDCG@10
LightOn-rerank-PW-0.8B Qwen3.5-0.8B pointwise 48.20
LightOn-rerank-LW-0.8B Qwen3.5-0.8B listwise 58.25
LightOn-rerank-PW-2B Qwen3.5-2B pointwise 59.87
LightOn-rerank-LW-2B Qwen3.5-2B listwise 62.66
LightOn-rerank-PW-4B Qwen3.5-4B pointwise 59.80
LightOn-rerank-LW-4B Qwen3.5-4B listwise 64.69

Rule of thumb: LW models are stronger at every size (and the gap grows with size); PW models are cheaper to serve and score candidates independently. For the best quality pick LW-4B; for the best quality/cost trade-off pick LW-2B; for maximum throughput on text-heavy workloads pick a PW model.

Citation

@misc{ananya2026lightonrerank,
  title={One Adapter, Both Modalities: Field Notes from Building and Serving a Multimodal Reranker},
  author={Ananya, Ishrat Jahan and Chatelain, Amelie},
  year={2026},
  howpublished={\url{https://huggingface.co/blog/lightonai/lighton-rerank}},
}
Downloads last month
126
Safetensors
Model size
0.9B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for lightonai/LightOn-rerank-PW-0.8B

Finetuned
(349)
this model

Datasets used to train lightonai/LightOn-rerank-PW-0.8B

Spaces using lightonai/LightOn-rerank-PW-0.8B 2

Collection including lightonai/LightOn-rerank-PW-0.8B

Article mentioning lightonai/LightOn-rerank-PW-0.8B