Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
74e23b9
feat: Unify ViT attention backend selection across vision models
baonudesifeizhai Aug 29, 2025
d1ed17d
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Aug 29, 2025
495fe4a
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Aug 30, 2025
624bcda
feat: Add unified VisionAttention interface for automatic backend sel…
baonudesifeizhai Aug 30, 2025
54160c5
fix: Remove trailing whitespace in vision.py
baonudesifeizhai Aug 30, 2025
5e5c80c
feat: Enhance VisionAttention with ROCM_AITER_FA support and proper r…
baonudesifeizhai Aug 30, 2025
25f4ea4
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Aug 30, 2025
3537659
feat: unify vision attention implementations using MultiHeadAttention
baonudesifeizhai Aug 30, 2025
8fc3cd6
Merge branch 'feature/vit-attention-unification' of https://github.co…
baonudesifeizhai Aug 30, 2025
ad3d9c3
fix: add fallback for MultiHeadAttention backend detection
baonudesifeizhai Aug 30, 2025
8398155
fix: correct tensor dimensions in VisionAttention forward method
baonudesifeizhai Aug 30, 2025
b13f5ba
Fix MultiHeadAttention backend selection logic, remove forced convers…
baonudesifeizhai Aug 30, 2025
f3a28c2
Add FLEX_ATTENTION backend support to MultiHeadAttention.forward
baonudesifeizhai Aug 30, 2025
0491cd5
Remove unnecessary FlexAttention import in MultiHeadAttention
baonudesifeizhai Aug 30, 2025
39b4002
Add FLASH_ATTN_VLLM_V1 support to MultiHeadAttention
baonudesifeizhai Aug 30, 2025
3fd3c9f
Fix Flash Attention function import name
baonudesifeizhai Aug 30, 2025
b4bb47b
Fix Flash Attention function call with proper parameters
baonudesifeizhai Aug 30, 2025
f6a81d5
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Aug 31, 2025
aa0c158
Fix ruff linting errors: line length and unused imports
baonudesifeizhai Aug 31, 2025
bb1b8c8
Simplify vision attention modules to use unified MultiHeadAttention
baonudesifeizhai Aug 31, 2025
8bfd32e
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Aug 31, 2025
588cc8b
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Sep 1, 2025
b4c4037
Fix MultiHeadAttention issues based on review feedback
baonudesifeizhai Sep 2, 2025
0e7cc3c
Fix MultiHeadAttention FlashAttention implementation and ROCm AITer F…
baonudesifeizhai Sep 2, 2025
e49b7df
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 2, 2025
9eb1db3
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 3, 2025
4b80a17
refactor: address Isotr0py's review comments
baonudesifeizhai Sep 3, 2025
0067c04
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Sep 3, 2025
b877af8
Delete layer.py out = out.reshape(bsz, q_len, -1)
baonudesifeizhai Sep 3, 2025
cdfa3bd
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 3, 2025
a43a02a
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Sep 4, 2025
f6d9e7b
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 5, 2025
80fac65
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 6, 2025
c861dba
Fix reviewer comments: remove unnecessary transpose and improve fallb…
baonudesifeizhai Sep 6, 2025
3315273
Merge branch 'vllm-project:main' into feature/vit-attention-unification
baonudesifeizhai Sep 6, 2025
88ddf98
revert pixtral and code format
Isotr0py Sep 8, 2025
3373156
Merge branch 'main' into feature/vit-attention-unification
Isotr0py Sep 8, 2025
46912e3
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 8, 2025
3ecdb62
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 9, 2025
e5fee4e
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 9, 2025
2b7ece5
Merge branch 'main' into feature/vit-attention-unification
baonudesifeizhai Sep 9, 2025
dcb457b
retry Fix test_mha_attn.py
baonudesifeizhai Sep 9, 2025
ad360e5
Fix test_mha_attn.py
baonudesifeizhai Sep 9, 2025
86f1b87
Fix
baonudesifeizhai Sep 9, 2025
01a5410
Fix:
baonudesifeizhai Sep 9, 2025
d03c4da
Fix
baonudesifeizhai Sep 9, 2025
77f7654
Fix
baonudesifeizhai Sep 9, 2025
8f16d02
remove never used ViT FA
Isotr0py Sep 10, 2025
e3a2bab
Merge branch 'main' into feature/vit-attention-unification
Isotr0py Sep 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions vllm/attention/layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,22 +350,23 @@ def __init__(
self.num_queries_per_kv = self.num_heads // self.num_kv_heads

dtype = torch.get_default_dtype()
attn_backend = get_attn_backend(head_size,
dtype,
kv_cache_dtype=None,
block_size=16,
is_attention_free=False)
backend = backend_name_to_enum(attn_backend.get_name())
try:
attn_backend = get_attn_backend(head_size,
dtype,
kv_cache_dtype=None,
block_size=16,
is_attention_free=False)
backend = backend_name_to_enum(attn_backend.get_name())
except (ValueError, AttributeError):
# Fallback to TORCH_SDPA if backend detection fails
backend = _Backend.TORCH_SDPA
Comment thread
Isotr0py marked this conversation as resolved.
Outdated
if current_platform.is_rocm():
# currently, only torch_sdpa is supported on rocm
self.attn_backend = _Backend.TORCH_SDPA
else:
if backend in (_Backend.FLASH_ATTN, _Backend.FLASH_ATTN_VLLM_V1,
_Backend.FLEX_ATTENTION):
backend = _Backend.XFORMERS

self.attn_backend = backend if backend in {
_Backend.TORCH_SDPA, _Backend.XFORMERS, _Backend.PALLAS_VLLM_V1
_Backend.TORCH_SDPA, _Backend.XFORMERS, _Backend.PALLAS_VLLM_V1,
_Backend.ROCM_AITER_FA, _Backend.FLEX_ATTENTION
} else _Backend.TORCH_SDPA
Comment thread
Isotr0py marked this conversation as resolved.
Outdated

if (self.attn_backend == _Backend.XFORMERS
Expand Down Expand Up @@ -413,6 +414,30 @@ def forward(
from torch_xla.experimental.custom_kernel import flash_attention
out = flash_attention(query, key, value, sm_scale=self.scale)
out = out.transpose(1, 2)
elif self.attn_backend in (_Backend.FLASH_ATTN,
_Backend.FLASH_ATTN_VLLM_V1):
# Flash Attention for ViT is not essential, fallback to PyTorch SDPA
query, key, value = (x.transpose(1, 2) for x in (query, key, value))
out = F.scaled_dot_product_attention(
query, key, value, scale=self.scale)
out = out.transpose(1, 2)
elif self.attn_backend == _Backend.ROCM_AITER_FA:
from aiter import flash_attn_varlen_func
out = flash_attn_varlen_func(
query, key, value, softmax_scale=self.scale)
elif self.attn_backend == _Backend.FLEX_ATTENTION:
# FlexAttention requires specific tensor format
query, key, value = (x.transpose(1, 2) for x in (query, key, value))
# Use a simple fallback to torch SDPA for now
out = F.scaled_dot_product_attention(
query, key, value, scale=self.scale)
out = out.transpose(1, 2)
Comment thread
Isotr0py marked this conversation as resolved.
Outdated
else:
# Fallback to torch SDPA for unsupported backends
query, key, value = (x.transpose(1, 2) for x in (query, key, value))
out = F.scaled_dot_product_attention(
query, key, value, scale=self.scale)
out = out.transpose(1, 2)

return out.reshape(bsz, q_len, -1)

Expand Down
8 changes: 6 additions & 2 deletions vllm/model_executor/models/idefics2_vision_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ def __init__(
quant_config=quant_config,
prefix=f"{prefix}.out_proj",
)
# Use unified MultiHeadAttention with Flash Attention support
self.attn = MultiHeadAttention(self.num_heads_per_partition,
self.head_dim, self.scale)

Expand All @@ -181,8 +182,11 @@ def forward(
hidden_states
) # batch_size, q_len, 3 * num_heads_per_partition * head_dim
query_states, key_states, value_states = qkv.chunk(3, dim=-1)
out = self.attn(query_states, key_states, value_states)
attn_output, _ = self.out_proj(out)

# Use unified MultiHeadAttention implementation
attn_output = self.attn(query_states, key_states, value_states)

attn_output, _ = self.out_proj(attn_output)
Comment thread
Isotr0py marked this conversation as resolved.
Outdated
return attn_output


Expand Down
15 changes: 9 additions & 6 deletions vllm/model_executor/models/intern_vit.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.model_loader.weight_utils import default_weight_loader


NORM2FN = {
'rms_norm': RMSNorm,
'layer_norm': nn.LayerNorm,
Expand Down Expand Up @@ -254,6 +255,10 @@ def __init__(
var_hidden_size=self.embed_dim)

self.proj = nn.Linear(self.dummy_dim, self.embed_dim)

# Use unified MultiHeadAttention with automatic backend selection
self.attn = MultiHeadAttention(
self.num_heads, self.head_dim, self.scale)

def forward(self, x: torch.Tensor) -> torch.Tensor:
B, N, C = x.shape
Expand All @@ -268,12 +273,10 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:
B_, N_, H_, D_ = q.shape
q = self.q_norm(q.flatten(-2, -1)).view(B_, N_, H_, D_)
k = self.k_norm(k.flatten(-2, -1)).view(B_, N_, H_, D_)
q = q.transpose(1, 2)
k = k.transpose(1, 2)
v = v.transpose(1, 2)

x = F.scaled_dot_product_attention(q, k, v, scale=self.scale)
x = x.transpose(1, 2).reshape(B, N, -1)

# Use unified MultiHeadAttention with automatic backend selection
x = self.attn(q, k, v)
x = x.reshape(B, N, -1)
Comment thread
Isotr0py marked this conversation as resolved.
Outdated

x = self.proj(x)
return x
Expand Down
20 changes: 9 additions & 11 deletions vllm/model_executor/models/interns1_vit.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import PretrainedConfig
from transformers.utils import torch_int

Expand All @@ -22,6 +21,7 @@
RowParallelLinear)
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm.attention.layer import MultiHeadAttention

NORM2FN = {
'rms_norm': RMSNorm,
Expand Down Expand Up @@ -205,6 +205,10 @@ def __init__(
var_hidden_size=self.embed_dim)

self.projection_layer = nn.Linear(self.dummy_dim, self.embed_dim)

# Use unified MultiHeadAttention with automatic backend selection
self.attn = MultiHeadAttention(
self.num_heads, self.head_dim, self.scale)

def forward(self, x: torch.Tensor) -> torch.Tensor:
B, N, C = x.shape
Expand All @@ -213,20 +217,14 @@ def forward(self, x: torch.Tensor) -> torch.Tensor:
k = self.k_proj(x)
v = self.v_proj(x)

q = q.view(B, N, self.num_heads, self.head_dim)
k = k.view(B, N, self.num_heads, self.head_dim)
v = v.view(B, N, self.num_heads, self.head_dim)

if self.qk_normalization:
B_, N_, H_, D_ = q.shape
q = self.q_norm(q.flatten(-2, -1)).view(B_, N_, H_, D_)
k = self.k_norm(k.flatten(-2, -1)).view(B_, N_, H_, D_)
q = q.transpose(1, 2)
k = k.transpose(1, 2)
v = v.transpose(1, 2)

x = F.scaled_dot_product_attention(q, k, v, scale=self.scale)
x = x.transpose(1, 2).reshape(B, N, -1)

# Use unified MultiHeadAttention with automatic backend selection
x = self.attn(q, k, v)
x = x.reshape(B, N, -1)
Comment thread
Isotr0py marked this conversation as resolved.
Outdated

x = self.projection_layer(x)
return x
Expand Down
26 changes: 11 additions & 15 deletions vllm/model_executor/models/mllama.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from vllm.model_executor.model_loader.weight_utils import (
default_weight_loader, maybe_remap_kv_scale_name)
from vllm.model_executor.models.module_mapping import MultiModelKeys

Comment thread
Isotr0py marked this conversation as resolved.
Outdated
from vllm.model_executor.sampling_metadata import SamplingMetadata
from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalEncDecInputs,
Expand All @@ -70,6 +71,7 @@
from .interfaces import SupportsMultiModal, SupportsV0Only
from .llama import LlamaDecoderLayer, LlamaMLP
from .utils import AutoWeightsLoader, WeightsMapper, maybe_prefix
from vllm.attention.layer import MultiHeadAttention

logger = init_logger(__name__)

Expand Down Expand Up @@ -516,6 +518,10 @@ def __init__(self,
quant_config=quant_config,
prefix=f"{prefix}.o_proj",
)

# Use unified MultiHeadAttention with automatic backend selection
self.attn = MultiHeadAttention(
self.num_local_heads, self.head_dim, 1.0 / math.sqrt(self.head_dim))

def forward(
self,
Expand All @@ -524,21 +530,11 @@ def forward(
) -> torch.Tensor:
qkv, _ = self.qkv_proj(hidden_state)
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
q = q.view(q.shape[0], q.shape[1], self.num_local_heads,
self.head_dim).transpose(1, 2)
k = k.view(k.shape[0], k.shape[1], self.num_local_heads,
self.head_dim).transpose(1, 2)
v = v.view(v.shape[0], v.shape[1], self.num_local_heads,
self.head_dim).transpose(1, 2)

# TODO: remove padding in image encoder
attn_output = F.scaled_dot_product_attention(q,
k,
v,
attn_mask=attention_mask,
dropout_p=0.0)

attn_output = attn_output.transpose(1, 2).contiguous()

# Use unified MultiHeadAttention with automatic backend selection
attn_output = self.attn(q, k, v)

attn_output = attn_output.contiguous()
Comment thread
Isotr0py marked this conversation as resolved.
Outdated
attn_output = attn_output.reshape(attn_output.shape[0],
attn_output.shape[1], -1)
output, _ = self.o_proj(attn_output)
Expand Down
26 changes: 13 additions & 13 deletions vllm/model_executor/models/pixtral.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
RowParallelLinear)
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm.attention.layer import MultiHeadAttention
from vllm.model_executor.sampling_metadata import SamplingMetadata

from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalKwargsItems
from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
NestedTensors)
Expand Down Expand Up @@ -1079,6 +1081,10 @@ def __init__(
quant_config=quant_config,
prefix=f"{prefix}.o_proj",
)

# Use unified MultiHeadAttention with automatic backend selection
self.attn = MultiHeadAttention(
self.n_heads, self.head_dim, 1.0 / math.sqrt(self.head_dim))

def forward(
self,
Expand All @@ -1098,19 +1104,13 @@ def forward(
cos, sin = position_embeddings
q, k = apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=0)

if USE_XFORMERS_OPS:
# Transpose q and k back for attention
q = q.transpose(1, 2).contiguous()
k = k.transpose(1, 2).contiguous()
out = xops.memory_efficient_attention(q,
k,
v,
attn_bias=attention_mask)
else:
v = v.transpose(1, 2)
out = nn.functional.scaled_dot_product_attention(
q, k, v, attn_mask=attention_mask)
out = out.transpose(1, 2)
Comment on lines -1101 to -1113

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HF format Pixtral generates gibberish with these changes because of missing attention mask.

python examples/offline_inference/vision_language.py -m pixtral_hf
INFO 09-08 08:15:39 [__init__.py:216] Automatically detected platform cuda.
INFO 09-08 08:15:43 [utils.py:328] non-default args: {'max_model_len': 6144, 'tensor_parallel_size': 2, 'max_num_seqs': 2, 'quantization': 'bitsandbytes', 'enforce_eager': True, 'limit_mm_per_prompt': {'image': 1, 'video': 0, 'audio': 0}, 'model': 'mistral-community/pixtral-12b'}
...
Adding requests: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:06<00:00,  1.50s/it]
Processed prompts:   0%|                                                                            | 0/4 [00:00<?, ?it/s, est. speed input: 0.00 toks/s, output: 0.00 toks/s](VllmWorker TP0 pid=2497) WARNING 09-08 08:22:53 [cudagraph_dispatcher.py:102] cudagraph dispatching keys are not initialized. No cudagraph will be used.
(VllmWorker TP1 pid=2498) WARNING 09-08 08:22:53 [cudagraph_dispatcher.py:102] cudagraph dispatching keys are not initialized. No cudagraph will be used.
Processed prompts: 100%|███████████████████████████████████████████████████████████████████| 4/4 [02:22<00:00, 35.67s/it, est. speed input: 78.67 toks/s, output: 1.79 toks/s]
--------------------------------------------------
The image you have provided appears to be a close-up view of a mosaic or a pattern composed of small, repeating square tiles. Each tile contains a tiny, pixelated image that forms a larger, recognizable image when viewed from a distance. This type of image is often referred to as a "mosaic" or
--------------------------------------------------
The image you are referring to appears to be a mosaic or a pattern composed of small, repeated elements. These elements are arranged in a grid-like fashion, creating a dense, colorful background. The colors predominantly include shades of blue, red, and brown, with some variations in hue and saturation. The pattern is intricate and
--------------------------------------------------
The image you're referring to appears to be a mosaic or a pattern composed of small, repeating squares or tiles. Each square contains a tiny, pixelated image that collectively forms a larger image. This type of image is often referred to as a "mosaic" or a "pixel art" image.

The colors
--------------------------------------------------
The image you provided appears to be a mosaic or a pattern composed of tiny, repeated elements that create a visually intricate and colorful texture. The dominant colors in the image are shades of blue, red, and black, which are interspersed with smaller amounts of other colors like green and yellow. The pattern is dense
--------------------------------------------------

Let's leave it to be handled together with Qwen2-VL-style ViTs in following PR.

# Reshape for MultiHeadAttention: (batch, seq, hidden_size)
q_reshaped = q.transpose(1, 2).contiguous().view(batch, patches, -1)
k_reshaped = k.transpose(1, 2).contiguous().view(batch, patches, -1)
v_reshaped = v.transpose(1, 2).contiguous().view(batch, patches, -1)

@Isotr0py Isotr0py Sep 5, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems there is no need to transpose value_states?


# Use unified MultiHeadAttention with automatic backend selection
out = self.attn(q_reshaped, k_reshaped, v_reshaped)

out = out.view(batch, patches, self.n_heads * self.head_dim)
Comment thread
Isotr0py marked this conversation as resolved.
attn_output, _ = self.o_proj(out)
Expand Down
30 changes: 13 additions & 17 deletions vllm/model_executor/models/step3_vl.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
RowParallelLinear)
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler
from vllm.attention.layer import MultiHeadAttention
from vllm.model_executor.sampling_metadata import SamplingMetadata
from vllm.multimodal import MULTIMODAL_REGISTRY
from vllm.multimodal.inputs import (MultiModalDataDict, MultiModalFieldConfig,
Expand Down Expand Up @@ -696,10 +697,10 @@ def __init__(self,
bias=True,
quant_config=quant_config,
prefix=prefix)

def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
return tensor.view(bsz, seq_len, self.num_heads,
self.head_dim).transpose(1, 2).contiguous()
# Use unified MultiHeadAttention with automatic backend selection
self.attn = MultiHeadAttention(
self.num_heads, self.head_dim, self.scale)

def forward(
self,
Expand All @@ -711,19 +712,14 @@ def forward(
# get query proj
qkv, _ = self.qkv_proj(hidden_states)
q, k, v = qkv.chunk(chunks=3, dim=-1)
q = q.view(bsz, tgt_len, self.num_heads, self.head_dim)
k = k.view(bsz, tgt_len, self.num_heads, self.head_dim)
v = v.view(bsz, tgt_len, self.num_heads, self.head_dim)
q = q.transpose(1, 2)
k = k.transpose(1, 2)
v = v.transpose(1, 2)
attn_output = F.scaled_dot_product_attention(q,
k,
v,
scale=self.scale,
is_causal=False)
attn_output = attn_output.transpose(1, 2).reshape(
bsz, tgt_len, self.num_heads * self.head_dim)

# Reshape for MultiHeadAttention: (batch, seq, hidden_size)
q_reshaped = q.view(bsz, tgt_len, -1)
k_reshaped = k.view(bsz, tgt_len, -1)
v_reshaped = v.view(bsz, tgt_len, -1)

Comment thread
Isotr0py marked this conversation as resolved.
Outdated
# Use unified MultiHeadAttention with automatic backend selection
attn_output = self.attn(q_reshaped, k_reshaped, v_reshaped)

attn_output, _ = self.out_proj(attn_output)

Expand Down
2 changes: 1 addition & 1 deletion vllm/model_executor/models/vision.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,4 @@ def resolve_visual_encoder_outputs(
uses_last_layer = feature_sample_layers[-1] in (len(hs_pool) - 1, -1)
if post_layer_norm is not None and uses_last_layer:
hs_pool[-1] = post_layer_norm(encoder_outputs)
return torch.cat(hs_pool, dim=-1)
return torch.cat(hs_pool, dim=-1)
Loading