strata metal opts v3 (opus partner): v2 verdict, last kernel polish, and the Phase 4 audit #
patch: /tmp/opus_metal_probes/opus_v3.diff (against the current vendor tree with v1+v2 in; 105 changed lines, git apply --check clean,
window.cpp passes -fsyntax-only, the Metal library with the new gr instantiations compiles). not run on the GPU.
1. v2, interleaved (ops_1024, 7.0 GB, wired) #
| arm | rep 1 ms/tok | rep 2 ms/tok | CB "GPU" | wait |
|---|---|---|---|---|
| stock (all off, PF 8) | 58.11 | 59.30 | 49.7 / 51.0 | 7.2 |
| v2, EXPERT_SPLIT=0, unpert PF 6 | 47.68 | 48.05 | 41.4 / 41.6 | 5.0 |
| v2, EXPERT_SPLIT=1, unpert PF 6 | 46.00 | 46.18 | 39.6 / 39.6 | 5.0 |
| + DN tile 8,2,4 (1 rep) | 45.75 | β | 39.6 | 4.7 |
- net: 58.7 β 46.1 ms/tok (β21%), 17.0 β 21.7 tok/s (+27%), repeatable across both reps (spread β€ 0.4 ms). the resident-first split is worth a clean β1.7β¦β1.9 ms on top of the rest. that's about the ~3 ms of handshakes minus what v1's shared-expert overlap already hid.
- CB time (39.6) is now within 1.0 ms of KPROF's serialized kernel sum (38.6): the handshakes are essentially gone from the GPU's view.
- open item: the window is 46.05 ms but the CB is 39.6. if the 5.0 ms of SSD wait were inside the CB it couldn't be 39.6 (38.6 of kernels
alone); phase 1 can hide at most ~140 Β΅s per layer of it. so either the CB timestamps don't count event-wait idle, or ~6 ms sit outside
the CB. one
STRATA_METAL_PROFILE=1line for the v2 arm (host before / run_graph / GPU / patch+finish) settles it. it matters because it says whether the SSD wait or something on the host is the next 5β6 ms.
2. v3 patch (all small) #
moe_downQ2_0 tile 8,2 on the M5 by default (8,1 stays on the M4). your sweep: 2.185 vs 2.431 ms (β0.25 ms).- exact BF16 router (β63 MB a token, ~β0.5 ms, zero change in weights). checked over the healed GGUF: all 48
ffn_gate_inpF32 tensors (655,360 values each) have zero low 16 bits, i.e. they're upcast BF16. the routing GEMV reads a BF16 copy built at init (63 MB), only if every value is exact (else it keeps F32);router_routegets the same buffer.STRATA_METAL_ROUTER_BF16=0turns it off. note the Q8_0 alternative is not free: on the 1022-token trace it keeps 99.66% of top-10 sets and 99.46% of top-1, with 0.0049 mean routing-weight L1 change (rq.cpp). BF16 gives 100% / 100% / 0. check once thatidsstay bitwise equal (gemv_30 vs gemv_0 tile choice is the same). - per-group HC precision.
STRATA_HC_Q4and newSTRATA_HC_Q5each take1|attn|ffn(Q4 wins where both):- Q5_0 = 22 B / 32 weights (0.647 Γ Q8_0's bytes). there's a host quantizer (ggml block_q5_0 layout, round-tripped through
ref::dequantinq5test.cpp) andgr_down_q5_8_8/gr_up_q5instantiations. - on Gaussian blocks: rel-RMSE Q8_0 0.0054, Q5_0 0.0428, Q4_0 (your Lloyd) 0.0816. Q5's weight MSE is 0.275Γ Q4's.
- expected, if KLD tracks weight MSE: ops_tools ~0.13 β ~0.035 all-Q5; HC time β1.6 ms (vs β2.14 for all-Q4); +240 MB β ~170 more arena slots.
- suggested ladder:
HC_Q5=1, thenHC_Q4=attn HC_Q5=ffn, thenHC_Q4=1. gate each by KLD β€ 0.02 on all three domains (ops_tools is the one that moves). - your (a) hypothesis β that hc_ffn is the sensitive half because it feeds the router β is plausible, but hc_attn feeds the same
residual one sublayer earlier, so measure
HC_Q4=attnrather than assume it.
- Q5_0 = 22 B / 32 weights (0.647 Γ Q8_0's bytes). there's a host quantizer (ggml block_q5_0 layout, round-tripped through
- not in the patch, worth an experiment: the two-stage head (output.weight = 3.96 ms, 10.3%):
- a ~2.6 bpw copy (~205 MB) picks the top 256; exact Q6_K on those rows; ~β2 ms net after the lost arena slots.
- it's exact for greedy only when the true argmax is in the candidates, and for top-k/top-p only if the sampler normalizes over the top-k (llama.cpp order).
- measure argmax/top-20 recall with
STRATA_METAL_LOGPOSon all three domains before writing the kernel.
after v3: ~45.3 ms/tok with the router + tile (~22.1 tok/s); with HC_Q5=1 ~43.7 ms (~22.9 tok/s) if its KLD passes. what's left is ~36 ms of kernels at 122β145 GB/s, ~5 ms of SSD wait, and whatever Β§1's open item turns out to be.
3. Phase 4 audit (phase4-colab-persona-ftpo, results/phase4_colab_validation.json) #
verdict: reject the bundle; keep Phase 3's writers. your real-engine numbers (KLD vs Phase 3: 0.276 ops_tools, 0.408 chat_json; PPL 3.02 β 3.42, 1.20 β 1.81) are the only measurements of this artifact on the real model, and they fail every bar we've used. beyond the two proxy errors you found:
- the reported metrics come from a proxy that isn't the model. "before" teacher-forced CE on the persona turns is 12.08 nats (top-1 7.5%), abstention 13.14, known facts 12.63. ln(248,320) = 12.42, so the proxy predicts these turns about as well as a uniform distribution. the real model is ~1β3 nats on such text. every "after" number (CE β1.2, win rate 50 β 93.75%) is measured on that proxy, and so is the 0.000897 calibration KLD.
- the FTPO set measures non-events. the 126 tuples are 7 phrases Γ 18 two-token contexts (e.g.
[1, 61745] β 1056). before training the rejected token already has p β 1e-6β¦1e-9 (e.g. "end to end" 8.3e-9), so "β98.13% rejected probability" is a drop from almost-never to almost-never.- FTPO needs contexts mined from the model's own generations, where the slop token is actually likely (PLAN Β§7 phase 4: "slop mining on tuned outputs").
- not reproducible from the repo.
scripts/run_phase4_colab_ftpo.pyholds only the FTPO loss reference and a self-test that checks the artifacts' format and the JSON's own numbers. the training code only exists in the Colab transcript. total runtime was 25.9 s. - its own table shows L40β47 MoE MSE got worse (11749 β 11833 after quantization), and
scope: swift_writers_plus_routermodified no router. - the 16 SFT pairs / 807 target tokens are far too few to move voice, even with a real forward.
3.1 the layer-47 zero-proxy formulation: correct, with these precise terms #
from window.cpp (decode) / prompt_chain.inc L326β366 (prefill), gr.metal, glue.metal, prompt.metal. per token t, streams
c = 0..3, n = 2560:
hh = silu(W_sg47 Β· XF47) β (W_su47 Β· XF47) [640] (dump: .hh; fp16 on the prompt path)
ys = W_sd47 Β· hh [2560] <- the trainable W_sd47 = blk.47.ffn_down_shexp.weight (Q8_0)
bo = yr47 + Ο(sgv47) Β· ys [2560] yr47 = Ξ£_k w_kΒ·expert_k(XF47) (dump: .yr), sgv47 = w_gsh47Β·XF47 (.sgv)
R'_c = R_c + bo Β· 2Ο(inj47_c / 4) [4Γ2560] R = the streams after layer 47's ATTENTION write (applied by
hc_ffn_47's gr_norm), inj47 = layer 47's hc_ffn inject (4 floats)
xn_c = R'_c β w_onorm_c Β· rsqrt(mean_d R'_cΒ² + 1e-6) w_onorm = output_hc_norm.weight (F32, 4Γ2560)
lo = silu((W_odown Β· xn) / 4) [320] W_odown = output_hc_down rows 0..319 [320Γ10240]
mix = ΒΌ Ξ£_c xn_c β Ο(W_oup[cΒ·2560 + d] Β· lo) [2560] W_oup = output_hc_up [10240Γ320]
logits = W_out Β· mix [248320] W_out = output.weight (Q6_K); no further norm
- yes, nothing sits between
ffn_down_shexp.47and the logits butmoe_combine_shβ theoutput_hcread βoutput.weight. for a ΞW onblk.47.ffn_down_shexp(or onoutput_hc_*/output.weight) this is the real network, not a proxy. Ξ(R'_c) = 2Ο(inj_c/4) Β· Ο(sgv) Β· ΞWΒ·hh, and the routing and everything before it is untouched. - your dump list is one term short:
(R_post47, inj_47, hh_47, sgv_47)also needsyr_47, unlessR_post47means the streams after the base FFN write. then use R'_base + 2Ο(inj/4)Β·Ο(sgv)Β·(ΞWΒ·hh). both work; pick one and name it in the dump. you also need R itself (4Γ2560 f32 per token), whichSTRATA_METAL_DUMP_MOEdoesn't write today (.mixed is XF, not R). inj47 is the inject buffer produced by layer 47's hc_ffngr_down, i.e.inj_[cur_inj_]at the output read. - use the runtime's weights, not the file's:
output_hc_down/upare BF16 in the GGUF but the engine reads its Q8_0 copies (q8off_ covers every BF16hc_*except norms; the Q4/Q5 options never touchoutput_hc). dequantize throughref::quantize_q8_0βref::dequant.output.weightthrough Q6_K dequant.- train ΞW in float, then score the loss after re-quantizing
blk.47.ffn_down_shexpto Q8_0, as it will ship.
- precision: the prompt path rounds hh (and its GEMM operands) to fp16, the decode window doesn't. differences are ~1e-3 relative. "identical" holds up to float rounding, not bitwise.
- routing: yr47 and everything upstream depend on MoCE residency when Ξ» > 0. dump with
STRATA_METAL_MOCE_BONUS=0for a deterministic training/eval set; measure deployed Ξ» separately. - what it can and can't do:
- it's exact only for L47 / output-side tensors. any ΞW at L40β46 goes through later attention, GDN, QSA and routed experts (routing has discontinuities). no closed form exists; those need the real forward.
- capacity is one 2560Γ640 map, gated per token by Ο(sgv47) and the stream gates. that's plausible for lexical FTPO (moving a few next-token logits in specific contexts) and weak for persona voice. keep persona/abstention in Phase 3's real-forward LoRA.
- acceptance for a re-run, all measured on the real engine (dawn's text bars aside):
- KLD vs Phase 3 β€ ~0.01 on the 3 eval domains,
- p(rejected) dropping in mined contexts where it started β₯ 1e-2,
- the transplanted GGUF's own LOGPOS, not the trainer's numbers.
4. v3 verification addendum (M5 Air, ops_1024, 7.0 GB, wired; parent's call_1809920, call_2299199) #
- v3 defaults (
ROUTER_BF16=1, Q2_0 down tile 8,2,EXPERT_SPLIT=1, unpert PF 6): 45.28 ms/tok = 22.08 tok/s (predicted ~45.3 / ~22.1). hit 97.59%, 11.6 misses/tok, 479 late read-aheads, host 5.21 ms of which waiting 5.08 (+0.46 after the GPU went on).route_parity: 0 mismatches. - Β§1's open item is closed; there's no hidden host gap. profile: window 45.298 = host before 0.225 + run_graph 45.027 (CB GPU span 44.068
- encode/submit/wait 0.959) + patch+finish 0.047. the CB span does include the event waits: 44.07 β 38.99 kernels + 5.08 SSD wait. the
"39.6 ms" in the earlier table was the validation script's
pure_gpu = GPU β host_wait, not the CB span. so the budget is ~39 ms of kernels, ~5 ms of SSD wait, ~1.2 ms of host/submit, and the SSD wait is the next item.
- encode/submit/wait 0.959) + patch+finish 0.047. the CB span does include the event waits: 44.07 β 38.99 kernels + 5.08 SSD wait. the
"39.6 ms" in the earlier table was the validation script's
- ROUTER_BF16 is not bitwise in the end. the weights are identical, but
gemv_30andgemv_0sum in a different order, and that flipped one rank-10 tie on ops_tools: KLD 0.0087, top-1 96.88%, PPL 3.0165 β 2.9914. kept as default. read the 0.0087 as ops_tools' noise floor for any change that only reorders floats: the HC rows below should be compared against ~0.01, not 0. - HC ladder, KLD vs HC_Q8 (top-1):
| config | ops_tools | chat_json | ja | macro KLD | macro top-1 |
|---|---|---|---|---|---|
| HC_Q4=1 (earlier) | 0.1298 | 0.00997 | 0.02755 | β | β |
| HC_Q5=1 | 0.0786 (89.06%) | 0.0029 (98.44%) | 0.0123 (96.88%) | 0.0313 | 94.79% |
| Q4 attn + Q5 ffn | 0.0889 (90.62%) | 0.0043 (98.44%) | 0.0235 (96.88%) | 0.0389 | 95.31% |
| Q4 attn + Q8 ffn | 0.0656 (90.62%) | 0.0042 (98.44%) | 0.0197 (96.88%) | 0.0298 | 95.31% |
| Q8 attn + Q5 ffn | 0.0575 (93.75%) | 0.0020 (98.44%) | 0.0062 (96.88%) | 0.0219 | 96.36% |
- chat_json and ja tracked the weight-MSE prediction (Q5 β 0.25β0.45Γ Q4). ops_tools didn't: Q5 is 0.61Γ Q4, not ~0.28Γ. it's dominated by
discrete routing flips, not by the smooth error, and every row is still 6β9Γ its floor. so HC_Q8 stays the default, with
HC_Q5/HC_Q4opt-in. - if one is ever wanted: Q8 attn + Q5 ffn has the best ratio (half the HC bytes saved of all-Q5, lowest KLD).
- a better route for ops_tools would make routing ties robust rather than the HC weights finer, but that's beyond the kernel pass.
- next: the 5 ms of SSD wait (479 late read-aheads at PF 6, up from 118 at PF 8 with MRU, so lateness, not recall, is now the cost). and the layer-47 Phase 4 bundle once it's back: verify with real-engine KLD vs Phase 3 and LOGPOS on mined slop contexts (Β§3.1).
5. Phase 4 v2 (zero-proxy L47) verification #
independent re-implementation (/tmp/opus_metal_probes/l47eval.cpp): my own C++ forward of Β§3.1 from the dumps, with W_sd47 from the Phase 3 GGUF
vs from the bundle, through Q8_0-round-tripped output_hc_* and the exact Q6_K head over all 248,320 rows, on both 1,022-token traces.
per-position output: /tmp/opus_metal_probes/l47eval.tsv.
what checks out:
- the bundle: 47 of 48 tensors are byte-identical to
K256-q2_0-healed-metal.gguf; onlyblk.47.ffn_down_shexp.weight(Q8_0) differs. βΞWβ/βWβ = 0.32 (large; it's the SVD filter that keeps it quiet on the background). - the dumps + equations are the real model. base CE on the natural cal text is 2.5496 with top-1 53.91% (a proxy would sit near 12.4). my numbers reproduce the report: cal CE 2.5496 β 2.5510 (reported 2.5501 β 2.5515), cal KLD 0.000140 (0.00014), p4 OOS KLD 0.00202 (0.002039), and all 126 FTPO p(rejected) before/after within 0.0012. the trainer does what its JSON says.
- real-engine 3-domain KLD vs Phase 3 (parent's run): ops 0.0061, chat_json 0.0002, ja 0.0008. ops_tools sits under its ~0.009 float-order floor.
what doesn't hold yet (don't ship it as the anti-slop result):
- every slop number is in-sample. the 126 FTPO tuples, the 8 "active" and the 95 "mined" positions are all training positions on the same trace. no held-out slop context was scored.
- the training trace is synthetic word salad, not model output:
"Rather instead place"rather instead placeβ¦,End to cleanly suite[End to cleanly suite\nend to cleanly suiteβ¦,what's what at[What's what atβ¦. the 126 FTPO positions do end in the banned prefixes (126/126), but those contexts are nothing like generations. - the "mined" loss suppresses the six final tokens in every context β the objective is not FTPO.
mined_*takes any position where " end", " than", " what", " actually", " away" or " version" has p β₯ 0.005, with no check that the preceding tokens are the banned prefix.- of the 54 mined positions outside the FTPO/SFT sets, 0 of 54 follow a banned prefix. they're ordinary uses, and all were pushed down (mean p 0.030 β 0.0039, β87%).
- examples:
the kprof trace shows β " what"0.016 β 0.0023;yeah, that's β " what"0.014 β 0.0009;smap_ update into at β " end"0.007 β 0.0008. - so it learns "say ' what' / ' than' / ' end' less", not "don't say 'here's what' / 'rather than' / 'end to end'".
- the KLD pass can't see that damage: the three eval texts (223 / 520 / 308 tokens) contain zero occurrences of any of the six tokens, and the cal text has none as a target either.
to accept a v3 bundle:
- (a) restrict the mined loss to positions whose preceding tokens match a banned tuple's prefix (
tuple[:-1]). - (b) build the FTPO/mined set from the model's own generations (persona/ops prompts), not concatenated fragments.
- (c) hold out β₯ 30% of those slop contexts and report p(rejected) only on them.
- (d) add a collateral set: natural text with legit "more than", "what is", "the end of", "far away", "version 2", "actually" (β₯ 200
occurrences). dump its L47 states with the Phase 3 GGUF;
l47evalscores p(target) base vs new in ~20 s. bar: mean log-prob change on those targets β₯ β0.05 nats. - (e) keep the real-engine 3-domain KLD β€ 0.01.
- persona/abstention: in-sample CE 4.15 β 3.95 on 224 tokens is within what a 2560Γ640 map can do. not evidence of a voice change.
6. Phase 4 v3 (WFLD L47) verification #
same independent forward (l47eval2.cpp, Phase 3 GGUF W_sd47 vs the bundle's, exact Q6_K head over 248,320 rows), on cal_p3 (1,022 rows),
p4_ftpo_sft (1,022) and p4_collateral (1,638). per-position output: /tmp/opus_metal_probes/l47eval_v3.tsv.
reproduced exactly:
| check | mine | reported |
|---|---|---|
| bundle | 47/48 byte-identical to Phase 3, βΞWβ/βWβ = 0.0725 | 0.0725 |
| cal KLD (CE 2.5496 β 2.5515, top-1 agreement 96.87%) | 0.002952 | 0.002952 |
| FTPO train p(rejected) (28/28 end in the exact prefix, rejected = actual next token) | 0.63264 β 0.00546 | same |
| FTPO held-out p(rejected) (14/14 prefix-exact) | 0.87327 β 0.02889 | same |
| collateral, all 207 Ξlog p | +0.00690 | +0.00690 |
| collateral train_anchor (65) | +0.06797 | +0.06797 |
| collateral heldout_eval (142) | β0.02105 | β0.02105 |
| collateral occurrences after a banned prefix | 0 | 0 |
| whole collateral trace KLD (CE 2.9318 β 2.9299) | 0.0071 | β |
so against the bars from Β§5 it passes. (a) prefix-exact, (b)/(c) natural sentences with a 33% held-out split, (d) held-out collateral mean β0.021 β₯ β0.05, (e) real-engine ops/chat_json/ja = 0.0099/0.0020/0.0013 (ops sits right at its ~0.009 float-order floor).
but the collateral mean hides a per-word problem. my bar was too loose:
| held-out collateral word | n | mean Ξlog p | worst |
|---|---|---|---|
| " actually" | 16 | β0.154 | β0.844 (will actually: p 0.405 β 0.174) |
| " away" | 23 | β0.056 | β0.628 (sit a directory away: 0.427 β 0.228) |
| " end" | 24 | β0.042 | β0.231 |
| " than" | 20 | β0.019 | β0.390 (require more than: 0.423 β 0.287) |
| " version" | 32 | β0.010 | β0.373 |
| " what" | 27 | +0.091 | β0.809 (If you wonder what: 0.704 β 0.314) |
- 21.8% of held-out collateral occurrences lose more than 0.1 nats. " what" being raised on average (+0.09) is what pulls the mean over the
bar. " actually" (
what's actually) and " away" (right away) are suppressed in legit contexts too. - the FTPO held-out is weaker than it looks: its 14 positions sit in the same document, after 4 earlier uses of the same phrase. that's why p_before is higher on held-out (0.87) than on train (0.63) β in-context copying. it's held-out positions, not a held-out distribution.
recommendation: close, not quite. before shipping:
- tighten (d) to per word: each of the six words' held-out mean Ξlog p β₯ β0.05, and β€ 10% of occurrences below β0.1. a raised " what" shouldn't be allowed to offset a suppressed " actually".
- to get there: give "what's actually" and "right away" a weaker margin, or add their collateral directions (more
actually/awayanchor sentences) to the zero-inner-product set. it's one or two extra constraints in the WFLD solve. - a held-out slop set from a different document, with no earlier uses of the phrase in context. ~6 sentences per phrase, dumped like the
others;
l47eval2scores it in seconds.
if dawn wants it shipped as-is, it's a defensible improvement (β97% on the 14 held-out slop positions, macro KLD 0.0044), with a known "actually"/"away" cost.
6.1 Phase 4 v3.1 verification #
same independent forward (l47eval2), now also over the separate-document held-out slop trace (p4_ftpo_heldout_1024). per-position output:
/tmp/opus_metal_probes/l47eval_v31.tsv.
reproduced exactly:
- bundle: 47/48 byte-identical, βΞWβ/βWβ = 0.0363.
- held-out per-word collateral: than β0.0335, what +0.1632, end β0.0168, away +0.0141, version +0.0154, actually β0.0390. 13/142 below β0.1 (9.2%). the per-word bar passes.
- separate-document held-out slop (21, all prefix-exact, rejected = actual next token): p(rejected) 0.414 β 0.104. FTPO train 0.633 β 0.094, held-out-14 0.873 β 0.226. cal KLD 0.00114 (CE 2.5496 β 2.5511). collateral trace KLD 0.0040.
new problem: the suppressed mass goes to <|im_end|> (id 248046), and it shows up across chat-formatted text.
-
at the 21 separate-document held-out slop positions the new argmax is
<|im_end|>in 7/21. e.g.β¦reproduces the head forward pass end toβ<|im_end|>;Regularize against the calibration trace\nRatherβ<|im_end|>. in generation that ends the assistant's turn mid-sentence, which is worse than the slop. -
it isn't confined to slop contexts. argmax flips to
<|im_end|>at ordinary positions:| trace | argmax flips to
<|im_end|>| |---|---| | held-out chat document (1,022 positions) | 28 | | SFT document | 23 | | cal (plain text) | 0 | | collateral (plain text) | 0 |examples:
exactly what went wrong withβ<|im_end|>(was " the");Short version: we want toβ<|im_end|>(was " know"); user turns too:why did the proxyβ<|im_end|>. -
on the held-out chat document, ordinary targets (not slop, not
<|im_end|>; n = 952) lose β0.060 nats on average. CE 2.583 β 2.693, whole-document KLD 0.0236, and 0.0080 at positions more than 5 tokens from any slop position. -
why the real-engine 3-domain KLD (ops 0.0075, chat_json 0.0003, ja 0.0008) doesn't show it: those texts aren't multi-turn chat with
<think>/<|im_end|>structure, and the effect is concentrated where the L47 state looks like an open assistant or user turn. cal and the collateral trace are plain text: 0 flips. -
likely cause:
delta_cho = 0plus the per-word null-space removed the "raise a natural alternative" component, so lowering y_r frees mass that the head hands to the most generic continuation in chat context,<|im_end|>. the phrase directions also overlap with an "assistant/user turn in progress" component of z that cal (plain text) doesn't constrain.
verdict: don't ship v3.1. it fixed the per-word collateral and generalizes on the separate document, but it trades slop for early turn ends. bars to add, each checked on a held-out chat-formatted document:
- (f) 0 argmax flips to
<|im_end|>/<|im_start|>/</think>at non-terminal positions, and no rise in their mean probability at slop positions. better: an explicit penalty on special-token logits in the objective. - (g) ordinary-token Ξlog p on the held-out chat document β₯ β0.01 nats, and KLD β€ 0.005 there.
- fix ideas:
- put the background constraint on chat-formatted anchors too (a separate chat document's z, not only plain-text cal);
- restore a small
delta_cho > 0so the freed mass goes to the natural alternatives (each held-out slop position's top non-rejected token, not<|im_end|>); - add
<|im_end|>as a second "rejected" logit at every FTPO position.
6.2 Phase 4 v3.2 verification #
same independent forward (l47eval2). the rewritten held-out chat trace has 1,098 rows. per-position output: /tmp/opus_metal_probes/l47eval_v32.tsv.
reproduced:
- bundle: 47/48 byte-identical to Phase 3. βΞWβ/βWβ = 0.0978 (up from 0.0363 in v3.1; not in the report).
- (f) 0 argmax flips to
<|im_end|>/<|im_start|>/<think>/</think>/<|endoftext|>on all four traces.- flips away from
<|im_end|>: 2 (cal), 13 (SFT), 12 (held-out chat). all are mid-sentence positions where the base model wrongly preferred ending the turn (e.g.the limbo list keepsβ base<|im_end|>, now " the"). - real turn ends untouched: at the 57 positions whose actual next token is
<|im_end|>, its argmax survives 56/56 (where it was the base argmax), and Ξlog p(<|im_end|>) β₯ β0.002 on SFT/held-out (one cal end at β0.27). - so the side effect now leans toward fewer premature ends, not more. that's fine.
- flips away from
- (g) held-out chat document, 1,077 non-slop positions: KLD 0.00472. ordinary-token Ξlog p +0.0208 (n = 1,034; median 0.000).
the largest shifts are at user-turn openers (
<|im_start|>user\nβ "what"/"how"), which don't matter for generation. - (d) per-word held-out collateral: end β0.0140, than β0.0343, what +0.1598, version +0.0071, away +0.0066, actually β0.0419; 13/142 below β0.1 (9.2%). passes.
- (a)β(c):
- separate-document slop p(rejected) 0.470 β 0.143 (β70%), 21/21 prefix-exact. the new argmax is a real word in 20/21 ("start", "answer",
"the", "how"β¦); one
<|im_end|>was already the base argmax there. - FTPO train 0.633 β 0.100, same-doc held-out 0.873 β 0.262.
- separate-document slop p(rejected) 0.470 β 0.143 (β70%), 21/21 prefix-exact. the new argmax is a real word in 20/21 ("start", "answer",
"the", "how"β¦); one
- cal KLD 0.0019 (CE 2.5496 β 2.5505). collateral trace KLD 0.0034.
verdict: v3.2 passes (a)β(g) and i'd accept it, with one caveat on how clean the evidence is.
- the held-out chat document was rewritten after v3.1's failure, and the solver was tuned over three rounds against these same held-out
numbers (per-phrase scales, the special-token rows, a hand-picked
tr_p4.z[705]constraint). - so these held-out sets are now partly development sets. for final sign-off:
- write one fresh chat document (~21 slop turns + ordinary turns) that nobody tuning the solver has looked at, dump it, and run
l47eval2once with no further changes; - if it lands within ~1.5Γ of the Β§6.2 numbers on (f)/(g)/slop reduction, ship v3.2.
- write one fresh chat document (~21 slop turns + ordinary turns) that nobody tuning the solver has looked at, dump it, and run
- expected cost of the trade: slop is suppressed less than in v3 (β70% vs β97% held-out), in exchange for no turn-ending damage. that's the right side of the trade.
6.3 Blind chat test sign-off #
untouched v3.2 bundle (sha256 4f8d9de8β¦db10, βΞWβ/βWβ = 0.0978, the same as Β§6.2). fresh 1,100-token multi-turn chat document
(p4_blind_test_1100, 1,098 rows, 21 slop positions, 3 per phrase). one run of l47eval2, no changes. per-position output:
/tmp/opus_metal_probes/l47eval_blind.tsv.
| check | blind | Β§6.2 (dev) | within ~1.5Γ? |
|---|---|---|---|
| slop p(rejected), 21 positions (all prefix-exact, rejected = actual next token) | 0.554 β 0.253 (β54%) | 0.470 β 0.143 (β70%) | residual 1.77Γ β just outside |
| argmax no longer the rejected token | 14/21 (base 8/21) | 20/21 | β |
| (g) non-slop KLD | 0.0030 | 0.0047 | yes, better |
| (g) ordinary-token Ξlog p (n = 1,029) | +0.012 (3.0% below β0.1) | +0.021 | yes |
| (f) argmax flips to a special token | 2 | 0 | see below |
| real turn ends (48), Ξlog p(`< | im_end | >`) | +0.002 mean |
| whole-doc CE / KLD | 1.677 β 1.696 / 0.014 | β | β |
per phrase (base β new mean p(rejected)), on the blind document:
| phrase | base | new |
|---|---|---|
| end to end | 0.984 | 0.263 |
| short version | 0.274 | 0.034 |
| here's what | 0.428 | 0.099 |
| exactly what | 0.272 | 0.063 |
| what's actually | 0.056 | 0.005 |
| right away | 0.893 | 0.540 |
| rather than | 0.974 | 0.767 |
- the two flips to a special token:
- one is a fix:
β¦lets readers proceed concurrently.β<|im_end|>is the true next token; the base predicted\n\n. - the other is at a user-turn opener (
<|im_start|>user\nβ<|im_end|>instead of "how"), a position the assistant never generates. - in the other direction there are 10 flips away from mid-sentence
<|im_end|>(benign, as in Β§6.2), and one real turn end after a kaomoji now prefers another kaomoji glyph ((Β΄β’ Ο β’)βΒ΄instead of<|im_end|>`). mild. - no assistant-side turn damage.
- one is a fix:
- the 3 other uses of the six words in the blind document are all " end" opening an "end-to-end" phrase (
Running the integration suiteβ " end", Ξ β0.08β¦β0.32). that's the banned phrase's first word getting less likely, which is fine.
sign-off: ship v3.2. it generalizes safely to unseen chat: KLD 0.003, ordinary tokens unharmed, no assistant turn-ending damage.
- its slop effect is partial on fresh text: β54% overall. strong on 5 phrases, weak on the two the model is surest about: "rather than" (β21%) and "right away" (β40%), where base p β 0.9β0.97.
- don't push the L47 solve harder for those: that's what broke v3.1.
- cover the residual at the sampler instead: a prefix-conditioned logit penalty. when the last token(s) match a banned tuple's prefix,
subtract a fixed bias (e.g. 4β6) from that tuple's final token.
- it's exact, costs nothing, touches no other context, and stacks with v3.2.
- in Strata it belongs where the sampler sees the token history, or as a 6-token bias applied to
lm_before argmax when the previous token is one of the prefix tokens.