@@ -279,30 +279,19 @@ def fit(self) -> None:
279279 # deepspeed: engine.step() already ran inside backward at the sync boundary
280280 grad_norm = self ._deepspeed_engine .get_grad_norm ()
281281 else :
282- # FSDP2 shards params/grads as DTensors across the fsdp mesh, so each rank only
283- # holds 1/shard_size of every parameter. `get_total_norm`/`clip_grad_norm_`
284- # return a *per-rank local shard* norm and `.item()` reads only that local
285- # partial (== global_norm / sqrt(shard_size)). Using it directly makes the
286- # reported grad_norm scale as 1/sqrt(dp_size) (e.g. 8xdp mbs1 vs 4xdp mbs2
287- # differ by sqrt(2)), and -- worse -- makes `clip_grad_norm_` apply the clip
288- # coefficient per-shard, corrupting the update once clipping is actually on.
289- # Fix: reduce to the true global norm first (full_tensor all-reduces across the
290- # fsdp shard mesh), then clip with that scalar via clip_grads_with_norm_.
282+ # FSDP2 shards params/grads across the fsdp mesh, so clip_grad_norm_ returns a
283+ # per-rank local shard norm (global / sqrt(shard_size)): reported grad_norm then
284+ # scales as 1/sqrt(dp_size) and the clip coefficient is applied per-shard. Reduce
285+ # to the true global norm first, then clip with it.
291286 from torch .distributed .tensor import DTensor
292287
293288 grads = [p .grad for p in self .model .parameters () if p .grad is not None ]
294289 total_norm = torch .nn .utils .get_total_norm (grads )
295290 if isinstance (total_norm , DTensor ):
296- # full_tensor already all-reduces across the whole fsdp shard mesh. With the
297- # default mp_shard_size = world_size this mesh spans CP too, so FSDP's
298- # reduce-scatter has already summed grads across CP -- no separate CP
299- # reduction is wanted (it would over-count grad_norm by sqrt(cp_size) and
300- # also skew the clip coefficient below). CP-on and CP-off both report the
301- # true global norm this way.
291+ # full_tensor all-reduces across the fsdp mesh (spans CP under default
292+ # mp_shard=world); a separate CP reduce would over-count by sqrt(cp_size).
302293 total_norm = total_norm .full_tensor ()
303- # pass the (replicated) global norm as a Tensor -- clip_grads_with_norm_ does
304- # torch.clamp(max_norm / (total_norm + 1e-6), max=1.0), which rejects a bare
305- # python float. .item() for reporting only, after clipping.
294+ # pass a Tensor: clip_grads_with_norm_ clamps max_norm / (total_norm + 1e-6).
306295 torch .nn .utils .clip_grads_with_norm_ (
307296 self .model .parameters (), self .args .max_grad_norm , total_norm
308297 )
0 commit comments