Skip to content

Fix tie_word_embeddings not lifted from text_config for some VLM configs (BC regression) - #45857

Open
qgallouedec wants to merge 1 commit into
mainfrom
fix-vlm-tie-word-embeddings-bc
Open

Fix tie_word_embeddings not lifted from text_config for some VLM configs (BC regression)#45857
qgallouedec wants to merge 1 commit into
mainfrom
fix-vlm-tie-word-embeddings-bc

Conversation

@qgallouedec

@qgallouedec qgallouedec commented May 9, 2026

Copy link
Copy Markdown
Member

For Qwen2_5_VLConfig, Qwen2VLConfig, Glm4vConfig, and Glm4vMoeConfig, pre-v5 saves placed tie_word_embeddings inside text_config (and dropped it from the root) because the inner text class declared the field. v5 moved the field to the outer config (#42420) but __post_init__ doesn't lift the value from text_config. The outer attribute therefore falls through to its dataclass default (False), get_expanded_tied_weights_keys returns {}, no tying happens, lm_head.weight is reported MISSING and randomly initialized — the model loads silently broken.

Regression introduced in #41541, which removed the forwarding line kwargs["tie_word_embeddings"] = self.text_config.tie_word_embeddings originally added in #42420 with a FIXME: tying has to be used from the text config comment. The vestigial tie_word_embeddings: bool = False field was later removed from the inner text classes in #44976.

This patch mirrors the existing fix in LlavaConfig.__post_init__: when text_config arrives as a dict (the deserialization path), forward tie_word_embeddings to the outer when the root is the default and the dict has a truthy value.

Reproducer

# transformers v5 (e.g. main / 5.8.0.dev0)
from transformers import Qwen2_5_VLForConditionalGeneration

m = Qwen2_5_VLForConditionalGeneration.from_pretrained("trl-internal-testing/tiny-Qwen2_5_VLForConditionalGeneration")
print("outer tie_word_embeddings:", m.config.tie_word_embeddings)
print("lm_head tied:", m.lm_head.weight.data_ptr() == m.model.language_model.embed_tokens.weight.data_ptr())

Before this PR (loud LOAD REPORT, broken model):

Qwen2_5_VLForConditionalGeneration LOAD REPORT from: trl-internal-testing/tiny-Qwen2_5_VLForConditionalGeneration
Key            | Status  |
---------------+---------+
lm_head.weight | MISSING |
- MISSING: those params were newly initialized because missing from the checkpoint.

outer tie_word_embeddings: False
lm_head tied: False

After this PR:

outer tie_word_embeddings: True
lm_head tied: True

(no LOAD REPORT printed)

The Hub artifact above was saved by transformers 4.57.5; under 4.x its __post_init__ still propagated tie_word_embeddings from text → root, so the bug is v5-only and only triggers on configs saved by 4.x (the vast majority of pre-existing Hub repos for these architectures).

Affected configs

Empirically determined by saving each composite VLM config under transformers 4.56.2 with tie_word_embeddings=True and inspecting the resulting config.json:

config root text_config needs lift in v5?
qwen2_5_vl <absent> True yes
qwen2_vl <absent> True yes
glm4v <absent> True yes
glm4v_moe <absent> True yes
got_ocr2 <absent> True no — outer class default is True already
llava{,_next,_next_video,_onevision} True <absent> no — already saved at root or already has the lift
idefics3, smolvlm, llama4, pix2struct, fuyu True no

Files touched: qwen2_5_vl, qwen2_vl, glm4v (configuration + modular), glm4v_moe.

@qgallouedec
qgallouedec requested review from vasqu and zucchini-nlp May 9, 2026 04:17
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: glm4v, glm4v_moe, qwen2_5_vl, qwen2_vl

@qgallouedec
qgallouedec requested a review from ArthurZucker May 9, 2026 04:18
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh interesting, I suppose testing models tie weights unlike official releases

We can fix it in case there are more models like that on the hub (which i am not really sure), but we have to check text config after it is fully initialized imo

Comment on lines +176 to 183
# BC: pre-v5 saves placed `tie_word_embeddings` inside text_config. Forward it to the
# outer config (where v5's tying logic looks) when the root value is the default.
if not self.tie_word_embeddings and self.text_config.get("tie_word_embeddings"):
self.tie_word_embeddings = self.text_config["tie_word_embeddings"]
self.text_config = self.sub_configs["text_config"](**self.text_config)
elif self.text_config is None:
self.text_config = self.sub_configs["text_config"](**kwargs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wont work if text config is already a PreTrainedConfig

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.

4 participants