Skip to content

Commit f3db7b0

Browse files
authored
Cast the lm_head weight to the compute dtype in the chunked projections
Under FSDP2 mixed precision the patched forward reads the fp32 sharded master weight directly while the hidden states come out bf16, so the tensor-core projection failed with a dtype mismatch (tests/distributed test_sft_peft[fsdp2]). Cast the weight to the hidden-states dtype, which is what lm_head's own forward computes in. Same fix in all three copies: SFT, distillation, async distillation.
1 parent a1ab3db commit f3db7b0

3 files changed

Lines changed: 14 additions & 9 deletions

File tree

trl/experimental/async_distillation/async_distillation_trainer.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,9 @@ def _jsd_loss_chunk(
225225
are `torch.no_grad()` sums for this chunk only — callers accumulate them across chunks and reduce across ranks,
226226
exactly as the non-chunked path already did.
227227
"""
228-
# Project in the model dtype and upcast only afterwards, as the other chunked projections do.
229-
logits = (hidden_chunk @ lm_head_weight.t()).float()
228+
# Project in the compute dtype and upcast only afterwards, as the other chunked projections do, casting
229+
# the weight to the hidden-states dtype in case it is an fp32 master weight (FSDP2 mixed precision).
230+
logits = (hidden_chunk @ lm_head_weight.to(hidden_chunk.dtype).t()).float()
230231
if lm_head_bias is not None:
231232
logits = logits + lm_head_bias.float()
232233
if logit_scale != 1.0:

trl/trainer/distillation_trainer.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ def _chunk(h_s, w_s, b_s, s_scale, s_softcap, h_t, w_t, b_t, t_scale, t_softcap,
112112
# the backward, never `(chunk, V)`. ZeRO-3 shards the `lm_head`, so gather it tightly around each projection.
113113
# `logit_scale` (Cohere) / `final_logit_softcapping` (Gemma) are applied per model to match its full forward.
114114
with maybe_gather_lm_head_ctx(w_s, b_s):
115-
# Project in the model dtype and upcast only afterwards, as `"nll"` and `transformers`'
116-
# `ForCausalLMLoss` do.
117-
student_logits = (h_s @ w_s.t()).float()
115+
# Project in the compute dtype and upcast only afterwards, as `"nll"` and `transformers`'
116+
# `ForCausalLMLoss` do. The weight can be an fp32 master weight while the hidden states are
117+
# bf16 (FSDP2 mixed precision reads the sharded weight directly, bypassing the cast its
118+
# forward hooks apply), so cast it to the dtype `lm_head`'s own forward would compute in.
119+
student_logits = (h_s @ w_s.to(h_s.dtype).t()).float()
118120
if b_s is not None:
119121
student_logits = student_logits + b_s.float()
120122
if s_scale != 1.0:
@@ -125,7 +127,7 @@ def _chunk(h_s, w_s, b_s, s_scale, s_softcap, h_t, w_t, b_t, t_scale, t_softcap,
125127
# and the teacher accumulates no gradients (the teacher params are not frozen by `prepare_model`). Everything
126128
# downstream inherits this since `teacher_logits` is already detached.
127129
with maybe_gather_lm_head_ctx(w_t, b_t), torch.no_grad():
128-
teacher_logits = (h_t @ w_t.t()).float()
130+
teacher_logits = (h_t @ w_t.to(h_t.dtype).t()).float()
129131
if b_t is not None:
130132
teacher_logits = teacher_logits + b_t.float()
131133
if t_scale != 1.0:

trl/trainer/sft_trainer.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,11 @@ class _ChunkedCELMHeadOutput(CausalLMOutputWithPast):
9999

100100
def _chunk(h, w, b, lbl, logit_scale, final_logit_softcapping):
101101
with maybe_gather_lm_head_ctx(w, b):
102-
# Project in the model dtype and upcast only for the softmax, like `"nll"` and
103-
# `transformers`' own `ForCausalLMLoss` do.
104-
logits = (h @ w.t()).float()
102+
# Project in the compute dtype and upcast only for the softmax, like `"nll"` and
103+
# `transformers`' own `ForCausalLMLoss` do. `w` can be an fp32 master weight while `h` is
104+
# bf16 (FSDP2 mixed precision reads the sharded weight directly, bypassing the cast its
105+
# forward hooks apply), so cast it to the dtype `lm_head`'s own forward would compute in.
106+
logits = (h @ w.to(h.dtype).t()).float()
105107
if b is not None:
106108
logits = logits + b.float()
107109
if logit_scale != 1.0:

0 commit comments

Comments
 (0)