|
| 1 | +import pytest |
| 2 | + |
| 3 | +torch = pytest.importorskip("torch") |
| 4 | + |
| 5 | +from gpu.gkd import gkd_weight, reverse_kl, token_logprobs # noqa: E402 |
| 6 | + |
| 7 | + |
| 8 | +class TestGkdWeight: |
| 9 | + def test_full_ratio_before_anneal(self): |
| 10 | + assert gkd_weight(0, 300, 0.3) == 0.3 |
| 11 | + assert gkd_weight(199, 300, 0.3) == 0.3 |
| 12 | + |
| 13 | + def test_linear_anneal_over_final_third(self): |
| 14 | + assert gkd_weight(200, 300, 0.3) == pytest.approx(0.3) # anneal starts at full |
| 15 | + assert gkd_weight(250, 300, 0.3) == pytest.approx(0.15) |
| 16 | + assert gkd_weight(299, 300, 0.3) == pytest.approx(0.003) |
| 17 | + |
| 18 | + def test_zero_at_end_and_degenerate(self): |
| 19 | + assert gkd_weight(300, 300, 0.3) == 0.0 |
| 20 | + assert gkd_weight(0, 0, 0.3) == 0.0 |
| 21 | + |
| 22 | + |
| 23 | +class TestTokenLogprobs: |
| 24 | + def test_shift_and_gather(self): |
| 25 | + torch.manual_seed(0) |
| 26 | + logits = torch.randn(2, 5, 7) |
| 27 | + targets = torch.randint(0, 7, (2, 5)) |
| 28 | + lp = token_logprobs(logits, targets) |
| 29 | + assert lp.shape == (2, 4) |
| 30 | + ref = torch.log_softmax(logits[0, :-1].float(), -1) |
| 31 | + expect = ref.gather(-1, targets[0, 1:].unsqueeze(-1)).squeeze(-1) |
| 32 | + assert torch.allclose(lp[0], expect) |
| 33 | + |
| 34 | + |
| 35 | +class TestReverseKl: |
| 36 | + def test_identical_distributions_zero(self): |
| 37 | + torch.manual_seed(0) |
| 38 | + logits = torch.randn(2, 6, 7) |
| 39 | + targets = torch.randint(0, 7, (2, 6)) |
| 40 | + assert reverse_kl(logits, logits.clone(), targets).abs() < 1e-6 |
| 41 | + |
| 42 | + def test_mask_restricts_positions(self): |
| 43 | + torch.manual_seed(0) |
| 44 | + s = torch.randn(1, 6, 7) |
| 45 | + t = torch.randn(1, 6, 7) |
| 46 | + targets = torch.randint(0, 7, (1, 6)) |
| 47 | + mask = torch.zeros(1, 6, dtype=torch.bool) |
| 48 | + mask[:, 4:] = True # only the continuation region (positions >= 4) |
| 49 | + masked = reverse_kl(s, t, targets, mask) |
| 50 | + full = reverse_kl(s, t, targets) |
| 51 | + assert not torch.allclose(masked, full) |
| 52 | + |
| 53 | + def test_gradient_flows_through_student_only(self): |
| 54 | + s = torch.randn(1, 4, 5, requires_grad=True) |
| 55 | + t = torch.randn(1, 4, 5, requires_grad=True) |
| 56 | + targets = torch.randint(0, 5, (1, 4)) |
| 57 | + loss = reverse_kl(s, t, targets) |
| 58 | + loss.backward() |
| 59 | + assert s.grad is not None |
| 60 | + assert t.grad is None |
0 commit comments