Repository navigation
perf(rlhf): chunk selective_log_softmax instead of looping per token - #10337
rrrxxx0510 wants to merge 2 commits into
Conversation
|
Hi — this is my first PR from a fork, so the workflows are parked waiting for a maintainer to approve them before CI runs. It replaces TRL's per-row |
|
GPU numbers are in — measured on an H200 (torch 2.9.1+cu130, bf16), output asserted bit-identical in every case:
I also swept Everything else in the description is unchanged: bf16/fp16 stay bit-for-bit identical, fp32/fp64 keep TRL's |
|
Hi @hjh0119 @tastelikefeet - sorry to ping directly. This one has been parked with every workflow run at It replaces TRL's per-row Could one of you approve the workflow runs when you get a chance? Happy to rebase or change anything once I can see the CI result. |
PR type
PR information
Fixes #10357
swiftimportsselective_log_softmaxfromtrl.trainer.utils. TRL's version loops over the leading dimension oflogits"to reduce peak mem consumption". That is fine when the leading dimension is a batch dimension, but underpadding_free/packing swift flattens the logits to[total_nnz, vocab_size](grpo_trainer.py:1591), so the loop degenerates to one iteration per token — thousands of smalllog_softmax+gatherlaunches per call instead of a few dozen.This adds a chunked implementation in
swift/rlhf_trainers/utils.py: the flattened rows are processedchunk_sizeat a time. Peak memory is still bounded by the chunk size, which was the point of the original loop, but the work gets batched again. The three call sites ingrpo_trainer.py(sequence-parallel / padding_free / default) and the two inrlhf_mixin.py(DPO/CPO/KTO/ORPO) switch to it.Correctness, checked against the naive
gather(logits.log_softmax(-1), ...):torch.equal) across 2-D and 3-D shapes,chunk_size1/3/1024/10^6, top-K indices,temperature,row_mask, and gradients. bf16 is what training actually uses.x - logsumexp(x)form, so results can differ in the last bit; asserted withinrtol=atol=1e-6.chunk_size=1024is close to optimal (2.6 ms vs 2.4 ms at 16384 on the 8192x152064 case), so the default is left alone.Experiment results
Measured on an H200 (torch 2.9.1+cu130, bf16), output asserted bit-identical in every case:
The win is almost entirely launch overhead, which is why it is far larger on GPU than the ~5x measured on CPU.
Tests:
tests/general/test_selective_log_softmax.py, 7 cases, no GPU or model needed.Checklist