Spaces:
Runtime error
Runtime error
moose Claude Opus 4.7 (1M context) commited on
Commit ·
1543d6f
1
Parent(s): 3e914c4
Replace external vllm-flash-attn3 kernel with PyTorch cuDNN FA3 backend
Browse filesHF's kernels-community/vllm-flash-attn3 was last rebuilt 2026-04-30,
before HF rolled ZeroGPU's base image to torch 2.11+cu130 on ~2026-05-08.
The cached kernel binary's ABI no longer matches the new runtime — every
FA3 call dies with "no kernel image is available for execution on the
device".
PyTorch 2.11's cuDNN attention backend ships with the base image and
dispatches to the same Hopper FA3 kernel family on H200 — no external
kernel dependency, no ABI risk, ~5-15% slower than vLLM FA3 at worst.
Also drops the torch.library.custom_op wrapper, which was a known
dispatcher conflict source on PyTorch 2.11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- qwenimage/qwen_fa3_processor.py +17 -49
qwenimage/qwen_fa3_processor.py
CHANGED
|
@@ -3,59 +3,26 @@ Paired with a good language model. Thanks!
|
|
| 3 |
"""
|
| 4 |
|
| 5 |
import torch
|
|
|
|
|
|
|
| 6 |
from typing import Optional, Tuple
|
| 7 |
from diffusers.models.transformers.transformer_qwenimage import apply_rotary_emb_qwen
|
| 8 |
|
| 9 |
-
try:
|
| 10 |
-
from kernels import get_kernel
|
| 11 |
-
_k = get_kernel("kernels-community/vllm-flash-attn3")
|
| 12 |
-
_flash_attn_func = _k.flash_attn_func
|
| 13 |
-
except Exception as e:
|
| 14 |
-
_flash_attn_func = None
|
| 15 |
-
_kernels_err = e
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
def _ensure_fa3_available():
|
| 19 |
-
if _flash_attn_func is None:
|
| 20 |
-
raise ImportError(
|
| 21 |
-
"FlashAttention-3 via Hugging Face `kernels` is required. "
|
| 22 |
-
"Tried `get_kernel('kernels-community/vllm-flash-attn3')` and failed with:\n"
|
| 23 |
-
f"{_kernels_err}"
|
| 24 |
-
)
|
| 25 |
-
|
| 26 |
-
@torch.library.custom_op("flash::flash_attn_func", mutates_args=())
|
| 27 |
-
def flash_attn_func(
|
| 28 |
-
q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, causal: bool = False
|
| 29 |
-
) -> torch.Tensor:
|
| 30 |
-
outputs, lse = _flash_attn_func(q, k, v, causal=causal)
|
| 31 |
-
return outputs
|
| 32 |
-
|
| 33 |
-
@flash_attn_func.register_fake
|
| 34 |
-
def _(q, k, v, **kwargs):
|
| 35 |
-
# two outputs:
|
| 36 |
-
# 1. output: (batch, seq_len, num_heads, head_dim)
|
| 37 |
-
# 2. softmax_lse: (batch, num_heads, seq_len) with dtype=torch.float32
|
| 38 |
-
meta_q = torch.empty_like(q).contiguous()
|
| 39 |
-
return meta_q #, q.new_empty((q.size(0), q.size(2), q.size(1)), dtype=torch.float32)
|
| 40 |
-
|
| 41 |
|
| 42 |
class QwenDoubleStreamAttnProcessorFA3:
|
| 43 |
"""
|
| 44 |
-
FA3-
|
| 45 |
-
|
| 46 |
-
|
|
|
|
| 47 |
|
| 48 |
Notes / limitations:
|
| 49 |
-
- General attention masks are not supported here
|
| 50 |
-
- Optional windowed attention / sink tokens / softcap can be plumbed through if you use those features.
|
| 51 |
- Expects an available `apply_rotary_emb_qwen` in scope (same as your non-FA3 processor).
|
| 52 |
"""
|
| 53 |
|
| 54 |
_attention_backend = "fa3" # for parity with your other processors, not used internally
|
| 55 |
|
| 56 |
-
def __init__(self):
|
| 57 |
-
_ensure_fa3_available()
|
| 58 |
-
|
| 59 |
@torch.no_grad()
|
| 60 |
def __call__(
|
| 61 |
self,
|
|
@@ -69,11 +36,8 @@ class QwenDoubleStreamAttnProcessorFA3:
|
|
| 69 |
if encoder_hidden_states is None:
|
| 70 |
raise ValueError("QwenDoubleStreamAttnProcessorFA3 requires encoder_hidden_states (text stream).")
|
| 71 |
if attention_mask is not None:
|
| 72 |
-
# FA3 kernel path here does not consume arbitrary masks; fail fast to avoid silent correctness issues.
|
| 73 |
raise NotImplementedError("attention_mask is not supported in this FA3 implementation.")
|
| 74 |
|
| 75 |
-
_ensure_fa3_available()
|
| 76 |
-
|
| 77 |
B, S_img, _ = hidden_states.shape
|
| 78 |
S_txt = encoder_hidden_states.shape[1]
|
| 79 |
|
|
@@ -110,20 +74,24 @@ class QwenDoubleStreamAttnProcessorFA3:
|
|
| 110 |
# ---- RoPE (Qwen variant) ----
|
| 111 |
if image_rotary_emb is not None:
|
| 112 |
img_freqs, txt_freqs = image_rotary_emb
|
| 113 |
-
# expects tensors shaped (B, S, H, D_h)
|
| 114 |
img_q = apply_rotary_emb_qwen(img_q, img_freqs, use_real=False)
|
| 115 |
img_k = apply_rotary_emb_qwen(img_k, img_freqs, use_real=False)
|
| 116 |
txt_q = apply_rotary_emb_qwen(txt_q, txt_freqs, use_real=False)
|
| 117 |
txt_k = apply_rotary_emb_qwen(txt_k, txt_freqs, use_real=False)
|
| 118 |
|
| 119 |
# ---- Joint attention over [text, image] along sequence axis ----
|
| 120 |
-
|
| 121 |
-
q = torch.cat([txt_q, img_q], dim=1)
|
| 122 |
k = torch.cat([txt_k, img_k], dim=1)
|
| 123 |
v = torch.cat([txt_v, img_v], dim=1)
|
| 124 |
|
| 125 |
-
#
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
# ---- Back to (B, S, D_model) ----
|
| 129 |
out = out.flatten(2, 3).to(q.dtype)
|
|
@@ -139,4 +107,4 @@ class QwenDoubleStreamAttnProcessorFA3:
|
|
| 139 |
|
| 140 |
txt_attn_out = attn.to_add_out(txt_attn_out)
|
| 141 |
|
| 142 |
-
return img_attn_out, txt_attn_out
|
|
|
|
| 3 |
"""
|
| 4 |
|
| 5 |
import torch
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
from torch.nn.attention import sdpa_kernel, SDPBackend
|
| 8 |
from typing import Optional, Tuple
|
| 9 |
from diffusers.models.transformers.transformer_qwenimage import apply_rotary_emb_qwen
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
class QwenDoubleStreamAttnProcessorFA3:
|
| 13 |
"""
|
| 14 |
+
FA3-grade attention processor for Qwen double-stream architecture.
|
| 15 |
+
Routes through PyTorch's cuDNN attention backend, which on Hopper (SM 9.0+)
|
| 16 |
+
dispatches to the same FlashAttention-3 family of kernels as vLLM's FA3 —
|
| 17 |
+
bundled with the base image so there's no external-kernel ABI risk.
|
| 18 |
|
| 19 |
Notes / limitations:
|
| 20 |
+
- General attention masks are not supported here. `is_causal=False` and no arbitrary mask.
|
|
|
|
| 21 |
- Expects an available `apply_rotary_emb_qwen` in scope (same as your non-FA3 processor).
|
| 22 |
"""
|
| 23 |
|
| 24 |
_attention_backend = "fa3" # for parity with your other processors, not used internally
|
| 25 |
|
|
|
|
|
|
|
|
|
|
| 26 |
@torch.no_grad()
|
| 27 |
def __call__(
|
| 28 |
self,
|
|
|
|
| 36 |
if encoder_hidden_states is None:
|
| 37 |
raise ValueError("QwenDoubleStreamAttnProcessorFA3 requires encoder_hidden_states (text stream).")
|
| 38 |
if attention_mask is not None:
|
|
|
|
| 39 |
raise NotImplementedError("attention_mask is not supported in this FA3 implementation.")
|
| 40 |
|
|
|
|
|
|
|
| 41 |
B, S_img, _ = hidden_states.shape
|
| 42 |
S_txt = encoder_hidden_states.shape[1]
|
| 43 |
|
|
|
|
| 74 |
# ---- RoPE (Qwen variant) ----
|
| 75 |
if image_rotary_emb is not None:
|
| 76 |
img_freqs, txt_freqs = image_rotary_emb
|
|
|
|
| 77 |
img_q = apply_rotary_emb_qwen(img_q, img_freqs, use_real=False)
|
| 78 |
img_k = apply_rotary_emb_qwen(img_k, img_freqs, use_real=False)
|
| 79 |
txt_q = apply_rotary_emb_qwen(txt_q, txt_freqs, use_real=False)
|
| 80 |
txt_k = apply_rotary_emb_qwen(txt_k, txt_freqs, use_real=False)
|
| 81 |
|
| 82 |
# ---- Joint attention over [text, image] along sequence axis ----
|
| 83 |
+
q = torch.cat([txt_q, img_q], dim=1) # (B, S_total, H, D_h)
|
|
|
|
| 84 |
k = torch.cat([txt_k, img_k], dim=1)
|
| 85 |
v = torch.cat([txt_v, img_v], dim=1)
|
| 86 |
|
| 87 |
+
# SDPA wants (B, H, S, D_h); route through cuDNN's FA3 path on Hopper.
|
| 88 |
+
with sdpa_kernel(SDPBackend.CUDNN_ATTENTION):
|
| 89 |
+
out = F.scaled_dot_product_attention(
|
| 90 |
+
q.transpose(1, 2),
|
| 91 |
+
k.transpose(1, 2),
|
| 92 |
+
v.transpose(1, 2),
|
| 93 |
+
is_causal=False,
|
| 94 |
+
).transpose(1, 2) # back to (B, S_total, H, D_h)
|
| 95 |
|
| 96 |
# ---- Back to (B, S, D_model) ----
|
| 97 |
out = out.flatten(2, 3).to(q.dtype)
|
|
|
|
| 107 |
|
| 108 |
txt_attn_out = attn.to_add_out(txt_attn_out)
|
| 109 |
|
| 110 |
+
return img_attn_out, txt_attn_out
|