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:
- 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.
- 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.
tests/test_rlhf.py::test_dpo_learns_preferencesis flaky.The flake, established
CITATION.cff, and changes nothing else.workflow_dispatchrun on untouchedmainminutes later passed every leg, including 3.12.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.pyseeds every RNG before each test through an autouse fixture, and its docstring already names the exact hazard:set_seedcoversrandom,numpy,PYTHONHASHSEED,torch.manual_seedandtorch.cuda.manual_seed_all. Andci.ymlandverify.ymlboth pinOMP_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:
ubuntu-latestis 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.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.8is a hard threshold on a trained quantity with no stated tolerance. The two alternatives that survive a machine change:The second is module 1 of
lemmaapplied 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.