amonshano's picture
Add Echo-Memory codebase used for this run (CC BY 4.0, JD Echo Team) (part 3)
b66f552 verified
Raw
History Blame Contribute Delete
13 kB
import torch
import triton
import triton.language as tl
import torch.nn.functional as F
from fla.utils import input_guard
from fla.ops.utils.softmax import softmax_bwd
@triton.autotune(
configs=[
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
for num_warps in [2, 4, 8]
for num_stages in [2, 3]
],
key=['BN'],
)
@triton.jit
def _fused_softmax_topk_fwd_kernel(
e,
e_o,
mw,
mr,
stride_e_b,
stride_e_l,
B,
T,
N,
NUM_WRITER: tl.constexpr,
NUM_READER: tl.constexpr,
BN: tl.constexpr,
):
i_b, i_t = tl.program_id(0), tl.program_id(1)
offsets_n = tl.arange(0, BN)
mask_n = offsets_n < N
p_e = e + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_e_o = e_o + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_mw = mw + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_mr = mr + i_b * stride_e_b + i_t * stride_e_l + offsets_n
### stable softmax and topk ###
b_e = tl.load(p_e, mask=mask_n, other=-float('inf')).to(tl.float32)
b_m = tl.max(b_e, axis=0)
b_e = tl.exp(b_e - b_m)
b_p = b_e / tl.sum(b_e, axis=0)
b_p = tl.where(mask_n, b_p.to(p_e.dtype.element_ty), -float('inf'))
b_ps = tl.sort(b_p, descending=True)
tl.store(p_e_o, b_p.to(p_e_o.dtype.element_ty), mask=mask_n)
mask_w = tl.full((BN,), 1, dtype=b_p.dtype)
if NUM_WRITER < N:
threshold_w = tl.sum(b_ps * (offsets_n == NUM_WRITER - 1))
mask_w_gr = b_p > threshold_w
need = NUM_WRITER - tl.sum(mask_w_gr.to(tl.int32))
mask_w_eq = b_p == threshold_w
mask_w_eq_need = mask_w_eq & (tl.cumsum(mask_w_eq.to(tl.int32), axis=0) <= need)
mask_w = mask_w_gr | mask_w_eq_need
mask_w = mask_w.to(b_p.dtype)
tl.store(p_mw, mask_w.to(p_mw.dtype.element_ty), mask=mask_n)
mask_r = tl.full((BN,), 1, dtype=b_p.dtype)
if NUM_READER < N:
threshold_r = tl.sum(b_ps * (offsets_n == NUM_READER - 1))
mask_r_gr = b_p > threshold_r
need = NUM_READER - tl.sum(mask_r_gr.to(tl.int32))
mask_r_eq = b_p == threshold_r
mask_r_eq_need = mask_r_eq & (tl.cumsum(mask_r_eq.to(tl.int32), axis=0) <= need)
mask_r = mask_r_gr | mask_r_eq_need
mask_r = mask_r.to(b_p.dtype)
tl.store(p_mr, mask_r.to(p_mr.dtype.element_ty), mask=mask_n)
@triton.autotune(
configs=[
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
for num_warps in [2, 4, 8]
for num_stages in [2, 3]
],
key=['BN', 'BK', 'BV'],
)
@triton.jit
def _fused_mask_fwd_kernel(
q, k, v, g, e, mw, mr,
q_o, k_o, v_o, g_o,
stride_k_b, stride_k_l, stride_k_h,
stride_v_b, stride_v_l, stride_v_h,
stride_e_b, stride_e_l,
B, T, N, H, K, V,
BN: tl.constexpr, BK: tl.constexpr, BV: tl.constexpr,
):
i_b, i_t, i_h = tl.program_id(0), tl.program_id(1), tl.program_id(2)
offsets_n = tl.arange(0, BN)
offsets_k = tl.arange(0, BK)
offsets_v = tl.arange(0, BV)
mask_n = offsets_n < N
mask_k = offsets_k < K
mask_v = offsets_v < V
p_e = e + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_mw = mw + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_mr = mr + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_q = q + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_k = k + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_g = g + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_v = v + i_b * stride_v_b + i_t * stride_v_l + i_h * stride_v_h + offsets_v
p_q_o = q_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_k_o = k_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_g_o = g_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_v_o = v_o + i_b * stride_v_b * N + i_t * stride_v_l * N + i_h * stride_v_h \
+ offsets_n[:, None] * H * V + offsets_v[None, :]
b_e = tl.load(p_e, mask=mask_n, other=0.)
mask_w = tl.load(p_mw, mask=mask_n, other=0.).to(b_e.dtype)
mask_r = tl.load(p_mr, mask=mask_n, other=0.).to(b_e.dtype)
b_e_topk_w = b_e * mask_w
b_e_topk_r = b_e * mask_r
### mask qkvg ###
b_q = tl.load(p_q, mask=mask_k, other=0.)
b_q = b_q[None, :] * b_e_topk_r[:, None]
b_k = tl.load(p_k, mask=mask_k, other=0.)
b_k = b_k[None, :] * b_e_topk_w[:, None]
b_g = tl.load(p_g, mask=mask_k, other=0.)
b_g = b_g[None, :] * mask_w[:, None]
b_v = tl.load(p_v, mask=mask_v, other=0.)
b_v = b_v[None, :] * mask_w[:, None]
mask_nk = mask_n[:, None] & mask_k[None, :]
mask_nv = mask_n[:, None] & mask_v[None, :]
tl.store(p_q_o, b_q.to(p_q_o.dtype.element_ty), mask=mask_nk)
tl.store(p_k_o, b_k.to(p_k_o.dtype.element_ty), mask=mask_nk)
tl.store(p_g_o, b_g.to(p_g_o.dtype.element_ty), mask=mask_nk)
tl.store(p_v_o, b_v.to(p_v_o.dtype.element_ty), mask=mask_nv)
@triton.autotune(
configs=[
triton.Config({}, num_warps=num_warps, num_stages=num_stages)
for num_warps in [2, 4, 8]
for num_stages in [2, 3]
],
key=['BN', 'BK', 'BV'],
)
@triton.jit
def _fused_mask_bwd_kernel(
q, k, e, mw, mr,
dq_o, dk_o, dv_o, dg_o,
dq, dk, dv, dg, de,
stride_k_b, stride_k_l, stride_k_h,
stride_v_b, stride_v_l, stride_v_h,
stride_e_b, stride_e_l,
B, T, N, H, K, V,
BN: tl.constexpr, BK: tl.constexpr, BV: tl.constexpr,
):
i_b, i_t, i_h = tl.program_id(0), tl.program_id(1), tl.program_id(2)
offsets_n = tl.arange(0, BN)
offsets_k = tl.arange(0, BK)
offsets_v = tl.arange(0, BV)
mask_n = offsets_n < N
mask_k = offsets_k < K
mask_v = offsets_v < V
p_e = e + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_de = de + (i_b * stride_e_b + i_t * stride_e_l + offsets_n) * H + i_h
p_mw = mw + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_mr = mr + i_b * stride_e_b + i_t * stride_e_l + offsets_n
p_q = q + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_k = k + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_dq = dq + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_dk = dk + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_dg = dg + i_b * stride_k_b + i_t * stride_k_l + i_h * stride_k_h + offsets_k
p_dv = dv + i_b * stride_v_b + i_t * stride_v_l + i_h * stride_v_h + offsets_v
p_dq_o = dq_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_dk_o = dk_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_dg_o = dg_o + i_b * stride_k_b * N + i_t * stride_k_l * N + i_h * stride_k_h \
+ offsets_n[:, None] * H * K + offsets_k[None, :]
p_dv_o = dv_o + i_b * stride_v_b * N + i_t * stride_v_l * N + i_h * stride_v_h \
+ offsets_n[:, None] * H * V + offsets_v[None, :]
b_e = tl.load(p_e, mask=mask_n, other=0.)
mask_w = tl.load(p_mw, mask=mask_n, other=0.).to(b_e.dtype)
mask_r = tl.load(p_mr, mask=mask_n, other=0.).to(b_e.dtype)
b_e_topk_w = b_e * mask_w
b_e_topk_r = b_e * mask_r
mask_nk = mask_n[:, None] & mask_k[None, :]
mask_nv = mask_n[:, None] & mask_v[None, :]
b_dq_o = tl.load(p_dq_o, mask=mask_nk, other=0.)
b_dk_o = tl.load(p_dk_o, mask=mask_nk, other=0.)
b_dg_o = tl.load(p_dg_o, mask=mask_nk, other=0.)
b_dv_o = tl.load(p_dv_o, mask=mask_nv, other=0.)
b_dq = tl.sum((b_dq_o * b_e_topk_r[:, None]).to(tl.float32), axis=0).to(b_dq_o.dtype)
b_dk = tl.sum((b_dk_o * b_e_topk_w[:, None]).to(tl.float32), axis=0).to(b_dk_o.dtype)
b_dg = tl.sum((b_dg_o * mask_w[:, None]).to(tl.float32), axis=0).to(b_dg_o.dtype)
b_dv = tl.sum((b_dv_o * mask_w[:, None]).to(tl.float32), axis=0).to(b_dv_o.dtype)
b_q = tl.load(p_q, mask=mask_k, other=0.)
b_k = tl.load(p_k, mask=mask_k, other=0.)
b_de = b_dq_o * b_q[None, :] * mask_r[:, None] + b_dk_o * b_k[None, :] * mask_w[:, None]
b_de = tl.sum(b_de.to(tl.float32), axis=1).to(b_de.dtype)
tl.store(p_de, b_de.to(p_de.dtype.element_ty), mask=mask_n)
tl.store(p_dq, b_dq.to(p_dq.dtype.element_ty), mask=mask_k)
tl.store(p_dk, b_dk.to(p_dk.dtype.element_ty), mask=mask_k)
tl.store(p_dg, b_dg.to(p_dg.dtype.element_ty), mask=mask_k)
tl.store(p_dv, b_dv.to(p_dv.dtype.element_ty), mask=mask_v)
class SoftmaxAndMask(torch.autograd.Function):
r"""
Applies softmax to router weights, repeats and masks inputs,
scales queries and keys with the router weights, and generates reader/writer masks.
Notation:
B: batch size
T: sequence length
H: number of attention heads
K: key/query head dimension
V: value head dimension
N: number of state partitions
Args:
q (torch.Tensor):
Queries of shape `(B, T, H, K)`.
k (torch.Tensor):
Keys of shape `(B, T, H, K)`.
v (torch.Tensor):
Values of shape `(B, T, H, V)`.
g (torch.Tensor):
Gates of shape `(B, T, H, V)`.
e (torch.Tensor):
Router weights before softmax of shape `(B, T, N)`.
num_writer (int):
Number of state partitions to write.
num_reader (int):
Number of state partitions to read.
Returns:
q_out (torch.Tensor):
Repeated and masked queries of shape `(B, T, N * H, K)`.
k_out (torch.Tensor):
Repeated and masked keys of shape `(B, T, N * H, K)`.
v_out (torch.Tensor):
Repeated and masked values of shape `(B, T, N * H, V)`.
g_out (torch.Tensor):
Repeated and masked gates of shape `(B, T, N * H, V)`.
e_out (torch.Tensor):
Router weights after softmax of shape `(B, T, N)`.
mask_w (torch.Tensor):
Writer mask of shape `(B, T, N)`.
mask_r (torch.Tensor):
Reader mask of shape `(B, T, N)`.
"""
@staticmethod
@input_guard
def forward(ctx, q, k, v, g, e, num_writer, num_reader):
B, T, H, K, V, N = *k.shape, v.shape[-1], e.shape[-1]
BN = triton.next_power_of_2(N)
BK = triton.next_power_of_2(K)
BV = triton.next_power_of_2(V)
q_out = q.new_empty(B, T, N * H, K)
k_out = k.new_empty(B, T, N * H, K)
v_out = v.new_empty(B, T, N * H, V)
g_out = g.new_empty(B, T, N * H, K)
e_out = torch.empty_like(e)
mask_w = torch.empty_like(e, dtype=torch.int32)
mask_r = torch.empty_like(e, dtype=torch.int32)
_fused_softmax_topk_fwd_kernel[(B, T)](
e,
e_out,
mask_w,
mask_r,
e.stride(0),
e.stride(1),
B,
T,
N,
NUM_WRITER=num_writer,
NUM_READER=num_reader,
BN=BN,
)
_fused_mask_fwd_kernel[(B, T, H)](
q, k, v, g, e_out, mask_w, mask_r,
q_out, k_out, v_out, g_out,
k.stride(0), k.stride(1), k.stride(2),
v.stride(0), v.stride(1), v.stride(2),
e.stride(0), e.stride(1),
B, T, N, H, K, V,
BN=BN,
BK=BK,
BV=BV,
)
ctx.save_for_backward(q, k, v, g, e_out, mask_w, mask_r)
ctx.num_writer = num_writer
ctx.num_reader = num_reader
return q_out, k_out, v_out, g_out, e_out, mask_w, mask_r
@staticmethod
@input_guard
def backward(ctx, dq_out, dk_out, dv_out, dg_out, de_out, dmask_w, dmask_r):
q, k, v, g, e_out, mask_w, mask_r = ctx.saved_tensors
B, T, H, K, V, N = *k.shape, v.shape[-1], e_out.shape[-1]
BN = triton.next_power_of_2(N)
BK = triton.next_power_of_2(K)
BV = triton.next_power_of_2(V)
dq = torch.empty_like(q)
dk = torch.empty_like(k)
dv = torch.empty_like(v)
dg = torch.empty_like(g)
de = g.new_empty(B, T, N, H)
grid = (B, T, H)
_fused_mask_bwd_kernel[grid](
q, k, e_out, mask_w, mask_r,
dq_out, dk_out, dv_out, dg_out,
dq, dk, dv, dg, de,
k.stride(0), k.stride(1), k.stride(2),
v.stride(0), v.stride(1), v.stride(2),
e_out.stride(0), e_out.stride(1),
B, T, N, H, K, V,
BN=BN,
BK=BK,
BV=BV,
)
de = de.sum(dim=-1).add_(de_out)
de = softmax_bwd(e_out, de, dtype=de.dtype)
return dq.to(q), dk.to(k), dv.to(v), dg.to(g), de.to(e_out), None, None
softmax_and_mask = SoftmaxAndMask.apply