Repository navigation
Conversation
…ost, equal to its Python within fp32 rounding
…ine's final rows; the Qwen 27B CUDA family gives them
…ies in encoder order
… block, and its attention split by query rows across threads; bits unchanged
ashhart
left a comment
There was a problem hiding this comment.
Thanks @gilby. We can't take this as written: bf16vec rounds the stock checkpoint's f32 GDN norm weights to bf16 at load, for every request including chat, and on by default. That's a lossy load, which the engine doesn't do even opt-in. Two ways forward: read the f32 weights through a kernel variant (registered with the CUDA kernel contract, with records), or keep refusing these checkpoints and document an explicit conversion step. The rest of the stack follows #1361 and #1364's changes.
|
Yup. I didn't like it either, and am having my agent do it better. ;) We had an awesome setup with Qwen3.8-27B and Clef on the old python branch, where it just loads the weights once, and wanted to bring this to the Zig branch along with the faster 9B one. We'll get'r done |
…s the qwen3_5 CUDA family
…pass, on a cache reserved at startup
…vs 4 head threads bit-equal, in CI
…s decision passes only, y += (bf16(x A^T)) B^T in fp32; never inside a capture
d4559f9 to
6ab5c0f
Compare
|
Thanks, agreed: a lossy cast at load is the wrong default. I took the second route. The branch no longer touches the loader (
The conversion is an explicit step instead:
Gate on this head (one DGX Spark, GB10, CUDA 13.0,
The title and body are updated to match. |
|
Review against main e9b69ed (shared notes on #1361). |
… (tensor cores), packed and their scratch reserved at startup; DenseRows stays the fallback
…32 GDN norms rounded to bf16, which the CUDA family refuses rather than rounds at load Stdlib only; round to nearest, ties to even, as torch's cast; every other tensor and file is unchanged (shards without such a norm are hard-linked) and the index's total size is rewritten. Its unittest runs in no-gpu CI.
6ab5c0f to
7f1b31c
Compare
Clef-Flash as a runtime adapter on
Qwen/Qwen3.5-9B, like the 27B Clef of #1364: one server answers chat with the base weights andPOST /v1/systemonewith Cloudflare's Clef-Flash applied to decision pieces only. Stacked on #1364 (runtime adapter), which is stacked on #1361 (Clef endpoint and head, issue #1360); the only new commit here is the last one.Change
The qwen3_5 CUDA family reads GDN norm weights as bf16, and stock
Qwen/Qwen3.5-9Bstores its 24linear_attn.norm.weighttensors in f32, so the stock checkpoint is refused (… f32 { 128 }, expected bf16 { 128 }). This PR keeps that refusal and adds an explicit conversion step instead of rounding at load:*.linear_attn.norm.weightis rounded to bf16 (nearest, ties to even, as torch's cast; bit-identical to torch on 200,006 values); every other tensor is unchanged (A_logstays f32, which the family reads as f32). Cloudflare's merged release stores these norms rounded the same way.tools/zig/qwen35_bf16_norms_test.py(ties, NaN, only the norms change, other files byte-identical, the refusal) runs inno-gpu.yml.Nothing else changes: #1364's adapter code already walks every layer and projection by name and checks each factor against the model's widths, so the 9B's dense FFN, GDN and attention projections work as they are.
Adapter weights
Build the adapter directory with #1366's
tools/clef_adapter.py --merged <Cloudflare/clef-flash> --base <Qwen/Qwen3.5-9B> --out <dir>: 248 projections differ (every linear projection of all 32 layers, rank 256 or the full rank 32 for the GDNin_proj_a/in_proj_b); the other 48 differing tensors are the f32A_log/ norm weights, equal after rounding to bf16. The residual of each factorisation is at most 1.02× the bf16 rounding floor of the merge.Receipts (one DGX Spark, GB10 128 GB, CUDA 13.0,
-Dsm=121; Cloudflare's 60 records / 557 questions; head7f1b31c2d)Qwen/Qwen3.5-9B+--clef <adapter dir>, against Cloudflare's own bf16 output: mean KL 5.20e-5, top choice 99.64% (≤1K 100%, 1–4K 100%, 4–10K 99.6%, >10K 99.4%). Gate (mean KL ≤ 0.05, top choice ≥ 95%): pass.bench_concurrent --tokens 128 --reps 3 --temperatures 0 --levels 1,4 --alone --serial: 0 unequal, 0 failed.--clef, same binary, same session (two rounds each):bench_concurrent --tokens 256 --reps 3 --temperatures 0 --levels 1,4 --alone --serialgives the same tok/s (code 14.9 / 56.2, chat 14.3 / 54.0 at 1 / 4 streams) and the same token shas; the adapter costs 2.1 GiB at startup (model line 17.61 → 19.75 GiB, the decision cache and the packed adapter).Decision latency (60 records one at a time,
--parallel 4 --context 32768)#1364 now runs the adapter's two products on the base's bf16 lane GEMM (tensor cores) instead of
DenseRows. On this base, where all 32 layers are adapted, that took decisions from about 4× the merged release served on its own to about 1.2×:DenseRowsp50 / p95 (s)The rest is the adapter's own work (rank-256 products on every projection, about 7% of the base's multiply-adds, plus a bf16 add per projection).
Checks
python3 -m unittest discover -s tools/zig -p qwen35_bf16_norms_test.py: 3/3.lean_check.pyadds no problems; the sm_121 CUDA build succeeds.