Skip to content

ggml: CLAMP has a backward, and a training graph the gradient cannot pass through refuses by name instead of aborting the server - #84

Merged
joelteply merged 1 commit into
889580fc/seq-state-across-stream-countsfrom
cormac/clamp-backward-refuse
Nov 1, 2026
Merged

joelteply merged 1 commit into
889580fc/seq-state-across-stream-countsfrom
cormac/clamp-backward-refuse

Conversation

@joelteply

Copy link
Copy Markdown

The M5's 35B-A3B aborted its serving engine on its first /train (continuum
card 1287f154): the MoE router normalizes expert weights by
clamp(weights_sum, eps, inf) (llama-graph.cpp, node
ffn_moe_weights_sum_clamped), CLAMP had no backward, and
ggml_build_backward_expand GGML_ABORTs on it, taking every resident's
engine with it.

  • CLAMP backward: grad * step(x - min) * step(max - x); an infinite bound
    never clamps.
  • ggml_backward_mark is the build's marking walk, extracted; it returns the
    first node the loss's gradient cannot pass through, with why: no
    backward, an in-place write, a non-float source, or a source the
    backward asserts it gives no gradient (a MoE's experts, a softmax mask,
    rope freq factors, loss labels; a non-split GLU and a gathered
    MUL_MAT_ID are refused as structure). ggml_graph_backward_refusal
    exposes it; ggml_build_backward_expand still aborts on what it refuses.
  • ggml_opt_build runs it on the forward graph before building anything
    and refuses through opt_ctx->refusal, which opt_fail already carries to
    /train's error and /learn's outcome; static graphs built in
    ggml_opt_init keep the abort, with the reason.
  • test-backend-ops: the grad mode's own copy of the walk is replaced by
    ggml_graph_backward_refusal. mean_abs_asymm counted (0-0)/(0+0) = NaN,
    and a NaN mean passed every tolerance, so any case with a zero gradient
    anywhere passed whatever its backward computed (a CLAMP backward that
    let clamped elements through passed). Equal gradients now count zero
    error; the cases this exposes are card 05834b7c. CLAMP's case sets its
    param (it never ran) and gains the router's infinite-bound shape.
  • test-opt-dynamic-accum: an in-place clamp is refused by name, then the
    same context trains a differentiable graph.

Measured on IntelMac CPU (Release, GGML_METAL=OFF): a toy qwen35moe from
test-llama-archs trains its attention weights (loss 4.85 over 1024
tokens); with CLAMP's backward removed the same run refuses naming
ffn_moe_weights_sum_clamped-0 (the M5's abort, now a refusal); training
every weight refuses naming the experts' MUL_MAT_ID. CLAMP grad passes,
and a mutant dropping the upper bound fails it (MAA 0.27). test-opt and
test-opt-dynamic-accum pass. Metal and CUDA unbuilt here: the backward
uses STEP, SCALE (with bias) and MUL, which both backends run.

Co-Authored-By: Claude Opus 5.5 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE

…pass through refuses by name instead of aborting the server

The M5's 35B-A3B aborted its serving engine on its first /train (continuum
card 1287f154): the MoE router normalizes expert weights by
clamp(weights_sum, eps, inf) (llama-graph.cpp, node
ffn_moe_weights_sum_clamped), CLAMP had no backward, and
ggml_build_backward_expand GGML_ABORTs on it, taking every resident's
engine with it.

- CLAMP backward: grad * step(x - min) * step(max - x); an infinite bound
  never clamps.
- ggml_backward_mark is the build's marking walk, extracted; it returns the
  first node the loss's gradient cannot pass through, with why: no
  backward, an in-place write, a non-float source, or a source the
  backward asserts it gives no gradient (a MoE's experts, a softmax mask,
  rope freq factors, loss labels; a non-split GLU and a gathered
  MUL_MAT_ID are refused as structure). ggml_graph_backward_refusal
  exposes it; ggml_build_backward_expand still aborts on what it refuses.
- ggml_opt_build runs it on the forward graph before building anything
  and refuses through opt_ctx->refusal, which opt_fail already carries to
  /train's error and /learn's outcome; static graphs built in
  ggml_opt_init keep the abort, with the reason.
- test-backend-ops: the grad mode's own copy of the walk is replaced by
  ggml_graph_backward_refusal. mean_abs_asymm counted (0-0)/(0+0) = NaN,
  and a NaN mean passed every tolerance, so any case with a zero gradient
  anywhere passed whatever its backward computed (a CLAMP backward that
  let clamped elements through passed). Equal gradients now count zero
  error; the cases this exposes are card 05834b7c. CLAMP's case sets its
  param (it never ran) and gains the router's infinite-bound shape.
- test-opt-dynamic-accum: an in-place clamp is refused by name, then the
  same context trains a differentiable graph.

Measured on IntelMac CPU (Release, GGML_METAL=OFF): a toy qwen35moe from
test-llama-archs trains its attention weights (loss 4.85 over 1024
tokens); with CLAMP's backward removed the same run refuses naming
ffn_moe_weights_sum_clamped-0 (the M5's abort, now a refusal); training
every weight refuses naming the experts' MUL_MAT_ID. CLAMP grad passes,
and a mutant dropping the upper bound fails it (MAA 0.27). test-opt and
test-opt-dynamic-accum pass. Metal and CUDA unbuilt here: the backward
uses STEP, SCALE (with bias) and MUL, which both backends run.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE
@joelteply
joelteply changed the base branch from master to 889580fc/seq-state-across-stream-counts November 1, 2026 02:46
@joelteply

Copy link
Copy Markdown
Author

PASS at 5d7986b (Fable), by reading. Verified: (1) ggml_graph_backward_refusal and ggml_build_backward_expand share ggml_backward_mark, so the refused set IS the abort set by construction (no second list to drift); (2) the CLAMP backward is grad·step(x−min)·step(max−x), 0 at a clamped value and 1 for an infinite bound (the router's clamp(sum, eps, inf)); (3) on Metal both GGML_UNARY_OP_STEP and GGML_OP_SCALE are supported (ggml-metal-device.m:1457/1435), so the backward stays on the M5's GPU; (4) ggml_opt_alloc returns false with refusal set and /train, /learn surface opt_ctx->refusal, while ggml_opt_init (static graphs, no request to refuse) keeps its abort. Nit, non-blocking: ggml_graph_backward_refusal's calloc result is unchecked. I did not build it (M5 is serving); after the pin bump I run the 35B-A3B /train probe on the live lane in a serving-down window and post the result.

@joelteply
joelteply merged commit 832d78c into 889580fc/seq-state-across-stream-counts Nov 1, 2026
30 of 53 checks passed
joelteply added a commit to CambrianTech/continuum that referenced this pull request Nov 3, 2026
… were at 0.0, and CLAMP has a backward (#5724)

Both pin records move (the gitlink and core/vendor/LLAMA_CPP_PIN), 6606d1aad -> 0d7dda6ae:
- CambrianTech/llama.cpp#90: a saved page's adapter identity is its active adapters (scale != 0)
  at their file revision. The IntelMac refused every page after each relaunch because the dream
  stage had attached a second adapter at 0.0; each act then re-prefilled cold (task 519 on
  2026-11-03: 1,139 s for 13,144 tokens).
- CambrianTech/llama.cpp#84: CLAMP has a backward; a training graph the gradient cannot pass
  through refuses by name instead of aborting the server.

continuum card 3d747266


Claude-Session: https://claude.ai/code/session_01FRQWzgSfo79JtnwZywHKrE

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant