def rope(x, offset=0):
"""Rotary rotation of x at absolute positions offset, offset+1, ..."""
d = x.shape[-1]
pos = offset + torch.arange(x.shape[-2], dtype=torch.float32,
device=x.device)
inv_freq = 10000.0 ** (-torch.arange(0, d, 2, device=x.device) / d)
theta = pos[:, None] * inv_freq[None, :]
cos, sin = torch.cos(theta), torch.sin(theta)
x1, x2 = x[..., 0::2], x[..., 1::2]
return torch.stack([x1 * cos - x2 * sin,
x1 * sin + x2 * cos], -1).flatten(-2)
def forward_step(self, X, cache):
"""Attention for the new tokens X against cached keys and values."""
B, T, D = X.shape
assert not (cache and T > 1), \
'chunked append to a non-empty cache is unsupported ' \
'(is_causal would misalign the mask)'
q, k, v = self.W_qkv(X).chunk(3, -1)
q, k, v = (u.reshape(B, T, self.num_heads, -1).transpose(1, 2)
for u in (q, k, v))
offset = cache['k'].shape[2] if cache else 0
if self.rope:
q, k = rope(q, offset), rope(k, offset)
if cache:
k = torch.cat([cache['k'], k], dim=2)
v = torch.cat([cache['v'], v], dim=2)
# contiguous(): at prefill k and v are still views into the fused QKV
# projection, and caching a view would pin the whole 3x-wide buffer
cache['k'], cache['v'] = k.contiguous(), v.contiguous()
# Prefill (T > 1, empty cache) needs the causal mask; a single decoded
# token (T = 1) attends to the entire cache
Y = F.scaled_dot_product_attention(q, k, v, is_causal=(T > 1))
return self.W_o(Y.transpose(1, 2).reshape(B, T, D))