Repository navigation
[OMNIML-6060] [1/3] Add require_transformers, and build a VLM without its LM head #3131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
87b96f0
32d3b1d
e0b7b17
158ec34
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -924,18 +924,22 @@ def parallel_load_and_prepare_fsdp2( | |
| return model | ||
|
|
||
|
|
||
| def _auto_class(hf_config, trust_remote_code: bool = False) -> Any: | ||
| """The auto class that builds ``hf_config``'s whole model, LM head included. | ||
|
|
||
| A causal LM builds through ``AutoModelForCausalLM``. A VLM -- a nested text config, a vision | ||
| config, or a ``*ForConditionalGeneration`` architecture -- builds through the remote | ||
| ``AutoModelForCausalLM`` when the checkpoint registers its ``*ForConditionalGeneration`` there, | ||
| its code is trusted and it registers no image-text class, else through | ||
| def _auto_class(hf_config, require_lm_head: bool = True, trust_remote_code: bool = False) -> Any: | ||
| """The auto class that builds ``hf_config``'s whole model. | ||
|
|
||
| A causal LM builds through ``AutoModelForCausalLM``, head included: ``require_lm_head`` only | ||
| applies to a model whose architecture reads as a VLM's -- a nested text config, a vision | ||
| config, or a ``*ForConditionalGeneration`` architecture -- or that is an encoder-decoder model | ||
| (``is_encoder_decoder``), whether or not it names its architecture. Without ``require_lm_head`` | ||
| such a model builds through ``AutoModel``, its base model, when that can build it (the config | ||
| is a native one, or a trusted remote one registering ``AutoModel``): most base models leave the | ||
| head out, since it is not required, though some keep it (Llama4's). Otherwise it builds through | ||
| the remote ``AutoModelForCausalLM`` when the checkpoint registers its | ||
| ``*ForConditionalGeneration`` there, its code is trusted and it registers no image-text class, | ||
| else through its seq2seq class if it is an encoder-decoder model, else through | ||
| ``AutoModelForImageTextToText`` if that recognizes the config or, its code trusted, the | ||
| checkpoint registers one, else through ``AutoModelForCausalLM`` if that recognizes it (as it | ||
| does Phi-4-multimodal), else through ``AutoModel``, which leaves the head out. An | ||
| encoder-decoder model (``is_encoder_decoder``, whether or not it names its architecture, which | ||
| reads like a VLM's) builds through its seq2seq class. | ||
| does Phi-4-multimodal), else still through ``AutoModel``. | ||
| """ | ||
| architectures = getattr(hf_config, "architectures", None) or [] | ||
| is_vlm = ( | ||
|
|
@@ -946,6 +950,11 @@ def _auto_class(hf_config, trust_remote_code: bool = False) -> Any: | |
| if not is_vlm and not getattr(hf_config, "is_encoder_decoder", False): | ||
| return AutoModelForCausalLM | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SUGGESTION] Two ways to close it, either is fine:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping the behaviour and the name. The docstring now states the contract (8e32f5e).
So |
||
| auto_map = getattr(hf_config, "auto_map", None) or {} | ||
| if not require_lm_head and ( | ||
| type(hf_config) in AutoModel._model_mapping | ||
| or (trust_remote_code and "AutoModel" in auto_map) | ||
| ): | ||
| return AutoModel | ||
|
coderabbitai[bot] marked this conversation as resolved.
Resolved
Comment on lines
+953
to
+957
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [SUGGESTION] The
The two disagree for a remote-code checkpoint whose config class name collides with a native one — common for a model shipped with remote code before (or alongside) its upstreaming, e.g. a vendored Two reasons this is a SUGGESTION and not a blocker: the same name-based idiom already backs the pre-existing branches below (lines 967, 971), so it is the file's established convention rather than something this PR introduces; and switching to (Noting for completeness, not as a separate finding: CodeRabbit's open point on
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keeping the name-based check, for the reasons you give: the branches below use the same idiom, and matching by identity would import every registered config module. The corner you describe ends in a refusal with a vaguer message, not a wrong load, since |
||
| remote_with_head = ( | ||
| trust_remote_code | ||
| and "conditionalgeneration" in str(auto_map.get("AutoModelForCausalLM", "")).lower() | ||
|
|
@@ -1027,7 +1036,7 @@ def _decide_distribution( | |
| trust_remote_code, | ||
| None, | ||
| hf_config, | ||
| auto_class=_auto_class(hf_config, trust_remote_code), | ||
| auto_class=_auto_class(hf_config, trust_remote_code=trust_remote_code), | ||
| ) | ||
| # init_empty_weights re-wraps every parameter it registers, which unties what from_config | ||
| # tied: re-tie, so a tied embedding and LM head count once, as the loaded model holds them. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[SUGGESTION] A pre-release of the floor release is hard-refused. Under PEP 440,
5.7normalizes to5.7.0andparse("5.7.0.dev0") < parse("5.7.0"), so a transformers install frommainreporting5.7.0.dev0(or anrc) is refused against a"5.7"floor even though it carries the API the floor exists for. Since new transformers MoE/EP APIs land onmainfirst, that is a likely configuration for exactly the users this gate targets — and unlike the existing gate inmodelopt/torch/__init__.py:51, which only warns, this one raises and blocks the load outright.Comparing base versions accepts a dev build of the floor release while still refusing
5.6.2:If refusing pre-releases is deliberate (a
5.7.0.dev0predating the feature is possible), say so in the docstring so the next reader doesn't "fix" it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kept the refusal, and the docstring now says it is deliberate (2a046d6).
A pre-release of the floor release can predate the feature the floor stands for. Comparing base versions would let such a build through and fail later, inside the load. Every floor the stack checks (5.7, 5.8, 5.13) is a published release, and transformers main (5.21.0.dev0) is above all of them, so current main builds pass.