| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- # SPDX-License-Identifier: Apache-2.0
- # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
- import torch
- import torch.nn.functional as F
- from torch import nn
- from vllm.compilation.backends import set_model_tag
- from vllm.compilation.decorators import support_torch_compile
- from vllm.config import CacheConfig, VllmConfig
- from vllm.model_executor.layers.linear import ReplicatedLinear
- from vllm.model_executor.layers.logits_processor import LogitsProcessor
- from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
- from .qwen3_dflash import (
- DFlashQwen3DecoderLayer,
- DFlashQwen3ForCausalLM,
- DFlashQwen3Model,
- )
- from .utils import maybe_prefix
- def _grouped_conv(
- hidden_states: torch.Tensor,
- delta: torch.Tensor,
- base: torch.Tensor,
- block_size: int,
- num_groups: int,
- group_size: int,
- taps: int,
- ) -> torch.Tensor:
- blocks = hidden_states.unflatten(-1, (num_groups, group_size))
- coefficients = base.view(1, taps, num_groups, group_size) + delta.unsqueeze(-1)
- output = coefficients[:, 0] * blocks
- position = torch.arange(hidden_states.shape[0], device=hidden_states.device)
- if block_size & (block_size - 1) == 0:
- position = position & (block_size - 1)
- else:
- position = position % block_size
- for tap in range(1, taps):
- shifted = F.pad(blocks[:-tap], (0, 0, 0, 0, tap, 0))
- output += coefficients[:, tap] * shifted * (position >= tap).view(-1, 1, 1)
- return output.flatten(-2)
- class DFlashGroupedConv(nn.Module):
- def __init__(
- self,
- hidden_size: int,
- taps: int,
- group_size: int,
- block_size: int,
- params_dtype: torch.dtype,
- quant_config: QuantizationConfig | None,
- prefix: str,
- ) -> None:
- super().__init__()
- if hidden_size % group_size:
- raise ValueError(
- f"conv_group_size={group_size} must divide hidden_size={hidden_size}."
- )
- self.block_size = block_size
- self.taps = taps
- self.group_size = group_size
- self.num_groups = hidden_size // group_size
- self.base_kernel = nn.Parameter(
- torch.empty(2, taps, hidden_size, dtype=params_dtype),
- requires_grad=False,
- )
- self.kernel_projection = ReplicatedLinear(
- hidden_size,
- 2 * taps * self.num_groups,
- bias=False,
- params_dtype=params_dtype,
- quant_config=quant_config,
- prefix=maybe_prefix(prefix, "kernel_projection"),
- return_bias=False,
- )
- def _convolve(
- self, hidden_states: torch.Tensor, delta: torch.Tensor, side: int
- ) -> torch.Tensor:
- return _grouped_conv(
- hidden_states,
- delta,
- self.base_kernel[side],
- self.block_size,
- self.num_groups,
- self.group_size,
- self.taps,
- )
- def prepare(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
- coefficients = self.kernel_projection(hidden_states).reshape(
- hidden_states.shape[0], 2, self.taps, self.num_groups
- )
- return self._convolve(hidden_states, coefficients[:, 0], 0), coefficients[:, 1]
- def finish(
- self, hidden_states: torch.Tensor, coefficients: torch.Tensor
- ) -> torch.Tensor:
- return self._convolve(hidden_states, coefficients, 1)
- class DFlash2Qwen3DecoderLayer(DFlashQwen3DecoderLayer):
- def __init__(
- self,
- vllm_config: VllmConfig,
- *,
- config,
- layer_idx: int,
- cache_config: CacheConfig | None = None,
- quant_config: QuantizationConfig | None = None,
- prefix: str = "",
- ) -> None:
- super().__init__(
- vllm_config,
- config=config,
- layer_idx=layer_idx,
- cache_config=cache_config,
- quant_config=quant_config,
- prefix=prefix,
- )
- draft_config = config.dflash_config
- speculative_config = vllm_config.speculative_config
- assert speculative_config is not None
- conv_args = dict(
- hidden_size=config.hidden_size,
- taps=int(draft_config["conv_kernel_size"]),
- group_size=int(draft_config["conv_group_size"]),
- # Query tokens per request: the bonus token plus the mask tokens.
- block_size=1 + speculative_config.num_speculative_tokens,
- params_dtype=vllm_config.model_config.dtype,
- quant_config=quant_config,
- )
- self.attention_conv = DFlashGroupedConv(
- **conv_args, prefix=maybe_prefix(prefix, "attention_conv")
- )
- self.mlp_conv = DFlashGroupedConv(
- **conv_args, prefix=maybe_prefix(prefix, "mlp_conv")
- )
- def forward(
- self,
- positions: torch.Tensor,
- hidden_states: torch.Tensor,
- residual: torch.Tensor | None,
- ) -> tuple[torch.Tensor, torch.Tensor]:
- if residual is None:
- residual = hidden_states
- hidden_states = self.input_layernorm(hidden_states)
- else:
- hidden_states, residual = self.input_layernorm(hidden_states, residual)
- hidden_states, coefficients = self.attention_conv.prepare(hidden_states)
- hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
- hidden_states = self.attention_conv.finish(hidden_states, coefficients)
- hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
- hidden_states, coefficients = self.mlp_conv.prepare(hidden_states)
- hidden_states = self.mlp(hidden_states)
- hidden_states = self.mlp_conv.finish(hidden_states, coefficients)
- return hidden_states, residual
- def _score_edges(
- predecessor_table: torch.Tensor,
- successor_table: torch.Tensor,
- candidate_ids: torch.Tensor,
- unary_logits: torch.Tensor,
- hidden: torch.Tensor,
- anchor_token_ids: torch.Tensor,
- top_k: int,
- ) -> torch.Tensor:
- successors = successor_table[candidate_ids]
- predecessor_ids = torch.cat(
- (
- anchor_token_ids[:, None, None].expand(-1, 1, top_k),
- candidate_ids[:, :-1],
- ),
- dim=1,
- )
- predecessors = predecessor_table[predecessor_ids]
- return unary_logits[:, :, None] + torch.einsum(
- "blpr,blcr->blpc", predecessors * hidden[:, :, None], successors
- )
- @support_torch_compile
- class CandidateSelector(nn.Module):
- def __init__(
- self,
- hidden_size: int,
- vocab_size: int,
- rank: int,
- top_k: int,
- params_dtype: torch.dtype,
- quant_config: QuantizationConfig | None,
- prefix: str,
- ) -> None:
- super().__init__()
- self.top_k = top_k
- self.predecessor_codebook = nn.Parameter(
- torch.empty(vocab_size, rank, dtype=params_dtype), requires_grad=False
- )
- self.successor_codebook = nn.Parameter(
- torch.empty(vocab_size, rank, dtype=params_dtype), requires_grad=False
- )
- self.hidden_projection = ReplicatedLinear(
- hidden_size,
- rank,
- bias=False,
- params_dtype=params_dtype,
- quant_config=quant_config,
- prefix=maybe_prefix(prefix, "hidden_projection"),
- return_bias=False,
- )
- def forward(
- self,
- candidate_ids: torch.Tensor,
- unary_logits: torch.Tensor,
- hidden_states: torch.Tensor,
- anchor_token_ids: torch.Tensor,
- ) -> torch.Tensor:
- hidden = self.hidden_projection(hidden_states)
- return _score_edges(
- self.predecessor_codebook,
- self.successor_codebook,
- candidate_ids,
- unary_logits,
- hidden,
- anchor_token_ids,
- self.top_k,
- )
- class DFlash2Qwen3Model(DFlashQwen3Model):
- decoder_layer_cls = DFlash2Qwen3DecoderLayer
- def __init__(
- self,
- *,
- vllm_config: VllmConfig,
- start_layer_id: int = 0,
- prefix: str = "",
- ) -> None:
- super().__init__(
- vllm_config=vllm_config,
- start_layer_id=start_layer_id,
- prefix=prefix,
- )
- draft_config = self.config.dflash_config
- self.input_embedding_scale = float(
- draft_config.get("input_embedding_scale", 1.0)
- )
- # Without its own tag the selector shares the draft head's compile cache.
- with set_model_tag("dflash2_candidate_selector"):
- self.candidate_selector = CandidateSelector(
- hidden_size=self.config.hidden_size,
- vocab_size=self.config.vocab_size,
- rank=int(draft_config["selector_rank"]),
- top_k=int(draft_config["selector_top_k"]),
- params_dtype=vllm_config.model_config.dtype,
- quant_config=self.quant_config,
- prefix=maybe_prefix(prefix, "candidate_selector"),
- )
- def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
- return super().embed_input_ids(input_ids) * self.input_embedding_scale
- class DFlash2Qwen3ForCausalLM(DFlashQwen3ForCausalLM):
- model_cls = DFlash2Qwen3Model
- def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
- super().__init__(vllm_config=vllm_config, prefix=prefix)
- draft_config = self.config.dflash_config
- softcap = float(draft_config.get("final_logit_softcapping") or 0.0)
- self.candidate_logits_processor = LogitsProcessor(
- vllm_config.model_config.get_vocab_size(),
- scale=float(draft_config.get("output_multiplier", 1.0)),
- soft_cap=softcap if softcap > 0 else None,
- )
- def compute_candidates(
- self, hidden_states: torch.Tensor
- ) -> tuple[torch.Tensor, torch.Tensor]:
- return self.candidate_logits_processor.get_top_k_tokens(
- self.lm_head, hidden_states, self.model.candidate_selector.top_k
- )
- EntryClass = DFlash2Qwen3ForCausalLM
|