Skip to content

dflash: load and apply DSpark log-SNR conditioning - #123

Merged
khosravipasha merged 1 commit into
prism-v7from
dflash-log-snr-v7
Aug 26, 2026
Merged

dflash: load and apply DSpark log-SNR conditioning#123
khosravipasha merged 1 commit into
prism-v7from
dflash-log-snr-v7

Conversation

@bri-prism

Copy link
Copy Markdown

Re-lands the DSpark log-SNR conditioning from the closed prism-v6 work, which is what currently makes GIDD-bundle drafters unloadable on this branch.

The symptom

llama_model_load: error loading model: done_getting_tensors: wrong number of tensors; expected 79, got 75
common_speculative_init_result: failed to load draft model

The message names no tensor, which is misleading. The file has 79 tensors and the loader creates 75, and the natural first guess is the DSpark Markov head, since that is the one optional block in load_arch_tensors. It is not: instrumenting the loader shows markov_w1.weight is found and the head's four tensors are created normally.

The four that are never created are log_snr_fc1.weight, log_snr_fc1.bias, log_snr_fc2.weight and log_snr_fc2.bias. This branch has no log-SNR support at all, so nothing ever claims them, and done_getting_tensors refuses the load.

Why not just skip them

The drafter's metadata sets log_snr_conditioning = true along with min_log_snr and max_log_snr. That means it was trained with the conditioning, so the weights are part of the function. Marking the tensors optional would let the model load and quietly compute a different draft embedding on every forward pass. A load failure is the better of the two, which is why this ports the feature rather than relaxing the check.

What this adds

  • three metadata keys, two tensor kinds, the hparams, and the four model tensors
  • llm_graph_input_dspark_logsnr, which stages the sinusoidal feature matrix. The per-position log-SNR pattern (each block's anchor at max_log_snr, mask positions at min_log_snr) and its featurization are a pure function of values known at graph-build time, so the host precomputes the whole [128, n_tokens] matrix once. That keeps it auditable line-for-line against the training reference instead of chaining arange/sin/cos in-graph.
  • the two-layer SiLU MLP, added to the draft noise embedding before the layer loop

The weights are required when the flag is set, and min/max must be finite with max > min, because a zero span divides through the featurizer and fills the conditioning input with NaNs rather than failing.

The DSV4 hyper-connection backbone replicates the embedding across hc lanes, so the term would need adding per lane. No checkpoint exercises that combination yet, so that path asserts rather than silently dropping a trained input.

Validation

Measured on Metal with a block-4 drafter that sets the flag, against a low-bit target:

result
before drafter cannot be loaded
after 65.891% acceptance, mean accepted length 3.58

The same numbers come out with this branch alone, and with this branch plus the layout auto-detect in the sibling PR, on both the correct and the deliberately wrong --spec-type. That last case matters because the auto-detect could not be exercised on this branch at all before this change, since no DSpark drafter would load.

Not covered: CUDA. This is graph and loader code with no backend-specific paths, and it builds clean, but every number above is Metal.

Drafters exported with the GIDD bundle carry log_snr_fc1 and log_snr_fc2, four
tensors in all, and set log_snr_conditioning, min_log_snr and max_log_snr in
their metadata. This branch had no support for any of it, so the loader created
four fewer tensors than the file holds and refused to load:

  done_getting_tensors: wrong number of tensors; expected 79, got 75

The failure names no tensor, which sent the investigation toward the Markov head
before instrumentation showed the head loads fine and the shortfall is these
four.

Ignoring the tensors instead would be worse than refusing to load. The metadata
flag means the drafter was trained with this conditioning, so dropping it would
silently change the draft embedding on every forward pass.

Ported from the closed prism-v6 work:

- three metadata keys, two tensor kinds, the hparams and the four model tensors
- llm_graph_input_dspark_logsnr, which stages the sinusoidal feature matrix. The
  per-position pattern (each block's anchor at max_log_snr, mask positions at
  min_log_snr) and its featurization are a pure function of values known at
  graph-build time, so the host precomputes the [128, n_tokens] matrix once. That
  keeps it line-for-line auditable against the training reference rather than
  chaining arange/sin/cos in-graph.
- the two-layer SiLU MLP added to the draft noise embedding before the layer loop

Required, not optional: when the flag is set the four tensors must be present,
and min/max must be finite with max greater than min, because a zero span would
fill the conditioning input with NaNs rather than fail.

The DSV4 hyper-connection backbone replicates the embedding across hc lanes, so
the term would have to be added per lane. No such checkpoint exists yet, so that
path asserts instead of silently dropping the input.

Measured on Metal with a block-4 drafter that sets the flag: the drafter now
loads, and drafts at 65.891% acceptance, mean accepted length 3.58. Before this
it could not be loaded at all.

@khosravipasha khosravipasha left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks clean merging first.

@khosravipasha
khosravipasha merged commit b8e0bf0 into prism-v7 Aug 26, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds DSpark log-SNR conditioning so GIDD-bundle drafters load and apply their trained conditioning.

Changes:

  • Adds GGUF metadata and tensor definitions.
  • Validates and loads conditioning parameters and weights.
  • Applies the sinusoidal two-layer conditioning MLP in DFlash graphs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/models/dflash.cpp Loads, validates, and applies conditioning.
src/llama-model.h Stores conditioning tensors.
src/llama-hparams.h Stores conditioning metadata.
src/llama-graph.h Defines the graph input.
src/llama-graph.cpp Uploads precomputed features.
src/llama-arch.h Adds metadata and tensor enums.
src/llama-arch.cpp Registers metadata and tensor names.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/llama-graph.h
llm_graph_input_dspark_logsnr(std::vector<float> feat) : v_feat(std::move(feat)) {}
virtual ~llm_graph_input_dspark_logsnr() = default;

void set_input(const llama_ubatch * ubatch) override;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, and this one was a real regression rather than a style point. Fixed in 9ed9b9b.

You are right that allow_reuse already compares n_tokens and n_seqs_unq, which together with min/max_log_snr (fixed per model) are everything v_feat depends on, so the input can opt in. can_reuse now returns true when the staged tensor still matches n_tokens, following the same shape as llm_graph_input_embd::can_reuse.

Re-ran the drafter afterwards to confirm nothing moved: acceptance 65.891%, mean length 3.58, identical to before.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants