bridge.training.post_training.dspark.loss#
DSpark draft training objective and acceptance metrics.
DSpark (arXiv:2607.05147) trains the draft with three position-weighted terms:
ce: cross-entropy of the draft logits against the target’s next tokens;l1: the raw probability L1 distance||p_draft - p_target||_1to the target’s next-token distribution (paper Eq. 10, weight 0.9). This is the dominant term. The factor1/2that turns L1 into a total-variation distance belongs to the acceptance label of Eq. 8 only, never to this training term;confidence: binary cross-entropy training the confidence head against the analytical acceptance label1 - 0.5 * L1.
Each block position k is weighted by exp(-k / loss_decay_gamma).
Acceptance is measured analytically as accept_rate = 1 - 0.5 * L1 and the
expected accepted prefix length of a block as tau = sum_k cumprod(accept_rate)_k + 1.
Return contract
- func:
dspark_lossreturns the Megatron-Core 3-tuple(loss, num_tokens, report), matching :func:megatron.bridge.training.losses.masked_next_token_loss:
lossis the unnormalized weighted numerator summed over the micro-batch;num_tokensis this micro-batch’s supervised token count;reportmaps each metric to a[numerator, denominator]pair so the training loop can sum both over the log window and the data-parallel group and form the global ratio once.
Nothing here divides by a local denominator, because dividing per micro-batch and
averaging afterwards computes a mean of micro-batch means, which is biased
whenever micro-batches carry different supervised-token counts. The unbiased
global mean requires calculate_per_token_loss=True on the model config, which
makes Megatron-Core accumulate num_tokens across micro-batches and scale the
gradient once by the data-parallel total in finalize_model_grads_func. With
the default calculate_per_token_loss=False, Megatron-Core divides each
micro-batch by its own num_tokens and re-introduces exactly that bias, so the
integration that calls this function must set the flag; see
- func:
assert_per_token_loss_config.
This module depends only on torch. Wiring it to a Megatron-Core forward step
is a separate integration concern.
Module Contents#
Classes#
Outputs of one DSpark training forward, consumed by :func: |
Functions#
Fail fast unless |
|
|
|
Per-row cross-entropy and probability L1 over one |
|
Per-position cross-entropy and exact FP32 probability L1, without full-vocab temporaries. |
|
Pack a reducible |
|
Compute the DSpark draft loss and acceptance metrics. |
|
Analytical acceptance diagnostics as reducible |
Data#
API#
- bridge.training.post_training.dspark.loss._PROBABILITY_CHUNK_TOKENS#
128
- class bridge.training.post_training.dspark.loss.DSparkForwardOutput#
Outputs of one DSpark training forward, consumed by :func:
dspark_loss.Shape symbols:
batch,num_blocks(sampled anchor blocks per sample),block_size(draft positions per anchor),vocab... attribute:: draft_logits
Draft logits
[batch, num_blocks, block_size, vocab]... attribute:: target_ids
Teacher-forced next token per position
[batch, num_blocks, block_size]... attribute:: eval_mask
Bool/float supervision mask
[batch, num_blocks, block_size](a block is a contiguous, in-bounds, loss-enabled prefix)... attribute:: block_keep_mask
Kept-anchor mask
[batch, num_blocks]... attribute:: confidence_pred
Optional per-position acceptance logit
[batch, num_blocks, block_size]... attribute:: aligned_target_logits
Optional target next-token logits
[batch, num_blocks, block_size, vocab](the L1 / confidence teacher).- draft_logits: torch.Tensor#
None
- target_ids: torch.Tensor#
None
- eval_mask: torch.Tensor#
None
- block_keep_mask: torch.Tensor#
None
- confidence_pred: torch.Tensor | None#
None
- aligned_target_logits: torch.Tensor | None#
None
- bridge.training.post_training.dspark.loss.assert_per_token_loss_config(model_config: Any) None#
Fail fast unless
model_config.calculate_per_token_lossis set.- Func:
dspark_lossreturns an unnormalized numerator; only the per-token-loss path sumsnum_tokensacross micro-batches and scales the gradient once by the global total. Under the default path Megatron-Core normalizes each micro-batch by its own token count, which silently trains a mean of micro-batch means.
When the forward-step wiring lands, this belongs in
ConfigContainer.validatealongside the existing context-parallelcalculate_per_token_lossrule, so it fires before training rather than at the first loss call. It lives here for now because this package has no config field to key that rule off yet. The parameter is untyped because this module deliberately imports nothing beyondtorch; the attribute is read without a default, so passing the wrong object raisesAttributeErrorrather than reporting a config error that is not there.- Parameters:
model_config – The Megatron-Core model config used for the run.
- Raises:
AttributeError – If
model_confighas nocalculate_per_token_loss.ValueError – If
calculate_per_token_lossis not enabled.
- bridge.training.post_training.dspark.loss._loss_weight_mask(
- eval_mask: torch.Tensor,
- loss_decay_gamma: float | None,
eval_mask(float) scaled by the per-position decayexp(-k / gamma).
- bridge.training.post_training.dspark.loss._chunk_terms(
- draft_logits: torch.Tensor,
- target_ids: torch.Tensor,
- target_logits: torch.Tensor | None,
Per-row cross-entropy and probability L1 over one
[rows, vocab]chunk.Both terms are derived from a single FP32
log_softmaxof the draft logits, so the draft distribution is normalized once rather than once per term.
- bridge.training.post_training.dspark.loss._ce_and_l1_per_token(
- draft_logits: torch.Tensor,
- target_ids: torch.Tensor,
- target_logits: torch.Tensor | None,
- chunk_tokens: int = _PROBABILITY_CHUNK_TOKENS,
Per-position cross-entropy and exact FP32 probability L1, without full-vocab temporaries.
Both terms need the draft’s normalized distribution over the whole vocabulary. A single
[batch, num_blocks, block_size, vocab]FP32 tensor is 2.03 GiB at reference scale (num_blocks=512,block_size=7,vocab≈150k), and computing the two terms separately keeps several of them live at once, since the cross-entropy’s ownlog_softmaxis saved for its backward. Measured on an 80 GB card, an unchunked pass costs 10.15 GiB of forward-plus-backward overhead above its inputs, against 2.06 GiB here.Chunking the flattened rows caps the temporaries at
chunk_tokens * vocabelements, and each chunk is recomputed in the backward (:func:torch.utils.checkpoint.checkpoint) so no chunk keeps its distribution alive for autograd. Chunking does not change the result: every row is reduced independently over the vocab axis.- Parameters:
draft_logits –
[..., vocab].target_ids – Teacher-forced next token per position
[...].target_logits –
[..., vocab]already detached by the caller, orNoneto compute cross-entropy only (the L1 output is then zeros).chunk_tokens – Rows of the flattened logits per chunk.
- Returns:
(ce, l1), both[...].l1is twice the total-variation distance.
- bridge.training.post_training.dspark.loss._pair(
- numerator: torch.Tensor,
- denominator: torch.Tensor,
Pack a reducible
[numerator, denominator]reporting pair.
- bridge.training.post_training.dspark.loss.dspark_loss(
- outputs: bridge.training.post_training.dspark.loss.DSparkForwardOutput,
- *,
- ce_alpha: float = 0.1,
- l1_alpha: float = 0.9,
- confidence_alpha: float = 1.0,
- loss_decay_gamma: float | None = 4.0,
- chunk_tokens: int = _PROBABILITY_CHUNK_TOKENS,
Compute the DSpark draft loss and acceptance metrics.
Follows the Megatron-Core 3-tuple loss contract: the returned loss is the unnormalized micro-batch numerator, and normalization happens once against the globally reduced
num_tokens. See the module docstring for why, and- Func:
assert_per_token_loss_configfor the config this requires.- Parameters:
outputs – The draft forward outputs; see :class:
DSparkForwardOutput. All tensors are[batch, num_blocks, block_size(, vocab)].ce_alpha – Cross-entropy weight.
l1_alpha – Weight of the raw probability-L1 term (paper Eq. 10; no
1/2).confidence_alpha – Confidence-BCE weight (used only when
outputs.confidence_predis set).loss_decay_gamma – Per-position decay;
Nonedisables it.chunk_tokens –
Rows per chunk of the FP32 probability L1; see
- func:
_l1_distance_per_token.
- Returns:
Tuple
(loss, num_tokens, report).lossis the unnormalized weighted numerator for this micro-batch;num_tokensis its supervised token count as an int tensor;reportmaps each metric name to a detached[numerator, denominator]pair that the caller sums over the log window and the data-parallel group before dividing once.- Raises:
ValueError – If the L1 term or the confidence head is requested without
aligned_target_logits.
- bridge.training.post_training.dspark.loss._acceptance_report(
- outputs: bridge.training.post_training.dspark.loss.DSparkForwardOutput,
- eval_mask: torch.Tensor,
- accept_rate: torch.Tensor,
Analytical acceptance diagnostics as reducible
[numerator, denominator]pairs.accept_rate(= 1 - 0.5 * L1) is the per-position acceptance probability. A draft token survives only if every earlier token in its block is accepted, hencetauis the running product over the block,+ 1for the verified anchor token.- Parameters:
outputs – The forward outputs (for
block_keep_mask).eval_mask – The float supervision mask
[batch, num_blocks, block_size].accept_rate – Per-position acceptance
[batch, num_blocks, block_size].
- Returns:
{"dspark accept rate": pair, "dspark tau": pair}.