NexusCoder / nexus /model /alibi.py
AdminReal's picture
Import NexusCoder from github.com/mhieuhonda/NexusCoder
eca5751 verified
Raw
History Blame Contribute Delete
5.28 kB
"""
ALiBi (Attention with Linear Biases) position bias for Nexus Coder v0.3
======================================================================
Alternative to RoPE. No positional embeddings — biases are added directly
to attention scores. Extrapolates better to longer sequences than RoPE.
Reference: Press et al., "Train Short, Test Long: Attention with Linear
Biases Enables Input Length Extrapolation" (ICLR 2022).
https://arxiv.org/abs/2108.12409
Attribution: Algorithm adapted from the original paper. Implementation
references both the original alibi-transformers repo and HuggingFace's
integration in `bloom` / `mntptr` projects.
"""
from __future__ import annotations
import math
from typing import List
import torch
import torch.nn as nn
def get_alibi_slopes(num_heads: int, max_slope: float = 8.0) -> torch.Tensor:
"""Compute ALiBi slopes for `num_heads` attention heads.
v0.4 fix: use `max_slope` correctly (was hardcoded to 8.0 → log2(8)=3).
v0.4 fix: non-power-of-2 head counts now pick the *closest* n slopes
(standard ALiBi behavior), not "evenly spaced" (which was buggy).
Args:
num_heads: number of attention heads
max_slope: steepest slope (controls decay). Default 8.0.
Returns:
slopes: tensor of shape [num_heads]
"""
if num_heads <= 0:
return torch.tensor([], dtype=torch.float32)
log_max = math.log2(max_slope) # e.g. log2(8)=3
def _get_slopes_power_of_2(n: int) -> List[float]:
start = 2.0 ** (-(2.0 ** -(math.log2(n) - log_max)))
return [start * (2.0 ** (-i)) for i in range(n)]
if (num_heads & (num_heads - 1)) == 0:
# Power of 2 — direct
slopes = _get_slopes_power_of_2(num_heads)
else:
# Non-power-of-2: standard ALiBi picks the n closest slopes
# by computing slopes for the nearest power of 2 >= n and
# interleaving them, then taking the first n.
base = 1
while base < num_heads:
base *= 2
full = _get_slopes_power_of_2(base)
# Interleave: take even-indexed first, then odd, to pick "closest" slopes
interleaved = (
[full[i] for i in range(0, base, 2)]
+ [full[i] for i in range(1, base, 2)]
)
slopes = interleaved[:num_heads]
return torch.tensor(slopes, dtype=torch.float32)
def build_alibi_tensor(
num_heads: int,
seq_len: int,
device: torch.device,
dtype: torch.dtype = torch.float32,
max_slope: float = 8.0,
) -> torch.Tensor:
"""Build the additive ALiBi bias tensor.
Args:
num_heads: number of attention heads
seq_len: attention sequence length
device: target device
dtype: target dtype
max_slope: maximum slope (controls decay)
Returns:
alibi: tensor of shape [1, num_heads, seq_len, seq_len]
Ready to ADD to attention weights before softmax.
"""
slopes = get_alibi_slopes(num_heads, max_slope=max_slope).to(device=device, dtype=dtype)
# positions: [seq_len, seq_len], value = j - i (j is query, i is key)
positions = torch.arange(seq_len, device=device, dtype=dtype)
relative_positions = positions[None, :] - positions[:, None] # [T, T]
# Mask future positions to -inf (handled by causal mask elsewhere, but be safe)
relative_positions = relative_positions.clamp(min=0)
# alibi: [num_heads, seq_len, seq_len] = -slope * relative_positions
alibi = slopes.view(-1, 1, 1) * relative_positions.unsqueeze(0)
alibi = -alibi # bias is negative (decreases attention with distance)
# Add batch dim
alibi = alibi.unsqueeze(0) # [1, num_heads, seq_len, seq_len]
return alibi.to(dtype=dtype)
class AlibiPositionBias(nn.Module):
"""Module wrapper for ALiBi bias — registered as buffer, recomputed if seq_len grows."""
def __init__(self, num_heads: int, max_slope: float = 8.0):
super().__init__()
self.num_heads = num_heads
self.max_slope = max_slope
slopes = get_alibi_slopes(num_heads, max_slope=max_slope)
self.register_buffer("slopes", slopes, persistent=False)
self._cached_seq_len = 0
self._cached_bias: torch.Tensor | None = None
def forward(
self,
seq_len: int,
device: torch.device,
dtype: torch.dtype = torch.float32,
) -> torch.Tensor:
"""Return ALiBi bias of shape [1, num_heads, seq_len, seq_len]."""
if self._cached_bias is None or seq_len > self._cached_seq_len:
self._cached_bias = build_alibi_tensor(
self.num_heads, seq_len, device=device, dtype=dtype, max_slope=self.max_slope,
)
self._cached_seq_len = seq_len
bias = self._cached_bias.to(device=device, dtype=dtype)
if bias.shape[-1] < seq_len:
# Re-build for new length
self._cached_bias = build_alibi_tensor(
self.num_heads, seq_len, device=device, dtype=dtype, max_slope=self.max_slope,
)
self._cached_seq_len = seq_len
bias = self._cached_bias
return bias[:, :, :seq_len, :seq_len]
def extra_repr(self) -> str:
return f"num_heads={self.num_heads}, max_slope={self.max_slope}"