rotary-embedding-torch

repository·main·Indexed 21 days ago

https://github.com/lucidrains/rotary-embedding-torch

A 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.

Tokens
4.3K
Snippets
15
Records
19
Agent score
71%

What's inside rotary-embedding-torch

  1. How rotary embeddings are integrated into Flash Attention

    main

    In 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.

    Key Features:

    • Fused Kernel: Uses Triton to perform rotation and attention in a single pass.
    • Support for GQA/MQA: Automatically handles cases where heads_q != heads_kv by repeating $K$ and $V$ heads.
    • Positional Masking: Supports a pos_mask to selectively apply rotary embeddings to specific positions.
    • Causal Masking: Built-in support for causal attention via is_causal.
  2. How XPOS and length extrapolation work

    main

    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:

    1. The embeddings are scaled based on their position relative to the center of the sequence.
    2. You must use 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.
    3. The interpolate_factor can be used to further scale positions for fine-grained control over the frequency distribution.
  3. Basic usage of RotaryEmbedding

    main

    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)
  4. Use Axial Rotary Embeddings for n-dimensional data

    main

    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)
  5. Interpolate sequence positions for context extension

    main

    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
    )
  6. Use Fused Flash Attention with Rotary

    main

    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
    )
  7. Enable length extrapolation with XPos

    main

    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)
  8. Handle Inference Key-Value Cache with rotary_emb

    main

    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])
  9. Apply rotary embeddings manually with `apply_rotary_emb`

    main

    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)
  10. Use `flash_attn_with_rotary` for fused rotary attention

    main

    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).

    Input Tensor Shapes

    • q: (batch, heads_q, seq_len_q, dim)
    • k: (batch, heads_kv, seq_len_k, dim)
    • v: (batch, heads_kv, seq_len_k, dim)
    • rotary_pos_emb: (seq_len, rotary_dim) or similar, representing the frequencies to be rotated.
    • rotary_pos_emb_indices: (seq_len,) used for indexing into a padded frequency buffer (useful for non-contiguous or sparse positions).
    • attn_mask: (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.
    • pos_mask: (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
    )
  11. Initialize and use RotaryEmbedding

    main

    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.

    Key Parameters

    • 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)