@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 767c3ab869
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 14, 05:30 UTC

[Model][0/N] Improve all pooling task | clean up (#25817)

Signed-off-by: wang.yuqi <noooop@126.com>

wang.yuqi committed Oct 13, 2025 at 16:44 UTC 767c3ab8693aba5c5c629a1c9c6550d5cfab0cc4
19 files changed +197 -188
docs/models/supported_models.md
+1 -1
@@ -581,7 +581,7 @@ These models primarily support the [`LLM.encode`](./pooling_models.md#llmencode)
581 | `ModernBertForTokenClassification` | ModernBERT-based | `disham993/electrical-ner-ModernBERT-base` | | | ✅︎ |
582
583 !!! note
584 - Named Entity Recognition (NER) usage, please refer to <gh-file:examples/offline_inference/pooling/ner.py>, <gh-file:examples/online_serving/pooling/ner.py>.
584 + Named Entity Recognition (NER) usage, please refer to <gh-file:examples/offline_inference/pooling/ner.py>, <gh-file:examples/online_serving/pooling/ner_client.py>.
585
586 [](){ #supported-mm-models }
587
examples/online_serving/pooling/README.md
+1 -1
@@ -15,7 +15,7 @@ python examples/online_serving/pooling/jinaai_rerank_client.py
15 ## Named Entity Recognition (NER) usage
16
17 ```bash
18 -python examples/online_serving/pooling/ner.py
18 +python examples/online_serving/pooling/ner_client.py
19 ```
20
21 ## Openai chat embedding for multimodal usage
examples/online_serving/pooling/ner_client.py renamed
tests/ci_envs.py
+6
@@ -8,6 +8,8 @@ import os
8 from collections.abc import Callable
9 from typing import TYPE_CHECKING, Any
10
11 +from vllm.envs import maybe_convert_bool
12 +
13 if TYPE_CHECKING:
14 VLLM_CI_NO_SKIP: bool = False
15 VLLM_CI_DTYPE: str | None = None
@@ -25,6 +27,10 @@ environment_variables: dict[str, Callable[[], Any]] = {
27 "VLLM_CI_HEAD_DTYPE": lambda: os.getenv("VLLM_CI_HEAD_DTYPE", None),
28 # Allow changing the head dtype used by transformers in tests
29 "VLLM_CI_HF_DTYPE": lambda: os.getenv("VLLM_CI_HF_DTYPE", None),
30 + # Allow control over whether tests use enforce_eager
31 + "VLLM_CI_ENFORCE_EAGER": lambda: maybe_convert_bool(
32 + os.getenv("VLLM_CI_ENFORCE_EAGER", None)
33 + ),
34 }
35
36
tests/entrypoints/pooling/llm/test_classify.py
+2
@@ -58,7 +58,9 @@ def test_pooling_params(llm: LLM):
58 )
59
60
61 +@pytest.mark.skip_global_cleanup
62 def test_encode_api(llm: LLM):
63 + # chunked prefill does not support all pooling
64 err_msg = "pooling_task must be one of.+"
65 with pytest.raises(ValueError, match=err_msg):
66 llm.encode(prompts, use_tqdm=False)
tests/entrypoints/pooling/llm/test_embedding.py
-1
@@ -35,7 +35,6 @@ def llm():
35 cleanup_dist_env_and_memory()
36
37
38 -@pytest.mark.skip_global_cleanup
38 def test_pooling_params(llm: LLM):
39 def get_outputs(normalize):
40 outputs = llm.embed(
tests/entrypoints/pooling/llm/test_encode.py
-1
@@ -74,7 +74,6 @@ def test_multiple_pooling_params(llm: LLM):
74 assert len(PROMPTS) == len(outputs)
75
76
77 -@pytest.mark.skip_global_cleanup
77 def test_right_side_truncation(llm: LLM):
78 # Embeddings models should truncate the end of the prompt
79 tokenizer = llm.get_tokenizer()
tests/entrypoints/pooling/llm/test_score.py
-1
@@ -33,7 +33,6 @@ def llm():
33 cleanup_dist_env_and_memory()
34
35
36 -@pytest.mark.skip_global_cleanup
36 def test_pooling_params(llm: LLM):
37 def get_outputs(activation):
38 text_1 = "What is the capital of France?"
tests/models/language/generation_ppl_test/ppl_utils.py
+6 -20
@@ -3,12 +3,15 @@
3 # Adapted from https://huggingface.co/docs/transformers/perplexity
4 from typing import cast
5
6 -import pytest
6 import torch
7 from datasets import load_dataset
8
9 import tests.ci_envs as ci_envs
11 -from tests.models.utils import GenerateModelInfo, TokensTextLogprobsPromptLogprobs
10 +from tests.models.utils import (
11 + GenerateModelInfo,
12 + TokensTextLogprobsPromptLogprobs,
13 + get_vllm_extra_kwargs,
14 +)
15 from vllm.logprobs import Logprob
16
17 # See #24485
@@ -25,27 +28,10 @@ def wikitext_ppl_test(
28 vllm_extra_kwargs=None,
29 atol=PPL_TOL,
30 ):
28 - # A model family has many models with the same architecture,
29 - # and we don't need to test each one.
30 - if not ci_envs.VLLM_CI_NO_SKIP and not model_info.enable_test:
31 - pytest.skip("Skipping test.")
31 + vllm_extra_kwargs = get_vllm_extra_kwargs(model_info, vllm_extra_kwargs)
32
33 dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="test")
34
35 - # Allow vllm to test using the given dtype, such as float32
36 - vllm_extra_kwargs = vllm_extra_kwargs or {}
37 - vllm_extra_kwargs["dtype"] = ci_envs.VLLM_CI_DTYPE or model_info.dtype
38 -
39 - # Allow vllm to test using hf_overrides
40 - if model_info.hf_overrides is not None:
41 - vllm_extra_kwargs["hf_overrides"] = model_info.hf_overrides
42 -
43 - # Allow changing the head dtype used by vllm in tests
44 - if ci_envs.VLLM_CI_HEAD_DTYPE is not None:
45 - if "hf_overrides" not in vllm_extra_kwargs:
46 - vllm_extra_kwargs["hf_overrides"] = {}
47 - vllm_extra_kwargs["hf_overrides"]["head_dtype"] = ci_envs.VLLM_CI_HEAD_DTYPE
48 -
35 with vllm_runner(
36 model_info.name,
37 gpu_memory_utilization=0.7,
tests/models/language/pooling/test_head_dtype.py new
+47
@@ -0,0 +1,47 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +import pytest
4 +import torch
5 +from transformers import AutoModelForSequenceClassification
6 +
7 +
8 +@pytest.mark.parametrize(
9 + "model",
10 + ["nie3e/sentiment-polish-gpt2-small"],
11 +)
12 +@pytest.mark.parametrize("dtype", ["half"])
13 +def test_classify_models(
14 + hf_runner,
15 + vllm_runner,
16 + example_prompts,
17 + model: str,
18 + dtype: str,
19 +) -> None:
20 + with hf_runner(
21 + model, dtype=dtype, auto_cls=AutoModelForSequenceClassification
22 + ) as hf_model:
23 + hf_outputs = hf_model.classify(example_prompts)
24 +
25 + for head_dtype_str in ["float32", "model"]:
26 + with vllm_runner(
27 + model,
28 + max_model_len=512,
29 + dtype=dtype,
30 + hf_overrides={"head_dtype": head_dtype_str},
31 + ) as vllm_model:
32 + model_config = vllm_model.llm.llm_engine.model_config
33 + model_dtype = model_config.dtype
34 + head_dtype = model_config.head_dtype
35 +
36 + if head_dtype_str == "float32":
37 + assert head_dtype == torch.float32
38 + elif head_dtype_str == "model":
39 + assert head_dtype == model_dtype
40 +
41 + vllm_outputs = vllm_model.classify(example_prompts)
42 +
43 + for hf_output, vllm_output in zip(hf_outputs, vllm_outputs):
44 + hf_output = torch.tensor(hf_output).float()
45 + vllm_output = torch.tensor(vllm_output).float()
46 +
47 + assert torch.allclose(hf_output, vllm_output, atol=1e-2)
tests/models/language/pooling/test_splade_sparse_pooler.py
+9 -42
@@ -3,7 +3,6 @@
3
4 import types
5
6 -import numpy as np
6 import pytest
7 import torch
8 import torch.nn as nn
@@ -14,11 +13,12 @@ from vllm.model_executor.models.bert import (
13 )
14
15 # ---------------------------------------------------------------------
17 -# 1) Functional test: SPLADE formula correctness (no HF download needed)
16 +# Functional test: SPLADE formula correctness (no HF download needed)
17 # ---------------------------------------------------------------------
18
19
20 @pytest.mark.parametrize("B,T,H,V", [(2, 3, 5, 7)])
21 +@torch.inference_mode
22 def test_splade_pooler_matches_reference_formula(B, T, H, V):
23 """Ensure SPLADESparsePooler forward() matches the mathematical formula:
24 log1p(relu(logits)) -> max over sequence length (after masking)."""
@@ -26,9 +26,11 @@ def test_splade_pooler_matches_reference_formula(B, T, H, V):
26
27 # Prepare [B] sequences of shape [T, H]
28 hs_list = [torch.randn(T, H) for _ in range(B)]
29 + hs_tenser = torch.cat(hs_list)
30
31 # Simulate PoolingMetadata (only required fields)
32 prompt_lens = [T, T - 1]
33 + prompt_lens_tenser = torch.tensor(prompt_lens, dtype=torch.int32)
34 token_ids = torch.tensor(
35 [
36 [101, 5, 102], # Batch 0: [CLS], token, [SEP]
@@ -36,7 +38,9 @@ def test_splade_pooler_matches_reference_formula(B, T, H, V):
38 ],
39 dtype=torch.long,
40 )
39 - meta = types.SimpleNamespace(prompt_lens=prompt_lens, prompt_token_ids=token_ids)
41 + meta = types.SimpleNamespace(
42 + prompt_lens=prompt_lens_tenser, prompt_token_ids=token_ids
43 + )
44
45 # MLM head (prefer BertMLMHead, fallback to Linear if unavailable)
46 try:
@@ -46,10 +50,10 @@ def test_splade_pooler_matches_reference_formula(B, T, H, V):
50
51 # Forward pass through SPLADE pooler
52 pooler = SPLADESparsePooler(mlm_head=mlm_head, pooling="max", remove_cls_sep=True)
49 - pooled = pooler(hidden_states=hs_list, pooling_metadata=meta) # list of [V]
53 + pooled = pooler(hidden_states=hs_tenser, pooling_metadata=meta) # list of [V]
54
55 # Basic output checks
52 - assert isinstance(pooled, list) and len(pooled) == B
56 + assert isinstance(pooled, torch.Tensor) and len(pooled) == B
57 for vec in pooled:
58 assert vec.shape == (V,)
59 assert torch.isfinite(vec).all()
@@ -83,40 +87,3 @@ def test_splade_pooler_matches_reference_formula(B, T, H, V):
87 rtol=1e-4,
88 atol=1e-4,
89 )
86 -
87 -
88 -# ---------------------------------------------------------------------
89 -# 2) Integration smoke test: end-to-end embedding path wiring
90 -# ---------------------------------------------------------------------
91 -
92 -
93 -@pytest.mark.cpu_model
94 -def test_bert_splade_sparse_embed_smoke(vllm_runner, monkeypatch):
95 - """Ensure BertSpladeSparseEmbeddingModel loads and produces sparse embeddings."""
96 - from transformers import AutoTokenizer
97 -
98 - MODEL_ID = "hf-internal-testing/tiny-random-bert"
99 - hf_overrides = {"architectures": ["BertSpladeSparseEmbeddingModel"]}
100 -
101 - # Enforce CPU-only execution (optional)
102 - monkeypatch.setenv("CUDA_VISIBLE_DEVICES", "")
103 - monkeypatch.setenv("VLLM_USE_TRITON_FLASH_ATTN", "False")
104 -
105 - tok = AutoTokenizer.from_pretrained(MODEL_ID)
106 - vocab_size = tok.vocab_size
107 -
108 - # The embed path should route through SPLADESparsePooler
109 - with vllm_runner(
110 - MODEL_ID,
111 - runner="pooling",
112 - max_model_len=64,
113 - hf_overrides=hf_overrides,
114 - ) as vm:
115 - outs = vm.embed(["hello world", "splade sparse test"])
116 -
117 - # Basic sanity checks
118 - assert len(outs) == 2
119 - assert outs[0].shape[0] == vocab_size
120 - assert outs[1].shape[0] == vocab_size
121 - assert np.isfinite(outs[0]).all() and (outs[0] >= 0).all()
122 - assert np.isfinite(outs[1]).all() and (outs[1] >= 0).all()
tests/models/language/pooling_mteb_test/mteb_utils.py
+14 -41
@@ -6,12 +6,16 @@ from collections.abc import Sequence
6
7 import mteb
8 import numpy as np
9 -import pytest
9 import requests
10 import torch
11
12 import tests.ci_envs as ci_envs
14 -from tests.models.utils import EmbedModelInfo, RerankModelInfo, check_embeddings_close
13 +from tests.models.utils import (
14 + EmbedModelInfo,
15 + RerankModelInfo,
16 + check_embeddings_close,
17 + get_vllm_extra_kwargs,
18 +)
19
20 # Most embedding models on the STS12 task (See #17175):
21 # - Model implementation and minor changes in tensor dtype
@@ -165,28 +169,11 @@ def mteb_test_embed_models(
169 hf_model_callback=None,
170 atol=MTEB_EMBED_TOL,
171 ):
168 - # A model family has many models with the same architecture,
169 - # and we don't need to test each one.
170 - if not ci_envs.VLLM_CI_NO_SKIP and not model_info.enable_test:
171 - pytest.skip("Skipping test.")
172 + vllm_extra_kwargs = get_vllm_extra_kwargs(model_info, vllm_extra_kwargs)
173
174 # Test embed_dims, isnan and whether to use normalize
175 example_prompts = ["The chef prepared a delicious meal." * 1000]
176
176 - # Allow vllm to test using the given dtype, such as float32
177 - vllm_extra_kwargs = vllm_extra_kwargs or {}
178 - vllm_extra_kwargs["dtype"] = ci_envs.VLLM_CI_DTYPE or model_info.dtype
179 -
180 - # Allow vllm to test using hf_overrides
181 - if model_info.hf_overrides is not None:
182 - vllm_extra_kwargs["hf_overrides"] = model_info.hf_overrides
183 -
184 - # Allow changing the head dtype used by vllm in tests
185 - if ci_envs.VLLM_CI_HEAD_DTYPE is not None:
186 - if "hf_overrides" not in vllm_extra_kwargs:
187 - vllm_extra_kwargs["hf_overrides"] = {}
188 - vllm_extra_kwargs["hf_overrides"]["head_dtype"] = ci_envs.VLLM_CI_HEAD_DTYPE
189 -
177 with vllm_runner(
178 model_info.name,
179 runner="pooling",
@@ -212,9 +199,12 @@ def mteb_test_embed_models(
199 vllm_dtype = vllm_model.llm.llm_engine.model_config.dtype
200 head_dtype = model_config.head_dtype
201
215 - # Test embed_dims, isnan and whether to use normalize
202 + # Test embedding_size, isnan and whether to use normalize
203 vllm_outputs = vllm_model.embed(example_prompts, truncate_prompt_tokens=-1)
217 - assert not torch.any(torch.isnan(torch.tensor(vllm_outputs)))
204 + outputs_tensor = torch.tensor(vllm_outputs)
205 + assert not torch.any(torch.isnan(outputs_tensor))
206 + embedding_size = model_config.embedding_size
207 + assert torch.tensor(vllm_outputs).shape[-1] == embedding_size
208
209 # Accelerate mteb test by setting
210 # SentenceTransformers mteb score to a constant
@@ -231,7 +221,7 @@ def mteb_test_embed_models(
221 st_main_score = run_mteb_embed_task(hf_model, MTEB_EMBED_TASKS)
222 st_dtype = next(hf_model.model.parameters()).dtype
223
234 - # Test embed_dims and whether to use normalize
224 + # Check embeddings close to hf outputs
225 hf_outputs = hf_model.encode(example_prompts)
226 check_embeddings_close(
227 embeddings_0_lst=hf_outputs,
@@ -323,24 +313,7 @@ def mteb_test_rerank_models(
313 vllm_mteb_encoder=VllmMtebEncoder,
314 atol=MTEB_RERANK_TOL,
315 ):
326 - # A model family has many models with the same architecture,
327 - # and we don't need to test each one.
328 - if not ci_envs.VLLM_CI_NO_SKIP and not model_info.enable_test:
329 - pytest.skip("Skipping test.")
330 -
331 - # Allow vllm to test using the given dtype, such as float32
332 - vllm_extra_kwargs = vllm_extra_kwargs or {}
333 - vllm_extra_kwargs["dtype"] = ci_envs.VLLM_CI_DTYPE or model_info.dtype
334 -
335 - # Allow vllm to test using hf_overrides
336 - if model_info.hf_overrides is not None:
337 - vllm_extra_kwargs["hf_overrides"] = model_info.hf_overrides
338 -
339 - # Allow changing the head dtype used by vllm in tests
340 - if ci_envs.VLLM_CI_HEAD_DTYPE is not None:
341 - if "hf_overrides" not in vllm_extra_kwargs:
342 - vllm_extra_kwargs["hf_overrides"] = {}
343 - vllm_extra_kwargs["hf_overrides"]["head_dtype"] = ci_envs.VLLM_CI_HEAD_DTYPE
316 + vllm_extra_kwargs = get_vllm_extra_kwargs(model_info, vllm_extra_kwargs)
317
318 with vllm_runner(
319 model_info.name,
tests/models/utils.py
+30
@@ -15,6 +15,7 @@ from vllm.logprobs import Logprob, PromptLogprobs, SampleLogprobs
15 from vllm.multimodal.processing import InputProcessingContext
16 from vllm.transformers_utils.tokenizer import cached_tokenizer_from_config
17
18 +from .. import ci_envs
19 from .registry import HF_EXAMPLE_MODELS
20
21 TokensText = tuple[list[int], str]
@@ -414,6 +415,35 @@ class GenerateModelInfo(ModelInfo):
415 hf_ppl: float | None = None
416
417
418 +def get_vllm_extra_kwargs(model_info: ModelInfo, vllm_extra_kwargs):
419 + # A model family has many models with the same architecture,
420 + # and we don't need to test each one.
421 + if not ci_envs.VLLM_CI_NO_SKIP and not model_info.enable_test:
422 + import pytest
423 +
424 + pytest.skip("Skipping test.")
425 +
426 + # Allow vllm to test using the given dtype, such as float32
427 + vllm_extra_kwargs = vllm_extra_kwargs or {}
428 + vllm_extra_kwargs["dtype"] = ci_envs.VLLM_CI_DTYPE or model_info.dtype
429 +
430 + # Allow vllm to test using hf_overrides
431 + if model_info.hf_overrides is not None:
432 + vllm_extra_kwargs["hf_overrides"] = model_info.hf_overrides
433 +
434 + # Allow changing the head dtype used by vllm in tests
435 + if ci_envs.VLLM_CI_HEAD_DTYPE is not None:
436 + if "hf_overrides" not in vllm_extra_kwargs:
437 + vllm_extra_kwargs["hf_overrides"] = {}
438 + vllm_extra_kwargs["hf_overrides"]["head_dtype"] = ci_envs.VLLM_CI_HEAD_DTYPE
439 +
440 + # Allow control over whether tests use enforce_eager
441 + if ci_envs.VLLM_CI_ENFORCE_EAGER is not None:
442 + vllm_extra_kwargs["enforce_eager"] = ci_envs.VLLM_CI_ENFORCE_EAGER
443 +
444 + return vllm_extra_kwargs
445 +
446 +
447 def dummy_hf_overrides(
448 hf_config: PretrainedConfig,
449 *,
vllm/config/model.py
+15
@@ -30,6 +30,7 @@ from vllm.transformers_utils.config import (
30 get_sentence_transformer_tokenizer_config,
31 is_encoder_decoder,
32 is_interleaved,
33 + try_get_dense_modules,
34 try_get_generation_config,
35 try_get_safetensors_metadata,
36 try_get_tokenizer_config,
@@ -1681,6 +1682,20 @@ class ModelConfig:
1682 logger.debug_once("head dtype: %s", head_dtype)
1683 return head_dtype
1684
1685 + @property
1686 + def hidden_size(self):
1687 + if hasattr(self.hf_config, "hidden_size"):
1688 + return self.hf_config.hidden_size
1689 + text_config = self.hf_config.get_text_config()
1690 + return text_config.hidden_size
1691 +
1692 + @property
1693 + def embedding_size(self):
1694 + dense_modules = try_get_dense_modules(self.model, revision=self.revision)
1695 + if dense_modules is not None:
1696 + return dense_modules[-1]["out_features"]
1697 + return self.hidden_size
1698 +
1699 def get_and_verify_max_len(self, max_model_len: int):
1700 # Consider max_model_len in tokenizer_config only when
1701 # pooling models use absolute position_embedding.
vllm/model_executor/models/adapters.py
+17 -32
@@ -13,7 +13,10 @@ from vllm.config import VllmConfig
13 from vllm.logger import init_logger
14 from vllm.model_executor.layers.activation import get_act_fn
15 from vllm.model_executor.models.config import VerifyAndUpdateConfig
16 -from vllm.transformers_utils.config import get_hf_file_bytes, get_hf_file_to_dict
16 +from vllm.transformers_utils.config import (
17 + get_hf_file_bytes,
18 + try_get_dense_modules,
19 +)
20
21 from .interfaces_base import VllmModelForPooling, is_pooling_model
22
@@ -35,43 +38,25 @@ _GENERATE_SUFFIXES = [
38 def _load_st_projector(model_config: "ModelConfig") -> nn.Module | None:
39 """Load Sentence-Transformers Dense projection layers."""
40
38 - try:
39 - modules = get_hf_file_to_dict(
40 - "modules.json", model_config.model, model_config.revision
41 - )
42 - if not modules:
43 - return None
44 -
45 - if isinstance(modules, dict):
46 - modules = modules.get("modules", [])
41 + dense_modules = try_get_dense_modules(
42 + model_config.model, revision=model_config.revision
43 + )
44
48 - dense_modules = [
49 - m for m in modules if m.get("type") == "sentence_transformers.models.Dense"
50 - ]
51 - if not dense_modules:
52 - return None
45 + if dense_modules is None:
46 + return
47
48 + try:
49 layers = []
55 - for module in dense_modules:
56 - folder = module.get("path", "")
57 -
58 - config_path = f"{folder}/config.json" if folder else "config.json"
59 - layer_config = get_hf_file_to_dict(
60 - config_path, model_config.model, model_config.revision
61 - )
62 - if not layer_config:
63 - continue
64 -
50 + for layer_config in dense_modules:
51 + folder = layer_config["folder"]
52 linear = nn.Linear(
66 - layer_config.get("in_features", 768),
67 - layer_config.get("out_features", 768),
53 + layer_config["in_features"],
54 + layer_config["out_features"],
55 bias=layer_config.get("bias", True),
56 dtype=model_config.head_dtype,
57 )
71 -
58 if not _load_dense_weights(linear, folder, model_config):
59 continue
74 -
60 layers.append(linear)
61 if act_name := layer_config.get("activation_function"):
62 layers.append(get_act_fn(act_name))
@@ -303,18 +288,18 @@ def as_seq_cls_model(cls: _T) -> _T:
288 from vllm.model_executor.models.interfaces import SupportsCrossEncoding
289 from vllm.sequence import IntermediateTensors
290
306 - from .utils import get_model_hidden_size, maybe_prefix
291 + from .utils import maybe_prefix
292
293 class ModelForSequenceClassification(
294 _create_pooling_model_cls(cls), SupportsCrossEncoding
295 ):
296 def _init_pooler(self, vllm_config: "VllmConfig", prefix: str = ""):
297 config = vllm_config.model_config.hf_config
298 + model_config = vllm_config.model_config
299 quant_config = vllm_config.quant_config
314 - hidden_size = get_model_hidden_size(config)
300
301 self.score = ReplicatedLinear(
317 - hidden_size,
302 + model_config.hidden_size,
303 config.num_labels,
304 bias=False,
305 params_dtype=torch.float32,
vllm/model_executor/models/gpt2.py
+5 -2
@@ -50,7 +50,7 @@ from vllm.model_executor.model_loader.weight_utils import default_weight_loader
50 from vllm.sequence import IntermediateTensors
51
52 from ..layers.pooler import DispatchPooler, Pooler
53 -from .interfaces import SupportsPP
53 +from .interfaces import SupportsCrossEncoding, SupportsPP
54 from .utils import (
55 AutoWeightsLoader,
56 is_pp_missing_parameter,
@@ -321,7 +321,7 @@ class GPT2LMHeadModel(nn.Module, SupportsPP):
321 return loader.load_weights(weights)
322
323
324 -class GPT2ForSequenceClassification(nn.Module):
324 +class GPT2ForSequenceClassification(nn.Module, SupportsCrossEncoding):
325 """GPT2 Model for sequence classification.
326
327 This class expands GPT2Model with pooling and score functions - last token
@@ -358,6 +358,9 @@ class GPT2ForSequenceClassification(nn.Module):
358 }
359 )
360
361 + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor:
362 + return self.transformer.get_input_embeddings(input_ids)
363 +
364 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]):
365 loader = AutoWeightsLoader(self)
366 return loader.load_weights(weights)
vllm/model_executor/models/gritlm.py
+10 -39
@@ -148,37 +148,6 @@ class GritLMMeanPool(nn.Module):
148 def get_pooling_updates(self, task: PoolingTask) -> PoolingParamsUpdate:
149 return PoolingParamsUpdate(requires_token_ids=True)
150
151 - def forward_one(
152 - self,
153 - hidden_states: torch.Tensor,
154 - prompt_len: torch.Tensor | None = None,
155 - instr_len: torch.Tensor | None = None,
156 - ) -> torch.Tensor:
157 - assert prompt_len is None or prompt_len == hidden_states.shape[0], (
158 - "partial prefill not supported with MEAN pooling"
159 - )
160 -
161 - return hidden_states[instr_len:].mean(dim=0, dtype=torch.float32)
162 -
163 - def forward_all(
164 - self,
165 - hidden_states: torch.Tensor,
166 - prompt_lens: torch.Tensor,
167 - instr_lens: torch.Tensor,
168 - ) -> list[torch.Tensor] | torch.Tensor:
169 - offset = 0
170 - pooled_data = list[torch.Tensor]()
171 -
172 - for prompt_len, instr_len in zip(prompt_lens, instr_lens):
173 - pooled_data.append(
174 - hidden_states[offset + instr_len : offset + prompt_len].mean(
175 - dim=0, dtype=torch.float32
176 - )
177 - )
178 - offset += prompt_len
179 -
180 - return pooled_data
181 -
151 def forward(
152 self,
153 hidden_states: torch.Tensor | list[torch.Tensor],
@@ -190,18 +159,20 @@ class GritLMMeanPool(nn.Module):
159 self._get_instruction_len(token_ids.cpu().numpy())
160 for token_ids in get_prompt_token_ids(pooling_metadata)
161 ],
193 - device=prompt_lens.device,
162 + device="cpu",
163 )
164
196 - if isinstance(hidden_states, list):
197 - return [
198 - self.forward_one(h, prompt_len, instr_len)
199 - for h, prompt_len, instr_len in zip(
200 - hidden_states, prompt_lens, instr_lens
165 + offset = 0
166 + pooled_data = list[torch.Tensor]()
167 + for prompt_len, instr_len in zip(prompt_lens, instr_lens):
168 + pooled_data.append(
169 + hidden_states[offset + instr_len : offset + prompt_len].mean(
170 + dim=0, dtype=torch.float32
171 )
202 - ]
172 + )
173 + offset += prompt_len
174
204 - return self.forward_all(hidden_states, prompt_lens, instr_lens)
175 + return pooled_data
176
177
178 class GritLMPooler(Pooler):
vllm/model_executor/models/utils.py
-7
@@ -777,13 +777,6 @@ def fast_topk(
777 return torch.topk(values, topk, dim=dim)
778
779
780 -def get_model_hidden_size(hf_config: PretrainedConfig) -> int:
781 - if hasattr(hf_config, "hidden_size"):
782 - return hf_config.hidden_size
783 - text_config = hf_config.get_text_config()
784 - return text_config.hidden_size
785 -
786 -
780 # Chunk x along the num_tokens axis for sequence parallelism
781 # NOTE: This is wrapped in a torch custom op to work around the following issue:
782 # The output tensor can have a sequence length 0 at small input sequence lengths
vllm/transformers_utils/config.py
+34
@@ -1049,6 +1049,40 @@ def try_get_tokenizer_config(
1049 return None
1050
1051
1052 +@cache
1053 +def try_get_dense_modules(
1054 + model: str | Path,
1055 + revision: str | None = None,
1056 +) -> list[dict[str, Any]] | None:
1057 + try:
1058 + modules = get_hf_file_to_dict("modules.json", model, revision)
1059 + if not modules:
1060 + return None
1061 +
1062 + if isinstance(modules, dict):
1063 + modules = modules.get("modules", [])
1064 +
1065 + dense_modules = [
1066 + m for m in modules if m.get("type") == "sentence_transformers.models.Dense"
1067 + ]
1068 + if not dense_modules:
1069 + return None
1070 +
1071 + layer_configs = []
1072 + for module in dense_modules:
1073 + folder = module.get("path", "")
1074 +
1075 + config_path = f"{folder}/config.json" if folder else "config.json"
1076 + layer_config = get_hf_file_to_dict(config_path, model, revision)
1077 + if not layer_config:
1078 + continue
1079 + layer_config["folder"] = folder
1080 + layer_configs.append(layer_config)
1081 + return layer_configs
1082 + except Exception:
1083 + return None
1084 +
1085 +
1086 def get_safetensors_params_metadata(
1087 model: str,
1088 *,