native-sparse-attention-pytorch

repository·main·Indexed 21 days ago

https://github.com/lucidrains/native-sparse-attention-pytorch

A PyTorch implementation of the Native Sparse Attention pattern proposed by the Deepseek team. It provides a hardware-aligned, natively trainable mechanism that combines compressed attention, fine-grained selection attention, and sliding window attention. The library includes the SparseAttention class, various compression networks (ConvLinearCompress, AttentionPool, GroupedMLP, SingleProjection, CompressTransformer), and support for PyTorch's flex_attention and custom Triton kernels for optimized inference and training.

Tokens
6.7K
Snippets
18
Records
23
Agent score
74%

What's inside native-sparse-attention-pytorch

  1. Install Triton Nightly for Native Sparse Attention

    main

    The Triton-based kernels in this project require a specific version of Triton. It is recommended to install triton-nightly to ensure compatibility with the kernels.

    Run the following command to install the required version:

    pip install -U --index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/Triton-Nightly/pypi/simple/ triton-nightly

    Note: The project requires triton >= 3.0.0.

    pip install -U --index-url https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/Triton-Nightly/pypi/simple/ triton-nightly
  2. Understand the NSA Autograd Function

    main

    The NSA class is a torch.autograd.Function implementation that wraps the Triton kernels. It manages the forward and backward passes for the sparse attention mechanism.

    Forward Pass Requirements:

    • Inputs fq, fk, and fv are cast to half precision internally.
    • It saves tensors (fq, fk, fv, selected_block_indices, fmask, out, slide_out, lse, slide_lse) for the backward pass.
    • If sel_scale is provided, it enables gradient computation for the selection mechanism (must be 1.0 for straight-through).

    Backward Pass Behavior:

    • Computes gradients for queries (dq), keys (dk), and values (dv).
    • If return_sel_grads is enabled, it also returns gradients for the selection indices (sel_grads).
    • Supports sliding window gradients if return_sliding_window_out was used in the forward pass.
  3. Understand the Sparse Attention strategies

    main

    The SparseAttention module combines three pathways using a learned strategy_combine_mlp with a Sigmoid activation. The output is a weighted sum of the three branches:

    1. Compressed Pathway (compressed_attn_out): Performs coarse attention over a reduced set of KV tokens generated by a compression MLP. This captures long-range dependencies efficiently.
    2. Fine Pathway (fine_attn_out): Uses the importance scores from the compressed pathway to select the most relevant num_selected_blocks of high-resolution KV tokens for detailed attention.
    3. Sliding Window Pathway (sliding_window_attn_out): Performs standard local attention on the most recent tokens to ensure high-fidelity local context.

    This combination allows the model to balance computational efficiency (via compression) with high-resolution accuracy (via fine selection and sliding windows).

  4. How sparse attention modes work in the Transformer

    main

    The Transformer implementation integrates sparse attention through two primary paths:

    1. Standard Attention: If use_sparse_attn is False, it uses the standard Attention class which performs dense scaled dot-product attention.

    2. Sparse Attention: If use_sparse_attn is True, it instantiates SparseAttention layers. These layers can be configured for:

      • Sliding Window: Attending only to a local neighborhood of tokens.
      • Fine Selection: Attending to a sparse set of selected blocks.

    When using flex_attention (via use_flex_sliding_window or use_flex_fine_selection), the model automatically generates appropriate block masks using create_sliding_mask or create_fine_mask to optimize the attention pattern on supported hardware.

  5. Perform inference with SparseAttention KV cache

    main

    For autoregressive generation, use the forward method with a cache object. The cache is a nested tuple containing:

    1. cache_kv: (cache_k, cache_v) for standard KV storage.
    2. cache_compressed_kv: ((cache_ck, cache_cv), (run_k, run_v)) where ck/cv are compressed tokens and run_k/run_v are the running uncompressed tokens used for the next compression step.

    Note: During inference, the input inp must have a sequence length of 1.

    # Initial forward pass to get cache
    cache = None
    out, cache = model(inp, return_cache=True)
    
    # Subsequent inference steps
    # inp_next must be (batch, 1, dim)
    out_next, cache = model(inp_next, cache=cache, return_cache=True)
  6. Run Enwik8 language modeling example

    main

    To run the Enwik8 language modeling example, you must install the package with the [examples] extra, then execute the training script.

    1. Install with examples: pip install .[examples]
    2. Run training: python train.py

    Note: To record experiments via Weights & Biases, run wandb login before starting the training script.

    $ pip install .[examples]
    
    $ python train.py
  7. Use the SparseAttention class

    main

    The SparseAttention class implements the sparse attention pattern proposed by the Deepseek team. It allows for hardware-aligned and natively trainable sparse attention.

    Key parameters for initialization:

    • dim: The input dimension.
    • dim_head: The dimension of each attention head.
    • heads: The number of attention heads.
    • sliding_window_size: Size of the sliding window.
    • compress_block_size: Size of the compression blocks.
    • compress_block_sliding_stride: Stride for the compression block sliding window.
    • selection_block_size: Size of the selection blocks.
    • num_selected_blocks: Number of blocks to be selected.
    import torch
    from native_sparse_attention_pytorch import SparseAttention
    
    attn = SparseAttention(
        dim = 512,
        dim_head = 64,
        heads = 8,
        sliding_window_size = 2,
        compress_block_size = 4,
        compress_block_sliding_stride = 2,
        selection_block_size = 4,
        num_selected_blocks = 2
    )
    
    tokens = torch.randn(2, 31, 512)
    
    attended = attn(tokens)
    
    assert tokens.shape == attended.shape
  8. Configure SparseAttention parameters

    main

    When initializing SparseAttention, use the following parameters to control the sparsity and attention behavior:

    ParameterDescription
    dimInput feature dimension
    dim_headDimension of each attention head
    headsTotal number of query heads
    kv_headsNumber of key/value heads (set < heads for GQA)
    sliding_window_sizeSize of the local sliding window
    compress_block_sizeSize of the blocks used for compression
    compress_block_sliding_strideStride for the compression window
    selection_block_sizeSize of the blocks used for fine-grained selection
    num_selected_blocksNumber of top-k KV blocks to select for fine attention
    num_compressed_mem_kvNumber of compressed KV tokens to keep in memory
    causalWhether to apply causal masking
    query_heads_share_selected_kvIf True, importance scores are averaged across query heads to select KV buckets. If False, each query head can select different buckets (higher compute/memory).
    use_diff_topkWhether to use differential top-k gating for training
    use_triton_kernelWhether to use the optimized Triton kernel for fine attention
  9. Create Flex Attention masks for Sparse Attention

    main

    The implementation provides utility functions to create block_mask objects compatible with PyTorch's flex_attention. These can be passed to SparseAttention.forward via sliding_window_flex_mask or fine_selection_flex_mask to accelerate computation.

    • create_sliding_mask(seq_len, window_size, causal=True): Creates a mask for local sliding window attention.
    • create_compress_mask(seq_len, kv_seq_len, compress_block_sliding_stride, mem_kv_len=0, causal=True): Creates a mask for the compressed attention pathway.
    • create_fine_mask(seq_len, fine_block_size, causal=True): Returns a function that, when used with create_block_mask, defines the fine-grained selection mask based on provided selected_block_indices.
    from torch.nn.attention.flex_attention import create_block_mask
    
    # Example: Creating a sliding window mask for flex_attention
    seq_len = 1024
    window_size = 128
    
    def sliding_mask(_, __, q_idx, kv_idx):
        distance = q_idx - kv_idx
        backward_sliding_mask = distance <= window_size
        forward_distance = 0 # assuming causal
        forward_sliding_mask = distance >= forward_distance
        return backward_sliding_mask & forward_sliding_mask
    
    block_mask = create_block_mask(sliding_mask, B=None, H=None, Q_LEN=seq_len, KV_LEN=seq_len, _compile=True)
  10. Initialize the Transformer class

    main

    The Transformer class is the main entry point for building a model with sparse attention capabilities. You can choose between standard dense attention or various sparse attention mechanisms (sliding window, fine selection, etc.) using the use_sparse_attn flag and specific configuration kwargs.

    Key configuration parameters:

    • num_tokens: Vocabulary size.
    • dim: Model dimension.
    • depth: Number of layers.
    • use_sparse_attn: Set to True to enable sparse attention modes.
    • use_flex_sliding_window: Enables sliding window attention using PyTorch's flex_attention.
    • use_flex_fine_selection: Enables fine-grained selection using flex_attention.
    • use_triton_fine_selection: Enables fine-grained selection using a custom Triton kernel (cannot be used with use_flex_fine_selection).
    • sparse_attn_kwargs: A dictionary passed to the underlying SparseAttention module to configure window sizes, block sizes, and selection parameters.
    from native_sparse_attention_pytorch.transformer import Transformer
    
    model = Transformer(
        num_tokens = 50257,
        dim = 512,
        depth = 12,
        use_sparse_attn = True,
        use_flex_sliding_window = True,
        sparse_attn_kwargs = dict(
            sliding_window_size = 64,
            compress_block_size = 4,
            compress_block_overlap_len = 0,
            selection_block_size = 4,
            num_selected_blocks = 4,
        )
    )
  11. Use native_sparse_attn_forward for sparse attention

    main

    The native_sparse_attn_forward function provides a Triton-accelerated implementation of sparse attention. It supports causal masking, sliding window attention, and grouped query attention (GQA).

    Requirements:

    • q, k, v, and kv_block_indices must be contiguous tensors.
    • q, k, and v must be on a CUDA device.
    • q, k, and v must have the same dtype (either torch.float16 or torch.bfloat16).
    • The head dimension (dim) must be $\le 128$.
    • kv_block_indices and kv_block_mask must have matching shapes.

    Parameters:

    • q: Query tensor of shape (batch, nheads, seqlen_q, dim).
    • k: Key tensor of shape (batch, kv_heads, seqlen_k, dim).
    • v: Value tensor of shape (batch, kv_heads, seqlen_k, dim).
    • kv_block_indices: Indices of selected KV blocks.
    • kv_block_mask: Mask for the selected KV blocks.
    • block_size: Size of the attention block (must be a multiple of 16; default is 128).
    • include_block_causal: Whether to include block-diagonal causal masking (default True).
    • return_sliding_window_out: If True, returns both the standard output and a sliding window output.

    Returns:

    • o: The attention output tensor.
    • slide_o: The sliding window attention output (only if return_sliding_window_out is True).
    • lse: Log-sum-exp statistics for the attention.
    • slide_lse: Log-sum-exp statistics for the sliding window (only if return_sliding_window_out is True).
    from native_sparse_attention_pytorch.triton_native_sparse_attention import native_sparse_attn_forward
    
    # Example usage (shapes are illustrative)
    o, slide_o, lse, slide_lse = native_sparse_attn_forward(
        q=q, 
        k=k, 
        v=v, 
        kv_block_indices=kv_block_indices, 
        kv_block_mask=kv_block_mask,
        block_size=128,
        include_block_causal=True,
        return_sliding_window_out=True
    )