[AutoTP] Support frozen AutoTP models and manage AutoTP mode per model (#8851)
## Summary
Two bugs show up when a frozen teacher runs next to an AutoTP student in
one process, as in on-policy distillation
(deepspeedai/DeepSpeedExamples#1019, #8850).
**1. Frozen replicated parameters break `deepspeed.initialize`.**
`register_replicated_grad_hooks` registers a gradient all-reduce hook on
every replicated (non-TP) parameter. PyTorch refuses hooks on tensors
with `requires_grad=False`, so a fully or partially frozen model fails
during `initialize`.
- Frozen params are now skipped. They get no gradient, so their TP
replicas cannot diverge.
- To keep that guarantee, the engine remembers which replicated params
were frozen at `initialize`. If any of them becomes trainable later, the
next backward raises a clear error instead of letting TP replicas drift
apart silently.
**2. The AutoTP training mode leaks into `init_inference`.**
`deepspeed.initialize` with AutoTP sets a process-global training-mode
flag. A teacher passed to `deepspeed.init_inference` afterwards still
sees training mode. As a result:
- it is sharded with the student's TP group instead of its own
`tp_size`;
- its LM head is not handled as for inference;
- its parameters are left trainable.
The fix makes the mode per model instead of process-wide:
- `AutoTPMeta`, which AutoTP already passes to every layer it builds,
gains a `training` field. AutoTP fills it from its `training_mode`
argument, which `deepspeed.initialize` sets to `True` and
`init_inference` to `False`.
- Each `TensorParallel_Layer` takes its mode from `tp_meta`, so the
forward path (`RowParallel`, `add_bias`) follows how that model was
sharded. The student keeps training-mode behavior after the teacher is
built, and vice versa.
- `AutoTP.set_tensor_parallel_config` and `replace_transformer_layer`
(kernel injection, `set_lm_head`, MP checkpoint saving) use the explicit
`training_mode` instead of the global flag.
- Backward compatibility: `AutoTP(training_mode=None)` (the new default)
and layers built without `tp_meta` still follow `set_autotp_mode`, so
existing direct callers behave as before.
## Related work
I searched open and closed PRs and issues for
`register_replicated_grad_hooks`, `AUTOTP_MODE` and frozen AutoTP
models, and found no existing fix. Reported while integrating #1019.
Sibling paths checked:
- Every `is_autotp_training_mode()` reader in `deepspeed/`: `auto_tp.py`
(`set_tensor_parallel_config`) and `replace_module.py` (kernel
injection, `set_lm_head`, MP checkpoint saving) now use the explicit
mode; the remaining reads are the fallbacks for callers that pass no
mode.
- All `AutoTP(...)` call sites in `deepspeed/` (engine,
`TpTrainingManager`, `InferenceEngine`) already pass `training_mode`
explicitly. No code in `deepspeed/` builds TP layers outside AutoTP.
## Tests
New tests in `tests/unit/v1/autotp/test_tp_plan_e2e.py`. Each fails
without the corresponding fix and passes with it.
- `test_frozen_qwen3_initializes_and_runs`: 2 ranks; the frozen model's
outputs match the unsharded reference.
- `test_unfreezing_replicated_param_after_initialize_raises`: 2 ranks.
- `TestInferenceTeacherAfterTrainingStudent`: 4 ranks. A student with
TP=2 is trained, then a teacher is built with
`init_inference(tp_size=4)` and checked to be sharded 4 ways and frozen.
Results at head `68647c93e`:
| Tests | Result |
|---|---|
| `tests/unit/v1/autotp/test_tp_plan_e2e.py` | 8 passed |
| `tests/unit/module_inject` + the other `tests/unit/v1/autotp` files |
171 passed, 37 skipped |
| `tests/unit/v1/autotp/test_autotp_training.py` (skipped on XPU by
`skip_on_device`; run locally with that skip removed) | 42 passed, 2
skipped (bf16 overflow skip) |
| `tests/unit/v1/autotp/test_autotp_training.py` with
`DS_ACCELERATOR=cpu` | 11 passed, 33 skipped |
| `test_autoep_autotp_sharded_attention.py`,
`test_autoep_autotp_runtime.py`, `test_per_head_muon_accelerator.py`,
`test_checkpoint_sharding.py` (`-k "not True"`) | 44 passed, 4 skipped,
3 failed |
The 3 failures
(`TestTiedOutputHead::test_row_parallel_tied_head_is_refused[False]` and
two `test_per_head_on_a_shard_is_the_shard_of_per_head[2-*]`) fail the
same way on master `a4811fbf9`. The `zero_init=True` cases of
`test_autoep_autotp_sharded_attention.py` were deselected because
`test_grads_and_norm_match_reference[3-True-4-2-2]` hangs on master as
well.
Hardware and software:
- 8× Intel Arc Pro B70 (2/4/8 ranks depending on the test)
- Level Zero driver 1.15.39122 (compute runtime 26.27.39122.11)
- torch 2.14.0.dev20260804+xpu, transformers 5.17.0
## Compatibility matrix
The changes are mode bookkeeping and hook registration; no numerics
change on the existing training path.
| | Validated | Not validated |
|---|---|---|
| ZeRO | New tests: stage 0. Existing AutoTP suite: stages 0/1/2. |
Frozen model with ZeRO-3 + AutoTP |
| Precision | New tests: fp32. Existing suite: fp16/bf16. | |
| Offload | | Not exercised |
| Parallelism | TP=2 and TP=4; student TP=2 with teacher TP=4 | DP>1
together with a frozen teacher |
| Accelerator | XPU | CUDA, HPU |
## AI assistance
This PR was developed with AI assistance (GitHub Copilot CLI). I
reviewed every changed line and ran the tests above.
cc @0z5a I try to fix the bug you described in Vocab-parallel pipeline
with OPSD.
@delock And address AutoTP_MODE issue, I think OPSD will reach the issue
soon.
---------
Signed-off-by: Jin, Youzhi <youzhi.jin@intel.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ma, Guokai <guokai.ma@gmail.com>