Install rotary-embedding-torch
mainInstall the library using pip:
$ pip install rotary-embedding-torchrepository·main·Indexed 21 days ago
https://github.com/lucidrains/rotary-embedding-torchA PyTorch implementation of rotary positional embeddings (RoPE) featuring high-performance fused kernels via Triton for integration with Flash Attention. It supports XPOS for length extrapolation, axial rotary embeddings for n-dimensional data (e.g., video), and positional interpolation for context extension. The library provides the RotaryEmbedding class for managing frequencies and rotation, as well as the flash_attn_with_rotary function for efficient, single-pass attention and rotation.
Install the library using pip:
$ pip install rotary-embedding-torchIn standard implementations, rotary embeddings are applied to $Q$ and $K$ as a separate step: $Q_{rot} = ext{rotate}(Q)$ and $K_{rot} = ext{rotate}(K)$.
flash_attn_with_rotary fuses this operation into the attention kernel. During the kernel execution, the rotary transformation is applied to the loaded blocks of $Q$ and $K$ on-the-fly using the provided cos and sin values. This reduces memory bandwidth overhead by avoiding the need to write the intermediate rotated $Q$ and $K$ tensors back to global memory.
heads_q != heads_kv by repeating $K$ and $V$ heads.pos_mask to selectively apply rotary embeddings to specific positions.is_causal.XPOS (Extrapolatable Positional Encoding) is a method to allow models to handle sequence lengths longer than those seen during training.
When use_xpos=True is set in RotaryEmbedding:
rotate_queries_and_keys or rotate_queries_with_cached_keys instead of rotate_queries_or_keys. This is because XPOS requires applying a specific scale to the keys ($K$) that is the inverse of the scale applied to the queries ($Q$) to maintain the relative positional information correctly.interpolate_factor can be used to further scale positions for fine-grained control over the frequency distribution.To add rotary embeddings to a transformer, instantiate RotaryEmbedding with the desired dimension and apply it to your queries (q) and keys (k) after the heads have been split, but before the dot product.
Note: The input tensors for q and k must have dimensions ending in (seq_len, feature_dimension), with any number of preceding dimensions (e.g., batch, heads).
import torch
from rotary_embedding_torch import RotaryEmbedding
# dim is the dimension of the head
rotary_emb = RotaryEmbedding(dim = 32)
# q and k shape: (batch, heads, seq_len, dim)
q = torch.randn(1, 8, 1024, 64)
k = torch.randn(1, 8, 1024, 64)
# Apply rotations
q = rotary_emb.rotate_queries_or_keys(q)
k = rotary_emb.rotate_queries_or_keys(k)For n-dimensional data like video (e.g., frames, height, width), use get_axial_freqs to generate axial frequencies and apply_rotary_emb to rotate the tensors. This supports partial rotary automatically.
import torch
from rotary_embedding_torch import RotaryEmbedding, apply_rotary_emb
pos_emb = RotaryEmbedding(
dim = 16,
freqs_for = 'pixel',
max_freq = 256
)
# Example for video: (batch, frames, height, width, dim)
q = torch.randn(1, 8, 64, 32, 64)
k = torch.randn(1, 8, 64, 32, 64)
# Get axial frequencies for (8, 64, 32)
freqs = pos_emb.get_axial_freqs(8, 64, 32)
# Rotate
q = apply_rotary_emb(freqs, q)
k = apply_rotary_emb(freqs, k)To extend the context length of a pretrained model, you can use positional interpolation by setting interpolate_factor to a value greater than 1.0 during initialization. For example, if a model was trained on 2048 tokens, setting interpolate_factor = 2. allows fine-tuning for up to 4096 tokens.
from rotary_embedding_torch import RotaryEmbedding
rotary_emb = RotaryEmbedding(
dim = 32,
interpolate_factor = 2.0
)The flash_attn_with_rotary function provides a fused kernel that computes attention with rotary embeddings in a single pass. It uses Triton if available and falls back to a PyTorch implementation otherwise. It supports skipping specific tokens (like CLS or register tokens) via the rotary_pos_emb_indices argument.
import torch
from rotary_embedding_torch import RotaryEmbedding
from rotary_embedding_torch.flash_attn_with_rotary import flash_attn_with_rotary
rotary_emb = RotaryEmbedding(dim = 32)
freqs = rotary_emb(torch.arange(1024))
# q, k, v with extra tokens (e.g. CLS/register) at the start
q = torch.randn(1, 8, 1024 + 2, 64).cuda()
k = torch.randn(1, 8, 1024 + 2, 64).cuda()
v = torch.randn(1, 8, 1024 + 2, 64).cuda()
# Indices for the 1024 rotary positions, skipping the first 2 tokens
pos_indices = torch.arange(1024).cuda() + 2
out = flash_attn_with_rotary(
q, k, v,
rotary_pos_emb = freqs,
rotary_pos_emb_indices = pos_indices,
is_causal = True
)To improve length extrapolation for autoregressive transformers (allowing the model to handle sequences longer than those seen during training), set use_xpos = True during initialization. When using XPos, use the rotate_queries_and_keys method instead of rotate_queries_or_keys.
from rotary_embedding_torch import RotaryEmbedding
rotary_emb = RotaryEmbedding(
dim = 32,
use_xpos = True
)
# Use rotate_queries_and_keys to handle both q and k
q, k = rotary_emb.rotate_queries_and_keys(q, k)When performing inference with a KV cache, the query position must be offset by the difference between the total key/value sequence length and the current query sequence length.
You can use rotate_queries_with_cached_keys to handle this automatically, or manually pass an offset to rotate_queries_or_keys.
# Automatic method
q, k = rotary_emb.rotate_queries_with_cached_keys(q, k)
# Manual method
q = rotary_emb.rotate_queries_or_keys(q, offset = k.shape[-2] - q.shape[-2])If you have pre-computed frequencies or want to apply rotation to a specific slice of a tensor, use the standalone apply_rotary_emb function.
Signature:
apply_rotary_emb(freqs, t, start_index=0, scale=1., seq_dim=-2, freqs_seq_dim=None)
freqs: The pre-computed cosine/sine frequencies.t: The tensor to rotate (e.g., queries or keys).start_index: The starting index in the feature dimension where rotation should begin.scale: A scaling factor applied to the rotation.seq_dim: The dimension representing the sequence length.freqs_seq_dim: The dimension in freqs representing the sequence length (defaults to 0 if not provided and t is 3D).from rotary_embedding_torch import apply_rotary_emb
# t shape: (batch, heads, seq_len, dim)
# freqs shape: (seq_len, dim)
rotated_t = apply_rotary_emb(freqs, t)The flash_attn_with_rotary function provides a high-performance, fused implementation of attention that integrates rotary positional embeddings directly into the attention kernel. This is significantly more efficient than applying rotary embeddings to queries and keys separately before calling a standard attention function.
If triton is installed, it uses optimized Triton kernels. If triton is unavailable or if force_reference=True is passed to the factory, it falls back to a standard PyTorch implementation (reference_flash_attention).
(batch, heads_q, seq_len_q, dim)(batch, heads_kv, seq_len_k, dim)(batch, heads_kv, seq_len_k, dim)(seq_len, rotary_dim) or similar, representing the frequencies to be rotated.(seq_len,) used for indexing into a padded frequency buffer (useful for non-contiguous or sparse positions).(batch, seq_len_k) or (batch, seq_len_q, seq_len_k). If boolean, it is treated as a mask where False values are replaced with -inf.(seq_len_q,) used to define which positions in the query sequence should receive rotary embeddings (in conjunction with rotary_pos_emb_indices).from rotary_embedding_torch.flash_attn_with_rotary import flash_attn_with_rotary
# Assuming you have q, k, v, and rotary_pos_emb prepared
# rotary_pos_emb should be the frequencies (e.g., from a RotaryEmbedding object)
output = flash_attn_with_rotary(
q = q,
k = k,
v = v,
rotary_pos_emb = rotary_pos_emb,
is_causal = True
)The RotaryEmbedding class is the primary interface for applying rotary positional embeddings to queries and keys in a transformer. It supports various frequency generation modes, XPOS for length extrapolation, and learned frequencies.
dim: The dimension of the rotary embedding (must match the feature dimension of the part of the tensor being rotated).freqs_for: Determines how frequencies are generated. Options are 'lang' (default, standard RoPE), 'pixel' (for images/video), or 'constant'.theta: The base for frequency calculation (default 10000).learned_freq: If True, frequencies become trainable parameters.use_xpos: Enables XPOS (Extrapolatable Positional Encoding) for better length extrapolation.interpolate_factor: Used for scaling sequence positions (default 1.0).seq_before_head_dim: If True, assumes the sequence dimension is at index -3 instead of -2 (e.g., [batch, seq, heads, dim]).cache_if_possible: Enables caching of computed frequencies to improve performance.from rotary_embedding_torch import RotaryEmbedding
import torch
# Setup
dim = 64
rotary = RotaryEmbedding(dim = dim)
# Mock queries and keys: (batch, heads, seq_len, dim)
q = torch.randn(1, 8, 128, dim)
k = torch.randn(1, 8, 128, dim)
# Apply rotation
# By default, it expects seq_dim = -2
q, k = rotary.rotate_queries_or_keys(q, k)