phanerozoic's picture
spec-decode-ops v1: kernel-builder source tree
6aac1a2 verified
Raw
History Blame
4.7 kB
"""spec-decode-ops: fused sampling and speculative-decoding verification.
sample() executes the transformers logits pipeline (repetition penalty ->
temperature -> top-k -> top-p -> min-p -> categorical draw) in a fused form:
one optional segmented sort plus one kernel, sampling by the Gumbel-argmax
identity. Sort-free fast paths cover greedy decoding and unfiltered sampling.
verify() implements canonical speculative-decoding rejection sampling
(Leviathan et al.; Chen et al.). Draft token x_i is accepted with probability
min(1, p_target(x_i) / p_draft(x_i)); the first rejection is replaced by a
sample from the renormalized residual max(p_target - p_draft, 0); full
acceptance appends a bonus token from the target's final position. The
emitted token stream is distributed exactly as the target distribution.
Randomness is counter-based Philox parameterized by (seed, offset): results
are bitwise reproducible for fixed seed, offset, shape, and architecture.
"""
from typing import Optional, Tuple
import torch
from ._ops import ops
def sample(
logits: torch.Tensor,
temperature: float = 1.0,
top_k: int = 0,
top_p: float = 1.0,
min_p: float = 0.0,
repetition_penalty: float = 1.0,
prev_tokens: Optional[torch.Tensor] = None,
seed: int = 0,
offset: int = 0,
) -> torch.Tensor:
"""Sample one token per row of logits.
Args:
logits: [M, V] f32/bf16/f16 CUDA tensor.
temperature: 0 selects greedy argmax; otherwise logits are divided by it.
top_k: keep the k highest-probability tokens (0 disables).
top_p: nucleus filtering; keep the smallest prefix of the sorted
distribution with cumulative probability >= top_p (1.0 disables).
min_p: keep tokens with p >= min_p * p_max (0 disables).
repetition_penalty: applied to prev_tokens before temperature
(l/r for l > 0 else l*r), matching transformers.
prev_tokens: [M, P] int64, -1 padded; required if penalty != 1.
seed, offset: Philox counter parameters.
Returns:
[M] int64 token ids.
"""
logits = logits.contiguous()
tokens = torch.empty(logits.shape[0], dtype=torch.int64, device=logits.device)
ops.fused_sample(tokens, logits, prev_tokens, float(temperature), int(top_k),
float(top_p), float(min_p), float(repetition_penalty),
int(seed), int(offset))
return tokens
def filter_logits(
logits: torch.Tensor,
temperature: float = 1.0,
top_k: int = 0,
top_p: float = 1.0,
min_p: float = 0.0,
repetition_penalty: float = 1.0,
prev_tokens: Optional[torch.Tensor] = None,
) -> torch.Tensor:
"""Return the filtered logits (temperature-scaled, masked to -inf), the
same tensor the transformers processor chain would hand to the sampler.
Useful for composing filtered distributions with verify()."""
logits = logits.contiguous()
out = torch.empty(logits.shape, dtype=torch.float32, device=logits.device)
ops.fused_filter(out, logits, prev_tokens, float(temperature), int(top_k),
float(top_p), float(min_p), float(repetition_penalty))
return out
def verify(
target_logits: torch.Tensor,
draft_logits: torch.Tensor,
draft_tokens: torch.Tensor,
temperature: float = 1.0,
seed: int = 0,
offset: int = 0,
) -> Tuple[torch.Tensor, torch.Tensor]:
"""Speculative-decoding verification.
Args:
target_logits: [B, k+1, V] target-model logits over the draft
positions plus one (the bonus position).
draft_logits: [B, k, V] draft-model logits at the same positions.
draft_tokens: [B, k] int64 tokens the draft model sampled.
temperature: sampling temperature applied to both distributions;
0 selects greedy verification (accept while target argmax equals
the draft token).
Returns:
(accept_len [B] int64, out_tokens [B, k+1] int64). For sequence b,
out_tokens[b, :accept_len[b]] are accepted draft tokens and
out_tokens[b, accept_len[b]] is the correction or bonus token, so
accept_len[b] + 1 tokens are always emitted.
"""
B, kp1, V = target_logits.shape
k = kp1 - 1
accept_len = torch.empty(B, dtype=torch.int64, device=target_logits.device)
out_tokens = torch.empty(B, k + 1, dtype=torch.int64, device=target_logits.device)
ops.spec_verify(accept_len, out_tokens, target_logits.contiguous(),
draft_logits.contiguous(), draft_tokens.contiguous(),
float(temperature), int(seed), int(offset))
return accept_len, out_tokens
__all__ = ["sample", "filter_logits", "verify"]