Image-Text-to-Text
Transformers
Safetensors
sentence-transformers
English
French
qwen3_5
reranker
cross-encoder
multimodal
text-ranking
document-reranking
vidore
beir
conversational
🇪🇺 Region: EU
Instructions to use lightonai/LightOn-rerank-PW-0.8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use lightonai/LightOn-rerank-PW-0.8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="lightonai/LightOn-rerank-PW-0.8B") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("lightonai/LightOn-rerank-PW-0.8B") model = AutoModelForMultimodalLM.from_pretrained("lightonai/LightOn-rerank-PW-0.8B", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - sentence-transformers
How to use lightonai/LightOn-rerank-PW-0.8B with sentence-transformers:
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) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use lightonai/LightOn-rerank-PW-0.8B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "lightonai/LightOn-rerank-PW-0.8B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "lightonai/LightOn-rerank-PW-0.8B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/lightonai/LightOn-rerank-PW-0.8B
- SGLang
How to use lightonai/LightOn-rerank-PW-0.8B with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "lightonai/LightOn-rerank-PW-0.8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "lightonai/LightOn-rerank-PW-0.8B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "lightonai/LightOn-rerank-PW-0.8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "lightonai/LightOn-rerank-PW-0.8B", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use lightonai/LightOn-rerank-PW-0.8B with Docker Model Runner:
docker model run hf.co/lightonai/LightOn-rerank-PW-0.8B
Integrate with Sentence Transformers (#1)
Browse files- Integrate with Sentence Transformers (32137051d1ae394be9a6d345ea096b480b92b8ac)
- 1_LogitScore/config.json +5 -0
- README.md +52 -2
- additional_chat_templates/reranker.jinja +99 -0
- config_sentence_transformers.json +11 -0
- modules.json +14 -0
- sentence_bert_config.json +29 -0
1_LogitScore/config.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"true_token_id": 9175,
|
| 3 |
+
"false_token_id": 2665,
|
| 4 |
+
"module_input_name": "causal_logits"
|
| 5 |
+
}
|
README.md
CHANGED
|
@@ -15,6 +15,7 @@ tags:
|
|
| 15 |
- document-reranking
|
| 16 |
- vidore
|
| 17 |
- beir
|
|
|
|
| 18 |
datasets:
|
| 19 |
- vidore/colpali_train_set
|
| 20 |
- lightonai/embeddings-fine-tuning
|
|
@@ -128,11 +129,59 @@ On text, pointwise holds up at 0.8B (47.98 decontaminated mean, slightly ahead o
|
|
| 128 |
- **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)
|
| 129 |
- **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.
|
| 130 |
- **Languages:** English (training), French (zero-shot transfer)
|
| 131 |
-
- **Requirements:** `transformers >= 5.4.0` (`qwen3_5` architecture)
|
| 132 |
|
| 133 |
## Usage: pointwise reranking
|
| 134 |
|
| 135 |
-
Each candidate is scored independently as `logit("Yes") − logit("No")` at the first generated position
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
```python
|
| 138 |
import torch
|
|
@@ -180,6 +229,7 @@ inputs = processor(
|
|
| 180 |
with torch.inference_mode():
|
| 181 |
logits = model(**inputs).logits[:, -1]
|
| 182 |
scores = (logits[:, YES_TOKEN_ID] - logits[:, NO_TOKEN_ID]).tolist()
|
|
|
|
| 183 |
|
| 184 |
ranked = sorted(zip(scores, documents), reverse=True)
|
| 185 |
```
|
|
|
|
| 15 |
- document-reranking
|
| 16 |
- vidore
|
| 17 |
- beir
|
| 18 |
+
- sentence-transformers
|
| 19 |
datasets:
|
| 20 |
- vidore/colpali_train_set
|
| 21 |
- lightonai/embeddings-fine-tuning
|
|
|
|
| 129 |
- **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)
|
| 130 |
- **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.
|
| 131 |
- **Languages:** English (training), French (zero-shot transfer)
|
| 132 |
+
- **Requirements:** `transformers >= 5.4.0` (`qwen3_5` architecture); `sentence-transformers >= 5.4.0` for the CrossEncoder usage
|
| 133 |
|
| 134 |
## Usage: pointwise reranking
|
| 135 |
|
| 136 |
+
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.
|
| 137 |
+
|
| 138 |
+
### Using Sentence Transformers
|
| 139 |
+
|
| 140 |
+
Install Sentence Transformers (`>=5.4.0`):
|
| 141 |
+
|
| 142 |
+
```bash
|
| 143 |
+
pip install "sentence-transformers[image]"
|
| 144 |
+
```
|
| 145 |
+
|
| 146 |
+
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:
|
| 147 |
+
|
| 148 |
+
```python
|
| 149 |
+
from sentence_transformers import CrossEncoder
|
| 150 |
+
|
| 151 |
+
model = CrossEncoder("lightonai/LightOn-rerank-PW-0.8B")
|
| 152 |
+
|
| 153 |
+
query = "What is late interaction in neural information retrieval?"
|
| 154 |
+
documents = [
|
| 155 |
+
"ColBERT computes token-level query-document interactions at search time...",
|
| 156 |
+
"The Eiffel Tower is located on the Champ de Mars in Paris.",
|
| 157 |
+
]
|
| 158 |
+
|
| 159 |
+
pairs = [(query, doc) for doc in documents]
|
| 160 |
+
scores = model.predict(pairs)
|
| 161 |
+
print(scores)
|
| 162 |
+
# [-4.125 -7.1875]
|
| 163 |
+
|
| 164 |
+
rankings = model.rank(query, documents)
|
| 165 |
+
print(rankings)
|
| 166 |
+
# [{'corpus_id': 0, 'score': -4.125}, {'corpus_id': 1, 'score': -7.1875}]
|
| 167 |
+
```
|
| 168 |
+
|
| 169 |
+
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:
|
| 170 |
+
|
| 171 |
+
```python
|
| 172 |
+
from PIL import Image
|
| 173 |
+
|
| 174 |
+
pairs = [
|
| 175 |
+
(query, Image.open("page_1.png")),
|
| 176 |
+
(query, "https://example.com/page_2.png"),
|
| 177 |
+
(query, "A text passage candidate for the same query."),
|
| 178 |
+
]
|
| 179 |
+
scores = model.predict(pairs)
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
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())`.
|
| 183 |
+
|
| 184 |
+
### Using Transformers
|
| 185 |
|
| 186 |
```python
|
| 187 |
import torch
|
|
|
|
| 229 |
with torch.inference_mode():
|
| 230 |
logits = model(**inputs).logits[:, -1]
|
| 231 |
scores = (logits[:, YES_TOKEN_ID] - logits[:, NO_TOKEN_ID]).tolist()
|
| 232 |
+
# scores: [-4.125, -7.1875]
|
| 233 |
|
| 234 |
ranked = sorted(zip(scores, documents), reverse=True)
|
| 235 |
```
|
additional_chat_templates/reranker.jinja
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{%- set default_system = "Judge whether the document is relevant to the query. Answer Yes or No." -%}
|
| 2 |
+
{%- set ns = namespace(system_text="", doc_visual=false, doc_text=false) -%}
|
| 3 |
+
{%- for message in messages if message.role == "system" -%}
|
| 4 |
+
{%- if message.content is string -%}
|
| 5 |
+
{%- set ns.system_text = ns.system_text + message.content -%}
|
| 6 |
+
{%- else -%}
|
| 7 |
+
{%- for content in message.content -%}
|
| 8 |
+
{%- if 'text' in content -%}
|
| 9 |
+
{%- set ns.system_text = ns.system_text + content.text -%}
|
| 10 |
+
{%- endif -%}
|
| 11 |
+
{%- endfor -%}
|
| 12 |
+
{%- endif -%}
|
| 13 |
+
{%- endfor -%}
|
| 14 |
+
{%- if not ns.system_text -%}
|
| 15 |
+
{%- set ns.system_text = default_system -%}
|
| 16 |
+
{%- endif -%}
|
| 17 |
+
{%- for message in messages if message.role == "document" -%}
|
| 18 |
+
{%- if message.content is string -%}
|
| 19 |
+
{%- if message.content -%}
|
| 20 |
+
{%- set ns.doc_text = true -%}
|
| 21 |
+
{%- endif -%}
|
| 22 |
+
{%- else -%}
|
| 23 |
+
{%- for content in message.content -%}
|
| 24 |
+
{%- if content.type == 'image' or 'image' in content or 'image_url' in content or content.type == 'video' or 'video' in content -%}
|
| 25 |
+
{%- set ns.doc_visual = true -%}
|
| 26 |
+
{%- elif 'text' in content and content.text -%}
|
| 27 |
+
{%- set ns.doc_text = true -%}
|
| 28 |
+
{%- endif -%}
|
| 29 |
+
{%- endfor -%}
|
| 30 |
+
{%- endif -%}
|
| 31 |
+
{%- endfor -%}
|
| 32 |
+
{%- macro render_content(message) -%}
|
| 33 |
+
{%- if message.content is string -%}
|
| 34 |
+
{{- message.content -}}
|
| 35 |
+
{%- else -%}
|
| 36 |
+
{%- for content in message.content -%}
|
| 37 |
+
{%- if content.type == 'image' or 'image' in content or 'image_url' in content -%}
|
| 38 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' -}}
|
| 39 |
+
{%- elif content.type == 'video' or 'video' in content -%}
|
| 40 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' -}}
|
| 41 |
+
{%- elif 'text' in content -%}
|
| 42 |
+
{{- content.text -}}
|
| 43 |
+
{%- endif -%}
|
| 44 |
+
{%- endfor -%}
|
| 45 |
+
{%- endif -%}
|
| 46 |
+
{%- endmacro -%}
|
| 47 |
+
{%- macro render_visuals(message) -%}
|
| 48 |
+
{%- if message.content is not string -%}
|
| 49 |
+
{%- for content in message.content -%}
|
| 50 |
+
{%- if content.type == 'image' or 'image' in content or 'image_url' in content -%}
|
| 51 |
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' -}}
|
| 52 |
+
{%- elif content.type == 'video' or 'video' in content -%}
|
| 53 |
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' -}}
|
| 54 |
+
{%- endif -%}
|
| 55 |
+
{%- endfor -%}
|
| 56 |
+
{%- endif -%}
|
| 57 |
+
{%- endmacro -%}
|
| 58 |
+
{%- macro render_texts(message) -%}
|
| 59 |
+
{%- if message.content is string -%}
|
| 60 |
+
{{- message.content -}}
|
| 61 |
+
{%- else -%}
|
| 62 |
+
{%- for content in message.content -%}
|
| 63 |
+
{%- if content.type != 'image' and 'image' not in content and 'image_url' not in content and content.type != 'video' and 'video' not in content and 'text' in content -%}
|
| 64 |
+
{{- content.text -}}
|
| 65 |
+
{%- endif -%}
|
| 66 |
+
{%- endfor -%}
|
| 67 |
+
{%- endif -%}
|
| 68 |
+
{%- endmacro -%}
|
| 69 |
+
{%- set user_body -%}
|
| 70 |
+
{%- if ns.doc_visual -%}
|
| 71 |
+
{%- for message in messages if message.role == "document" -%}
|
| 72 |
+
{{- render_visuals(message) -}}
|
| 73 |
+
{%- endfor -%}
|
| 74 |
+
{{- 'Given a query, determine if the document image is relevant. The query is: ' -}}
|
| 75 |
+
{%- for message in messages if message.role == "query" -%}
|
| 76 |
+
{{- render_content(message) -}}
|
| 77 |
+
{%- endfor -%}
|
| 78 |
+
{%- if ns.doc_text -%}
|
| 79 |
+
{{- '\n\nDocument: ' -}}
|
| 80 |
+
{%- for message in messages if message.role == "document" -%}
|
| 81 |
+
{{- render_texts(message) -}}
|
| 82 |
+
{%- endfor -%}
|
| 83 |
+
{%- endif -%}
|
| 84 |
+
{%- else -%}
|
| 85 |
+
{{- 'Given a query, determine if the document is relevant. The query is: ' -}}
|
| 86 |
+
{%- for message in messages if message.role == "query" -%}
|
| 87 |
+
{{- render_content(message) -}}
|
| 88 |
+
{%- endfor -%}
|
| 89 |
+
{{- '\n\nDocument: ' -}}
|
| 90 |
+
{%- for message in messages if message.role == "document" -%}
|
| 91 |
+
{{- render_content(message) -}}
|
| 92 |
+
{%- endfor -%}
|
| 93 |
+
{%- endif -%}
|
| 94 |
+
{%- endset -%}
|
| 95 |
+
{{- '<|im_start|>system\n' + ns.system_text|trim + '<|im_end|>\n' -}}
|
| 96 |
+
{{- '<|im_start|>user\n' + user_body|trim + '<|im_end|>\n' -}}
|
| 97 |
+
{%- if add_generation_prompt -%}
|
| 98 |
+
{{- '<|im_start|>assistant\n<think>\n\n</think>\n\n' -}}
|
| 99 |
+
{%- endif -%}
|
config_sentence_transformers.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"__version__": {
|
| 3 |
+
"pytorch": "2.10.0+cu128",
|
| 4 |
+
"sentence_transformers": "5.6.0",
|
| 5 |
+
"transformers": "5.13.1"
|
| 6 |
+
},
|
| 7 |
+
"activation_fn": "torch.nn.modules.linear.Identity",
|
| 8 |
+
"default_prompt_name": null,
|
| 9 |
+
"model_type": "CrossEncoder",
|
| 10 |
+
"prompts": {}
|
| 11 |
+
}
|
modules.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"idx": 0,
|
| 4 |
+
"name": "0",
|
| 5 |
+
"path": "",
|
| 6 |
+
"type": "sentence_transformers.base.modules.transformer.Transformer"
|
| 7 |
+
},
|
| 8 |
+
{
|
| 9 |
+
"idx": 1,
|
| 10 |
+
"name": "1",
|
| 11 |
+
"path": "1_LogitScore",
|
| 12 |
+
"type": "sentence_transformers.cross_encoder.modules.logit_score.LogitScore"
|
| 13 |
+
}
|
| 14 |
+
]
|
sentence_bert_config.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"transformer_task": "any-to-any",
|
| 3 |
+
"modality_config": {
|
| 4 |
+
"text": {
|
| 5 |
+
"method": "forward",
|
| 6 |
+
"method_output_name": "logits"
|
| 7 |
+
},
|
| 8 |
+
"image": {
|
| 9 |
+
"method": "forward",
|
| 10 |
+
"method_output_name": "logits"
|
| 11 |
+
},
|
| 12 |
+
"video": {
|
| 13 |
+
"method": "forward",
|
| 14 |
+
"method_output_name": "logits"
|
| 15 |
+
},
|
| 16 |
+
"message": {
|
| 17 |
+
"method": "forward",
|
| 18 |
+
"method_output_name": "logits",
|
| 19 |
+
"format": "structured"
|
| 20 |
+
}
|
| 21 |
+
},
|
| 22 |
+
"module_output_name": "causal_logits",
|
| 23 |
+
"processing_kwargs": {
|
| 24 |
+
"chat_template": {
|
| 25 |
+
"chat_template": "reranker",
|
| 26 |
+
"add_generation_prompt": true
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
}
|