Repository navigation
fix(jsd): make the JSD advantage follow the gradient of the JSD - #640
Open
flyingdoubleG wants to merge 1 commit into
Open
flyingdoubleG wants to merge 1 commit into
flyingdoubleG wants to merge 1 commit into
Conversation
The JSD advantage only sees the sampled token's log-probs, and the tokens
are sampled from the student, so the per-token value is a REINFORCE cost.
For 0 < lambda_coef < 1 it used beta*(t - log_m) + (1-beta)*(s - log_m).
Under student samples t - log_m does not estimate KL(teacher || M), and
the resulting expected update does not follow -grad JSD: on random
categorical student/teacher pairs its cosine with -grad JSD is negative
for beta in {0.3, 0.5, 0.9}, i.e. training increases the divergence.
Differentiating beta*KL(T||M) + (1-beta)*KL(S||M) with respect to s(y)
gives (1-beta) * log(s(y)/m(y)), so the cost is (1-beta)*(s - log_m);
its expected REINFORCE gradient equals grad JSD exactly. beta = 0 and
beta = 1 are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Problem.
JSDAdvantagesees only the sampled token's log-probss = log π_student(y)andt = log π_teacher(y), andyis sampled from the student. The per-token value, used asadvantages = -kl_coef * valueunder the PPO loss, is therefore a REINFORCE cost: training follows-E_{y~student}[value(y) · ∇log π_student(y)].For
0 < lambda_coef < 1the value wasUnder student samples,
t − log mis not an estimate ofKL(teacher‖M), and the expected update does not follow−∇JSD_β. On random categorical student/teacher pairs, computing the expectation over the sampled token exactly, the cosine between the update and−∇JSD_βis:(3 seeds; the ranges are over seeds.) So with the default
lambda_coef: 0.5, used byexamples/opd_gsm8k/opd_gsm8k_jsd.yaml, the expected update mostly increases the divergence to the teacher. The loggedjsd/meanis also never positive, sinceβ·t + (1−β)·s ≤ log mby concavity.Fix. Differentiating
β·KL(T‖M) + (1−β)·KL(S‖M)with respect tos(y)gives(1−β)·log(s(y)/m(y)), because theβ·t/mand(1−β)·s/mterms cancel. The per-token cost is therefore(1 − β)·(s − log m), and its expected REINFORCE gradient equals∇JSD_βexactly. This PR applies the fix to both the plain and the chunked path.β = 0(costs − t, reverse KL) is unchanged.Not changed:
β = 1, which needs your decision. The docstring saysβ = 1isKL(teacher‖student), and the code uses costt − s. Under student samplesE[(s − t)∇log s] = ∇KL(student‖teacher), so costt − sascends the reverse KL: the cosine with−∇KL(S‖T)is exactly −1, and the cosine with−∇KL(T‖S)is −0.5 to −0.7. Two possible fixes:KL(teacher‖student). Its gradient is−E_student[e^{t−s} ∇log s], so the cost would be−exp(t − s), an unbounded importance weight that probably needs clipping.generalized_jsd_loss, whereβ = 0is forward KL andβ = 1is reverse KL. That matches the β→0 / β→1 limits of this mixture, but it swaps the current meaning ofβ = 0.Happy to add either.
How to test.
tests/algorithm/advantage_fn_test.py::TestJSDAdvantageFn::test_jsd_advantage_descends_jsdchecks that, for β ∈ {0.3, 0.5, 0.9} on both the plain and the chunked path, the exact expected policy gradient equals−∇JSD_β. It fails onmain(8/8 elements mismatched) and passes with this change. The whole file passes (9 tests, CPU).Checklist
tests/algorithm/advantage_fn_test.py, CPU; GPU tests not run)🤖 Generated with Claude Code