@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / c6b636f9fb
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 08:30 UTC

[V1][Spec Decoding] Use model_loader.get_model() to load models (#18273)

Signed-off-by: Mark McLoughlin <markmc@redhat.com>

Mark McLoughlin committed May 23, 2025 at 03:05 UTC c6b636f9fbfd0308ee8d883afd8ccd7ef823eb25
16 files changed +59 -135
tests/v1/spec_decode/test_eagle.py
+6 -52
@@ -117,34 +117,13 @@ def test_prepare_inputs():
117 ])
118 @mock.patch('vllm.v1.spec_decode.eagle.get_pp_group')
119 @mock.patch('vllm.v1.spec_decode.eagle.get_layers_from_vllm_config')
120 -@mock.patch('vllm.v1.spec_decode.eagle.ModelRegistry')
121 -@mock.patch('vllm.v1.spec_decode.eagle.get_model_loader')
122 -@mock.patch('vllm.v1.spec_decode.eagle.set_default_torch_dtype')
123 -@mock.patch('vllm.v1.spec_decode.eagle.set_current_vllm_config')
124 -def test_load_model(mock_set_config, mock_set_dtype, mock_get_loader,
125 - mock_registry, mock_get_layers, mock_get_pp_group, method,
120 +@mock.patch('vllm.v1.spec_decode.eagle.get_model')
121 +def test_load_model(mock_get_model, mock_get_layers, mock_get_pp_group, method,
122 proposer_helper, draft_model_dir, target_attribute_path):
123
128 - # Setup mock for model class
129 - mock_model_cls = mock.MagicMock()
130 - mock_registry.resolve_model_cls.return_value = (mock_model_cls,
131 - "test_arch")
132 -
133 - # Create a real context manager for mocks
134 - class MockContextManager:
135 -
136 - def __init__(self):
137 - pass
138 -
139 - def __enter__(self):
140 - return None
141 -
142 - def __exit__(self, exc_type, exc_val, exc_tb):
143 - return False
144 -
145 - # Make the mocks return actual context manager objects
146 - mock_set_dtype.return_value = MockContextManager()
147 - mock_set_config.return_value = MockContextManager()
124 + # Setup model mock
125 + mock_model = mock.MagicMock()
126 + mock_get_model.return_value = mock_model
127
128 # Setup mocks for attention layers
129 target_attn_layers = {
@@ -164,25 +143,6 @@ def test_load_model(mock_set_config, mock_set_dtype, mock_get_loader,
143 mock_pp_group.world_size = 2 if method == "eagle" else 1
144 mock_get_pp_group.return_value = mock_pp_group
145
167 - # Setup model loader mock
168 - mock_loader = mock.MagicMock()
169 - mock_get_loader.return_value = mock_loader
170 -
171 - # Setup model mock
172 - mock_model = mock.MagicMock()
173 - mock_model_cls.return_value = mock_model
174 - mock_model.to.return_value = mock_model
175 -
176 - # Configure mock to test the attribute sharing path
177 - if method == "eagle":
178 - # For eagle, test the lm_head path
179 - mock_model.load_weights.return_value = {
180 - "model.embed_tokens.weight": torch.zeros(1)
181 - }
182 - else:
183 - # For eagle3, test the embed_tokens path
184 - mock_model.load_weights.return_value = {}
185 -
146 # Setup target model with the appropriate attributes
147 target_model = mock.MagicMock()
148
@@ -204,13 +164,7 @@ def test_load_model(mock_set_config, mock_set_dtype, mock_get_loader,
164 proposer.load_model(target_model)
165
166 # Verify common interactions
207 - mock_get_loader.assert_called_once()
208 - mock_model_cls.assert_called_once()
209 - mock_model.to.assert_called_once()
210 - mock_model.load_weights.assert_called_once()
211 -
212 - # Verify the loader was called with the right config
213 - mock_get_loader.assert_called_once_with(proposer.vllm_config.load_config)
167 + mock_get_model.assert_called_once()
168
169 # Verify the specific attribute sharing based on the method
170 if method == "eagle":
vllm/model_executor/model_loader/__init__.py
+10 -3
@@ -1,8 +1,10 @@
1 # SPDX-License-Identifier: Apache-2.0
2
3 +from typing import Optional
4 +
5 from torch import nn
6
5 -from vllm.config import LoadConfig, LoadFormat, VllmConfig
7 +from vllm.config import LoadConfig, LoadFormat, ModelConfig, VllmConfig
8 from vllm.model_executor.model_loader.base_loader import BaseModelLoader
9 from vllm.model_executor.model_loader.bitsandbytes_loader import (
10 BitsAndBytesModelLoader)
@@ -47,9 +49,14 @@ def get_model_loader(load_config: LoadConfig) -> BaseModelLoader:
49 return DefaultModelLoader(load_config)
50
51
50 -def get_model(*, vllm_config: VllmConfig) -> nn.Module:
52 +def get_model(*,
53 + vllm_config: VllmConfig,
54 + model_config: Optional[ModelConfig] = None) -> nn.Module:
55 loader = get_model_loader(vllm_config.load_config)
52 - return loader.load_model(vllm_config=vllm_config)
56 + if model_config is None:
57 + model_config = vllm_config.model_config
58 + return loader.load_model(vllm_config=vllm_config,
59 + model_config=model_config)
60
61
62 __all__ = [
vllm/model_executor/model_loader/base_loader.py
+2 -1
@@ -18,6 +18,7 @@ class BaseModelLoader(ABC):
18 raise NotImplementedError
19
20 @abstractmethod
21 - def load_model(self, *, vllm_config: VllmConfig) -> nn.Module:
21 + def load_model(self, *, vllm_config: VllmConfig,
22 + model_config: ModelConfig) -> nn.Module:
23 """Load a model with the given configurations."""
24 raise NotImplementedError
vllm/model_executor/model_loader/bitsandbytes_loader.py
+2 -3
@@ -569,10 +569,9 @@ class BitsAndBytesModelLoader(BaseModelLoader):
569 def download_model(self, model_config: ModelConfig) -> None:
570 self._prepare_weights(model_config.model, model_config.revision)
571
572 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
572 + def load_model(self, vllm_config: VllmConfig,
573 + model_config: ModelConfig) -> nn.Module:
574 device_config = vllm_config.device_config
574 - model_config = vllm_config.model_config
575 -
575 with set_default_torch_dtype(model_config.dtype):
576 with torch.device(device_config.device):
577
vllm/model_executor/model_loader/default_loader.py
+4 -3
@@ -264,13 +264,14 @@ class DefaultModelLoader(BaseModelLoader):
264 fall_back_to_pt=True,
265 allow_patterns_overrides=None)
266
267 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
267 + def load_model(self, vllm_config: VllmConfig,
268 + model_config: ModelConfig) -> nn.Module:
269 device_config = vllm_config.device_config
269 - model_config = vllm_config.model_config
270 target_device = torch.device(device_config.device)
271 with set_default_torch_dtype(model_config.dtype):
272 with target_device:
273 - model = initialize_model(vllm_config=vllm_config)
273 + model = initialize_model(vllm_config=vllm_config,
274 + model_config=model_config)
275
276 weights_to_load = {name for name, _ in model.named_parameters()}
277 loaded_weights = model.load_weights(
vllm/model_executor/model_loader/dummy_loader.py
+2 -2
@@ -22,9 +22,9 @@ class DummyModelLoader(BaseModelLoader):
22 def download_model(self, model_config: ModelConfig) -> None:
23 pass # Nothing to download
24
25 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
25 + def load_model(self, vllm_config: VllmConfig,
26 + model_config: ModelConfig) -> nn.Module:
27 device_config = vllm_config.device_config
27 - model_config = vllm_config.model_config
28 target_device = torch.device(device_config.device)
29 with set_default_torch_dtype(model_config.dtype):
30 with target_device:
vllm/model_executor/model_loader/gguf_loader.py
+2 -2
@@ -92,9 +92,9 @@ class GGUFModelLoader(BaseModelLoader):
92 def download_model(self, model_config: ModelConfig) -> None:
93 self._prepare_weights(model_config.model)
94
95 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
95 + def load_model(self, vllm_config: VllmConfig,
96 + model_config: ModelConfig) -> nn.Module:
97 device_config = vllm_config.device_config
97 - model_config = vllm_config.model_config
98 local_model_path = self._prepare_weights(model_config.model)
99 gguf_weights_map = self._get_gguf_weights_map(model_config)
100 # we can only know if tie word embeddings after mapping weights
vllm/model_executor/model_loader/runai_streamer_loader.py
+2 -3
@@ -100,11 +100,10 @@ class RunaiModelStreamerLoader(BaseModelLoader):
100 """Download model if necessary"""
101 self._prepare_weights(model_config.model, model_config.revision)
102
103 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
103 + def load_model(self, vllm_config: VllmConfig,
104 + model_config: ModelConfig) -> nn.Module:
105 """Perform streaming of the model to destination"""
106 device_config = vllm_config.device_config
106 - model_config = vllm_config.model_config
107 -
107 target_device = torch.device(device_config.device)
108 with set_default_torch_dtype(model_config.dtype):
109 with target_device:
vllm/model_executor/model_loader/sharded_state_loader.py
+2 -2
@@ -100,9 +100,9 @@ class ShardedStateLoader(BaseModelLoader):
100 def download_model(self, model_config: ModelConfig) -> None:
101 self._prepare_weights(model_config.model, model_config.revision)
102
103 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
103 + def load_model(self, vllm_config: VllmConfig,
104 + model_config: ModelConfig) -> nn.Module:
105 device_config = vllm_config.device_config
105 - model_config = vllm_config.model_config
106 target_device = torch.device(device_config.device)
107
108 from vllm.distributed import get_tensor_model_parallel_rank
vllm/model_executor/model_loader/tensorizer_loader.py
+2 -2
@@ -93,8 +93,8 @@ class TensorizerLoader(BaseModelLoader):
93 with self.tensorizer_config.open_stream():
94 pass
95
96 - def load_model(self, vllm_config: VllmConfig) -> nn.Module:
97 - model_config = vllm_config.model_config
96 + def load_model(self, vllm_config: VllmConfig,
97 + model_config: ModelConfig) -> nn.Module:
98 parallel_config = vllm_config.parallel_config
99 self._verify_config(model_config, parallel_config)
100
vllm/model_executor/model_loader/utils.py
+3 -1
@@ -42,9 +42,11 @@ def initialize_model(
42 *,
43 prefix: str = "",
44 model_class: Optional[type[nn.Module]] = None,
45 + model_config: Optional[ModelConfig] = None,
46 ) -> nn.Module:
47 """Initialize a model with the given configurations."""
47 - model_config = vllm_config.model_config
48 + if model_config is None:
49 + model_config = vllm_config.model_config
50 if model_class is None:
51 model_class, _ = get_model_architecture(model_config)
52
vllm/model_executor/models/llama_eagle.py
+4 -2
@@ -130,13 +130,15 @@ class LlamaModel(nn.Module):
130
131 class EagleLlamaForCausalLM(LlamaForCausalLM):
132
133 - def __init__(self, *, vllm_config: VllmConfig, start_layer_id: int = 0):
133 + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
134 nn.Module.__init__(self)
135 self.config = vllm_config. \
136 speculative_config.draft_model_config.hf_config
137 + target_layer_num = vllm_config.model_config.get_num_layers(
138 + vllm_config.parallel_config)
139 self.model = LlamaModel(vllm_config=vllm_config,
140 prefix="model",
139 - start_layer_id=start_layer_id)
141 + start_layer_id=target_layer_num)
142
143 logit_scale = getattr(self.config, "logit_scale", 1.0)
144 self.logits_processor = LogitsProcessor(self.config.vocab_size,
vllm/model_executor/models/llama_eagle3.py
+6 -5
@@ -175,13 +175,15 @@ class LlamaModel(nn.Module):
175
176 class Eagle3LlamaForCausalLM(LlamaForCausalLM):
177
178 - def __init__(self, *, vllm_config: VllmConfig, start_layer_id: int = 0):
178 + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
179 nn.Module.__init__(self)
180 self.config = vllm_config. \
181 speculative_config.draft_model_config.hf_config
182 + target_layer_num = vllm_config.model_config.get_num_layers(
183 + vllm_config.parallel_config)
184 self.model = LlamaModel(vllm_config=vllm_config,
183 - start_layer_id=start_layer_id,
184 - prefix="model")
185 + prefix="model",
186 + start_layer_id=target_layer_num)
187
188 logit_scale = getattr(self.config, "logit_scale", 1.0)
189 self.lm_head = ParallelLMHead(
@@ -193,8 +195,7 @@ class Eagle3LlamaForCausalLM(LlamaForCausalLM):
195 self.logits_processor = LogitsProcessor(self.config.draft_vocab_size,
196 scale=logit_scale)
197 self.draft_id_to_target_id = nn.Parameter(
196 - torch.zeros((self.config.draft_vocab_size),
197 - dtype=torch.long).type(torch.LongTensor),
198 + torch.zeros(self.config.draft_vocab_size, dtype=torch.long),
199 requires_grad=False,
200 )
201
vllm/model_executor/models/medusa.py
+1 -4
@@ -51,10 +51,7 @@ class Medusa(nn.Module):
51 needs to have truncated_vocab_size (=k) as an attribute."""
52
53 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
54 - if hasattr(vllm_config, 'draft_model_config'):
55 - config = vllm_config.draft_model_config.hf_config
56 - else:
57 - config = vllm_config.model_config.hf_config
54 + config = vllm_config.speculative_config.draft_model_config.hf_config
55 super().__init__()
56 self.config = config
57 self.blocks = nn.ModuleList([
vllm/v1/spec_decode/eagle.py
+6 -32
@@ -4,14 +4,11 @@ import torch.nn as nn
4
5 from vllm.attention.layer import Attention
6 from vllm.config import (CompilationLevel, VllmConfig,
7 - get_layers_from_vllm_config, set_current_vllm_config)
7 + get_layers_from_vllm_config)
8 from vllm.distributed.parallel_state import get_pp_group
9 from vllm.forward_context import set_forward_context
10 from vllm.logger import init_logger
11 -from vllm.model_executor.model_loader import get_model_loader
12 -from vllm.model_executor.model_loader.utils import (
13 - process_weights_after_loading, set_default_torch_dtype)
14 -from vllm.model_executor.models import ModelRegistry
11 +from vllm.model_executor.model_loader import get_model
12 from vllm.model_executor.models.llama_eagle3 import Eagle3LlamaForCausalLM
13 from vllm.triton_utils import tl, triton
14 from vllm.v1.attention.backends.flash_attn import FlashAttentionMetadata
@@ -280,51 +277,28 @@ class EagleProposer:
277 return cu_num_tokens, token_indices
278
279 def load_model(self, target_model: nn.Module) -> None:
283 - loader = get_model_loader(self.vllm_config.load_config)
284 - target_layer_num = self.vllm_config.model_config.get_num_layers(
285 - self.vllm_config.parallel_config)
280 + draft_model_config = \
281 + self.vllm_config.speculative_config.draft_model_config
282 target_attn_layer_names = set(
283 get_layers_from_vllm_config(self.vllm_config, Attention).keys())
284
289 - draft_model_config = \
290 - self.vllm_config.speculative_config.draft_model_config
291 - # FIXME(lily): This does not handle with distributed inference.
292 - target_device = self.vllm_config.device_config.device
293 - # We need to set the vllm_config here to register attention
294 - # layers in the forward context.
295 - with set_default_torch_dtype(
296 - draft_model_config.dtype), set_current_vllm_config(
297 - self.vllm_config):
298 - draft_model_cls, arch = ModelRegistry.resolve_model_cls(
299 - draft_model_config.architectures)
300 - self.model = draft_model_cls(
301 - vllm_config=self.vllm_config,
302 - start_layer_id=target_layer_num).to(target_device)
285 + self.model = get_model(vllm_config=self.vllm_config,
286 + model_config=draft_model_config)
287
288 draft_attn_layer_names = (
289 get_layers_from_vllm_config(self.vllm_config, Attention).keys() -
290 target_attn_layer_names)
291 assert len(draft_attn_layer_names) == 1
292 self.attn_layer_name = next(iter(draft_attn_layer_names))
309 - loaded_weights = self.model.load_weights(
310 - loader.get_all_weights(draft_model_config, self.model))
311 -
312 - process_weights_after_loading(self.model, draft_model_config,
313 - target_device)
293
294 # share embed_tokens with the target model if needed
295 if get_pp_group().world_size == 1:
317 - assert "model.embed_tokens.weight" not in loaded_weights, \
318 - "For PP = 1, Eagle draft should share embed with target model"
296 logger.info(
297 "The EAGLE head shares the same vocab embedding" \
298 " with the target model."
299 )
300 self.model.model.embed_tokens = target_model.model.embed_tokens
301 else:
325 - assert "model.embed_tokens.weight" in loaded_weights, \
326 - "For PP > 1, Eagle draft checkpoint should its own copy of "
327 - " the model.embed_tokens.weight"
302 logger.info(
303 "Since PP > 1, the EAGLE head loaded its own vocab embedding" \
304 " weights instead of sharing them with the target model."
vllm/v1/spec_decode/medusa.py
+5 -18
@@ -3,12 +3,10 @@
3 import torch
4 import torch.nn as nn
5
6 -from vllm.config import VllmConfig, set_current_vllm_config
6 +from vllm.config import VllmConfig
7 from vllm.forward_context import set_forward_context
8 from vllm.logger import init_logger
9 -from vllm.model_executor.model_loader import get_model_loader
10 -from vllm.model_executor.model_loader.utils import set_default_torch_dtype
11 -from vllm.model_executor.models.medusa import Medusa
9 +from vllm.model_executor.model_loader import get_model
10 from vllm.v1.sample.metadata import SamplingMetadata
11
12 # Initialize logger
@@ -49,20 +47,9 @@ class MedusaProposer:
47 return [list(row) for row in zip(*draft_tokens)]
48
49 def load_model(self, target_model: nn.Module) -> None:
52 - # Get model loader and config
53 - loader = get_model_loader(self.vllm_config.load_config)
54 - draft_config = self.vllm_config.speculative_config.draft_model_config
55 -
56 - # Load model with proper dtype and config
57 - with set_default_torch_dtype(draft_config.dtype), \
58 - set_current_vllm_config(self.vllm_config):
59 - self.model = Medusa(
60 - vllm_config=self.vllm_config.speculative_config).to(
61 - self.device)
62 -
63 - # Load model weights
64 - weights = loader.get_all_weights(draft_config, self.model)
65 - self.model.load_weights(weights)
50 + self.model = get_model(vllm_config=self.vllm_config,
51 + model_config=self.vllm_config.
52 + speculative_config.draft_model_config)
53
54 @torch.inference_mode()
55 def dummy_run(self, num_tokens: int) -> None: