197 lines
6.7 KiB
Python
197 lines
6.7 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
from abc import ABC, abstractmethod
|
|
from collections.abc import Set
|
|
from typing import TypeAlias
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
|
|
from vllm.model_executor.layers.pooler import ActivationFn, ClassifierFn, ProjectorFn
|
|
from vllm.tasks import PoolingTask
|
|
from vllm.v1.pool.metadata import PoolingMetadata
|
|
|
|
from .methods import SequencePoolingMethodOutput
|
|
|
|
SequencePoolerHeadOutput: TypeAlias = torch.Tensor | list[torch.Tensor]
|
|
|
|
|
|
class SequencePoolerHead(nn.Module, ABC):
|
|
@abstractmethod
|
|
def get_supported_tasks(self) -> Set[PoolingTask]:
|
|
raise NotImplementedError
|
|
|
|
@abstractmethod
|
|
def forward(
|
|
self,
|
|
pooled_data: SequencePoolingMethodOutput,
|
|
pooling_metadata: PoolingMetadata,
|
|
) -> SequencePoolerHeadOutput:
|
|
raise NotImplementedError
|
|
|
|
|
|
class EmbeddingPoolerHead(SequencePoolerHead):
|
|
def __init__(
|
|
self,
|
|
projector: ProjectorFn | None = None,
|
|
head_dtype: torch.dtype | str | None = None,
|
|
activation: ActivationFn | None = None,
|
|
) -> None:
|
|
super().__init__()
|
|
|
|
self.projector = projector
|
|
self.head_dtype = head_dtype
|
|
self.activation = activation
|
|
|
|
def extra_repr(self) -> str:
|
|
attrs = []
|
|
if self.head_dtype is not None:
|
|
attrs.append(f"head_dtype={self.head_dtype}")
|
|
if self.projector is not None:
|
|
attrs.append("projector=True")
|
|
if self.activation is not None:
|
|
attrs.append(f"activation={self.activation.__class__.__name__}")
|
|
return ", ".join(attrs)
|
|
|
|
def get_supported_tasks(self) -> Set[PoolingTask]:
|
|
return {"embed"}
|
|
|
|
def forward(
|
|
self,
|
|
pooled_data: SequencePoolingMethodOutput,
|
|
pooling_metadata: PoolingMetadata,
|
|
) -> SequencePoolerHeadOutput:
|
|
pooling_params = pooling_metadata.pooling_params
|
|
if len(pooled_data) != len(pooling_params):
|
|
raise ValueError(
|
|
f"pooled_data length ({len(pooled_data)}) does not match "
|
|
f"pooling_params length ({len(pooling_params)})"
|
|
)
|
|
|
|
if isinstance(pooled_data, list):
|
|
pooled_data = torch.stack(pooled_data)
|
|
# pooled_data shape: [batchsize, hidden_size]
|
|
|
|
if self.head_dtype is not None:
|
|
pooled_data = pooled_data.to(self.head_dtype)
|
|
|
|
# Apply ST projector
|
|
if self.projector is not None:
|
|
embeddings = self.projector(pooled_data)
|
|
else:
|
|
embeddings = pooled_data
|
|
# embeddings shape: [batchsize, embedding_size]
|
|
|
|
# for matryoshka representation
|
|
dimensions_list = [pooling_param.dimensions for pooling_param in pooling_params]
|
|
if any(d is not None for d in dimensions_list):
|
|
# change the output dimension
|
|
if len(embeddings) != len(dimensions_list):
|
|
raise ValueError(
|
|
f"embeddings length ({len(embeddings)}) does not match "
|
|
f"dimensions_list length ({len(dimensions_list)})"
|
|
)
|
|
if len(set(dimensions_list)) == 1 and not isinstance(embeddings, list):
|
|
# if all dimensions are the same
|
|
d = dimensions_list[0]
|
|
embeddings = embeddings[..., :d]
|
|
else:
|
|
embeddings = [
|
|
vecs if d is None else vecs[..., :d]
|
|
for vecs, d in zip(embeddings, dimensions_list)
|
|
]
|
|
|
|
# for normalize
|
|
if self.activation is not None:
|
|
flags = [p.use_activation for p in pooling_params]
|
|
if len(set(flags)) == 1:
|
|
if flags[0]:
|
|
embeddings = self.activation(embeddings)
|
|
else:
|
|
embeddings = [
|
|
self.activation(vecs) if f else vecs
|
|
for vecs, f in zip(embeddings, flags)
|
|
]
|
|
|
|
# embeddings shape: [batchsize, embedding_size]
|
|
return embeddings
|
|
|
|
|
|
class ClassifierPoolerHead(SequencePoolerHead):
|
|
def __init__(
|
|
self,
|
|
classifier: ClassifierFn | None = None,
|
|
logit_mean: float | None = None,
|
|
logit_sigma: float | None = None,
|
|
head_dtype: torch.dtype | str | None = None,
|
|
activation: ActivationFn | None = None,
|
|
) -> None:
|
|
super().__init__()
|
|
|
|
self.classifier = classifier
|
|
self.logit_mean = logit_mean
|
|
self.logit_sigma = logit_sigma
|
|
self.head_dtype = head_dtype
|
|
self.activation = activation
|
|
|
|
def extra_repr(self) -> str:
|
|
attrs = []
|
|
if self.head_dtype is not None:
|
|
attrs.append(f"head_dtype={self.head_dtype}")
|
|
if self.classifier is not None:
|
|
attrs.append("classifier=True")
|
|
if self.logit_mean is not None:
|
|
attrs.append(f"logit_mean={self.logit_mean}")
|
|
if self.logit_sigma is not None:
|
|
attrs.append(f"logit_sigma={self.logit_sigma}")
|
|
if self.activation is not None:
|
|
attrs.append(f"activation={self.activation.__class__.__name__}")
|
|
return ", ".join(attrs)
|
|
|
|
def get_supported_tasks(self) -> Set[PoolingTask]:
|
|
return {"classify"}
|
|
|
|
def forward(
|
|
self,
|
|
pooled_data: SequencePoolingMethodOutput,
|
|
pooling_metadata: PoolingMetadata,
|
|
) -> SequencePoolerHeadOutput:
|
|
pooling_params = pooling_metadata.pooling_params
|
|
if len(pooled_data) != len(pooling_params):
|
|
raise ValueError(
|
|
f"pooled_data length ({len(pooled_data)}) does not match "
|
|
f"pooling_params length ({len(pooling_params)})"
|
|
)
|
|
|
|
if isinstance(pooled_data, list):
|
|
pooled_data = torch.stack(pooled_data)
|
|
# pooled_data shape: [batchsize, hidden_size]
|
|
|
|
if self.head_dtype is not None:
|
|
pooled_data = pooled_data.to(self.head_dtype)
|
|
|
|
if self.classifier is not None:
|
|
logits = self.classifier(pooled_data)
|
|
else:
|
|
logits = pooled_data
|
|
|
|
# logits shape: [batchsize, num_labels]
|
|
# Affine score calibration: activation((logit - mean) / sigma)
|
|
if self.logit_mean is not None:
|
|
logits = logits - self.logit_mean
|
|
if self.logit_sigma is not None:
|
|
logits = logits / self.logit_sigma
|
|
|
|
if self.activation is not None:
|
|
flags = [p.use_activation for p in pooling_params]
|
|
if len(set(flags)) == 1:
|
|
logits = self.activation(logits) if flags[0] else logits
|
|
else:
|
|
logits = [
|
|
self.activation(vecs) if f else vecs
|
|
for vecs, f in zip(logits, flags)
|
|
]
|
|
|
|
# logits shape: [batchsize, num_labels]
|
|
return logits
|