7e4ef6f98b
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
59 lines
1.9 KiB
Python
59 lines
1.9 KiB
Python
"""
|
|
RoPE rotary positional encoding, reused by the Denoiser and Transformer.
|
|
Extracted from model/vae/autoencoder.py.
|
|
"""
|
|
|
|
from typing import Tuple
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
|
|
|
|
class RotaryEmbedding(nn.Module):
|
|
"""RoPE rotary positional encoding (applied to Q/K)."""
|
|
|
|
def __init__(self, dim: int, base: int = 10000):
|
|
super().__init__()
|
|
if dim % 2 != 0:
|
|
raise ValueError("RoPE dim must be even")
|
|
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
|
|
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
|
|
|
def get_sin_cos(self, seq_len: int, device: torch.device, dtype: torch.dtype) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
t = torch.arange(seq_len, device=device, dtype=dtype)
|
|
freqs = torch.einsum("i,j->ij", t, self.inv_freq.to(device=device, dtype=dtype))
|
|
sin = freqs.sin()
|
|
cos = freqs.cos()
|
|
return sin, cos
|
|
|
|
@staticmethod
|
|
def get_sin_cos_from_positions(
|
|
positions: torch.Tensor,
|
|
inv_freq: torch.Tensor,
|
|
) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
"""
|
|
Compute sin/cos from arbitrary position indices (supports repeated indices).
|
|
|
|
Args:
|
|
positions: (L,) int — arbitrary position indices, duplicates allowed
|
|
inv_freq: (D/2,) — from RotaryEmbedding.inv_freq
|
|
|
|
Returns:
|
|
sin, cos: each (L, D/2)
|
|
"""
|
|
freqs = torch.einsum("i,j->ij", positions.float(), inv_freq.to(positions))
|
|
return freqs.sin(), freqs.cos()
|
|
|
|
@staticmethod
|
|
def apply_rope(x: torch.Tensor, sin: torch.Tensor, cos: torch.Tensor) -> torch.Tensor:
|
|
"""
|
|
x: (B, H, L, D)
|
|
sin/cos: (L, D/2)
|
|
"""
|
|
x1 = x[..., 0::2]
|
|
x2 = x[..., 1::2]
|
|
sin = sin.unsqueeze(0).unsqueeze(0)
|
|
cos = cos.unsqueeze(0).unsqueeze(0)
|
|
x_rot = torch.stack([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)
|
|
return x_rot.flatten(-2)
|