Text-to-Image
Diffusers
Safetensors
flow-matching
pyramidal-unet
256x256
ettin-150m
from-scratch
ra
udit
Instructions to use AiArtLab/Ra with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use AiArtLab/Ra with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("AiArtLab/Ra", dtype=torch.bfloat16, device_map="cuda") prompt = "Astronaut in a jungle, cold color palette, muted colors, detailed, 8k" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
File size: 5,189 Bytes
b4f5f26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | """Local 2D window attention.
Every token attends to all tokens within a square window of Chebyshev radius ``r``
(i.e. ``|dy| <= r and |dx| <= r``) on its 2D grid.
Two numerically equivalent implementations are provided:
* a fused Triton kernel (CUDA only, forward pass only);
* PyTorch ``scaled_dot_product_attention`` with a boolean window mask (CPU, or when
Triton is unavailable).
Both work on a packed batch: every sample is its own token grid of any size.
"""
import torch
import torch.nn.functional as F
try: # Triton is optional; without it the SDPA path is used.
import triton
import triton.language as tl
_HAS_TRITON = True
except Exception: # pragma: no cover
_HAS_TRITON = False
_LOG2E = 1.4426950408889634
# Global switch: "auto" (kernel when possible), "sdpa" (always masked SDPA).
BACKEND = "auto"
if _HAS_TRITON:
@triton.jit
def _window_attn_fwd(Q, K, V, O, DIMS, NH: tl.constexpr, NMAX, WY: tl.constexpr,
WX: tl.constexpr, R: tl.constexpr, D: tl.constexpr,
BR: tl.constexpr, qk_scale):
# One program = one WY x WX tile of queries for one (batch, head).
# Keys are streamed from the tile extended by R on every side ("halo").
t, bh = tl.program_id(0), tl.program_id(1)
b = bh // NH
gyp = tl.load(DIMS + b * 4 + 0)
gxp = tl.load(DIMS + b * 4 + 1)
gyr = tl.load(DIMS + b * 4 + 2)
gxr = tl.load(DIMS + b * 4 + 3)
ntx = gxp // WX
if t >= (gyp // WY) * ntx:
return
ty, tx = t // ntx, t % ntx
base = bh * NMAX * D
SX: tl.constexpr = WX + 2 * R
SY: tl.constexpr = WY + 2 * R
qi = tl.arange(0, WY * WX)
qyr, qxr = qi // WX, qi % WX
qy, qx = ty * WY + qyr, tx * WX + qxr
d = tl.arange(0, D)
qo = base + (qy * gxp + qx)[:, None] * D + d[None, :]
q = tl.load(Q + qo)
okq = (qy < gyr) & (qx < gxr)
kxr = tl.arange(0, BR * SX) % SX
okx = okq[:, None] & (tl.abs(qxr[:, None] - (kxr[None, :] - R)) <= R)
kxg = tx * WX - R + kxr
okg = (kxg >= 0) & (kxg < gxr)
m = tl.full((WY * WX,), -1e30, tl.float32)
l = tl.zeros((WY * WX,), tl.float32)
acc = tl.zeros((WY * WX, D), tl.float32)
for r0 in range(0, SY, BR):
kyr = r0 + tl.arange(0, BR * SX) // SX
kyg = ty * WY - R + kyr
ok = okg & (kyg >= 0) & (kyg < gyr) & (kyr < SY)
off = base + (kyg * gxp + kxg) * D
k = tl.load(K + off[:, None] + d[None, :], mask=ok[:, None], other=0.0)
v = tl.load(V + off[:, None] + d[None, :], mask=ok[:, None], other=0.0)
s = tl.dot(q, tl.trans(k)) * qk_scale
good = okx & (tl.abs(qyr[:, None] - (kyr[None, :] - R)) <= R) & ok[None, :]
s = tl.where(good, s, -1e30)
mn = tl.maximum(m, tl.max(s, 1))
p = tl.where(good, tl.math.exp2(s - mn[:, None]), 0.0)
a = tl.math.exp2(m - mn)
l = l * a + tl.sum(p, 1)
acc = acc * a[:, None] + tl.dot(p.to(v.dtype), v).to(tl.float32)
m = mn
lc = tl.maximum(l, 1e-30)
tl.store(O + qo, (acc / lc[:, None]).to(O.dtype.element_ty))
def _tile_width(r):
# The kernel needs a power-of-two key strip width (WX + 2R).
for wx in (16, 8):
sx = wx + 2 * r
if sx & (sx - 1) == 0:
return wx
raise ValueError(f"window radius {r}: no supported tile width")
@torch._dynamo.disable
def packed_window_attention(q, k, v, r, dims, max_tiles):
"""Windowed attention for a packed batch of differently shaped token grids.
Sample ``b`` is stored row-major in a padded ``gyp x gxp`` grid at the start of the
sequence, with its real ``gyr x gxr`` tokens in the top-left corner; the rest is padding.
dims: (B, 4) int32 ``[gyp, gxp, gyr, gxr]`` (gyp a multiple of 8, gxp of the tile width).
q, k, v: (B, heads, Nmax, head_dim). Padding outputs are zero (kernel) or unused (SDPA).
"""
B, nh, N, D = q.shape
if BACKEND == "auto" and _HAS_TRITON and q.is_cuda and D in (16, 32, 64, 128):
q, k, v = (t.to(torch.bfloat16).contiguous() for t in (q, k, v))
o = torch.zeros_like(q)
_window_attn_fwd[(max_tiles, B * nh)](
q, k, v, o, dims.to(q.device, torch.int32).contiguous(), NH=nh, NMAX=N, WY=8,
WX=_tile_width(r), R=r, D=D, BR=4, qk_scale=D ** -0.5 * _LOG2E, num_warps=8, num_stages=3)
return o
# reference path: per-sample block mask over the packed sequence
n = torch.arange(N, device=q.device)
d = dims.to(q.device).long()
yy, xx = n[None] // d[:, 1:2], n[None] % d[:, 1:2] # (B, N)
valid = (yy < d[:, 2:3]) & (xx < d[:, 3:4])
win = ((yy[:, :, None] - yy[:, None]).abs() <= r) & ((xx[:, :, None] - xx[:, None]).abs() <= r)
m = win & valid[:, None, :]
m = m | (~valid[:, :, None] & torch.eye(N, dtype=torch.bool, device=q.device))
return F.scaled_dot_product_attention(q.contiguous(), k.contiguous(), v.contiguous(),
attn_mask=m[:, None])
|