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
3.35 kB
import torch
from einops import rearrange
def naive_recurrent_kda(
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
scale: float | None = None,
initial_state: torch.Tensor | None = None,
output_final_state: bool = False,
):
dtype = v.dtype
B, T, H, K, V = *q.shape, v.shape[-1]
if scale is None:
scale = K ** -0.5
q, k, v, g, beta = map(lambda x: x.to(torch.float), [q, k, v, g, beta])
q = q * scale
S = k.new_zeros(B, H, K, V).to(q)
if initial_state is not None:
S += initial_state
o = torch.zeros_like(v)
for i in range(0, T):
q_i, k_i, v_i, g_i, b_i = q[:, i], k[:, i], v[:, i], g[:, i], beta[:, i]
S = S * g_i[..., None].exp()
S = S + torch.einsum('b h k, b h v -> b h k v', b_i[..., None] * k_i, v_i - (k_i[..., None] * S).sum(-2))
o[:, i] = torch.einsum('b h k, b h k v -> b h v', q_i, S)
if not output_final_state:
S = None
return o.to(dtype), S
def naive_chunk_kda(
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
scale: float | None = None,
initial_state: torch.Tensor | None = None,
output_final_state: bool = False,
chunk_size: int = 64,
):
dtype = v.dtype
B, T, H, K, V = *q.shape, v.shape[-1]
BT = chunk_size
NT = T // BT
if scale is None:
scale = K ** -0.5
assert T % BT == 0
q, k, v, g, beta = map(lambda x: rearrange(x, 'b (n c) h ... -> b h n c ...', c=BT).to(torch.float), [q, k, v, g, beta])
q = q * scale
g = g.cumsum(-2)
# note that diagonal is masked.
mask = torch.triu(torch.ones(BT, BT, dtype=torch.bool, device=q.device), diagonal=0)
A = torch.zeros(*q.shape[:-1], BT, dtype=torch.float, device=q.device)
for i in range(BT):
k_i = k[..., i, :]
g_i = g[..., i:i+1, :]
A[..., i] = torch.einsum('... c d, ... d -> ... c', k * (g - g_i).exp(), k_i)
A = A * beta[..., None]
A = -A.masked_fill(mask, 0)
for i in range(1, BT):
A[..., i, :i] = A[..., i, :i].clone() + (A[..., i, :, None].clone() * A[..., :, :i].clone()).sum(-2)
A = (A + torch.eye(BT, dtype=torch.float, device=q.device)) * beta[..., None, :]
w = A @ (g.exp() * k)
u = A @ v
S = k.new_zeros(B, H, K, V).to(q)
if initial_state is not None:
S += initial_state
o = torch.zeros_like(v)
mask = torch.triu(torch.ones(BT, BT, dtype=torch.bool, device=q.device), diagonal=1)
for i in range(0, NT):
# [B, H, BT, ...]
q_i, k_i, u_i, g_i, w_i = q[:, :, i], k[:, :, i], u[:, :, i], g[:, :, i], w[:, :, i]
A = torch.zeros(B, H, BT, BT, dtype=torch.float, device=q.device)
for j in range(BT):
k_j = k[:, :, i, j]
g_j = g[:, :, i, j:j+1, :]
A[..., j] = torch.einsum('... c d, ... d -> ... c', q_i * (g_i - g_j).exp(), k_j)
A = A.masked_fill(mask, 0)
v_i = u_i - w_i @ S
o[:, :, i] = (q_i * g_i.exp()) @ S + A @ v_i
S = S * rearrange(g_i[:, :, -1].exp(), 'b h k -> b h k 1')
S += rearrange((g_i[:, :, -1:] - g_i).exp() * k_i, 'b h c k -> b h k c') @ v_i
if not output_final_state:
S = None
return rearrange(o, 'b h n c d -> b (n c) h d').to(dtype), S