main
py 200 lines 7.18 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable
5
6 import torch
7 import torch.nn as nn
8 from transformers import CohereConfig
9
10 from vllm.compilation.decorators import support_torch_compile
11 from vllm.config import VllmConfig
12 from vllm.logger import init_logger
13 from vllm.model_executor.layers.linear import ReplicatedLinear
14 from vllm.model_executor.layers.logits_processor import LogitsProcessor
15 from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
16 from vllm.model_executor.layers.vocab_parallel_embedding import VocabParallelEmbedding
17 from vllm.model_executor.models.commandr import (
18 CohereDecoderLayer,
19 CohereForCausalLM,
20 LayerNorm,
21 )
22
23 from .utils import (
24 AutoWeightsLoader,
25 WeightsMapper,
26 get_draft_quant_config,
27 maybe_prefix,
28 process_eagle_weight,
29 )
30
31 logger = init_logger(__name__)
32
33
34 class CohereEagleDecoderLayer(CohereDecoderLayer):
35 """Eagle draft variant of CohereDecoderLayer."""
36
37 def __init__(
38 self,
39 config: CohereConfig,
40 cache_config=None,
41 quant_config: QuantizationConfig | None = None,
42 prefix: str = "",
43 ) -> None:
44 super().__init__(
45 config,
46 cache_config=cache_config,
47 quant_config=quant_config,
48 prefix=prefix,
49 )
50
51
52 @support_torch_compile
53 class CohereEagleModel(nn.Module):
54 def __init__(
55 self,
56 *,
57 vllm_config: VllmConfig,
58 prefix: str = "",
59 start_layer_id: int = 0,
60 ) -> None:
61 super().__init__()
62 assert vllm_config.speculative_config is not None
63 self.config = vllm_config.speculative_config.draft_model_config.hf_config
64 self.quant_config = get_draft_quant_config(vllm_config)
65
66 # Cohere2-targeted EAGLE drafts inherit the target's sliding-window
67 # attention pattern. ``CohereAttention`` resolves per-layer behavior
68 # via ``config.layer_types[layer_idx]`` and the eagle layers use
69 # absolute indices (target_layer_num + i), so prepend the target's
70 # ``layer_types`` to the draft's so the lookup succeeds.
71 target_text_config = vllm_config.model_config.hf_text_config
72 if hasattr(target_text_config, "layer_types") and hasattr(
73 self.config, "layer_types"
74 ):
75 self.config.layer_types = list(target_text_config.layer_types) + list(
76 self.config.layer_types
77 )
78
79 self.vocab_size = self.config.vocab_size
80 self.embed_tokens = VocabParallelEmbedding(
81 self.config.vocab_size,
82 self.config.hidden_size,
83 prefix=maybe_prefix(prefix, "embed_tokens"),
84 )
85
86 self.layers = nn.ModuleList(
87 [
88 CohereEagleDecoderLayer(
89 self.config,
90 cache_config=vllm_config.cache_config,
91 quant_config=self.quant_config,
92 prefix=maybe_prefix(prefix, f"layers.{i + start_layer_id}"),
93 )
94 for i in range(self.config.num_hidden_layers)
95 ]
96 )
97
98 # Cohere EAGLE checkpoints include a bias term on the input fusion
99 # projection (unlike LLaMA EAGLE which uses bias=False).
100 self.fc = ReplicatedLinear(
101 input_size=self.config.hidden_size * 2,
102 output_size=self.config.hidden_size,
103 bias=True,
104 params_dtype=vllm_config.model_config.dtype,
105 quant_config=self.quant_config,
106 prefix=maybe_prefix(prefix, "fc"),
107 return_bias=False,
108 )
109
110 # Cohere EAGLE applies an explicit final LayerNorm to the draft
111 # hidden states before they are consumed by the logits processor.
112 self.norm = LayerNorm(
113 param_shape=(self.config.hidden_size),
114 eps=self.config.layer_norm_eps,
115 )
116
117 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
118 return self.embed_tokens(input_ids)
119
120 def forward(
121 self,
122 input_ids: torch.Tensor,
123 positions: torch.Tensor,
124 hidden_states: torch.Tensor,
125 ) -> tuple[torch.Tensor, torch.Tensor]:
126 input_embeds = self.embed_tokens(input_ids)
127 hidden_states = self.fc(torch.cat((input_embeds, hidden_states), dim=-1))
128 residual = None
129 for layer in self.layers:
130 hidden_states, residual = layer(
131 positions,
132 hidden_states,
133 residual,
134 )
135 hidden_states, _ = self.norm(hidden_states, residual)
136 return hidden_states, hidden_states
137
138
139 class EagleCohereForCausalLM(CohereForCausalLM):
140 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
141 nn.Module.__init__(self)
142 assert vllm_config.speculative_config is not None
143 self.config = vllm_config.speculative_config.draft_model_config.hf_config
144 # Flags checked by the speculative proposer to decide whether to share
145 # embed_tokens / lm_head with the target model. Cohere EAGLE checkpoints
146 # use tied embeddings so these weights are absent from the draft file.
147 self.has_own_embed_tokens = False
148 self.has_own_lm_head = False
149 if self.config.tie_word_embeddings:
150 self.hf_to_vllm_mapper = self.hf_to_vllm_mapper | WeightsMapper(
151 orig_to_new_prefix={"model.embed_tokens.": None}
152 )
153 target_layer_num = vllm_config.model_config.get_num_layers(
154 vllm_config.parallel_config
155 )
156 self.model = CohereEagleModel(
157 vllm_config=vllm_config,
158 prefix=maybe_prefix(prefix, "model"),
159 start_layer_id=target_layer_num,
160 )
161
162 logit_scale = getattr(self.config, "logit_scale", 1.0)
163 self.logits_processor = LogitsProcessor(
164 self.config.vocab_size, scale=logit_scale
165 )
166
167 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
168 return self.model.embed_input_ids(input_ids)
169
170 def forward( # type: ignore[override]
171 self,
172 input_ids: torch.Tensor,
173 positions: torch.Tensor,
174 hidden_states: torch.Tensor,
175 inputs_embeds: torch.Tensor | None = None,
176 ) -> tuple[torch.Tensor, torch.Tensor]:
177 if inputs_embeds is not None:
178 raise NotImplementedError(
179 f"{type(self).__name__} does not support multimodal inputs yet."
180 )
181 return self.model(input_ids, positions, hidden_states)
182
183 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
184 def _track_and_forward(inputs):
185 name, weight = inputs
186 process_eagle_weight(self, name)
187 return name, weight
188
189 loader = AutoWeightsLoader(self)
190
191 loaded_weight_names = loader.load_weights(
192 map(_track_and_forward, weights), mapper=self.hf_to_vllm_mapper
193 )
194
195 # Embed tokens are tied with the target model and therefore not
196 # present in the EAGLE checkpoint; mark them as loaded explicitly to
197 # avoid a spurious "weight not found" warning from the default
198 # weight loader.
199 loaded_weight_names.add("model.embed_tokens.weight")
200 return loaded_weight_names