Skip to content

vllm.model_executor.models.clip

Classes:

Functions:

CLIPAttention

Bases: Module

Methods:

  • forward

    Input shape: Batch x Time x Channel

Source code in vllm/model_executor/models/clip.py
class CLIPAttention(nn.Module):
    def __init__(
        self,
        config: CLIPTextConfig | CLIPVisionConfig,
        quant_config: QuantizationConfig | None = None,
        *,
        prefix: str = "",
        attn_cls: type[Attention] | type[MMEncoderAttention],
    ) -> None:
        super().__init__()

        self.config = config
        self.embed_dim = config.hidden_size
        self.num_heads = config.num_attention_heads
        self.head_dim = self.embed_dim // self.num_heads
        if self.head_dim * self.num_heads != self.embed_dim:
            raise ValueError(
                f"embed_dim must be divisible by num_heads "
                f"(got `embed_dim`: {self.embed_dim} and "
                f"`num_heads`: {self.num_heads})."
            )
        self.scale = self.head_dim**-0.5

        use_data_parallel = is_vit_use_data_parallel()
        self.qkv_proj = QKVParallelLinear(
            hidden_size=self.embed_dim,
            head_size=self.head_dim,
            total_num_heads=self.num_heads,
            quant_config=quant_config,
            prefix=f"{prefix}.qkv_proj",
            disable_tp=use_data_parallel,
        )

        self.out_proj = RowParallelLinear(
            input_size=self.embed_dim,
            output_size=self.embed_dim,
            quant_config=quant_config,
            prefix=f"{prefix}.out_proj",
            disable_tp=use_data_parallel,
        )

        self.tp_size = (
            1 if use_data_parallel else get_tensor_model_parallel_world_size()
        )
        self.num_heads_per_partition = divide(self.num_heads, self.tp_size)

        self.attn = attn_cls(
            self.num_heads_per_partition,
            self.head_dim,
            self.scale,
            prefix=f"{prefix}.attn",
        )

    def forward(
        self,
        hidden_states: torch.Tensor,
    ):
        """Input shape: Batch x Time x Channel"""

        qkv_states, _ = self.qkv_proj(hidden_states)
        query_states, key_states, value_states = qkv_states.chunk(3, dim=-1)
        out = self.attn(query_states, key_states, value_states)
        attn_output, _ = self.out_proj(out)

        return attn_output, None

forward(hidden_states)

Input shape: Batch x Time x Channel

Source code in vllm/model_executor/models/clip.py
def forward(
    self,
    hidden_states: torch.Tensor,
):
    """Input shape: Batch x Time x Channel"""

    qkv_states, _ = self.qkv_proj(hidden_states)
    query_states, key_states, value_states = qkv_states.chunk(3, dim=-1)
    out = self.attn(query_states, key_states, value_states)
    attn_output, _ = self.out_proj(out)

    return attn_output, None

CLIPEncoder

Bases: Module

Transformer encoder consisting of config.num_hidden_layers self attention layers. Each layer is a [CLIPEncoderLayer].

Parameters:

  • config

    (CLIPTextConfig | CLIPVisionConfig) –

    CLIPConfig

Source code in vllm/model_executor/models/clip.py
class CLIPEncoder(nn.Module):
    """
    Transformer encoder consisting of `config.num_hidden_layers` self
    attention layers. Each layer is a [`CLIPEncoderLayer`].

    Args:
        config: CLIPConfig
    """

    def __init__(
        self,
        config: CLIPTextConfig | CLIPVisionConfig,
        quant_config: QuantizationConfig | None = None,
        num_hidden_layers_override: int | None = None,
        *,
        prefix: str = "",
        attn_cls: type[Attention] | type[MMEncoderAttention],
    ) -> None:
        super().__init__()

        self.config = config

        if num_hidden_layers_override is None:
            num_hidden_layers = config.num_hidden_layers
        else:
            num_hidden_layers = num_hidden_layers_override

        self.layers = nn.ModuleList(
            [
                CLIPEncoderLayer(
                    config=config,
                    quant_config=quant_config,
                    prefix=f"{prefix}.layers.{layer_idx}",
                    attn_cls=attn_cls,
                )
                for layer_idx in range(num_hidden_layers)
            ]
        )

    def forward(
        self,
        inputs_embeds: torch.Tensor,
        return_all_hidden_states: bool,
    ) -> torch.Tensor | list[torch.Tensor]:
        hidden_states_pool = [inputs_embeds]
        hidden_states = inputs_embeds

        for encoder_layer in self.layers:
            hidden_states = encoder_layer(hidden_states)
            if return_all_hidden_states:
                hidden_states_pool.append(hidden_states)
        # If we have multiple feature sample layers, we return all hidden
        # states in order and grab the ones we need by index.
        if return_all_hidden_states:
            return hidden_states_pool
        return hidden_states

CLIPImagePixelInputs

Bases: TensorSchema

Dimensions
  • bn: Batch size * number of images
  • c: Number of channels (3)
  • h: Height of each image
  • w: Width of each image
Source code in vllm/model_executor/models/clip.py
class CLIPImagePixelInputs(TensorSchema):
    """
    Dimensions:
        - bn: Batch size * number of images
        - c: Number of channels (3)
        - h: Height of each image
        - w: Width of each image
    """

    type: Literal["pixel_values"]
    data: Annotated[torch.Tensor, TensorShape("bn", 3, "h", "w")]

dual_encoder_has_text_tokens(has_mm_embeddings, is_multimodal)

Whether a dual-encoder pooling batch still contains text tokens.

embed_input_ids used to treat "the batch has any image embeddings" as "the whole batch is vision-only". Mixed image+text batches then skipped the text encoder, so completion-style text embeddings collapsed (issue

53091). Honor the per-token is_multimodal mask instead.

Source code in vllm/model_executor/models/clip.py
def dual_encoder_has_text_tokens(
    has_mm_embeddings: bool,
    is_multimodal: torch.Tensor | None,
) -> bool:
    """Whether a dual-encoder pooling batch still contains text tokens.

    ``embed_input_ids`` used to treat "the batch has any image embeddings" as
    "the whole batch is vision-only". Mixed image+text batches then skipped
    the text encoder, so completion-style text embeddings collapsed (issue
    #53091). Honor the per-token ``is_multimodal`` mask instead.
    """
    if not has_mm_embeddings or is_multimodal is None:
        return not has_mm_embeddings
    return bool((~is_multimodal).any().item())

merge_dual_encoder_text_and_vision(text_features, vision_embeds, is_multimodal)

Restore vision embeddings on multimodal tokens after the text encoder.

The model runner builds is_multimodal on CPU; text and vision features are on the compute device.

Source code in vllm/model_executor/models/clip.py
def merge_dual_encoder_text_and_vision(
    text_features: torch.Tensor,
    vision_embeds: torch.Tensor,
    is_multimodal: torch.Tensor | None,
) -> torch.Tensor:
    """Restore vision embeddings on multimodal tokens after the text encoder.

    The model runner builds ``is_multimodal`` on CPU; text and vision features
    are on the compute device.
    """
    if is_multimodal is None or not bool(is_multimodal.any().item()):
        return text_features
    mm_mask = is_multimodal.to(device=vision_embeds.device, non_blocking=True)
    return torch.where(mm_mask.unsqueeze(-1), vision_embeds, text_features)