Skip to content

fix(model): rewrite seq_cls architectures to *ForSequenceClassification - #10063

Open
RerankerGuo wants to merge 1 commit into
modelscope:mainfrom
RerankerGuo:fix/seq-cls-architectures
Open

fix(model): rewrite seq_cls architectures to *ForSequenceClassification#10063
RerankerGuo wants to merge 1 commit into
modelscope:mainfrom
RerankerGuo:fix/seq-cls-architectures

Conversation

@RerankerGuo

Copy link
Copy Markdown
Contributor

Summary

Fixes #9704.

When fine-tuning a VLM or LM with task_type=seq_cls (or reranker), the seq_cls patcher in _patch_sequence_classification monkey-patches a score head onto the generation model class without swapping the class itself. transformers.PreTrainedModel.save_pretrained then writes model.__class__.__name__ into config.json['architectures'], so the on-disk checkpoint advertises e.g. Qwen3VLForConditionalGeneration while shipping a score head, num_labels, id2label, and problem_type=multi_label_classification.

Downstream vLLM deployment reads architectures to pick the model class, finds the generation class, and rejects the checkpoint — even though PtEngine inference works correctly. The reporter in #9704 confirmed all four of score.weight: [20, 4096], id2label, label2id, and problem_type are written correctly; only architectures is wrong.

Fix

Rewrite model.config.architectures in-place to the matching *ForSequenceClassification class at the same place the score head is attached, so every save path (trainer _save_model, save_checkpoint, export-quant, peft merge-and-unload) writes the right value automatically. The existing vllm_engine.py:347-353 arch_mapping override is left in place — it remains useful for users who already have bad checkpoints on disk.

The new helper _seq_cls_architectures handles the suffix rewrite idempotently and leaves unknown / custom architectures untouched (so we never advertise a class that does not exist in transformers).

Mapping coverage

Source class Rewritten to
*ForConditionalGeneration (Qwen3-VL, Qwen2-VL, InternVL, LLaVA, GLM4V, …) *ForSequenceClassification
*ForCausalLM (Qwen2, Llama, Mistral, Mixtral, Yi, …) *ForSequenceClassification
Already *ForSequenceClassification unchanged (idempotent)
Custom / unknown suffix unchanged
[] / None unchanged

Tests

Added TestSeqClsArchitecturesRewrite in tests/general/test_model.py covering:

  • generation → seq_cls rewrite for both ForCausalLM and ForConditionalGeneration suffixes
  • idempotence on already-seq_cls classes
  • multi-arch lists
  • empty / None inputs
  • unknown / custom architectures left untouched

Local sanity checks (10/10 PASS) and syntax checks on both files PASS. Full ms-swift unit / smoke CI is needed to validate end-to-end on real seq_cls training runs; that will run on the upstream CI after this PR is opened.

Out of scope

  • Megatron seq_cls architectures rewrite — Megatron has its own seq_cls head code path (swift/megatron/utils/convert_utils.py:283, swift/megatron/model/utils.py:47); happy to follow up with a separate PR if maintainers want it.
  • vllm_engine.py:347-353 arch_mapping cleanup — kept for backward-compat with existing bad checkpoints.

🤖 Generated with Claude Code

…on (modelscope#9704)

When fine-tuning a VLM or LM with task_type=seq_cls (or reranker),
the seq_cls patcher monkey-patches a 'score' head onto the generation
model class without swapping the class itself. transformers'
PreTrainedModel.save_pretrained writes 'model.__class__.__name__'
into config.json['architectures'], so the on-disk checkpoint
advertises the generation architecture (e.g. Qwen3VLForConditionalGeneration)
while shipping a score head, num_labels, id2label, and problem_type.

Downstream vLLM deployment reads architectures to pick the model
class, finds the generation class, and rejects the checkpoint —
even though inference through PtEngine works correctly. The fix
rewrites model.config.architectures in-place to the matching
*ForSequenceClassification class at the same place the score head
is attached, so every save path (trainer, save_checkpoint, export,
peft merge) writes the right value.

A new helper _seq_cls_architectures() handles the suffix rewrite
idempotently and leaves unknown/custom architectures untouched.
Unit tests cover the rewrite, idempotence, multi-arch lists, and
empty/None inputs.

Refs: modelscope#9704
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Qwen3-VL 多标签序列分类(seq_cls, num_labels>2, multi_label_classification)训练后无法用 vLLM 部署

1 participant