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, 03:30 UTC
[Bugfix] Add `use_cross_encoder` flag to use correct activation in `ClassifierPooler` (#20527)
Signed-off-by: DarkLight1337 <tlleungac@connect.ust.hk>
Cyrus Leung committed
Jul 7, 2025 at 05:01 UTC
c18b3b8e8bdcebaa150311d2c4911a6428480162
8 files changed
+56
-41
vllm/entrypoints/llm.py
+1
-1
@@ -1204,7 +1204,7 @@ class LLM:
1204
1205
input_pairs = [(t1, t2) for t1, t2 in zip(text_1, text_2)]
1206
1207
- pooling_params = PoolingParams()
1207
+ pooling_params = PoolingParams(use_cross_encoder=True)
1208
1209
tokenization_kwargs: dict[str, Any] = {}
1210
_validate_truncation_size(self.llm_engine.model_config.max_model_len,
vllm/entrypoints/openai/protocol.py
+6
-4
@@ -1156,8 +1156,9 @@ class ScoreRequest(OpenAIBaseModel):
1156
1157
# --8<-- [end:score-extra-params]
1158
1159
- def to_pooling_params(self):
1160
- return PoolingParams(additional_data=self.additional_data)
1159
+ def to_pooling_params(self, *, use_cross_encoder: bool = False):
1160
+ return PoolingParams(use_cross_encoder=use_cross_encoder,
1161
+ additional_data=self.additional_data)
1162
1163
1164
class RerankRequest(OpenAIBaseModel):
@@ -1182,8 +1183,9 @@ class RerankRequest(OpenAIBaseModel):
1183
1184
# --8<-- [end:rerank-extra-params]
1185
1185
- def to_pooling_params(self):
1186
- return PoolingParams(additional_data=self.additional_data)
1186
+ def to_pooling_params(self, *, use_cross_encoder: bool = False):
1187
+ return PoolingParams(use_cross_encoder=use_cross_encoder,
1188
+ additional_data=self.additional_data)
1189
1190
1191
class RerankDocument(BaseModel):
vllm/entrypoints/openai/serving_score.py
+4
-6
@@ -25,9 +25,7 @@ from vllm.logger import init_logger
25
from vllm.lora.request import LoRARequest
26
from vllm.outputs import PoolingRequestOutput, ScoringRequestOutput
27
from vllm.prompt_adapter.request import PromptAdapterRequest
28
-from vllm.transformers_utils.tokenizer import (AnyTokenizer, MistralTokenizer,
29
- PreTrainedTokenizer,
30
- PreTrainedTokenizerFast)
28
+from vllm.transformers_utils.tokenizer import AnyTokenizer, MistralTokenizer
29
from vllm.utils import make_async, merge_async_iterators
30
31
logger = init_logger(__name__)
@@ -50,7 +48,7 @@ class ServingScores(OpenAIServing):
48
49
async def _embedding_score(
50
self,
53
- tokenizer: Union[PreTrainedTokenizer, PreTrainedTokenizerFast],
51
+ tokenizer: AnyTokenizer,
52
texts_1: list[str],
53
texts_2: list[str],
54
request: Union[RerankRequest, ScoreRequest],
@@ -141,7 +139,7 @@ class ServingScores(OpenAIServing):
139
140
async def _cross_encoding_score(
141
self,
144
- tokenizer: Union[AnyTokenizer],
142
+ tokenizer: AnyTokenizer,
143
texts_1: list[str],
144
texts_2: list[str],
145
request: Union[RerankRequest, ScoreRequest],
@@ -190,7 +188,7 @@ class ServingScores(OpenAIServing):
188
# Schedule the request and get the result generator.
189
generators: list[AsyncGenerator[PoolingRequestOutput, None]] = []
190
193
- pooling_params = request.to_pooling_params()
191
+ pooling_params = request.to_pooling_params(use_cross_encoder=True)
192
193
for i, engine_prompt in enumerate(engine_prompts):
194
request_id_item = f"{request_id}-{i}"
vllm/model_executor/layers/pooler.py
+31
-11
@@ -15,6 +15,7 @@ from vllm.model_executor.pooling_metadata import ( # noqa: E501
15
from vllm.model_executor.pooling_metadata import PoolingTensors
16
from vllm.sequence import PoolerOutput, PoolingSequenceGroupOutput
17
from vllm.transformers_utils.config import (
18
+ get_classification_activation_function,
19
get_cross_encoder_activation_function)
20
from vllm.v1.pool.metadata import PoolingMetadata as V1PoolingMetadata
21
@@ -388,15 +389,14 @@ class ClassifierPooler(nn.Module):
389
self.classifier = classifier
390
self.pooler = pooler
391
391
- if config.task == "score":
392
- self.default_activation_function = \
393
- get_cross_encoder_activation_function(config.hf_config)
394
- elif config.task == "classify":
395
- self.default_activation_function = nn.Sigmoid() \
396
- if config.hf_config.num_labels == 1 else nn.Softmax()
397
- else:
398
- raise NotImplementedError(f"task={config.task!r} is not supported"
399
- " with the classification pooler")
392
+ self.classification_act_fn = get_classification_activation_function(
393
+ config.hf_config)
394
+ self.cross_encoder_act_fn = get_cross_encoder_activation_function(
395
+ config.hf_config)
396
+
397
+ def _get_act_fn(self, use_cross_encoder: bool):
398
+ return (self.cross_encoder_act_fn
399
+ if use_cross_encoder else self.classification_act_fn)
400
401
def get_prompt_lens(
402
self,
@@ -446,8 +446,28 @@ class ClassifierPooler(nn.Module):
446
# apply classifier once on the full batch if possible
447
pooled_output = self.classifier(pooled_output)
448
449
- # shape: (batch_size, num_labels)
450
- scores = self.default_activation_function(pooled_output)
449
+ if isinstance(pooling_metadata, V0PoolingMetadata):
450
+ use_cross_encoder_list = [
451
+ pooling_param.use_cross_encoder
452
+ for _, pooling_param in pooling_metadata.seq_groups
453
+ ]
454
+ else:
455
+ use_cross_encoder_list = [
456
+ pooling_param.use_cross_encoder
457
+ for pooling_param in pooling_metadata.pooling_params
458
+ ]
459
+
460
+ # shape of scores: (batch_size, num_labels)
461
+ if all(use_cross_encoder == use_cross_encoder_list[0]
462
+ for use_cross_encoder in use_cross_encoder_list):
463
+ act_fn = self._get_act_fn(use_cross_encoder_list[0])
464
+ scores = act_fn(pooled_output)
465
+ else:
466
+ scores = torch.stack([
467
+ self._get_act_fn(use_cross_encoder)(vecs)
468
+ for use_cross_encoder, vecs in zip(use_cross_encoder_list,
469
+ pooled_output)
470
+ ])
471
472
pooled_outputs = [PoolingSequenceGroupOutput(data) for data in scores]
473
return PoolerOutput(outputs=pooled_outputs)
vllm/model_executor/models/bert.py
-5
@@ -25,8 +25,6 @@ from vllm.model_executor.layers.vocab_parallel_embedding import (
25
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
26
from vllm.model_executor.pooling_metadata import PoolingMetadata
27
from vllm.sequence import IntermediateTensors, PoolerOutput
28
-from vllm.transformers_utils.config import (
29
- get_cross_encoder_activation_function)
28
29
from .interfaces import SupportsCrossEncoding, SupportsQuant, SupportsV0Only
30
from .utils import WeightsMapper, maybe_prefix
@@ -462,9 +460,6 @@ class BertForSequenceClassification(nn.Module, SupportsV0Only,
460
super().__init__()
461
config = vllm_config.model_config.hf_config
462
465
- self.default_activation_function = \
466
- get_cross_encoder_activation_function(config)
467
-
463
self.num_labels = config.num_labels
464
self.bert = BertModel(vllm_config=vllm_config,
465
prefix=maybe_prefix(prefix, "bert"),
vllm/model_executor/models/roberta.py
-5
@@ -18,8 +18,6 @@ from vllm.model_executor.models.bert import BertEmbeddingModel, BertModel
18
from vllm.model_executor.models.utils import WeightsMapper, maybe_prefix
19
from vllm.model_executor.pooling_metadata import PoolingMetadata
20
from vllm.sequence import IntermediateTensors, PoolerOutput
21
-from vllm.transformers_utils.config import (
22
- get_cross_encoder_activation_function)
21
22
from .bert_with_rope import BertWithRope, JinaRobertaModel
23
from .interfaces import SupportsCrossEncoding, SupportsV0Only
@@ -178,9 +176,6 @@ class RobertaForSequenceClassification(nn.Module, SupportsCrossEncoding,
176
super().__init__()
177
config = vllm_config.model_config.hf_config
178
181
- self.default_activation_function = \
182
- get_cross_encoder_activation_function(config)
183
-
179
self.num_labels = config.num_labels
180
self.roberta = BertModel(vllm_config=vllm_config,
181
prefix=maybe_prefix(prefix, "bert"),
vllm/pooling_params.py
+3
@@ -24,12 +24,14 @@ class PoolingParams(
24
"""
25
26
dimensions: Optional[int] = None
27
+ use_cross_encoder: bool = False
28
additional_data: Optional[Any] = None
29
output_kind: RequestOutputKind = RequestOutputKind.FINAL_ONLY
30
31
def clone(self) -> "PoolingParams":
32
"""Returns a deep copy of the PoolingParams instance."""
33
return PoolingParams(dimensions=self.dimensions,
34
+ use_cross_encoder=self.use_cross_encoder,
35
additional_data=self.additional_data)
36
37
def verify(self, model_config: "ModelConfig") -> None:
@@ -54,6 +56,7 @@ class PoolingParams(
56
def __repr__(self) -> str:
57
return (f"PoolingParams("
58
f"dimensions={self.dimensions}, "
59
+ f"use_cross_encoder={self.use_cross_encoder}, "
60
f"additional_metadata={self.additional_data})")
61
62
def __post_init__(self) -> None:
vllm/transformers_utils/config.py
+11
-9
@@ -866,24 +866,26 @@ def try_get_generation_config(
866
return None
867
868
869
-def get_cross_encoder_activation_function(config: PretrainedConfig):
869
+def get_classification_activation_function(config: PretrainedConfig):
870
+ return nn.Sigmoid() if config.num_labels == 1 else nn.Softmax()
871
+
872
873
+def get_cross_encoder_activation_function(config: PretrainedConfig):
874
function_name: Optional[str] = None
872
- if hasattr(config, "sentence_transformers") and "activation_fn" in \
873
- config.sentence_transformers:
875
+ if (hasattr(config, "sentence_transformers")
876
+ and "activation_fn" in config.sentence_transformers):
877
function_name = config.sentence_transformers["activation_fn"]
875
-
878
elif (hasattr(config, "sbert_ce_default_activation_function")
879
and config.sbert_ce_default_activation_function is not None):
880
function_name = config.sbert_ce_default_activation_function
881
882
if function_name is not None:
881
- assert function_name.startswith("torch.nn.modules."), \
882
- "Loading of activation functions is restricted to " \
883
- "torch.nn.modules for security reasons"
883
+ assert function_name.startswith("torch.nn.modules."), (
884
+ "Loading of activation functions is restricted to "
885
+ "torch.nn.modules for security reasons")
886
return resolve_obj_by_qualname(function_name)()
885
- else:
886
- return nn.Sigmoid() if config.num_labels == 1 else nn.Identity()
887
+
888
+ return nn.Sigmoid() if config.num_labels == 1 else nn.Identity()
889
890
891
def try_get_safetensors_metadata(