2828read ~100% whether or not the head learned anything real.
2929
3030So this module never reports training accuracy. It reports cross-validated
31- accuracy, a p-value against chance, and a verdict in words. Four corrections
31+ accuracy, a p-value against chance, and a verdict in words. Five corrections
3232make that honest, each added after it was caught misreporting:
3333
34341. GROUPED cross-validation. A session yields several pairs from the same
4343 learnable signal at the same n. `significant: false` means the number
4444 carries no information however high it looks.
4545
46- 3. SHARED-CAPTURE merging, before the split. Grouping trusts a label, and no
47- label can say "these pairs share a baseline". Keep one reference render and
48- A/B candidates against it — the most natural way anyone compares anything —
49- and every pair carries the same anchor, so every fold learns "not the
50- anchor" and scores perfectly. Measured: 6 pairs over 3 labelled sessions
51- sharing one anchor reported 100% / p=0.016 / significant in 40 of 40 runs,
52- for a head that scores 0.507 on fresh pairs. See
53- `_merge_groups_sharing_a_capture`.
46+ 3. SHARED-REFERENCE detection, before the split. Grouping trusts a label, and
47+ no label can say "these pairs share a baseline". Keep one reference render
48+ and A/B candidates against it — the most natural way anyone compares
49+ anything — and every pair carries the same anchor, so every fold learns "not
50+ the anchor" and scores perfectly. Two guards, because the obvious one closes
51+ only a quarter of it: `_merge_groups_sharing_a_capture` catches a re-USED
52+ file, and `_anchor_asymmetry` catches a re-RECORDED one, which is what
53+ `capture_audio` actually produces. Measured at production geometry, 6
54+ sessions x 2 pairs, before the second guard existed:
55+
56+ re-recorded anchor cosine 0.98 0.95 0.90 0.85 0.80 0.70
57+ certified as real taste 100% 100% 100% 100% 100% 100%
58+ captures merged 0 0 0 0 0 0
59+
60+ (honest control, own reference per session: 1%.) After: 0% certified at
61+ every cosine, control untouched, and a genuine quality signal still
62+ certifies at 100%.
5463
55644. Significance over SESSIONS. Correction 1 grouped the cross-validation split
5665 but left the test counting pairs as independent trials. False-positive rate
5766 on corpora with no transferable signal, nominal 5%: 25% counting pairs, ~9%
5867 counting sessions. See `_group_sign_test`, which also records why the
5968 textbook permutation fix was built and rejected.
6069
70+ 5. Measuring any of this in the geometry the code ACTUALLY RUNS IN. Real CLAP
71+ embeddings are L2-normalised, so a difference vector has norm 0.3-1.4. The
72+ first two rounds of measurement used raw gaussians at norm ~30, where
73+ `_fit_weights` returns near-zero and every corpus looks clean. That is how a
74+ guard closing a quarter of its defect passed a full green suite with a
75+ docstring claiming it was measured. The test helpers now generate unit-norm
76+ captures at production dimension; see `_unit` in the test module.
77+
6178The cost of 3 and 4 is a higher bar: significance needs decisions from
6279MIN_GROUPS_FOR_SIGNIFICANCE separate sessions with no shared captures, and below
6380that nothing can be certified however good it looks. That bar is derived from
@@ -153,8 +170,21 @@ def _update(data: dict) -> dict:
153170def _fit_weights (diffs : np .ndarray , l2 : float ) -> np .ndarray :
154171 """Logistic regression on difference vectors, all labels positive.
155172
156- Minimises -sum(log sigmoid(w.d)) + l2*||w||^2 by gradient descent.
157- Convex, so the optimum is unique and plain GD is sufficient at this size.
173+ Targets -sum(log sigmoid(w.d)) + l2*||w||^2, which is convex.
174+
175+ Note what the iteration actually is at the default settings. The update
176+ w -= _LR * (-mean(d*p_wrong) + 2*l2*w) has memory coefficient
177+ (1 - _LR*2*l2), which is EXACTLY 0 at _LR=0.5 and l2=1.0 — so w is
178+ discarded and recomputed each step rather than accumulated. It is a fixed
179+ point iteration, not gradient descent, and it converges in one step for
180+ well-scaled inputs.
181+
182+ That is fine in the deployed regime (unit-norm CLAP embeddings, ||d|| in
183+ 0.3-1.4, giving ||w|| ~ 0.03-0.26 and clean margins) but it is fragile: at
184+ ||d|| ~ 30 the same code returns ~1e-8 and every corpus scores at chance.
185+ Anything that changes _LR or DEFAULT_L2 changes the character of this
186+ function, not just its speed. Measure in production geometry — see
187+ correction 5 in the module docstring for what happens when you do not.
158188 """
159189 w = np .zeros (diffs .shape [1 ], dtype = np .float64 )
160190 n = len (diffs )
@@ -281,8 +311,12 @@ def _group_sign_test(per_group: list[tuple[int, int]]) -> Optional[float]:
281311 A session counts once, as a win or a loss depending on whether the head beat
282312 chance on it. Ties carry no directional evidence and are dropped.
283313
284- KNOWN RESIDUAL, measured not assumed: ~9% against a nominal 5%, because the
285- CV folds share training data so sessions are not fully independent either.
314+ KNOWN RESIDUAL, re-measured at production geometry (unit-norm CLAP, 512-d):
315+ 5.8% at 6 sessions x 4 pairs, 6.7% at 5 x 4, and 12.5% at 8 x 3 — against a
316+ nominal 5%. It degrades as sessions get thinner, because the CV folds share
317+ training data so sessions are not fully independent either. Power is
318+ unaffected: 100% on a genuine shared direction at >= 5 sessions, 0% at 4
319+ (structurally impossible, not a failure).
286320 The textbook fix — a group sign-flip permutation test — was built and
287321 REJECTED: `_fit_weights` is deliberately robust (`p_wrong` downweights
288322 already-correct points), so on the bimodal +/-d data that sign-flipping
@@ -304,13 +338,23 @@ def _group_sign_test(per_group: list[tuple[int, int]]) -> Optional[float]:
304338
305339def _verdict (loo : Optional [float ], n_pairs : int , scheme : str = "" ,
306340 n_groups : int = 0 , p : Optional [float ] = None ,
307- n_decisive : int = 0 , merged : int = 0 ) -> str :
341+ n_decisive : int = 0 , merged : int = 0 , anchored : bool = False ,
342+ asymmetry : float = 0.0 ) -> str :
308343 if loo is None :
309344 return (f"Only { n_pairs } pair(s). Need at least { MIN_PAIRS } before "
310345 "cross-validation says anything; the head is not usable yet." )
311346
312347 prefix = ""
313- if merged :
348+ if anchored :
349+ side = "rejected" if asymmetry > 0 else "preferred"
350+ prefix = (f"Every session's { side } captures are far more alike than "
351+ f"chance (asymmetry { asymmetry :+.2f} against ~0.00 for "
352+ "independent material), which is the signature of one shared "
353+ "reference compared against over and over. Those decisions are "
354+ "one piece of evidence, not several, so they were scored as a "
355+ "single session. Compare candidates against EACH OTHER, not "
356+ "against a fixed baseline. " )
357+ elif merged :
314358 prefix = (f"{ merged + 1 } of the recorded groups share a capture — a "
315359 "reused baseline makes those pairs one piece of evidence, not "
316360 "several — so they were merged before scoring. " )
@@ -411,6 +455,59 @@ def find(x: str) -> str:
411455 return [find (g ) for g in groups ], merged
412456
413457
458+ # Honest corpora measure 0.000 +/- 0.008 (max 0.030 over 60 runs, including
459+ # corpora carrying a genuine quality signal). The weakest anchoring that still
460+ # certified at 100% measures 0.473. This sits >13 sd above honest and far below
461+ # the weakest real offender — a gap wide enough that the exact value is not
462+ # load-bearing.
463+ ANCHOR_ASYMMETRY_LIMIT = 0.15
464+
465+
466+ def _anchor_asymmetry (pairs : list [dict ], groups : list [str ]) -> float :
467+ """How much more alike the REJECTED captures are than the preferred ones,
468+ measured only ACROSS sessions, using the groups AS RECORDED. Positive means a shared reference on the
469+ rejected side; negative means one on the preferred side.
470+
471+ Guard 4 keyed on exact vector equality, which turned out to close a far
472+ narrower hole than it claimed. `capture_audio` records live playback, so a
473+ producer who re-captures their reference each session produces a NEW file
474+ every time — never byte-identical, always the same material. Measured at
475+ realistic CLAP geometry, 6 sessions x 2 pairs, no genuine quality signal:
476+
477+ re-recorded anchor cosine 0.98 0.95 0.90 0.85 0.80 0.70
478+ certified as real taste 100% 100% 100% 100% 100% 100%
479+ captures merged by guard 4 0 0 0 0 0 0
480+ (honest control, own anchor per session: 1%)
481+
482+ A cosine threshold cannot fix that: at anchor cosine 0.70 the two anchors are
483+ less alike (0.49) than two independent captures from one project, so any
484+ cutoff strict enough to catch it merges every real corpus. This statistic is
485+ relative instead, so it needs no notion of "how similar is one project":
486+
487+ anchored 0.70 .. 1.00 +0.473 .. +1.000 (sd ~0.006)
488+ honest, own anchor per session -0.000 (sd 0.008)
489+ honest + a real quality signal +0.002 (sd 0.011)
490+ anchor on the PREFERRED side -0.810 (sd 0.005)
491+
492+ A real preference direction moves preferred and rejected captures APART
493+ symmetrically, leaving this near zero — which is why a genuine signal does
494+ not trip it.
495+ """
496+ def mean_cross (key : str ) -> float :
497+ vs = np .array ([p [key ] for p in pairs ], dtype = np .float64 )
498+ norms = np .linalg .norm (vs , axis = 1 , keepdims = True )
499+ norms [norms == 0 ] = 1.0
500+ vs = vs / norms
501+ sims = []
502+ for i in range (len (vs )):
503+ for j in range (i + 1 , len (vs )):
504+ if groups [i ] != groups [j ]:
505+ sims .append (float (vs [i ] @ vs [j ]))
506+ return float (np .mean (sims )) if sims else 0.0
507+
508+ return mean_cross ("rejected" ) - mean_cross ("preferred" )
509+
510+
414511def train (store : TasteHeadStore , l2 : float = DEFAULT_L2 ) -> dict :
415512 """Fit the head and report cross-validated skill (never training fit)."""
416513 data = store .get_all ()
@@ -428,6 +525,15 @@ def train(store: TasteHeadStore, l2: float = DEFAULT_L2) -> dict:
428525 # Guard #4 runs BEFORE the split: a label cannot express "these pairs share
429526 # a baseline capture", and grouped CV is blind to it.
430527 groups , merged = _merge_groups_sharing_a_capture (pairs , raw_groups )
528+ # ...and exact equality only catches a re-USED file, not a re-RECORDED one,
529+ # which is what capture_audio actually produces. This catches the rest.
530+ # Measured against the groups as RECORDED, not the merged ones: the merge
531+ # can already have collapsed everything into a single group, leaving no
532+ # cross-group pairs and a statistic of 0.0 that would read as "clean".
533+ asymmetry = _anchor_asymmetry (pairs , raw_groups )
534+ anchored = abs (asymmetry ) > ANCHOR_ASYMMETRY_LIMIT
535+ if anchored :
536+ groups = ["_shared_reference" ] * n
431537 n_groups = len (set (groups ))
432538 weights = _fit_weights (diffs , l2 )
433539 loo , scheme , per_group = _cv_accuracy (diffs , groups , l2 )
@@ -454,14 +560,16 @@ def train(store: TasteHeadStore, l2: float = DEFAULT_L2) -> dict:
454560 "n_groups" : n_groups ,
455561 "n_groups_recorded" : len ({g for g in raw_groups if g }),
456562 "groups_merged" : merged ,
563+ "anchor_asymmetry" : round (asymmetry , 4 ),
564+ "shared_reference_detected" : bool (anchored ),
457565 "n_decisive_groups" : n_decisive ,
458566 "sessions_needed_for_significance" : MIN_GROUPS_FOR_SIGNIFICANCE ,
459567 "baseline_accuracy" : 0.5 ,
460568 "p_value" : None if not certifiable else round (p_value , 4 ),
461569 "significant" : (None if not certifiable
462570 else bool (p_value <= SIGNIFICANCE_ALPHA )),
463571 "verdict" : _verdict (loo , n , scheme , n_groups , p_value , n_decisive ,
464- merged ),
572+ merged , anchored , asymmetry ),
465573 "trained_at" : int (time .time ()),
466574 }
467575 # Training accuracy is deliberately absent: with n << d it is ~100%
0 commit comments