Skip to content

Honor per-param _is_hf_initialized flags for built-in models in _initialize_weights - #48140

Open
hungnnvidia wants to merge 3 commits into
huggingface:mainfrom
hungnnvidia:fix/init-per-param-flag-builtin-models
Open

Honor per-param _is_hf_initialized flags for built-in models in _initialize_weights#48140
hungnnvidia wants to merge 3 commits into
huggingface:mainfrom
hungnnvidia:fix/init-per-param-flag-builtin-models

Conversation

@hungnnvidia

@hungnnvidia hungnnvidia commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

CI

What does this PR do?

Fixes #47427.

_initialize_weights only honored the per-parameter/buffer _is_hf_initialized flags when is_custom_code was True, so for built-in models the check was dead code.

Under FSDP/ZeRO on non-rank-0 processes, _initialize_missing_keys flags the loaded params/buffers (which are broadcast from rank 0) but does not set the flag on their enclosing submodules. The guarded torch.nn.init wrappers only protect torch.nn.init.* calls, not in-place tensor methods such as weight.data.normal_(), so _init_weights redundantly re-ran on every submodule. This is wasted work on every process and is extremely costly on accelerators where normal_ is far slower than on CUDA (the reporter measured ~224s wasted on 16x Ascend NPU).

This generalizes the per-param/buffer skip to all models. Modules with any unflagged param/buffer (missing keys, non-persistent buffers) are still initialized, so the standard loading path is numerically unchanged.

Tests

Added tests/utils/test_modeling_utils.py::ModelUtilsTest::test_initialize_weights_skips_module_with_all_params_flagged, which reproduces the non-rank-0 flag state and asserts _init_weights is skipped for built-in models (and still runs when a param is unflagged). It fails on main and passes with this change. Existing BERT and CLIP init/save-load tests still pass locally.

Before submitting

Who can review?

Anyone in the community is free to review. cc @Cyrilvallez

…nitialize_weights`

`_initialize_weights` skipped `_init_weights` for a module whose params and
buffers are all already flagged as initialized only when `is_custom_code` was
True. For built-in models the check was dead code.

Under FSDP/ZeRO on non-rank-0 processes, `_initialize_missing_keys` flags the
loaded params/buffers (which are broadcast from rank 0) but does not set the
flag on their enclosing submodules, so `_initialize_weights` re-ran
`_init_weights` on every submodule. The guarded `torch.nn.init` wrappers only
protect `torch.nn.init.*` calls, not in-place tensor methods such as
`weight.data.normal_()`, so this is wasted work on every process and is
extremely costly on accelerators where `normal_` is far slower than on CUDA
(e.g. Ascend NPU, ~1s vs ~1ms per large tensor).

Generalize the per-param/buffer check to all models. Modules with any unflagged
param/buffer (missing keys, non-persistent buffers) are still initialized, so
the standard loading path is unchanged.

Fixes huggingface#47427
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution 🤗!

CI Security Gate — automatic approval blocked

This PR was not automatically approved for CI because the security gate failed.

Possible reasons:

  • The PR touches 50 or more files — only PRs with fewer than 50 changed files are automatically approved
  • A changed file is outside the allowed directories (src/, tests/, docs/, utils/), has a disallowed extension (only .py, .txt, .md permitted outside tests/ and docs/), or is not .md/.yml inside docs/
  • A new high-severity security issue was detected in the changed Python files (Bandit check)

See the workflow run for the exact violations.

A maintainer can review and manually approve CI if a finding is a false positive.

The `test_unexpected_keys_warnings` expected output embedded a literal ESC
control byte in the ANSI color code. The security-gate workflow fetches
changed .py files via `gh api`, which aborts on raw terminal escape
sequences. Write it as the equivalent `\x1b` escape (identical runtime value).
@hungnnvidia hungnnvidia reopened this Aug 20, 2026
…regression)

Skipping `_init_weights` via a generalized per-param check in
`_initialize_weights` changed the standard `from_pretrained` init path and
broke `test_can_init_all_missing_weights` (e.g. BridgeTower `class_embedding`).

Revert `_initialize_weights` to its original form and instead complete the
existing intent of `_initialize_missing_keys`: on non-rank-0 FSDP/ZeRO
processes, propagate `_is_hf_initialized` to every submodule whose params and
buffers are all already flagged (new `_mark_fully_loaded_submodules_as_initialized`
helper), so `_init_weights` is skipped there without touching the standard path.
Modules with unflagged (e.g. non-persistent) buffers are still re-initialized.
@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 32342068689:2
Result: failure | Jobs: 16 | Tests: 182,071 | Failures: 1 | Duration: 14h 55m

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.

_is_hf_initialized per-param check doesn't skip redundant init for built-in models on non-CUDA hardware

1 participant