初始化项目,由ModelHub XC社区提供模型

Model: LifetimeMistake/Qwen3-VL-Embedding-2B-AWQ-4bit
Source: Original Platform
This commit is contained in:
ModelHub XC
2026-06-13 00:47:04 +08:00
commit b3847331c1
17 changed files with 152675 additions and 0 deletions

36
.gitattributes vendored Normal file
View File

@@ -0,0 +1,36 @@
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
tokenizer.json filter=lfs diff=lfs merge=lfs -text

129
README.md Normal file
View File

@@ -0,0 +1,129 @@
---
license: apache-2.0
library_name: transformers
base_model: Qwen/Qwen3-VL-Embedding-2B
base_model_relation: quantized
pipeline_tag: feature-extraction
tags:
- transformers
- qwen3_vl
- multimodal embedding
- embedding
- feature-extraction
- quantized
- awq
- 4bit
- compressed-tensors
- custom_code
language:
- en
- zh
- multilingual
---
<p align="center">
<img src="https://model-demo.oss-cn-hangzhou.aliyuncs.com/Qwen3-VL-Embedding.png" width="400"/>
</p>
# Qwen3-VL-Embedding-2B-AWQ-4bit
[![Technical Report](https://img.shields.io/badge/Report-arXiv%202601.04720-8B0000.svg)](https://arxiv.org/abs/2601.04720)
[![Blog](https://img.shields.io/badge/Blog-Qwen3--VL--Embedding-0F172A.svg)](https://qwen.ai/blog?id=qwen3-vl-embedding)
[![GitHub](https://img.shields.io/badge/GitHub-Qwen3--VL--Embedding-333.svg?logo=github)](https://github.com/QwenLM/Qwen3-VL-Embedding)
[![Base Model](https://img.shields.io/badge/HuggingFace-Qwen3--VL--Embedding--2B-black.svg?logo=huggingface)](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B)
## Quantized Model Overview
This repository contains a 4-bit AWQ derivative of [`Qwen/Qwen3-VL-Embedding-2B`](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B) prepared for direct vLLM deployment through the `compressed-tensors` backend.
### What Was Quantized
- **Quantization method:** `llm-compressor` AWQ (`W4A16_ASYM`)
- **Export format:** `compressed-tensors`
- **Runtime backend:** vLLM `compressed-tensors`
- **Weight format:** 4-bit grouped asymmetric integer weights
- **Group size:** `128`
- **Calibration pipeline:** `layer_sequential`
- **Quantized modules:** text-side `Linear` layers in the Qwen3-VL decoder
- **Left unquantized:** all `model.visual*` modules and `lm_head`
### Calibration Data
This checkpoint was built from the same **1000-sample mixed retrieval manifest** as the FP16 and NVFP4 workflow, but the final AWQ pass used **876 text-only samples** and skipped **124 image-bearing rows** because the vision stack remained excluded from quantization.
Calibration sources:
- Polish text retrieval: `mteb/MSMARCO-PL`, `mteb/NQ-PL`, `mteb/FiQA-PL`
- Multilingual text retrieval: MIRACL hard-negative slices for `en`, `de`, `es`, `fr`, `ja`
- Multimodal retrieval in the master manifest: `vidore/colpali_train_set` and `lmms-lab/flickr30k`
- Hard-negative augmentation: MIRACL-derived negatives
### Local Benchmark Setup
The numbers below are from local full benchmark runs using the same harness for stock FP16 and quantized checkpoints.
Benchmark tasks:
- `mteb/MSMARCO-PL`
- `mteb/NQ-PL`
- MIRACL hard-negative slices: `en`, `de`, `es`, `fr`, `ja`
- `vidore/vidore_v3_industrial`
- `vidore/vidore_v3_computer_science`
Metrics:
- `nDCG@10`
- `Recall@10`
- `MRR@10`
### Baseline Comparison
Compared with the stock FP16 `Qwen/Qwen3-VL-Embedding-2B` checkpoint on the local full benchmark:
| Metric | Stock FP16 | AWQ 4-bit | Delta |
| --- | ---: | ---: | ---: |
| `nDCG@10` | `0.56222` | `0.54474` | `-0.01748` |
| `Recall@10` | `0.64934` | `0.63544` | `-0.01390` |
| `MRR@10` | `0.78883` | `0.80040` | `+0.01157` |
| Benchmark wall time | `434.853 s` | `435.140 s` | `0.07% slower` |
| Average request latency | `0.332726 s` | `0.333469 s` | `+0.000743 s` |
| Throughput | `18.4338 rps` | `18.4217 rps` | `-0.0121 rps` |
Notes:
- This was the **better multimodal** quantized checkpoint of the two we tested.
- It preserved the ViDoRe image benchmarks substantially better than NVFP4 and improved `vidore_v3_computer_science` over the FP16 baseline.
- It did **not** produce a meaningful runtime speedup versus the FP16 checkpoint in this harness.
- The AWQ export is larger than the NVFP4 export and took much longer to build.
### vLLM Usage
```bash
HF_TOKEN=hf_xxx \
vllm serve LifetimeMistake/Qwen3-VL-Embedding-2B-AWQ-4bit \
--runner pooling \
--convert embed \
--trust-remote-code \
--quantization compressed-tensors \
--limit-mm-per-prompt '{"image":1}'
```
If your vLLM build does not automatically pick up the bundled `chat_template.jinja`, download the repo locally and pass `--chat-template /path/to/chat_template.jinja`.
## Base Model Introduction
This model is a quantized derivative of [`Qwen/Qwen3-VL-Embedding-2B`](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B), the 2B member of Qwens multimodal embedding series.
Upstream model highlights:
- Multimodal inputs: text, images, screenshots, video, and mixed text+vision inputs
- 30+ language support
- 32k context length
- Output dimension up to `2048`, with support for smaller embedding dimensions
- Instruction-aware retrieval behavior, with English instructions recommended even for multilingual tasks
For the full base model card, broader benchmark tables, and upstream usage examples, see:
- Base model: https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B
- GitHub: https://github.com/QwenLM/Qwen3-VL-Embedding
- Technical report: https://arxiv.org/abs/2601.04720

28
added_tokens.json Normal file
View File

@@ -0,0 +1,28 @@
{
"</think>": 151668,
"</tool_call>": 151658,
"</tool_response>": 151666,
"<think>": 151667,
"<tool_call>": 151657,
"<tool_response>": 151665,
"<|box_end|>": 151649,
"<|box_start|>": 151648,
"<|endoftext|>": 151643,
"<|file_sep|>": 151664,
"<|fim_middle|>": 151660,
"<|fim_pad|>": 151662,
"<|fim_prefix|>": 151659,
"<|fim_suffix|>": 151661,
"<|im_end|>": 151645,
"<|im_start|>": 151644,
"<|image_pad|>": 151655,
"<|object_ref_end|>": 151647,
"<|object_ref_start|>": 151646,
"<|quad_end|>": 151651,
"<|quad_start|>": 151650,
"<|repo_name|>": 151663,
"<|video_pad|>": 151656,
"<|vision_end|>": 151653,
"<|vision_pad|>": 151654,
"<|vision_start|>": 151652
}

125
chat_template.jinja Normal file
View File

@@ -0,0 +1,125 @@
{%- set default_system_message = 'Represent the user\'s input.' -%}
{%- if tools %}
{{- '<|im_start|>system\n' }}
{%- if messages[0].role == 'system' %}
{%- if messages[0].content is string %}
{{- messages[0].content }}
{%- else %}
{%- for content in messages[0].content %}
{%- if 'text' in content %}
{{- content.text }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '\n\n' }}
{%- else %}
{{- default_system_message + '\n\n' }}
{%- endif %}
{{- "# Tools\n\nYou may call one or more functions to assist with the user query.\n\nYou are provided with function signatures within <tools></tools> XML tags:\n<tools>" }}
{%- for tool in tools %}
{{- "\n" }}
{{- tool | tojson }}
{%- endfor %}
{{- "\n</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call><|im_end|>\n" }}
{%- else %}
{%- if messages[0].role == 'system' %}
{{- '<|im_start|>system\n' }}
{%- if messages[0].content is string %}
{{- messages[0].content }}
{%- else %}
{%- for content in messages[0].content %}
{%- if 'text' in content %}
{{- content.text }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- else %}
{{- '<|im_start|>system\n' + default_system_message + '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- set image_count = namespace(value=0) %}
{%- set video_count = namespace(value=0) %}
{%- for message in messages %}
{%- if message.role == "user" %}
{{- '<|im_start|>' + message.role + '\n' }}
{%- if message.content is string %}
{{- message.content }}
{%- else %}
{%- for content in message.content %}
{%- if content.type == 'image' or 'image' in content or 'image_url' in content %}
{%- set image_count.value = image_count.value + 1 %}
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
<|vision_start|><|image_pad|><|vision_end|>
{%- elif content.type == 'video' or 'video' in content %}
{%- set video_count.value = video_count.value + 1 %}
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
<|vision_start|><|video_pad|><|vision_end|>
{%- elif 'text' in content %}
{{- content.text }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- elif message.role == "assistant" %}
{{- '<|im_start|>' + message.role + '\n' }}
{%- if message.content is string %}
{{- message.content }}
{%- else %}
{%- for content_item in message.content %}
{%- if 'text' in content_item %}
{{- content_item.text }}
{%- endif %}
{%- endfor %}
{%- endif %}
{%- if message.tool_calls %}
{%- for tool_call in message.tool_calls %}
{%- if (loop.first and message.content) or (not loop.first) %}
{{- '\n' }}
{%- endif %}
{%- if tool_call.function %}
{%- set tool_call = tool_call.function %}
{%- endif %}
{{- '<tool_call>\n{"name": "' }}
{{- tool_call.name }}
{{- '", "arguments": ' }}
{%- if tool_call.arguments is string %}
{{- tool_call.arguments }}
{%- else %}
{{- tool_call.arguments | tojson }}
{%- endif %}
{{- '}\n</tool_call>' }}
{%- endfor %}
{%- endif %}
{{- '<|im_end|>\n' }}
{%- elif message.role == "tool" %}
{%- if loop.first or (messages[loop.index0 - 1].role != "tool") %}
{{- '<|im_start|>user' }}
{%- endif %}
{{- '\n<tool_response>\n' }}
{%- if message.content is string %}
{{- message.content }}
{%- else %}
{%- for content in message.content %}
{%- if content.type == 'image' or 'image' in content or 'image_url' in content %}
{%- set image_count.value = image_count.value + 1 %}
{%- if add_vision_id %}Picture {{ image_count.value }}: {% endif -%}
<|vision_start|><|image_pad|><|vision_end|>
{%- elif content.type == 'video' or 'video' in content %}
{%- set video_count.value = video_count.value + 1 %}
{%- if add_vision_id %}Video {{ video_count.value }}: {% endif -%}
<|vision_start|><|video_pad|><|vision_end|>
{%- elif 'text' in content %}
{{- content.text }}
{%- endif %}
{%- endfor %}
{%- endif %}
{{- '\n</tool_response>' }}
{%- if loop.last or (messages[loop.index0 + 1].role != "tool") %}
{{- '<|im_end|>\n' }}
{%- endif %}
{%- endif %}
{%- endfor %}
{%- if add_generation_prompt %}
{{- '<|im_start|>assistant\n' }}
{%- endif %}

205
config.json Normal file
View File

@@ -0,0 +1,205 @@
{
"architectures": [
"Qwen3VLForConditionalGeneration"
],
"dtype": "bfloat16",
"image_token_id": 151655,
"model_type": "qwen3_vl",
"text_config": {
"attention_bias": false,
"attention_dropout": 0.0,
"bos_token_id": 151643,
"dtype": "bfloat16",
"eos_token_id": 151645,
"head_dim": 128,
"hidden_act": "silu",
"hidden_size": 2048,
"initializer_range": 0.02,
"intermediate_size": 6144,
"max_position_embeddings": 262144,
"model_type": "qwen3_vl_text",
"num_attention_heads": 16,
"num_hidden_layers": 28,
"num_key_value_heads": 8,
"rms_norm_eps": 1e-06,
"rope_scaling": {
"mrope_interleaved": true,
"mrope_section": [
24,
20,
20
],
"rope_type": "default"
},
"rope_theta": 5000000,
"tie_word_embeddings": true,
"use_cache": true,
"vocab_size": 151936
},
"tie_word_embeddings": true,
"transformers_version": "4.57.1",
"use_cache": false,
"video_token_id": 151656,
"vision_config": {
"deepstack_visual_indexes": [
5,
11,
17
],
"depth": 24,
"dtype": "bfloat16",
"hidden_act": "gelu_pytorch_tanh",
"hidden_size": 1024,
"in_channels": 3,
"initializer_range": 0.02,
"intermediate_size": 4096,
"model_type": "qwen3_vl",
"num_heads": 16,
"num_position_embeddings": 2304,
"out_hidden_size": 2048,
"patch_size": 16,
"spatial_merge_size": 2,
"temporal_patch_size": 2
},
"vision_end_token_id": 151653,
"vision_start_token_id": 151652,
"quantization_config": {
"config_groups": {
"group_0": {
"format": "pack-quantized",
"input_activations": null,
"output_activations": null,
"targets": [
"Linear"
],
"weights": {
"actorder": null,
"block_structure": null,
"dynamic": false,
"group_size": 128,
"num_bits": 4,
"observer": "minmax",
"observer_kwargs": {},
"strategy": "group",
"symmetric": false,
"type": "int"
}
}
},
"format": "pack-quantized",
"global_compression_ratio": null,
"ignore": [
"lm_head",
"model.visual.blocks.0.attn.proj",
"model.visual.blocks.0.attn.qkv",
"model.visual.blocks.0.mlp.linear_fc1",
"model.visual.blocks.0.mlp.linear_fc2",
"model.visual.blocks.1.attn.proj",
"model.visual.blocks.1.attn.qkv",
"model.visual.blocks.1.mlp.linear_fc1",
"model.visual.blocks.1.mlp.linear_fc2",
"model.visual.blocks.10.attn.proj",
"model.visual.blocks.10.attn.qkv",
"model.visual.blocks.10.mlp.linear_fc1",
"model.visual.blocks.10.mlp.linear_fc2",
"model.visual.blocks.11.attn.proj",
"model.visual.blocks.11.attn.qkv",
"model.visual.blocks.11.mlp.linear_fc1",
"model.visual.blocks.11.mlp.linear_fc2",
"model.visual.blocks.12.attn.proj",
"model.visual.blocks.12.attn.qkv",
"model.visual.blocks.12.mlp.linear_fc1",
"model.visual.blocks.12.mlp.linear_fc2",
"model.visual.blocks.13.attn.proj",
"model.visual.blocks.13.attn.qkv",
"model.visual.blocks.13.mlp.linear_fc1",
"model.visual.blocks.13.mlp.linear_fc2",
"model.visual.blocks.14.attn.proj",
"model.visual.blocks.14.attn.qkv",
"model.visual.blocks.14.mlp.linear_fc1",
"model.visual.blocks.14.mlp.linear_fc2",
"model.visual.blocks.15.attn.proj",
"model.visual.blocks.15.attn.qkv",
"model.visual.blocks.15.mlp.linear_fc1",
"model.visual.blocks.15.mlp.linear_fc2",
"model.visual.blocks.16.attn.proj",
"model.visual.blocks.16.attn.qkv",
"model.visual.blocks.16.mlp.linear_fc1",
"model.visual.blocks.16.mlp.linear_fc2",
"model.visual.blocks.17.attn.proj",
"model.visual.blocks.17.attn.qkv",
"model.visual.blocks.17.mlp.linear_fc1",
"model.visual.blocks.17.mlp.linear_fc2",
"model.visual.blocks.18.attn.proj",
"model.visual.blocks.18.attn.qkv",
"model.visual.blocks.18.mlp.linear_fc1",
"model.visual.blocks.18.mlp.linear_fc2",
"model.visual.blocks.19.attn.proj",
"model.visual.blocks.19.attn.qkv",
"model.visual.blocks.19.mlp.linear_fc1",
"model.visual.blocks.19.mlp.linear_fc2",
"model.visual.blocks.2.attn.proj",
"model.visual.blocks.2.attn.qkv",
"model.visual.blocks.2.mlp.linear_fc1",
"model.visual.blocks.2.mlp.linear_fc2",
"model.visual.blocks.20.attn.proj",
"model.visual.blocks.20.attn.qkv",
"model.visual.blocks.20.mlp.linear_fc1",
"model.visual.blocks.20.mlp.linear_fc2",
"model.visual.blocks.21.attn.proj",
"model.visual.blocks.21.attn.qkv",
"model.visual.blocks.21.mlp.linear_fc1",
"model.visual.blocks.21.mlp.linear_fc2",
"model.visual.blocks.22.attn.proj",
"model.visual.blocks.22.attn.qkv",
"model.visual.blocks.22.mlp.linear_fc1",
"model.visual.blocks.22.mlp.linear_fc2",
"model.visual.blocks.23.attn.proj",
"model.visual.blocks.23.attn.qkv",
"model.visual.blocks.23.mlp.linear_fc1",
"model.visual.blocks.23.mlp.linear_fc2",
"model.visual.blocks.3.attn.proj",
"model.visual.blocks.3.attn.qkv",
"model.visual.blocks.3.mlp.linear_fc1",
"model.visual.blocks.3.mlp.linear_fc2",
"model.visual.blocks.4.attn.proj",
"model.visual.blocks.4.attn.qkv",
"model.visual.blocks.4.mlp.linear_fc1",
"model.visual.blocks.4.mlp.linear_fc2",
"model.visual.blocks.5.attn.proj",
"model.visual.blocks.5.attn.qkv",
"model.visual.blocks.5.mlp.linear_fc1",
"model.visual.blocks.5.mlp.linear_fc2",
"model.visual.blocks.6.attn.proj",
"model.visual.blocks.6.attn.qkv",
"model.visual.blocks.6.mlp.linear_fc1",
"model.visual.blocks.6.mlp.linear_fc2",
"model.visual.blocks.7.attn.proj",
"model.visual.blocks.7.attn.qkv",
"model.visual.blocks.7.mlp.linear_fc1",
"model.visual.blocks.7.mlp.linear_fc2",
"model.visual.blocks.8.attn.proj",
"model.visual.blocks.8.attn.qkv",
"model.visual.blocks.8.mlp.linear_fc1",
"model.visual.blocks.8.mlp.linear_fc2",
"model.visual.blocks.9.attn.proj",
"model.visual.blocks.9.attn.qkv",
"model.visual.blocks.9.mlp.linear_fc1",
"model.visual.blocks.9.mlp.linear_fc2",
"model.visual.deepstack_merger_list.0.linear_fc1",
"model.visual.deepstack_merger_list.0.linear_fc2",
"model.visual.deepstack_merger_list.1.linear_fc1",
"model.visual.deepstack_merger_list.1.linear_fc2",
"model.visual.deepstack_merger_list.2.linear_fc1",
"model.visual.deepstack_merger_list.2.linear_fc2",
"model.visual.merger.linear_fc1",
"model.visual.merger.linear_fc2"
],
"kv_cache_scheme": null,
"quant_method": "compressed-tensors",
"quantization_status": "compressed",
"sparsity_config": {},
"transform_config": {},
"version": "0.12.2"
}
}

7
generation_config.json Normal file
View File

@@ -0,0 +1,7 @@
{
"_from_model_config": true,
"bos_token_id": 151643,
"eos_token_id": 151645,
"transformers_version": "4.57.6",
"use_cache": false
}

151388
merges.txt Normal file

File diff suppressed because it is too large Load Diff

3
model.safetensors Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2e1b04df208947055a5fd46db833460c02108fdf1859535de84e1bb8802a6e77
size 2791145616

39
preprocessor_config.json Normal file
View File

@@ -0,0 +1,39 @@
{
"crop_size": null,
"data_format": "channels_first",
"default_to_square": true,
"device": null,
"disable_grouping": null,
"do_center_crop": null,
"do_convert_rgb": true,
"do_normalize": true,
"do_pad": null,
"do_rescale": true,
"do_resize": true,
"image_mean": [
0.5,
0.5,
0.5
],
"image_processor_type": "Qwen2VLImageProcessorFast",
"image_std": [
0.5,
0.5,
0.5
],
"input_data_format": null,
"max_pixels": 1310720,
"merge_size": 2,
"min_pixels": 4096,
"pad_size": null,
"patch_size": 16,
"processor_class": "Qwen3VLProcessor",
"resample": 3,
"rescale_factor": 0.00392156862745098,
"return_tensors": null,
"size": {
"longest_edge": 1310720,
"shortest_edge": 4096
},
"temporal_patch_size": 2
}

46
quantize_summary.json Normal file
View File

@@ -0,0 +1,46 @@
{
"timestamp": 1774921447,
"model": "Qwen/Qwen3-VL-Embedding-2B",
"output_dir": "/home/szymon/source/repos/Qwen3-VL-Embedding-2B-quant/artifacts/checkpoints/qwen3_vl_embedding_2b_awq_v1",
"quant_mode": "awq",
"quantizer_impl": "llmcompressor",
"modelopt_cfg": null,
"llmcompressor_scheme": "W4A16_ASYM",
"llmcompressor_pipeline": "layer_sequential",
"llmcompressor_sequential_targets": [
"Qwen3VLTextDecoderLayer"
],
"vllm_quantization_backend": "compressed-tensors",
"vllm_dtype": null,
"manifest": "/home/szymon/source/repos/Qwen3-VL-Embedding-2B-quant/data/calibration/master/manifests/calibration_manifest.jsonl",
"num_calib_samples": 876,
"torch_dtype": "bfloat16",
"device": "cuda",
"disable_globs": [
"*visual*",
"*vision*"
],
"skipped_image_rows": 124,
"elapsed_s": 936.56,
"copied_remote_files": [
"added_tokens.json",
"chat_template.jinja",
"config.json",
"merges.txt",
"preprocessor_config.json",
"special_tokens_map.json",
"tokenizer.json",
"tokenizer_config.json",
"video_preprocessor_config.json",
"vocab.json"
],
"export_checks": {
"config_json": true,
"weights": true,
"tokenizer_config_json": true,
"tokenizer_json_or_vocab": true,
"chat_template": true,
"processor_definition": true,
"compressed_tensors_config": true
}
}

17
recipe.yaml Normal file
View File

@@ -0,0 +1,17 @@
default_stage:
default_modifiers:
AWQModifier:
targets: [Linear]
ignore: ['re:.*lm_head.*', 're:.*vision.*', 're:.*visual.*']
scheme: W4A16_ASYM
mappings:
- smooth_layer: re:.*input_layernorm$
balance_layers: ['re:.*q_proj$', 're:.*k_proj$', 're:.*v_proj$']
- smooth_layer: re:.*v_proj$
balance_layers: ['re:.*o_proj$']
- smooth_layer: re:.*post_attention_layernorm$
balance_layers: ['re:.*gate_proj$', 're:.*up_proj$']
- smooth_layer: re:.*up_proj$
balance_layers: ['re:.*down_proj$']
offload_device: !!python/object/apply:torch.device [cpu]
duo_scaling: false

View File

@@ -0,0 +1,337 @@
import torch
import torch.nn.functional as F
import unicodedata
import numpy as np
import logging
from PIL import Image
from dataclasses import dataclass
from typing import Optional, List, Union, Dict, Any
from transformers.models.qwen3_vl.modeling_qwen3_vl import Qwen3VLPreTrainedModel, Qwen3VLModel, Qwen3VLConfig
from transformers.models.qwen3_vl.processing_qwen3_vl import Qwen3VLProcessor
from transformers.modeling_outputs import ModelOutput
from transformers.processing_utils import Unpack
from transformers.utils import TransformersKwargs
from transformers.cache_utils import Cache
from transformers.utils.generic import check_model_inputs
from qwen_vl_utils.vision_process import process_vision_info
logger = logging.getLogger(__name__)
# Constants for configuration
MAX_LENGTH = 8192
IMAGE_BASE_FACTOR = 16
IMAGE_FACTOR = IMAGE_BASE_FACTOR * 2
MIN_PIXELS = 4 * IMAGE_FACTOR * IMAGE_FACTOR
MAX_PIXELS = 1800 * IMAGE_FACTOR * IMAGE_FACTOR
FPS = 1
MAX_FRAMES = 64
FRAME_MAX_PIXELS = 768 * IMAGE_FACTOR * IMAGE_FACTOR
MAX_TOTAL_PIXELS = 10 * FRAME_MAX_PIXELS
PAD_TOKEN = "<|endoftext|>"
# Define output structure for embeddings
@dataclass
class Qwen3VLForEmbeddingOutput(ModelOutput):
last_hidden_state: Optional[torch.FloatTensor] = None
attention_mask: Optional[torch.Tensor] = None
# Define model class to compute embeddings
class Qwen3VLForEmbedding(Qwen3VLPreTrainedModel):
_checkpoint_conversion_mapping = {}
accepts_loss_kwargs = False
config: Qwen3VLConfig
def __init__(self, config):
super().__init__(config)
self.model = Qwen3VLModel(config)
self.post_init()
def get_input_embeddings(self):
return self.model.get_input_embeddings()
def set_input_embeddings(self, value):
self.model.set_input_embeddings(value)
def set_decoder(self, decoder):
self.model.set_decoder(decoder)
def get_decoder(self):
return self.model.get_decoder()
# Extract video features from model
def get_video_features(self, pixel_values_videos: torch.FloatTensor,
video_grid_thw: Optional[torch.LongTensor] = None):
return self.model.get_video_features(pixel_values_videos, video_grid_thw)
# Extract image features from model
def get_image_features(self, pixel_values: torch.FloatTensor,
image_grid_thw: Optional[torch.LongTensor] = None):
return self.model.get_image_features(pixel_values, image_grid_thw)
# Make modules accessible through properties
@property
def language_model(self):
return self.model.language_model
@property
def visual(self):
return self.model.visual
# Forward pass through model with input parameters
# @check_model_inputs
def forward(self,
input_ids: torch.LongTensor = None,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_values: Optional[Cache] = None,
inputs_embeds: Optional[torch.FloatTensor] = None,
pixel_values: Optional[torch.Tensor] = None,
pixel_values_videos: Optional[torch.FloatTensor] = None,
image_grid_thw: Optional[torch.LongTensor] = None,
video_grid_thw: Optional[torch.LongTensor] = None,
cache_position: Optional[torch.LongTensor] = None,
logits_to_keep: Union[int, torch.Tensor] = 0,
**kwargs: Unpack[TransformersKwargs],
) -> Union[tuple, Qwen3VLForEmbeddingOutput]:
# Pass inputs through the model
outputs = self.model(
input_ids=input_ids,
pixel_values=pixel_values,
pixel_values_videos=pixel_values_videos,
image_grid_thw=image_grid_thw,
video_grid_thw=video_grid_thw,
position_ids=position_ids,
attention_mask=attention_mask,
past_key_values=past_key_values,
inputs_embeds=inputs_embeds,
cache_position=cache_position,
**kwargs,
)
# Return the model output
return Qwen3VLForEmbeddingOutput(
last_hidden_state=outputs.last_hidden_state,
attention_mask=attention_mask,
)
def sample_frames(frames: List[Union[str, Image.Image]], num_segments: int, max_segments: int) -> List[str]:
duration = len(frames)
frame_id_array = np.linspace(0, duration - 1, num_segments, dtype=int)
frame_id_list = frame_id_array.tolist()
last_frame_id = frame_id_list[-1]
# Create a list of sampled frames
sampled_frames = []
for frame_idx in frame_id_list:
try:
sampled_frames.append(frames[frame_idx])
except:
break
# Ensure the sampled list meets the required segment count
while len(sampled_frames) < num_segments:
sampled_frames.append(frames[last_frame_id])
return sampled_frames[:max_segments]
# Define embedder class for processing inputs and generating embeddings
class Qwen3VLEmbedder():
def __init__(
self,
model_name_or_path: str,
max_length: int = MAX_LENGTH,
min_pixels: int = MIN_PIXELS,
max_pixels: int = MAX_PIXELS,
total_pixels: int = MAX_TOTAL_PIXELS,
fps: float = FPS,
num_frames: int = MAX_FRAMES,
max_frames: int = MAX_FRAMES,
default_instruction: str = "Represent the user's input.",
**kwargs
):
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.max_length = max_length
self.min_pixels = min_pixels
self.max_pixels = max_pixels
self.total_pixels = total_pixels
self.fps = fps
self.num_frames = num_frames
self.max_frames = max_frames
self.default_instruction = default_instruction
self.model = Qwen3VLForEmbedding.from_pretrained(
model_name_or_path, trust_remote_code=True, **kwargs
).to(device)
self.processor = Qwen3VLProcessor.from_pretrained(
model_name_or_path, padding_side='right'
)
self.model.eval()
@torch.no_grad()
def forward(self, inputs: Dict[str, Any]) -> Dict[str, torch.Tensor]:
outputs = self.model(**inputs)
return {
'last_hidden_state': outputs.last_hidden_state,
'attention_mask': inputs.get('attention_mask')
}
# Truncate token sequence to a specified max length
def _truncate_tokens(self, token_ids: List[int], max_length: int) -> List[int]:
if len(token_ids) <= max_length:
return token_ids
special_token_ids = set(self.processor.tokenizer.all_special_ids)
num_special = sum(1 for token_idx in token_ids if token_idx in special_token_ids)
num_non_special_to_keep = max_length - num_special
final_token_ids = []
non_special_kept_count = 0
# Ensure retention of special tokens while truncating the rest
for token_idx in token_ids:
if token_idx in special_token_ids:
final_token_ids.append(token_idx)
elif non_special_kept_count < num_non_special_to_keep:
final_token_ids.append(token_idx)
non_special_kept_count += 1
return final_token_ids
# Format input based on provided text, image, video, and instruction
def format_model_input(
self, text: Optional[str] = None,
image: Optional[Union[str, Image.Image]] = None,
video: Optional[Union[str, List[Union[str, Image.Image]]]] = None,
instruction: Optional[str] = None,
fps: Optional[float] = None,
max_frames: Optional[int] = None
) -> List[Dict]:
# Ensure instruction ends with punctuation
if instruction:
instruction = instruction.strip()
if instruction and not unicodedata.category(instruction[-1]).startswith('P'):
instruction = instruction + '.'
# Initialize conversation with system prompts
content = []
conversation = [
{"role": "system", "content": [{"type": "text", "text": instruction or self.default_instruction}]},
{"role": "user", "content": content}
]
# Add text, image, or video content to conversation
if not text and not image and not video:
content.append({'type': 'text', 'text': "NULL"})
return conversation
if video:
video_content = None
video_kwargs = { 'total_pixels': self.total_pixels }
if isinstance(video, list):
video_content = video
if self.num_frames is not None or self.max_frames is not None:
video_content = sample_frames(video_content, self.num_frames, self.max_frames)
video_content = [
('file://' + ele if isinstance(ele, str) else ele)
for ele in video_content
]
elif isinstance(video, str):
video_content = video if video.startswith(('http://', 'https://')) else 'file://' + video
video_kwargs = {'fps': fps or self.fps, 'max_frames': max_frames or self.max_frames,}
else:
raise TypeError(f"Unrecognized video type: {type(video)}")
# Add video input details to content
if video_content:
content.append({
'type': 'video', 'video': video_content,
**video_kwargs
})
if image:
image_content = None
if isinstance(image, Image.Image):
image_content = image
elif isinstance(image, str):
image_content = image if image.startswith(('http', 'oss')) else 'file://' + image
else:
raise TypeError(f"Unrecognized image type: {type(image)}")
# Add image input details to content
if image_content:
content.append({
'type': 'image', 'image': image_content,
"min_pixels": self.min_pixels,
"max_pixels": self.max_pixels
})
if text:
content.append({'type': 'text', 'text': text})
return conversation
# Preprocess input conversations for model consumption
def _preprocess_inputs(self, conversations: List[List[Dict]]) -> Dict[str, torch.Tensor]:
text = self.processor.apply_chat_template(
conversations, add_generation_prompt=True, tokenize=False
)
try:
images, video_inputs, video_kwargs = process_vision_info(
conversations, image_patch_size=16,
return_video_metadata=True, return_video_kwargs=True
)
except Exception as e:
logger.error(f"Error in processing vision info: {e}")
images = None
video_inputs = None
video_kwargs = {'do_sample_frames': False}
text = self.processor.apply_chat_template(
[{'role': 'user', 'content': [{'type': 'text', 'text': 'NULL'}]}],
add_generation_prompt=True, tokenize=False
)
if video_inputs is not None:
videos, video_metadata = zip(*video_inputs)
videos = list(videos)
video_metadata = list(video_metadata)
else:
videos, video_metadata = None, None
inputs = self.processor(
text=text, images=images, videos=videos, video_metadata=video_metadata, truncation=True,
max_length=self.max_length, padding=True, do_resize=False, return_tensors='pt',
**video_kwargs
)
return inputs
# Pool the last hidden state by attention mask for embeddings
@staticmethod
def _pooling_last(hidden_state: torch.Tensor, attention_mask: torch.Tensor) -> torch.Tensor:
flipped_tensor = attention_mask.flip(dims=[1])
last_one_positions = flipped_tensor.argmax(dim=1)
col = attention_mask.shape[1] - last_one_positions - 1
row = torch.arange(hidden_state.shape[0], device=hidden_state.device)
return hidden_state[row, col]
# Process inputs to generate normalized embeddings
def process(self, inputs: List[Dict[str, Any]], normalize: bool = True) -> tuple:
conversations = [self.format_model_input(
text=ele.get('text'),
image=ele.get('image'),
video=ele.get('video'),
instruction=ele.get('instruction'),
fps=ele.get('fps'),
max_frames=ele.get('max_frames')
) for ele in inputs]
processed_inputs = self._preprocess_inputs(conversations)
processed_inputs = {k: v.to(self.model.device) for k, v in processed_inputs.items()}
outputs = self.forward(processed_inputs)
embeddings = self._pooling_last(outputs['last_hidden_state'], outputs['attention_mask'])
# Normalize the embeddings if specified
if normalize:
embeddings = F.normalize(embeddings, p=2, dim=-1)
return embeddings

31
special_tokens_map.json Normal file
View File

@@ -0,0 +1,31 @@
{
"additional_special_tokens": [
"<|im_start|>",
"<|im_end|>",
"<|object_ref_start|>",
"<|object_ref_end|>",
"<|box_start|>",
"<|box_end|>",
"<|quad_start|>",
"<|quad_end|>",
"<|vision_start|>",
"<|vision_end|>",
"<|vision_pad|>",
"<|image_pad|>",
"<|video_pad|>"
],
"eos_token": {
"content": "<|im_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
},
"pad_token": {
"content": "<|endoftext|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false
}
}

3
tokenizer.json Normal file
View File

@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:def76fb086971c7867b829c23a26261e38d9d74e02139253b38aeb9df8b4b50a
size 11423705

239
tokenizer_config.json Normal file
View File

@@ -0,0 +1,239 @@
{
"add_bos_token": false,
"add_prefix_space": false,
"added_tokens_decoder": {
"151643": {
"content": "<|endoftext|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151644": {
"content": "<|im_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151645": {
"content": "<|im_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151646": {
"content": "<|object_ref_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151647": {
"content": "<|object_ref_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151648": {
"content": "<|box_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151649": {
"content": "<|box_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151650": {
"content": "<|quad_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151651": {
"content": "<|quad_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151652": {
"content": "<|vision_start|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151653": {
"content": "<|vision_end|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151654": {
"content": "<|vision_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151655": {
"content": "<|image_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151656": {
"content": "<|video_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": true
},
"151657": {
"content": "<tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151658": {
"content": "</tool_call>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151659": {
"content": "<|fim_prefix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151660": {
"content": "<|fim_middle|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151661": {
"content": "<|fim_suffix|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151662": {
"content": "<|fim_pad|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151663": {
"content": "<|repo_name|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151664": {
"content": "<|file_sep|>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151665": {
"content": "<tool_response>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151666": {
"content": "</tool_response>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151667": {
"content": "<think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
},
"151668": {
"content": "</think>",
"lstrip": false,
"normalized": false,
"rstrip": false,
"single_word": false,
"special": false
}
},
"additional_special_tokens": [
"<|im_start|>",
"<|im_end|>",
"<|object_ref_start|>",
"<|object_ref_end|>",
"<|box_start|>",
"<|box_end|>",
"<|quad_start|>",
"<|quad_end|>",
"<|vision_start|>",
"<|vision_end|>",
"<|vision_pad|>",
"<|image_pad|>",
"<|video_pad|>"
],
"bos_token": null,
"clean_up_tokenization_spaces": false,
"eos_token": "<|im_end|>",
"errors": "replace",
"extra_special_tokens": {},
"model_max_length": 262144,
"pad_token": "<|endoftext|>",
"split_special_tokens": false,
"tokenizer_class": "Qwen2Tokenizer",
"unk_token": null
}

View File

@@ -0,0 +1,41 @@
{
"crop_size": null,
"data_format": "channels_first",
"default_to_square": true,
"device": null,
"do_center_crop": null,
"do_convert_rgb": true,
"do_normalize": true,
"do_rescale": true,
"do_resize": true,
"do_sample_frames": true,
"fps": 2,
"image_mean": [
0.5,
0.5,
0.5
],
"image_std": [
0.5,
0.5,
0.5
],
"input_data_format": null,
"max_frames": 768,
"merge_size": 2,
"min_frames": 4,
"num_frames": null,
"pad_size": null,
"patch_size": 16,
"processor_class": "Qwen3VLProcessor",
"resample": 3,
"rescale_factor": 0.00392156862745098,
"return_metadata": false,
"size": {
"longest_edge": 25165824,
"shortest_edge": 4096
},
"temporal_patch_size": 2,
"video_metadata": null,
"video_processor_type": "Qwen3VLVideoProcessor"
}

1
vocab.json Normal file

File diff suppressed because one or more lines are too long