Download src/models/attention.py from zz312/RiboSphere: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/zz312/RiboSphere/resolve/main/src/models/attention.py
- Command line
-
hf download hf://zz312/RiboSphere/src/models/attention.py
-
curl -L -o attention.py https://huggingface.co/zz312/RiboSphere/resolve/main/src/models/attention.py
11.9 kB
| """Attention building blocks used by RiboSphere.""" | |
| from __future__ import annotations | |
| from typing import Any | |
| import torch | |
| from torch import Tensor, nn | |
| import torch.nn.functional as F | |
| from torch.nn.attention.flex_attention import create_block_mask, flex_attention | |
| from .layers import FeedForward | |
| from .rotary import RotaryEmbedding | |
| AttentionArguments = dict[str, Any] | |
| def root_mean_square_norm(tensor: Tensor) -> Tensor: | |
| """Apply parameter-free RMS normalization over the final dimension.""" | |
| return F.rms_norm(tensor, (tensor.shape[-1],)) | |
| class TransformerStack(nn.Module): | |
| """Stack of local self-attention blocks.""" | |
| def __init__( | |
| self, | |
| *, | |
| num_channels: int, | |
| num_heads: int, | |
| mlp_factor: int, | |
| window_size: int, | |
| num_layers: int, | |
| attention_backend: str = "flex", | |
| dropout: float = 0.1, | |
| pairwise_channels: int = 0, | |
| is_causal: bool = False, | |
| ) -> None: | |
| super().__init__() | |
| if num_channels <= 0 or num_heads <= 0 or num_layers <= 0: | |
| raise ValueError( | |
| "num_channels, num_heads, and num_layers must be positive." | |
| ) | |
| if num_channels % num_heads != 0: | |
| raise ValueError("num_channels must be divisible by num_heads.") | |
| if window_size <= 0: | |
| raise ValueError("window_size must be positive.") | |
| if pairwise_channels < 0: | |
| raise ValueError("pairwise_channels cannot be negative.") | |
| attention_backend = attention_backend.lower() | |
| if attention_backend not in {"sdpa", "flex"}: | |
| raise ValueError("attention_backend must be 'sdpa' or 'flex'.") | |
| use_pair_bias = pairwise_channels > 0 | |
| self.blocks = nn.ModuleList( | |
| [ | |
| TransformerBlock( | |
| num_channels=num_channels, | |
| num_heads=num_heads, | |
| mlp_factor=mlp_factor, | |
| attention_backend=attention_backend, | |
| dropout=dropout, | |
| use_pairwise_bias=use_pair_bias, | |
| pairwise_channels=pairwise_channels, | |
| ) | |
| for _ in range(num_layers) | |
| ] | |
| ) | |
| self.window_size = window_size | |
| self.is_causal = is_causal | |
| self.attention_backend = attention_backend | |
| def _window_mask( | |
| self, | |
| batch_index: Tensor, | |
| head_index: Tensor, | |
| query_index: Tensor, | |
| key_value_index: Tensor, | |
| ) -> Tensor: | |
| del batch_index, head_index | |
| within_window = (query_index - key_value_index).abs() <= self.window_size | |
| if self.is_causal: | |
| within_window = within_window & (query_index >= key_value_index) | |
| return within_window | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| pairwise_features: Tensor | None = None, | |
| ) -> Tensor: | |
| """Transform ``[B, L, D]`` token features.""" | |
| if hidden_states.ndim != 3: | |
| raise ValueError("hidden_states must have shape [B, L, D].") | |
| sequence_length = hidden_states.shape[1] | |
| if pairwise_features is not None and pairwise_features.shape[:3] != ( | |
| hidden_states.shape[0], | |
| sequence_length, | |
| sequence_length, | |
| ): | |
| raise ValueError( | |
| "pairwise_features must have shape [B, L, L, P]." | |
| ) | |
| if self.attention_backend == "flex": | |
| attention_arguments: AttentionArguments = { | |
| "block_mask": create_block_mask( | |
| self._window_mask, | |
| B=None, | |
| H=None, | |
| Q_LEN=sequence_length, | |
| KV_LEN=sequence_length, | |
| device=hidden_states.device, | |
| ), | |
| "score_mod": None, | |
| } | |
| else: | |
| positions = torch.arange( | |
| sequence_length, | |
| device=hidden_states.device, | |
| ) | |
| attention_mask = ( | |
| positions[:, None] - positions[None, :] | |
| ).abs() <= self.window_size | |
| if self.is_causal: | |
| attention_mask = attention_mask & ( | |
| positions[:, None] >= positions[None, :] | |
| ) | |
| attention_arguments = {"attn_mask": attention_mask.unsqueeze(0)} | |
| for block in self.blocks: | |
| hidden_states = block( | |
| hidden_states, | |
| pairwise_features=pairwise_features, | |
| **attention_arguments, | |
| ) | |
| return hidden_states | |
| class TransformerBlock(nn.Module): | |
| """Pre-normalized self-attention and feed-forward block.""" | |
| def __init__( | |
| self, | |
| *, | |
| num_channels: int, | |
| num_heads: int, | |
| mlp_factor: int, | |
| attention_backend: str = "flex", | |
| dropout: float = 0.1, | |
| use_pairwise_bias: bool = False, | |
| pairwise_channels: int = 0, | |
| ) -> None: | |
| super().__init__() | |
| self.attention_backend = attention_backend | |
| self.attention = SelfAttention( | |
| model_dimension=num_channels, | |
| num_heads=num_heads, | |
| dropout=dropout, | |
| attention_backend=attention_backend, | |
| ) | |
| self.feed_forward = FeedForward( | |
| num_channels, | |
| num_channels * mlp_factor, | |
| num_channels, | |
| activation=nn.GELU, | |
| dropout=dropout, | |
| ) | |
| self.use_pairwise_bias = use_pairwise_bias | |
| if use_pairwise_bias: | |
| if pairwise_channels <= 0: | |
| raise ValueError( | |
| "pairwise_channels must be positive when pair bias is enabled." | |
| ) | |
| self.pair_bias_projection = nn.Linear( | |
| pairwise_channels, 1, bias=True | |
| ) | |
| self.pair_bias_norm = nn.LayerNorm(pairwise_channels) | |
| else: | |
| self.pair_bias_projection = None | |
| self.pair_bias_norm = None | |
| def _add_pair_bias( | |
| self, | |
| pairwise_features: Tensor, | |
| attention_arguments: AttentionArguments, | |
| ) -> AttentionArguments: | |
| if self.pair_bias_projection is None or self.pair_bias_norm is None: | |
| return attention_arguments | |
| pair_bias = self.pair_bias_projection( | |
| self.pair_bias_norm(pairwise_features) | |
| ).squeeze(-1) | |
| attention_arguments = dict(attention_arguments) | |
| if self.attention_backend == "flex": | |
| def pair_biased_score( | |
| score: Tensor, | |
| batch_index: Tensor, | |
| head_index: Tensor, | |
| query_index: Tensor, | |
| key_value_index: Tensor, | |
| ) -> Tensor: | |
| del head_index | |
| return score + pair_bias[ | |
| batch_index, | |
| query_index, | |
| key_value_index, | |
| ] | |
| attention_arguments["score_mod"] = pair_biased_score | |
| else: | |
| attention_mask = attention_arguments["attn_mask"] | |
| additive_pair_bias = torch.where( | |
| attention_mask, | |
| pair_bias, | |
| torch.full_like(pair_bias, -torch.inf), | |
| ) | |
| attention_arguments["attn_mask"] = additive_pair_bias.unsqueeze(1) | |
| return attention_arguments | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| *, | |
| pairwise_features: Tensor | None = None, | |
| **attention_arguments: Any, | |
| ) -> Tensor: | |
| if self.use_pairwise_bias: | |
| if pairwise_features is None: | |
| raise ValueError( | |
| "pairwise_features are required when pair bias is enabled." | |
| ) | |
| attention_arguments = self._add_pair_bias( | |
| pairwise_features, | |
| attention_arguments, | |
| ) | |
| hidden_states = hidden_states + self.attention( | |
| root_mean_square_norm(hidden_states), | |
| **attention_arguments, | |
| ) | |
| hidden_states = hidden_states + self.feed_forward( | |
| root_mean_square_norm(hidden_states) | |
| ) | |
| return hidden_states | |
| class SelfAttention(nn.Module): | |
| """Multi-head self-attention with rotary position embeddings.""" | |
| def __init__( | |
| self, | |
| model_dimension: int, | |
| num_heads: int, | |
| *, | |
| normalize_queries_and_keys: bool = False, | |
| attention_backend: str = "flex", | |
| dropout: float = 0.1, | |
| ) -> None: | |
| super().__init__() | |
| if model_dimension <= 0 or num_heads <= 0: | |
| raise ValueError("model_dimension and num_heads must be positive.") | |
| if model_dimension % num_heads != 0: | |
| raise ValueError("model_dimension must be divisible by num_heads.") | |
| if not 0.0 <= dropout < 1.0: | |
| raise ValueError("dropout must be in [0, 1).") | |
| attention_backend = attention_backend.lower() | |
| if attention_backend not in {"flex", "sdpa"}: | |
| raise ValueError("backend must be 'flex' or 'sdpa'.") | |
| self.model_dimension = model_dimension | |
| self.num_heads = num_heads | |
| self.head_dimension = self.model_dimension // self.num_heads | |
| self.attention_dropout = nn.Dropout(dropout) | |
| self.dropout = dropout | |
| self.normalize_queries_and_keys = normalize_queries_and_keys | |
| self.rotary_embedding = RotaryEmbedding(self.head_dimension) | |
| self.qkv_projection = nn.Linear( | |
| model_dimension, 3 * model_dimension, bias=True | |
| ) | |
| self.output_projection = nn.Linear(model_dimension, model_dimension) | |
| self.residual_dropout = nn.Dropout(dropout) | |
| self.attention_backend = attention_backend | |
| def forward( | |
| self, | |
| hidden_states: Tensor, | |
| **attention_arguments: Any, | |
| ) -> Tensor: | |
| """Apply self-attention to ``[B, L, D]`` hidden states.""" | |
| if hidden_states.ndim != 3: | |
| raise ValueError("hidden_states must have shape [B, L, D].") | |
| batch_size, sequence_length, hidden_dimension = hidden_states.shape | |
| if hidden_dimension != self.model_dimension: | |
| raise ValueError( | |
| f"Expected hidden dimension {self.model_dimension}, " | |
| f"received {hidden_dimension}." | |
| ) | |
| query, key, value = self.qkv_projection(hidden_states).split( | |
| self.model_dimension, dim=-1 | |
| ) | |
| def split_heads(tensor: Tensor) -> Tensor: | |
| return tensor.reshape( | |
| batch_size, | |
| sequence_length, | |
| self.num_heads, | |
| self.head_dimension, | |
| ).transpose(1, 2) | |
| query, key, value = map(split_heads, (query, key, value)) | |
| if self.normalize_queries_and_keys: | |
| query = root_mean_square_norm(query) | |
| key = root_mean_square_norm(key) | |
| query, key = self.rotary_embedding(query, key) | |
| if self.attention_backend == "flex": | |
| attention_output = flex_attention( | |
| query, | |
| key, | |
| value, | |
| block_mask=attention_arguments.get("block_mask"), | |
| score_mod=attention_arguments.get("score_mod"), | |
| ) | |
| else: | |
| attention_output = F.scaled_dot_product_attention( | |
| query, | |
| key, | |
| value, | |
| **attention_arguments, | |
| ) | |
| attention_output = self.attention_dropout(attention_output) | |
| attention_output = attention_output.transpose(1, 2).contiguous().view( | |
| batch_size, | |
| sequence_length, | |
| self.model_dimension, | |
| ) | |
| attention_output = self.residual_dropout( | |
| self.output_projection(attention_output) | |
| ) | |
| return attention_output | |