Fix: Add qwen_fa3_processor.py and update __init__.py
Browse files- qwenimage/__init__.py +0 -1
- qwenimage/qwen_fa3_processor.py +90 -0
qwenimage/__init__.py
CHANGED
|
@@ -1,3 +1,2 @@
|
|
| 1 |
from .pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 2 |
from .transformer_qwenimage import QwenImageTransformer2DModel
|
| 3 |
-
from .qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
|
|
|
|
| 1 |
from .pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
|
| 2 |
from .transformer_qwenimage import QwenImageTransformer2DModel
|
|
|
qwenimage/qwen_fa3_processor.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FA3 attention processor for Qwen-Image-Edit with SDPA fallback for Blackwell GPUs.
|
| 3 |
+
"""
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn.functional as F
|
| 6 |
+
from typing import Optional, Tuple
|
| 7 |
+
from diffusers.models.transformers.transformer_qwenimage import apply_rotary_emb_qwen
|
| 8 |
+
|
| 9 |
+
def _is_blackwell() -> bool:
|
| 10 |
+
if not torch.cuda.is_available():
|
| 11 |
+
return False
|
| 12 |
+
cap = torch.cuda.get_device_capability()
|
| 13 |
+
return cap[0] >= 10
|
| 14 |
+
|
| 15 |
+
_fa3_available = False
|
| 16 |
+
_fa3_unavailable_reason = ""
|
| 17 |
+
_flash_attn_func = None
|
| 18 |
+
|
| 19 |
+
if _is_blackwell():
|
| 20 |
+
_fa3_unavailable_reason = "FlashAttention-3 unsupported on Blackwell. Falling back to SDPA."
|
| 21 |
+
else:
|
| 22 |
+
try:
|
| 23 |
+
from kernels import get_kernel
|
| 24 |
+
_k = get_kernel("kernels-community/vllm-flash-attn3")
|
| 25 |
+
_flash_attn_func = _k.flash_attn_func
|
| 26 |
+
_fa3_available = True
|
| 27 |
+
except Exception as e:
|
| 28 |
+
_fa3_unavailable_reason = f"kernels unavailable: {e}. Falling back to SDPA."
|
| 29 |
+
|
| 30 |
+
if _fa3_available:
|
| 31 |
+
@torch.library.custom_op("flash::flash_attn_func", mutates_args=())
|
| 32 |
+
def flash_attn_func(q, k, v, causal=False):
|
| 33 |
+
output, _ = _flash_attn_func(q, k, v, causal=causal)
|
| 34 |
+
return output
|
| 35 |
+
@flash_attn_func.register_fake
|
| 36 |
+
def _fa_fake(q, k, v, causal=False):
|
| 37 |
+
return torch.empty_like(q).contiguous()
|
| 38 |
+
else:
|
| 39 |
+
def flash_attn_func(q, k, v, causal=False):
|
| 40 |
+
raise RuntimeError(_fa3_unavailable_reason)
|
| 41 |
+
|
| 42 |
+
def _sdpa_attention(q, k, v, causal=False):
|
| 43 |
+
return F.scaled_dot_product_attention(q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2), is_causal=causal).transpose(1, 2)
|
| 44 |
+
|
| 45 |
+
class QwenDoubleStreamAttnProcessorFA3:
|
| 46 |
+
def __init__(self):
|
| 47 |
+
if _fa3_available:
|
| 48 |
+
self._attention_backend = "fa3"
|
| 49 |
+
else:
|
| 50 |
+
import warnings
|
| 51 |
+
warnings.warn(f"QwenDoubleStreamAttnProcessorFA3: {_fa3_unavailable_reason}")
|
| 52 |
+
self._attention_backend = "sdpa"
|
| 53 |
+
|
| 54 |
+
def _attend(self, q, k, v, causal=False):
|
| 55 |
+
if self._attention_backend == "fa3":
|
| 56 |
+
return flash_attn_func(q, k, v, causal=causal)
|
| 57 |
+
return _sdpa_attention(q, k, v, causal=causal)
|
| 58 |
+
|
| 59 |
+
@torch.no_grad()
|
| 60 |
+
def __call__(self, attn, hidden_states, encoder_hidden_states=None, encoder_hidden_states_mask=None, attention_mask=None, image_rotary_emb=None):
|
| 61 |
+
if encoder_hidden_states is None:
|
| 62 |
+
raise ValueError("QwenDoubleStreamAttnProcessorFA3 requires encoder_hidden_states.")
|
| 63 |
+
B, S_img, _ = hidden_states.shape
|
| 64 |
+
S_txt = encoder_hidden_states.shape[1]
|
| 65 |
+
img_q = attn.to_q(hidden_states).unflatten(-1, (attn.heads, -1))
|
| 66 |
+
img_k = attn.to_k(hidden_states).unflatten(-1, (attn.heads, -1))
|
| 67 |
+
img_v = attn.to_v(hidden_states).unflatten(-1, (attn.heads, -1))
|
| 68 |
+
txt_q = attn.add_q_proj(encoder_hidden_states).unflatten(-1, (attn.heads, -1))
|
| 69 |
+
txt_k = attn.add_k_proj(encoder_hidden_states).unflatten(-1, (attn.heads, -1))
|
| 70 |
+
txt_v = attn.add_v_proj(encoder_hidden_states).unflatten(-1, (attn.heads, -1))
|
| 71 |
+
if getattr(attn, "norm_q", None) is not None: img_q = attn.norm_q(img_q)
|
| 72 |
+
if getattr(attn, "norm_k", None) is not None: img_k = attn.norm_k(img_k)
|
| 73 |
+
if getattr(attn, "norm_added_q", None) is not None: txt_q = attn.norm_added_q(txt_q)
|
| 74 |
+
if getattr(attn, "norm_added_k", None) is not None: txt_k = attn.norm_added_k(txt_k)
|
| 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 |
+
q = torch.cat([txt_q, img_q], dim=1)
|
| 82 |
+
k = torch.cat([txt_k, img_k], dim=1)
|
| 83 |
+
v = torch.cat([txt_v, img_v], dim=1)
|
| 84 |
+
out = self._attend(q, k, v, causal=False).flatten(2, 3).to(q.dtype)
|
| 85 |
+
txt_attn_out = out[:, :S_txt, :]
|
| 86 |
+
img_attn_out = out[:, S_txt:, :]
|
| 87 |
+
img_attn_out = attn.to_out[0](img_attn_out)
|
| 88 |
+
if len(attn.to_out) > 1: img_attn_out = attn.to_out[1](img_attn_out)
|
| 89 |
+
txt_attn_out = attn.to_add_out(txt_attn_out)
|
| 90 |
+
return img_attn_out, txt_attn_out
|