Sentence Similarity
sentence-transformers
Safetensors
Transformers
qwen3_vl
image-text-to-text
multimodal embedding
qwen
embedding
Instructions to use Qwen/Qwen3-VL-Embedding-8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- sentence-transformers
How to use Qwen/Qwen3-VL-Embedding-8B with sentence-transformers:
from sentence_transformers import SentenceTransformer model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-8B") sentences = [ "That is a happy person", "That is a happy dog", "That is a very happy person", "Today is a sunny day" ] embeddings = model.encode(sentences) similarities = model.similarity(embeddings, embeddings) print(similarities.shape) # [4, 4] - Transformers
How to use Qwen/Qwen3-VL-Embedding-8B with Transformers:
# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("Qwen/Qwen3-VL-Embedding-8B") model = AutoModelForMultimodalLM.from_pretrained("Qwen/Qwen3-VL-Embedding-8B", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Update README.md
Browse files
README.md
CHANGED
|
@@ -46,19 +46,64 @@ While the Embedding model generates high-dimensional vectors for broad applicati
|
|
| 46 |
- Context Length: 32k
|
| 47 |
- Embedding Dimension: Up to 4096, supports user-defined output dimensions ranging from 64 to 4096
|
| 48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
## Model Performance
|
| 50 |
|
| 51 |
### Evaluation Results on [MMEB-V2](https://huggingface.co/spaces/TIGER-Lab/MMEB-Leaderboard)
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
### Evaluation Results on [MMTEB](https://huggingface.co/spaces/mteb/leaderboard)
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
## Usage
|
| 64 |
|
|
@@ -77,13 +122,16 @@ from scripts.qwen3_vl_embedding import Qwen3VLEmbedder
|
|
| 77 |
# Define a list of query texts
|
| 78 |
queries = [
|
| 79 |
{"text": "A woman playing with her dog on a beach at sunset."},
|
| 80 |
-
{"text": "Pet owner training dog outdoors near water."}
|
|
|
|
|
|
|
| 81 |
]
|
| 82 |
|
| 83 |
# Define a list of document texts and images
|
| 84 |
documents = [
|
| 85 |
-
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset.
|
| 86 |
-
{"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"}
|
|
|
|
| 87 |
]
|
| 88 |
|
| 89 |
# Specify the model path
|
|
@@ -91,6 +139,8 @@ model_name_or_path = "Qwen/Qwen3-VL-Embedding-8B"
|
|
| 91 |
|
| 92 |
# Initialize the Qwen3VLEmbedder model
|
| 93 |
model = Qwen3VLEmbedder(model_name_or_path=model_name_or_path)
|
|
|
|
|
|
|
| 94 |
|
| 95 |
# Combine queries and documents into a single input list
|
| 96 |
inputs = queries + documents
|
|
@@ -99,20 +149,293 @@ inputs = queries + documents
|
|
| 99 |
embeddings = model.process(inputs)
|
| 100 |
|
| 101 |
# Compute similarity scores between query embeddings and document embeddings
|
| 102 |
-
similarity_scores = (embeddings[:
|
| 103 |
|
| 104 |
-
# Print out the similarity scores
|
| 105 |
print(similarity_scores.tolist())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
```
|
| 107 |
|
| 108 |
-
For more usage examples
|
|
|
|
| 109 |
|
| 110 |
## Citation
|
| 111 |
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
@article{qwen3vlembedding,
|
| 114 |
title={Qwen3-VL-Embedding and Qwen3-VL-Reranker: A Unified Framework for State-of-the-Art Multimodal Retrieval and Ranking},
|
| 115 |
-
author={Li, Mingxin and Zhang, Yanzhao and Long, Dingkun and Chen
|
| 116 |
journal={arXiv},
|
| 117 |
year={2026}
|
| 118 |
}
|
|
|
|
| 46 |
- Context Length: 32k
|
| 47 |
- Embedding Dimension: Up to 4096, supports user-defined output dimensions ranging from 64 to 4096
|
| 48 |
|
| 49 |
+
For more details, including benchmark evaluation, hardware requirements, and inference performance, please refer to our [blog](https://qwen.ai/blog?id=qwen3-vl-embedding), [GitHub](https://github.com/QwenLM/Qwen3-VL-Embedding).
|
| 50 |
+
|
| 51 |
+
## Qwen3-VL-Embedding and Qwen3-VL-Reranker Model list
|
| 52 |
+
|
| 53 |
+
| Model | Size | Model Layers | Sequence Length | Embedding Dimension | Quantization Support | MRL Support | Instruction Aware |
|
| 54 |
+
|---|---|---|---|---|----------------------|---|---|
|
| 55 |
+
| [Qwen3-VL-Embedding-2B](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B) | 2B | 28 | 32K | 2048 | Yes | Yes | Yes |
|
| 56 |
+
| [Qwen3-VL-Embedding-8B](https://huggingface.co/Qwen/Qwen3-VL-Embedding-8B) | 8B | 36 | 32K | 4096 | Yes | Yes | Yes |
|
| 57 |
+
| [Qwen3-VL-Reranker-2B](https://huggingface.co/Qwen/Qwen3-VL-Reranker-2B) | 2B | 28 | 32K | - | - | - | Yes |
|
| 58 |
+
| [Qwen3-VL-Reranker-8B](https://huggingface.co/Qwen/Qwen3-VL-Reranker-8B) | 8B | 36 | 32K | - | - | - | Yes |
|
| 59 |
+
|
| 60 |
+
> **Note**:
|
| 61 |
+
> - `Quantization Support` indicates the supported quantization post process for the output embedding.
|
| 62 |
+
> - `MRL Support` indicates whether the embedding model supports custom dimensions for the final embedding.
|
| 63 |
+
> - `Instruction Aware` notes whether the embedding or reranking model supports customizing the input instruction according to different tasks.
|
| 64 |
+
> Our evaluation indicates that, for most downstream tasks, using instructions (instruct) typically yields an improvement of 1% to 5% compared to not using them. Therefore, we recommend that developers create tailored instructions specific to their tasks and scenarios. In multilingual contexts, we also advise users to write their instructions in English, as most instructions utilized during the model training process were originally written in English.
|
| 65 |
+
|
| 66 |
## Model Performance
|
| 67 |
|
| 68 |
### Evaluation Results on [MMEB-V2](https://huggingface.co/spaces/TIGER-Lab/MMEB-Leaderboard)
|
| 69 |
|
| 70 |
+
Results on the MMEB-V2 benchmark. All models except IFM-TTE have been re-evaluated on the updated VisDoc OOD split. CLS: classification, QA: question answering, RET: retrieval, GD: grounding, MRET: moment retrieval, VDR: ViDoRe, VR: VisRAG, OOD: out-of-distribution.
|
| 71 |
+
|
| 72 |
+
| Model | Model Size | Image CLS | Image QA | Image RET | Image GD | Image Overall | Video CLS | Video QA | Video RET | Video MRET | Video Overall | VisDoc VDRv1 | VisDoc VDRv2 | VisDoc VR | VisDoc OOD | VisDoc Overall | All |
|
| 73 |
+
|----------------------------|---------|-------|------|------|------|-----------|------|------|------|------|------|-------|------|--------|------|------|--------|
|
| 74 |
+
| **# of Datasets →** | | 10 | 10 | 12 | 4 | 36 | 5 | 5 | 5 | 3 | 18 | 10 | 4 | 6 | 4 | 24 | 78 |
|
| 75 |
+
| VLM2Vec | 2B | 58.7 | 49.3 | 65.0 | 72.9 | 59.7 | 33.4 | 30.5 | 20.6 | 30.7 | 28.6 | 49.8 | 13.5 | 51.8 | 48.2 | 44.0 | 47.7 |
|
| 76 |
+
| VLM2Vec-V2 | 2B | 62.9 | 56.3 | 69.5 | 77.3 | 64.9 | 39.3 | 34.3 | 28.8 | 36.8 | 34.6 | 75.5 | 44.9 | 79.4 | 62.2 | 69.2 | 59.2 |
|
| 77 |
+
| GME-2B | 2B | 54.4 | 29.9 | 66.9 | 55.5 | 51.9 | 34.9 | 42.0 | 25.6 | 31.1 | 33.6 | 86.1 | 54.0 | 82.5 | 67.5 | 76.8 | 55.3 |
|
| 78 |
+
| GME-7B | 7B | 57.7 | 34.7 | 71.2 | 59.3 | 56.0 | 37.4 | 50.4 | 28.4 | 37.0 | 38.4 | 89.4 | 55.6 | 85.0 | 68.3 | 79.3 | 59.1 |
|
| 79 |
+
| Ops-MM-embedding-v1 | 8B | 69.7 | 69.6 | 73.1 | 87.2 | 72.7 | 59.7 | 62.2 | 45.7 | 43.2 | 53.8 | 80.1 | 59.6 | 79.3 | 67.8 | 74.4 | 68.9 |
|
| 80 |
+
| IFM-TTE | 8B | 76.7 | 78.5 | 74.6 | 89.3 | 77.9 | 60.5 | 67.9 | 51.7 | 54.9 | 59.2 | 85.2 | 71.5 | 92.7 | 53.3 | 79.5 | 74.1 |
|
| 81 |
+
| RzenEmbed | 8B | 70.6 | 71.7 | 78.5 | 92.1 | 75.9 | 58.8 | 63.5 | 51.0 | 45.5 | 55.7 | 89.7 | 60.7 | 88.7 | 69.9 | 81.3 | 72.9 |
|
| 82 |
+
| Seed-1.6-embedding-1215 | unknown | 75.0 | 74.9 | 79.3 | 89.0 | 78.0 | 85.2 | 66.7 | 59.1 | 54.8 | 67.7 | 90.0 | 60.3 | 90.0 | 70.7 | 82.2 | 76.9 |
|
| 83 |
+
| **Qwen3-VL-Embedding-2B** | 2B | 70.2 | 74.4 | 74.9 | 88.6 | 75.0 | 72.8 | 63.8 | 52.3 | 51.6 | 61.1 | 85.2 | 66.0 | 86.3 | 74.3 | 80.2 | 73.4 |
|
| 84 |
+
| **Qwen3-VL-Embedding-8B** | 8B | 74.4 | 81.0 | 80.0 | 92.2 | 80.1 | 79.1 | 70.1 | 57.0 | 53.2 | 66.1 | 88.2 | 69.9 | 88.8 | 78.3 | 83.3 | **77.9** |
|
| 85 |
|
| 86 |
### Evaluation Results on [MMTEB](https://huggingface.co/spaces/mteb/leaderboard)
|
| 87 |
|
| 88 |
+
Results on the MMTEB benchmark.
|
| 89 |
+
|
| 90 |
+
| Model | Size | Mean (Task) | Mean (Type) | Bitxt Mining | Class. | Clust. | Inst. Retri. | Multi. Class. | Pair. Class. | Rerank | Retri. | STS |
|
| 91 |
+
|----------------------------------|:-------:|:-------------:|:-------------:|:--------------:|:--------:|:--------:|:--------------:|:---------------:|:--------------:|:--------:|:--------:|:------:|
|
| 92 |
+
| NV-Embed-v2 | 7B | 56.29 | 49.58 | 57.84 | 57.29 | 40.80 | 1.04 | 18.63 | 78.94 | 63.82 | 56.72 | 71.10|
|
| 93 |
+
| GritLM-7B | 7B | 60.92 | 53.74 | 70.53 | 61.83 | 49.75 | 3.45 | 22.77 | 79.94 | 63.78 | 58.31 | 73.33|
|
| 94 |
+
| BGE-M3 | 0.6B | 59.56 | 52.18 | 79.11 | 60.35 | 40.88 | -3.11 | 20.1 | 80.76 | 62.79 | 54.60 | 74.12|
|
| 95 |
+
| multilingual-e5-large-instruct | 0.6B | 63.22 | 55.08 | 80.13 | 64.94 | 50.75 | -0.40 | 22.91 | 80.86 | 62.61 | 57.12 | 76.81|
|
| 96 |
+
| gte-Qwen2-1.5B-instruct | 1.5B | 59.45 | 52.69 | 62.51 | 58.32 | 52.05 | 0.74 | 24.02 | 81.58 | 62.58 | 60.78 | 71.61|
|
| 97 |
+
| gte-Qwen2-7b-Instruct | 7B | 62.51 | 55.93 | 73.92 | 61.55 | 52.77 | 4.94 | 25.48 | 85.13 | 65.55 | 60.08 | 73.98|
|
| 98 |
+
| text-embedding-3-large | - | 58.93 | 51.41 | 62.17 | 60.27 | 46.89 | -2.68 | 22.03 | 79.17 | 63.89 | 59.27 | 71.68|
|
| 99 |
+
| Cohere-embed-multilingual-v3.0 | - | 61.12 | 53.23 | 70.50 | 62.95 | 46.89 | -1.89 | 22.74 | 79.88 | 64.07 | 59.16 | 74.80|
|
| 100 |
+
| Gemini Embedding | - | 68.37 | 59.59 | 79.28 | 71.82 | 54.59 | 5.18 | **29.16** | 83.63 | 65.58 | 67.71 | 79.40|
|
| 101 |
+
| Qwen3-Embedding-0.6B | 0.6B | 64.33 | 56.00 | 72.22 | 66.83 | 52.33 | 5.09 | 24.59 | 80.83 | 61.41 | 64.64 | 76.17|
|
| 102 |
+
| Qwen3-Embedding-4B | 4B | 69.45 | 60.86 | 79.36 | 72.33 | 57.15 | **11.56** | 26.77 | 85.05 | 65.08 | 69.60 | 80.86|
|
| 103 |
+
| Qwen3-Embedding-8B | 8B | **70.58** | **61.69** | **80.89** | **74.00** | **57.65** | 10.06 | 28.66 | **86.40** | **65.63** | **70.88** | **81.08** |
|
| 104 |
+
| Qwen3-VL-Embedding-2B | 2B | 63.87 | 55.84 | 69.51 | 65.86 | 52.50 | 3.87 | 26.08 | 78.50 | 64.80 | 67.12 | 74.29 |
|
| 105 |
+
| Qwen3-VL-Embedding-8B | 8B | 67.88 | 58.88 | 77.48 | 71.95 | 55.82 | 4.46 | 28.59 | 81.08 | 65.72 | 69.41 | 75.41 |
|
| 106 |
+
|
| 107 |
|
| 108 |
## Usage
|
| 109 |
|
|
|
|
| 122 |
# Define a list of query texts
|
| 123 |
queries = [
|
| 124 |
{"text": "A woman playing with her dog on a beach at sunset."},
|
| 125 |
+
{"text": "Pet owner training dog outdoors near water."},
|
| 126 |
+
{"text": "Woman surfing on waves during a sunny day."},
|
| 127 |
+
{"text": "City skyline view from a high-rise building at night."}
|
| 128 |
]
|
| 129 |
|
| 130 |
# Define a list of document texts and images
|
| 131 |
documents = [
|
| 132 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust."},
|
| 133 |
+
{"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"},
|
| 134 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust.", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"}
|
| 135 |
]
|
| 136 |
|
| 137 |
# Specify the model path
|
|
|
|
| 139 |
|
| 140 |
# Initialize the Qwen3VLEmbedder model
|
| 141 |
model = Qwen3VLEmbedder(model_name_or_path=model_name_or_path)
|
| 142 |
+
# We recommend enabling flash_attention_2 for better acceleration and memory saving,
|
| 143 |
+
# model = Qwen3VLEmbedder(model_name_or_path=model_name_or_path, torch_dtype=torch.float16, attn_implementation="flash_attention_2")
|
| 144 |
|
| 145 |
# Combine queries and documents into a single input list
|
| 146 |
inputs = queries + documents
|
|
|
|
| 149 |
embeddings = model.process(inputs)
|
| 150 |
|
| 151 |
# Compute similarity scores between query embeddings and document embeddings
|
| 152 |
+
similarity_scores = (embeddings[:4] @ embeddings[4:].T)
|
| 153 |
|
| 154 |
+
# Print out the similarity scores in a list format
|
| 155 |
print(similarity_scores.tolist())
|
| 156 |
+
|
| 157 |
+
# [[0.74267578125, 0.6630859375, 0.6328125], [0.443603515625, 0.33349609375, 0.396484375], [0.3671875, 0.2354736328125, 0.289306640625], [0.060821533203125, -0.01557159423828125, 0.0165863037109375]]
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
### vLLM Basic Usage Example
|
| 161 |
+
```python
|
| 162 |
+
import argparse
|
| 163 |
+
import numpy as np
|
| 164 |
+
import os
|
| 165 |
+
from typing import List, Dict, Any
|
| 166 |
+
from vllm import LLM, EngineArgs
|
| 167 |
+
from vllm.multimodal.utils import fetch_image
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
# Define a list of query texts
|
| 171 |
+
queries = [
|
| 172 |
+
{"text": "A woman playing with her dog on a beach at sunset."},
|
| 173 |
+
{"text": "Pet owner training dog outdoors near water."},
|
| 174 |
+
{"text": "Woman surfing on waves during a sunny day."},
|
| 175 |
+
{"text": "City skyline view from a high-rise building at night."}
|
| 176 |
+
]
|
| 177 |
+
|
| 178 |
+
# Define a list of document texts and images
|
| 179 |
+
documents = [
|
| 180 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust."},
|
| 181 |
+
{"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"},
|
| 182 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust.", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"}
|
| 183 |
+
]
|
| 184 |
+
|
| 185 |
+
def format_input_to_conversation(input_dict: Dict[str, Any], instruction: str = "Represent the user's input.") -> List[Dict]:
|
| 186 |
+
content = []
|
| 187 |
+
|
| 188 |
+
text = input_dict.get('text')
|
| 189 |
+
image = input_dict.get('image')
|
| 190 |
+
|
| 191 |
+
if image:
|
| 192 |
+
image_content = None
|
| 193 |
+
if isinstance(image, str):
|
| 194 |
+
if image.startswith(('http', 'https', 'oss')):
|
| 195 |
+
image_content = image
|
| 196 |
+
else:
|
| 197 |
+
abs_image_path = os.path.abspath(image)
|
| 198 |
+
image_content = 'file://' + abs_image_path
|
| 199 |
+
else:
|
| 200 |
+
image_content = image
|
| 201 |
+
|
| 202 |
+
if image_content:
|
| 203 |
+
content.append({
|
| 204 |
+
'type': 'image',
|
| 205 |
+
'image': image_content,
|
| 206 |
+
})
|
| 207 |
+
|
| 208 |
+
if text:
|
| 209 |
+
content.append({'type': 'text', 'text': text})
|
| 210 |
+
|
| 211 |
+
if not content:
|
| 212 |
+
content.append({'type': 'text', 'text': ""})
|
| 213 |
+
|
| 214 |
+
conversation = [
|
| 215 |
+
{"role": "system", "content": [{"type": "text", "text": instruction}]},
|
| 216 |
+
{"role": "user", "content": content}
|
| 217 |
+
]
|
| 218 |
+
|
| 219 |
+
return conversation
|
| 220 |
+
|
| 221 |
+
def prepare_vllm_inputs(input_dict: Dict[str, Any], llm, instruction: str = "Represent the user's input.") -> Dict[str, Any]:
|
| 222 |
+
text = input_dict.get('text')
|
| 223 |
+
image = input_dict.get('image')
|
| 224 |
+
|
| 225 |
+
conversation = format_input_to_conversation(input_dict, instruction)
|
| 226 |
+
|
| 227 |
+
prompt_text = llm.llm_engine.tokenizer.apply_chat_template(
|
| 228 |
+
conversation,
|
| 229 |
+
tokenize=False,
|
| 230 |
+
add_generation_prompt=True
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
multi_modal_data = None
|
| 234 |
+
if image:
|
| 235 |
+
if isinstance(image, str):
|
| 236 |
+
if image.startswith(('http', 'https', 'oss')):
|
| 237 |
+
try:
|
| 238 |
+
image_obj = fetch_image(image)
|
| 239 |
+
multi_modal_data = {"image": image_obj}
|
| 240 |
+
except Exception as e:
|
| 241 |
+
print(f"Warning: Failed to fetch image {image}: {e}")
|
| 242 |
+
else:
|
| 243 |
+
abs_image_path = os.path.abspath(image)
|
| 244 |
+
if os.path.exists(abs_image_path):
|
| 245 |
+
from PIL import Image
|
| 246 |
+
image_obj = Image.open(abs_image_path)
|
| 247 |
+
multi_modal_data = {"image": image_obj}
|
| 248 |
+
else:
|
| 249 |
+
print(f"Warning: Image file not found: {abs_image_path}")
|
| 250 |
+
else:
|
| 251 |
+
multi_modal_data = {"image": image}
|
| 252 |
+
|
| 253 |
+
result = {
|
| 254 |
+
"prompt": prompt_text,
|
| 255 |
+
"multi_modal_data": multi_modal_data
|
| 256 |
+
}
|
| 257 |
+
return result
|
| 258 |
+
|
| 259 |
+
def main():
|
| 260 |
+
parser = argparse.ArgumentParser(description="Offline Similarity Check with vLLM")
|
| 261 |
+
parser.add_argument("--model-path", type=str, default="models/Qwen3-VL-Embedding-8B", help="Path to the model")
|
| 262 |
+
parser.add_argument("--dtype", type=str, default="bfloat16", help="Data type (e.g., bfloat16)")
|
| 263 |
+
args = parser.parse_args()
|
| 264 |
+
|
| 265 |
+
print(f"Loading model from {args.model_path}...")
|
| 266 |
+
|
| 267 |
+
engine_args = EngineArgs(
|
| 268 |
+
model=args.model_path,
|
| 269 |
+
runner="pooling",
|
| 270 |
+
dtype=args.dtype,
|
| 271 |
+
trust_remote_code=True,
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
llm = LLM(**vars(engine_args))
|
| 275 |
+
|
| 276 |
+
all_inputs = queries + documents
|
| 277 |
+
vllm_inputs = [prepare_vllm_inputs(inp, llm) for inp in all_inputs]
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
outputs = llm.embed(vllm_inputs)
|
| 281 |
+
|
| 282 |
+
embeddings_list = []
|
| 283 |
+
for i, output in enumerate(outputs):
|
| 284 |
+
emb = output.outputs.embedding
|
| 285 |
+
embeddings_list.append(emb)
|
| 286 |
+
print(f"Input {i} embedding shape: {len(emb)}")
|
| 287 |
+
|
| 288 |
+
embeddings = np.array(embeddings_list)
|
| 289 |
+
print(f"\nEmbeddings shape: {embeddings.shape}")
|
| 290 |
+
|
| 291 |
+
num_queries = len(queries)
|
| 292 |
+
query_embeddings = embeddings[:num_queries]
|
| 293 |
+
doc_embeddings = embeddings[num_queries:]
|
| 294 |
+
|
| 295 |
+
similarity_scores = query_embeddings @ doc_embeddings.T
|
| 296 |
+
|
| 297 |
+
print("\nSimilarity Scores:")
|
| 298 |
+
print(similarity_scores.tolist())
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
if __name__ == "__main__":
|
| 302 |
+
main()
|
| 303 |
+
```
|
| 304 |
+
|
| 305 |
+
### SGLang Basic Usage Example
|
| 306 |
+
```python
|
| 307 |
+
import argparse
|
| 308 |
+
import numpy as np
|
| 309 |
+
import torch
|
| 310 |
+
import os
|
| 311 |
+
from typing import List, Dict, Any
|
| 312 |
+
from sglang.srt.entrypoints.engine import Engine
|
| 313 |
+
|
| 314 |
+
# Define a list of query texts
|
| 315 |
+
queries = [
|
| 316 |
+
{"text": "A woman playing with her dog on a beach at sunset."},
|
| 317 |
+
{"text": "Pet owner training dog outdoors near water."},
|
| 318 |
+
{"text": "Woman surfing on waves during a sunny day."},
|
| 319 |
+
{"text": "City skyline view from a high-rise building at night."}
|
| 320 |
+
]
|
| 321 |
+
|
| 322 |
+
# Define a list of document texts and images
|
| 323 |
+
documents = [
|
| 324 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust."},
|
| 325 |
+
{"image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"},
|
| 326 |
+
{"text": "A woman shares a joyful moment with her golden retriever on a sun-drenched beach at sunset, as the dog offers its paw in a heartwarming display of companionship and trust.", "image": "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-VL/assets/demo.jpeg"}
|
| 327 |
+
]
|
| 328 |
+
|
| 329 |
+
def format_input_to_conversation(input_dict: Dict[str, Any], instruction: str = "Represent the user's input.") -> List[Dict]:
|
| 330 |
+
content = []
|
| 331 |
+
|
| 332 |
+
text = input_dict.get('text')
|
| 333 |
+
image = input_dict.get('image')
|
| 334 |
+
|
| 335 |
+
if image:
|
| 336 |
+
image_content = None
|
| 337 |
+
if isinstance(image, str):
|
| 338 |
+
if image.startswith(('http', 'oss')):
|
| 339 |
+
image_content = image
|
| 340 |
+
else:
|
| 341 |
+
abs_image_path = os.path.abspath(image)
|
| 342 |
+
image_content = 'file://' + abs_image_path
|
| 343 |
+
else:
|
| 344 |
+
image_content = image
|
| 345 |
+
if image_content:
|
| 346 |
+
content.append({
|
| 347 |
+
'type': 'image', 'image': image_content,
|
| 348 |
+
})
|
| 349 |
+
|
| 350 |
+
if text:
|
| 351 |
+
content.append({'type': 'text', 'text': text})
|
| 352 |
+
|
| 353 |
+
if not content:
|
| 354 |
+
content.append({'type': 'text', 'text': ""})
|
| 355 |
+
|
| 356 |
+
conversation = [
|
| 357 |
+
{"role": "system", "content": [{"type": "text", "text": instruction}]},
|
| 358 |
+
{"role": "user", "content": content}
|
| 359 |
+
]
|
| 360 |
+
|
| 361 |
+
return conversation
|
| 362 |
+
|
| 363 |
+
def convert_to_sglang_format(input_dict: Dict[str, Any], engine: Engine, instruction: str = "Represent the user's input.") -> Dict[str, Any]:
|
| 364 |
+
conversation = format_input_to_conversation(input_dict, instruction)
|
| 365 |
+
|
| 366 |
+
text_for_api = engine.tokenizer_manager.tokenizer.apply_chat_template(
|
| 367 |
+
conversation,
|
| 368 |
+
tokenize=False,
|
| 369 |
+
add_generation_prompt=True
|
| 370 |
+
)
|
| 371 |
+
|
| 372 |
+
result = {"text": text_for_api}
|
| 373 |
+
|
| 374 |
+
image = input_dict.get('image')
|
| 375 |
+
if image and isinstance(image, str):
|
| 376 |
+
result["image"] = image
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
return result
|
| 380 |
+
|
| 381 |
+
def main():
|
| 382 |
+
parser = argparse.ArgumentParser(description="Offline Similarity Check with SGLang")
|
| 383 |
+
parser.add_argument("--model-path", type=str, default="models/Qwen3-VL-Embedding-8B", help="Path to the model")
|
| 384 |
+
parser.add_argument("--dtype", type=str, default="bfloat16", help="Data type (e.g., bfloat16)")
|
| 385 |
+
args = parser.parse_args()
|
| 386 |
+
|
| 387 |
+
print(f"Loading model from {args.model_path}...")
|
| 388 |
+
|
| 389 |
+
engine = Engine(
|
| 390 |
+
model_path=args.model_path,
|
| 391 |
+
is_embedding=True,
|
| 392 |
+
dtype=args.dtype,
|
| 393 |
+
trust_remote_code=True,
|
| 394 |
+
)
|
| 395 |
+
|
| 396 |
+
inputs = queries + documents
|
| 397 |
+
sglang_inputs = [convert_to_sglang_format(inp, engine) for inp in inputs]
|
| 398 |
+
print(sglang_inputs[:])
|
| 399 |
+
print(f"sglang_inputs: {sglang_inputs}")
|
| 400 |
+
print(f"Processing {len(sglang_inputs)} inputs...")
|
| 401 |
+
|
| 402 |
+
prompts = [inp['text'] for inp in sglang_inputs]
|
| 403 |
+
images = [inp.get('image') for inp in sglang_inputs]
|
| 404 |
+
|
| 405 |
+
|
| 406 |
+
results = engine.encode(prompts, image_data=images)
|
| 407 |
+
|
| 408 |
+
embeddings_list = []
|
| 409 |
+
for res in results:
|
| 410 |
+
embeddings_list.append(res['embedding'])
|
| 411 |
+
|
| 412 |
+
embeddings = np.array(embeddings_list)
|
| 413 |
+
print(f"Embeddings shape: {embeddings.shape}")
|
| 414 |
+
|
| 415 |
+
num_queries = len(queries)
|
| 416 |
+
query_embeddings = embeddings[:num_queries]
|
| 417 |
+
doc_embeddings = embeddings[num_queries:]
|
| 418 |
+
|
| 419 |
+
similarity_scores = (query_embeddings @ doc_embeddings.T)
|
| 420 |
+
|
| 421 |
+
print("\nSimilarity Scores:")
|
| 422 |
+
print(similarity_scores.tolist())
|
| 423 |
+
|
| 424 |
+
if __name__ == "__main__":
|
| 425 |
+
main()
|
| 426 |
```
|
| 427 |
|
| 428 |
+
For more usage examples, please visit our [GitHub repository](https://github.com/QwenLM/Qwen3-VL-Embedding).
|
| 429 |
+
|
| 430 |
|
| 431 |
## Citation
|
| 432 |
|
| 433 |
+
If you find our work helpful, feel free to give us a cite.
|
| 434 |
+
|
| 435 |
+
```
|
| 436 |
@article{qwen3vlembedding,
|
| 437 |
title={Qwen3-VL-Embedding and Qwen3-VL-Reranker: A Unified Framework for State-of-the-Art Multimodal Retrieval and Ranking},
|
| 438 |
+
author={Li, Mingxin and Zhang, Yanzhao and Long, Dingkun and Chen Keqin and Song, Sibo and Bai, Shuai and Yang, Zhibo and Xie, Pengjun and Yang, An and Liu, Dayiheng and Zhou, Jingren and Lin, Junyang},
|
| 439 |
journal={arXiv},
|
| 440 |
year={2026}
|
| 441 |
}
|