Skip to content

test_dpo_learns_preferences is flaky #15

Description

@DenisDrobyshev

Corrected. The first version of this issue claimed seed= never reaches torch and that nothing in the package calls manual_seed. That is wrong, and the evidence I based it on was bad — I used GitHub's code search API, which returns zero matches for this repository because it is not indexed. It also returns zero for torch in praxis, which is how I caught it. Seeding here is fine; the flake is real but has another cause. The original text is below the line.

tests/test_rlhf.py::test_dpo_learns_preferences is flaky.

>       assert metrics["accuracy"] > 0.8
E       assert 0.755625 > 0.8
tests/test_rlhf.py:59: AssertionError

The flake, established

  • It failed on a branch that adds one file, CITATION.cff, and changes nothing else.
  • A workflow_dispatch run on untouched main minutes later passed every leg, including 3.12.
  • Re-running the same failed job on the same commit, with nothing changed, passed: lint + fast tests (py3.12) -> success.

Identical commit, identical interpreter, identical resolved dependencies, opposite results.

What is already correct, and rules out the obvious causes

tests/conftest.py seeds every RNG before each test through an autouse fixture, and its docstring already names the exact hazard:

Network initialization uses the global PyTorch RNG, so without this a test's outcome could depend on which tests ran before it.

set_seed covers random, numpy, PYTHONHASHSEED, torch.manual_seed and torch.cuda.manual_seed_all. And ci.yml and verify.yml both pin OMP_NUM_THREADS: "1", so thread-count variation in float accumulation is already off the table.

So this is not a seeding bug and not a threading bug. Both were handled before I looked.

What is left

Two candidates, neither verified:

  1. Runner hardware. ubuntu-latest is a heterogeneous fleet. Different vector instruction sets take different reduction paths, the last bits differ, and 600 training iterations amplify that. This fits the evidence exactly — identical software, different machine.
  2. A genuine bimodality in the run. A five-point gap in final accuracy is large for pure float jitter, which would point at training landing in a different basin depending on a tiny initial difference rather than at rounding.

set_seed(..., deterministic=True) is not currently used by the tests, and would close the first of those if it is the cause.

The part I would still change

Guessing between those two is the wrong next move. The useful one is to measure: run this test twenty or fifty times in CI and look at the distribution of metrics["accuracy"]. That answers whether 0.7556 is a tail or a second mode, and it turns the threshold into something chosen rather than assumed.

Whatever it shows, > 0.8 is a hard threshold on a trained quantity with no stated tolerance. The two alternatives that survive a machine change:

  • assert DPO ranks held-out preferences better than an untrained model by a margin — a claim about the method rather than about one number;
  • assert a lower bound over several seeds rather than a point from one.

The second is module 1 of lemma applied to this repository's own suite.

Blocking

Nothing now — decisionrl#14 went green on a re-run and is merged. But any run can flake until this is characterised.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions