Enable GTP for heterogeneous MIMO training - #6249
Conversation
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
- Thread pg_collection from TransformerLayer through Attention/MLP into the TE and core linear layers. - Assert in finalize_model_grads when the config enables the axis but the group is missing, instead of treating it as "GTP inactive" and training on wrong gradients. - Add test_gtp_custom_pgs.py: two TransformerBlocks, identical degrees (TP=1, CP=1, GTP_remat=2 over world=4), identical weights and input, one from the MPU groups and one from a custom collection with permuted gtp_remat rank membership. Output and gradients must match; verified it fails without the plumbing. Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
hasattr() is always True on ProcessGroupCollection: __getattr__ returns None for
declared-but-unset fields. resolve_gtp_remat_group() gated its MPU fallback on
hasattr, so the fallback was unreachable.
* A collection omitting gtp_remat resolved to None -> weights silently built
UNSHARDED under GTP. Check `attr in vars(...)` instead.
* Add test_pg_collection_without_gtp_remat_falls_back_to_mpu. The existing test
always set gtp_remat explicitly, so it could not catch this.
Signed-off-by: Shiqing Fan <shiqingf@nvidia.com>
Signed-off-by: ykarnati <ykarnati@nvidia.com>
| | `test_gtp_fp8_param_gather.py` | Native-FP8 GTP_remat (§1.3): fp8-vs-BF16 loss parity (TP1/TP2, MoE), post-save-spike guard. | | ||
|
|
||
| The fp32-accumulation primitive itself is covered outside this suite, by `tests/unit_tests/distributed/test_reduce_scatter_with_fp32_accumulation.py`, which does not require GTP_remat. | ||
| | `test_gtp_custom_pgs.py` | `pg_collection` plumbing: a custom `gtp_remat` group (permuted ranks, same size) must give the same fwd/bwd results as the MPU groups — catches modules reading `parallel_state` instead of the collection passed to them. | |
|
|
||
| set -euo pipefail | ||
|
|
||
| export CUDA_DEVICE_MAX_CONNECTIONS=${CUDA_DEVICE_MAX_CONNECTIONS:-1} |
There was a problem hiding this comment.
why did we add this back ? not needed ?
| #!/bin/bash | ||
|
|
||
| # Run an eight-rank heterogeneous mock training loop with Nemotron6-MoE VLM 20L. | ||
| # Run heterogeneous mock training with the Nemotron6-MoE VLM 20L recipe. |
There was a problem hiding this comment.
no need to change this comment, avoid unnecessary edits
| EXPERT_TENSOR_PARALLEL_NUM_WEIGHT_SHARDS=${EXPERT_TENSOR_PARALLEL_NUM_WEIGHT_SHARDS:-${LLM_EXPT_TP}} | ||
|
|
||
| if ((TENSOR_PARALLEL_NUM_WEIGHT_SHARDS % LLM_TP != 0)); then | ||
| echo "TENSOR_PARALLEL_NUM_WEIGHT_SHARDS must be divisible by LLM_TP" >&2 |
There was a problem hiding this comment.
avoid these verbose checks in the sbatch script, also dont introduce new env variables if not necessary
| TORCHRUN_ARGS=( | ||
| --standalone | ||
| --nproc-per-node 8 | ||
| --nproc-per-node "${NPROC_PER_NODE}" |
There was a problem hiding this comment.
the diff should be minimal dont change unnecessary things
| --llm-tp 2 \ | ||
| --llm-cp 1 \ | ||
| --llm-pp 1 \ | ||
| --encoder-tp "${ENCODER_TP}" \ |
There was a problem hiding this comment.
we dont need env variables for this
| # so it yields llm_dp. The physical world incl. encoder ranks is restored below. | ||
| # Stock validation owns the derived training arguments. Give it the language module's | ||
| # parallel degrees and logical world so its DP/GTP accounting matches the explicit MIMO grid. | ||
| args.tensor_model_parallel_size = args.llm_tp |
There was a problem hiding this comment.
why did we newly introduce this args.tensor_model_parallel_size = args.llm_tp
args.pipeline_model_parallel_size = args.llm_pp
args.context_parallel_size = args.llm_cp
args.expert_model_parallel_size = args.llm_ep
args.expert_tensor_parallel_size = args.llm_expt_tp or 1
| embd_group = parallel_state.get_embedding_group(check_initialized=False) | ||
| pos_emb_group = parallel_state.get_position_embedding_group(check_initialized=False) | ||
| dp_cp_group = parallel_state.get_data_parallel_group(with_context_parallel=True) | ||
| gtp_remat_group = parallel_state.get_gtp_weight_remat_group(check_initialized=False) |
There was a problem hiding this comment.
is these changes already on main ?
| """Communication role for ranks in bridge communication. | ||
|
|
||
| SENDER: Leader tp-cp rank within each DP replica of source grid. | ||
| SENDER: Leader tp-cp rank within each data lane of source grid. |
There was a problem hiding this comment.
this change should already be in main ? for bridge comm? why we need these changes again here ? seems only cosmetic ? #6263
| pg_collection.dp = parallel_state.get_data_parallel_group( | ||
| with_context_parallel=False, partial_data_parallel=False | ||
| ) | ||
| # gtp_remat axis: consumers read these with getattr and silently skip the gtp_remat |
There was a problem hiding this comment.
this change already seems to be on main
| if is_pp_last_stage(p2p_communicator.pp_group): | ||
| recv_next = False | ||
| (input_tensor, output_tensor_grad) = ( | ||
| input_tensor, output_tensor_grad = ( |
There was a problem hiding this comment.
cosmetic changes and already on main not necessary
| tp_group, | ||
| dp_cp_group, | ||
| intra_dp_cp_group=None, | ||
| intra_expt_dp_group=None, |
There was a problem hiding this comment.
why do we need changes to this file ? seems not relevant ?
| tp_comm_buffer_name: Optional[str] = None, | ||
| tp_group: Optional[torch.distributed.ProcessGroup] = None, | ||
| name: str | None = None, | ||
| pg_collection: Optional[ProcessGroupCollection] = None, |
There was a problem hiding this comment.
inference layers changes is out of scope
| is_expert=False, | ||
| tp_comm_buffer_name='proj', | ||
| tp_group=self.pg_collection.tp, | ||
| pg_collection=self.pg_collection, |
There was a problem hiding this comment.
are these changes not on main already ?
Summary
ProcessGroupCollectionthrough Mamba projections, shared experts, language embeddings, and the output headWhy
The heterogeneous MIMO path builds disjoint module grids instead of using MPU-global groups. On top of #6234, it still lacked the GTP/EGTP axes and process-group plumbing required by the dense, expert, bridge, optimizer, and distributed-checkpoint paths.
Depends on #6234.
Validation
skipped=0, andnan=0