feat: add attention dimension dispatch
This commit is contained in:
@@ -18,12 +18,13 @@ when available, otherwise falls back to ``torch.nn.functional.scaled_dot_product
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from astrai.extension.loader import KERNEL_NAMES, is_available
|
from astrai.extension.loader import KERNEL_NAMES, is_available
|
||||||
from astrai.extension.ops import attn_decode, attn_paged_decode, attn_prefill
|
from astrai.extension.ops import attention, attn_decode, attn_paged_decode, attn_prefill
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"attn_decode",
|
"attn_decode",
|
||||||
"attn_paged_decode",
|
"attn_paged_decode",
|
||||||
"attn_prefill",
|
"attn_prefill",
|
||||||
|
"attention",
|
||||||
"is_available",
|
"is_available",
|
||||||
"KERNEL_NAMES",
|
"KERNEL_NAMES",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -244,3 +244,55 @@ def attn_paged_decode(
|
|||||||
return _torch_fallback(
|
return _torch_fallback(
|
||||||
q, k, v, mask, causal_offset, scale, q_layout=li, kv_layout=1
|
q, k, v, mask, causal_offset, scale, q_layout=li, kv_layout=1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def attention(
|
||||||
|
q: torch.Tensor,
|
||||||
|
k: torch.Tensor,
|
||||||
|
v: torch.Tensor,
|
||||||
|
mask: torch.Tensor | None = None,
|
||||||
|
causal_offset: int = -1,
|
||||||
|
scale: float = 0.0,
|
||||||
|
layout: str = "bhld",
|
||||||
|
) -> torch.Tensor:
|
||||||
|
"""Dispatch to decode or prefill attention based on the query length.
|
||||||
|
|
||||||
|
A query length of one is the decode case; longer queries use prefill.
|
||||||
|
The paged-cache decode path cannot be selected here because its page-table
|
||||||
|
arguments are not part of this interface.
|
||||||
|
"""
|
||||||
|
li = _parse_layout(layout)
|
||||||
|
|
||||||
|
if q.ndim not in (2, 3, 4) or k.ndim != q.ndim or v.ndim != q.ndim:
|
||||||
|
raise ValueError(
|
||||||
|
"q, k, and v must all have the same rank in {2, 3, 4}, "
|
||||||
|
f"got {q.ndim}D, {k.ndim}D, {v.ndim}D"
|
||||||
|
)
|
||||||
|
if k.shape != v.shape:
|
||||||
|
raise ValueError(
|
||||||
|
f"k and v must have the same shape, got {k.shape} and {v.shape}"
|
||||||
|
)
|
||||||
|
|
||||||
|
original_ndim = q.ndim
|
||||||
|
if original_ndim == 2:
|
||||||
|
# [L, D] -> [1, 1, L, D] or [1, L, 1, D]
|
||||||
|
q = q.unsqueeze(0).unsqueeze(1 if li == 0 else 2)
|
||||||
|
k = k.unsqueeze(0).unsqueeze(1 if li == 0 else 2)
|
||||||
|
v = v.unsqueeze(0).unsqueeze(1 if li == 0 else 2)
|
||||||
|
elif original_ndim == 3:
|
||||||
|
# [B, L, D] -> single-head 4D input.
|
||||||
|
q = q.unsqueeze(1 if li == 0 else 2)
|
||||||
|
k = k.unsqueeze(1 if li == 0 else 2)
|
||||||
|
v = v.unsqueeze(1 if li == 0 else 2)
|
||||||
|
|
||||||
|
q_len = q.size(2 if li == 0 else 1)
|
||||||
|
if q_len == 1:
|
||||||
|
out = attn_decode(q, k, v, mask, causal_offset, scale, layout)
|
||||||
|
else:
|
||||||
|
out = attn_prefill(q, k, v, mask, causal_offset, scale, layout)
|
||||||
|
|
||||||
|
if original_ndim == 2:
|
||||||
|
return out.squeeze(0).squeeze(0 if li == 0 else 1)
|
||||||
|
if original_ndim == 3:
|
||||||
|
return out.squeeze(1 if li == 0 else 2)
|
||||||
|
return out
|
||||||
|
|||||||
Reference in New Issue
Block a user