main
py 191 lines 6.94 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """NVIDIA multi-token predictor for Dots3Note."""
4
5 from collections.abc import Iterable, Iterator
6
7 import torch
8 import torch.nn as nn
9
10 from vllm.config import VllmConfig
11 from vllm.distributed import (
12 tensor_model_parallel_all_gather,
13 tensor_model_parallel_all_reduce,
14 )
15 from vllm.model_executor.layers.fused_embed_norm import (
16 fused_embed_eh_norm,
17 has_full_vocab_on_rank,
18 )
19 from vllm.model_executor.layers.layernorm import RMSNorm
20 from vllm.model_executor.layers.linear import ReplicatedLinear
21 from vllm.model_executor.layers.logits_processor import LogitsProcessor
22 from vllm.model_executor.layers.vocab_parallel_embedding import VocabParallelEmbedding
23 from vllm.model_executor.models.deepseek_mtp import SharedHead
24 from vllm.model_executor.models.utils import maybe_prefix
25 from vllm.models.deepseek_v32.common.kernels import fused_eh_norm
26 from vllm.models.deepseek_v32.nvidia.mtp import (
27 DeepseekV32MTP,
28 DeepseekV32MultiTokenPredictor,
29 )
30
31 from .model import Dots3NoteDecoderLayer, _pad_dense_mlp_weight
32
33
34 class Dots3NoteMultiTokenPredictorLayer(nn.Module):
35 def __init__(self, vllm_config: VllmConfig, prefix: str) -> None:
36 super().__init__()
37 assert vllm_config.speculative_config is not None
38 config = vllm_config.speculative_config.draft_model_config.hf_config
39 quant_config = vllm_config.quant_config
40
41 self.enorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
42 self.hnorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
43 self.eh_proj = ReplicatedLinear(
44 config.hidden_size * 2,
45 config.hidden_size,
46 bias=False,
47 quant_config=quant_config,
48 prefix=f"{prefix}.eh_proj",
49 )
50 self.shared_head = SharedHead(
51 config=config, prefix=prefix, quant_config=quant_config
52 )
53 self.mtp_block = Dots3NoteDecoderLayer(
54 vllm_config=vllm_config,
55 config=config,
56 prefix=prefix,
57 )
58
59 def forward(
60 self,
61 input_ids: torch.Tensor,
62 positions: torch.Tensor,
63 previous_hidden_states: torch.Tensor,
64 inputs_embeds: torch.Tensor | None = None,
65 embed_table: torch.Tensor | None = None,
66 spec_step_index: int = 0,
67 ) -> torch.Tensor:
68 del spec_step_index
69 if embed_table is not None:
70 eh_input = fused_embed_eh_norm(
71 positions,
72 input_ids,
73 embed_table,
74 previous_hidden_states,
75 self.enorm.weight,
76 self.hnorm.weight,
77 self.enorm.variance_epsilon,
78 )
79 else:
80 assert inputs_embeds is not None
81 eh_input = fused_eh_norm(
82 positions,
83 inputs_embeds,
84 previous_hidden_states,
85 self.enorm.weight,
86 self.hnorm.weight,
87 self.enorm.variance_epsilon,
88 )
89 hidden_states = self.eh_proj(eh_input)[0]
90 hidden_states, residual = self.mtp_block(
91 positions=positions, hidden_states=hidden_states, residual=None
92 )
93 is_sequence_parallel = self.mtp_block.use_sequence_parallel_moe
94 if not is_sequence_parallel:
95 hidden_states = tensor_model_parallel_all_reduce(hidden_states)
96 hidden_states, _ = self.shared_head.norm(hidden_states, residual)
97 if is_sequence_parallel:
98 hidden_states = tensor_model_parallel_all_gather(hidden_states, 0)
99 hidden_states = hidden_states[: positions.shape[0]]
100 return hidden_states
101
102
103 class Dots3NoteMultiTokenPredictor(DeepseekV32MultiTokenPredictor):
104 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
105 nn.Module.__init__(self)
106 assert vllm_config.speculative_config is not None
107 config = vllm_config.speculative_config.draft_model_config.hf_config
108 self.mtp_start_layer_idx = config.num_hidden_layers
109 self.num_mtp_layers = config.num_nextn_predict_layers
110 self.layers = nn.ModuleDict(
111 {
112 str(idx): Dots3NoteMultiTokenPredictorLayer(
113 vllm_config, f"{prefix}.layers.{idx}"
114 )
115 for idx in range(
116 self.mtp_start_layer_idx,
117 self.mtp_start_layer_idx + self.num_mtp_layers,
118 )
119 }
120 )
121 self.embed_tokens = VocabParallelEmbedding(
122 config.vocab_size,
123 config.hidden_size,
124 prefix=maybe_prefix(prefix, "embed_tokens"),
125 )
126 self.replicated_embed = has_full_vocab_on_rank(self.embed_tokens)
127 self.register_buffer(
128 "max_token_id",
129 torch.tensor(config.vocab_size - 1, dtype=torch.int64),
130 persistent=False,
131 )
132 self.logits_processor = LogitsProcessor(config.vocab_size)
133
134 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
135 input_ids = torch.minimum(input_ids, self.max_token_id)
136 return self.embed_tokens(input_ids)
137
138 def forward(
139 self,
140 input_ids: torch.Tensor,
141 positions: torch.Tensor,
142 previous_hidden_states: torch.Tensor,
143 inputs_embeds: torch.Tensor | None = None,
144 spec_step_idx: int = 0,
145 ) -> torch.Tensor:
146 if inputs_embeds is None:
147 input_ids = torch.minimum(input_ids, self.max_token_id)
148 return super().forward(
149 input_ids,
150 positions,
151 previous_hidden_states,
152 inputs_embeds,
153 spec_step_idx,
154 )
155
156
157 class Dots3NoteMTP(DeepseekV32MTP):
158 has_own_embed_tokens = True
159 has_own_lm_head = False
160
161 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
162 nn.Module.__init__(self)
163 self.config = vllm_config.model_config.hf_config
164 self.quant_config = vllm_config.quant_config
165 self.model = Dots3NoteMultiTokenPredictor(
166 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
167 )
168 self.set_moe_parameters()
169
170 def _adapt_weights(
171 self, weights: Iterable[tuple[str, torch.Tensor]]
172 ) -> Iterator[tuple[str, torch.Tensor]]:
173 mtp_layer = self.config.num_hidden_layers
174 for name, weight in weights:
175 if name.startswith("model.mtp.embed_tokens."):
176 name = name.replace(
177 "model.mtp.embed_tokens.",
178 f"model.layers.{mtp_layer}.embed_tokens.",
179 1,
180 )
181 yield (
182 name,
183 _pad_dense_mlp_weight(
184 name,
185 weight,
186 getattr(self.quant_config, "weight_block_size", None),
187 ),
188 )
189
190 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
191 return super().load_weights(self._adapt_weights(weights))