[Model] Support Qwen3ForSequenceClassification for Qwen3-Embed Model#7957
[Model] Support Qwen3ForSequenceClassification for Qwen3-Embed Model#7957zhyncs merged 11 commits intosgl-project:mainfrom
Conversation
There was a problem hiding this comment.
Summary of Changes
Hello @nysa-liu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly expands the model capabilities by integrating the Qwen3-embed series for sequence classification. It provides a dedicated implementation for this new model type and ensures it's properly documented, allowing users to leverage Qwen3 for embedding-based classification tasks.
Highlights
- New Model Support: I've identified that this pull request introduces support for the newly released Qwen3-embed series models, specifically enabling their use for sequence classification tasks.
- Dedicated Classification Class: A new Python file,
qwen3_classification.py, has been added to defineQwen3ForSequenceClassification. This class is built upon the existing Qwen3 script andQwen2ForSequenceClassificationpatterns, handling the model's forward pass, scoring, and pooling for embedding output. - Documentation Update: The
docs/supported_models/embedding_models.mdfile has been updated to include Qwen3-embed in the list of officially supported embedding models, providing an example HuggingFace identifier and a brief description. - Specialized Weight Loading: The
Qwen3ForSequenceClassificationincludes a customload_weightsmethod that specifically filters outlm_headweights, which is crucial for models primarily used for embedding rather than causal language modeling.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
The PR adds support for Qwen3-embed models by implementing Qwen3ForSequenceClassification. The review identifies a critical bug in weight loading for the new model class and suggests a fix. It also points out a documentation ambiguity.
| def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]): | ||
| # Filter out lm_head weights of Qwen3ForCausalLM | ||
| filtered_weights = [ | ||
| (name, w) for name, w in weights if not name.startswith("lm_head") | ||
| ] | ||
| return Qwen3ForCausalLM.load_weights(self, filtered_weights) |
There was a problem hiding this comment.
The current implementation of load_weights reuses Qwen3ForCausalLM.load_weights. However, the weight loading logic in Qwen3ForCausalLM has a special case for "Embedding" models that prefixes all weights with model.. This is incorrect for the score layer in Qwen3ForSequenceClassification, which is a top-level attribute, not part of the model submodule. As a result, the weights for the classification head will fail to load, leading to a model that produces incorrect outputs.
To fix this, implement a custom load_weights implementation that correctly handles the weight paths for the score layer.
def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]):
# HACK: Temporarily modify config to avoid incorrect prefixing in Qwen3ForCausalLM.load_weights
# for the 'score' layer, which is not under the 'model' submodule.
original_name_or_path = self.config.name_or_path
try:
self.config.name_or_path = "" # Disable the "Embedding" check in the reused loader
prefixed_weights = []
for name, w in weights:
if name.startswith("lm_head"):
continue
if not name.startswith("score."):
prefixed_weights.append((f"model.{name}", w))
else:
prefixed_weights.append((name, w))
return Qwen3ForCausalLM.load_weights(self, prefixed_weights)
finally:
# Restore config
self.config.name_or_path = original_name_or_path| | ------------------------------------------ | -------------------------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | **Llama/Mistral based (E5EmbeddingModel)** | `intfloat/e5-mistral-7b-instruct` | N/A | Mistral/Llama-based embedding model fine‑tuned for high‑quality text embeddings (top‑ranked on the MTEB benchmark). | | ||
| | **GTE (QwenEmbeddingModel)** | `Alibaba-NLP/gte-Qwen2-7B-instruct` | N/A | Alibaba’s general text embedding model (7B), achieving state‑of‑the‑art multilingual performance in English and Chinese. | | ||
| | **Qwen3 (QwenEmbeddingModel)** | `Qwen/Qwen3-Embedding-4B` | N/A | Qwen3-based text embedding model (4B), compatible with QwenEmbeddingModel interface; designed for accurate and efficient semantic representation. | |
There was a problem hiding this comment.
The model class in parenthesis (QwenEmbeddingModel) and the description "compatible with QwenEmbeddingModel interface" may be misleading for the new Qwen3 model. The actual class implemented is Qwen3ForSequenceClassification. To improve clarity for users, update the model class and description to accurately reflect the new implementation.
| | **Qwen3 (QwenEmbeddingModel)** | `Qwen/Qwen3-Embedding-4B` | N/A | Qwen3-based text embedding model (4B), compatible with QwenEmbeddingModel interface; designed for accurate and efficient semantic representation. | | |
| | **Qwen3 (Qwen3ForSequenceClassification)** | `Qwen/Qwen3-Embedding-4B` | N/A | Qwen3-based text embedding model (4B), using `Qwen3ForSequenceClassification`; designed for accurate and efficient semantic representation. | |
| config, quant_config=quant_config, prefix=add_prefix("model", prefix) | ||
| ) | ||
| self.score = nn.Linear(config.hidden_size, config.num_labels) | ||
| # Use normalize=True for qwen3 embedding based on official implementation |
There was a problem hiding this comment.
could you give a reference here?
There was a problem hiding this comment.
For the reference request on lines 38-44:
Thank you for the feedback. I've added a specific reference to the official Qwen3-Embedding implementation:
# Use normalize=True for qwen3 embedding based on official implementation
# Reference: https://github.com/QwenLM/Qwen3-Embedding/blob/main/examples/qwen3_embedding_transformers.py#L55
# Official code: output = F.normalize(output, p=2, dim=1)This references the official transformers implementation where they use F.normalize(output, p=2, dim=1) for L2 normalization, which corresponds to our normalize=True setting in the pooler.
There was a problem hiding this comment.
It seems this logic depends on whether we use embedding model or rerank model. This logic is right for embedding model, it looks like in rerank model it uses softmax in pooler, could we change pooler normalize or softmax according to model architectures?
|
|
||
| EntryClass = [ | ||
| Qwen3ForSequenceClassification, | ||
| ] No newline at end of file |
There was a problem hiding this comment.
could we add ut in test_embedding_models.py as Qwen2ForSequenceClassification does?
There was a problem hiding this comment.
For adding Qwen3ForSequenceClassification to is_generation_model unit tests:
I've made the following changes:
- Added unit tests: Added
("Qwen/Qwen3-Embedding-8B", 1, 1e-5)to the MODELS list intest/srt/models/test_embedding_models.py, following the same pattern as Qwen2ForSequenceClassification models.
| | **Qwen3 (QwenEmbeddingModel)** | `Qwen/Qwen3-Embedding-4B` | N/A | Qwen3-based text embedding model (4B), compatible with QwenEmbeddingModel interface; designed for accurate and efficient semantic representation. | | ||
| | **GME (MultimodalEmbedModel)** | `Alibaba-NLP/gme-Qwen2-VL-2B-Instruct` | `gme-qwen2-vl` | Multimodal embedding model (2B) based on Qwen2‑VL, encoding image + text into a unified vector space for cross‑modal retrieval. | | ||
| | **CLIP (CLIPEmbeddingModel)** | `openai/clip-vit-large-patch14-336` | N/A | OpenAI’s CLIP model (ViT‑L/14) for embedding images (and text) into a joint latent space; widely used for image similarity search. | | ||
| | **BGE (BgeEmbeddingModel)** | `BAAI/bge-large-en-v1.5` | N/A | Currently only support `attention-backend` `triton` and `torch_native`. BAAI's BGE embedding models optimized for retrieval and reranking tasks. | |
There was a problem hiding this comment.
could you provide more details about how to use Qwen3ForSequenceClassification as vllm-project/vllm#19260 does?
…eClassification - Added an inline reference to the official Qwen3-Embedding implementation using F.normalize() for L2 normalization. - Extended is_generation_model logic to treat Qwen3ForSequenceClassification as a non-generative model. - Added unit test for Qwen3-Reranker-0.6B-seq-cls to test_embedding_models.py to verify classification compatibility. This aligns Qwen3 support with existing Qwen2 sequence classification logic and improves clarity on normalization behavior.
|
Thanks for the feedback. I've updated the implementation accordingly. Please help take another look 🙏 @yizhang2077 |
|
Sorry for late reply. Instead of changing other model's descriptions in doc, I think we only need add Qwen3-Embedding part? |
|
@nysa-liu is it a dense mode ? |
|
Hey @nysa-liu, |
Motivation
Support the newly released Qwen3-embed series models by extending the classification capabilities of Qwen3.
To this end, I implemented
Qwen3ForSequenceClassificationbased on the existing Qwen3 script andQwen2ForSequenceClassification. This enables users to perform sequence classification tasks with Qwen3-embed models.Modifications
qwen3_classification.pyto defineQwen3ForSequenceClassification.docs/supported_models/embedding_models.mdto include relevant documentation for Qwen3-embed classification support.Checklist