| |
|
|
| """Full definition of a decoder-only transformer-based language model, all of it in this single file. |
| |
| Based on the nanoGPT implementation: https://github.com/karpathy/nanoGPT and |
| https://github.com/EleutherAI/gpt-neox/tree/main/megatron/model. |
| """ |
|
|
| import math |
| from copy import copy |
| from functools import partial |
| from typing import Any |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from torch.utils.checkpoint import checkpoint |
| from torch.nn.attention.flex_attention import BlockMask, create_block_mask, flex_attention, flex_attention |
| from typing_extensions import Self |
|
|
| from litgpt.config import Config |
| from litgpt.scripts.convert_hf_checkpoint import qkv_reassemble |
|
|
|
|
| def build_attention(config: Config, attention_idx: int) -> nn.Module: |
| """Construct the configured mixer for one attention-layer ordinal.""" |
| if config.kda_enabled: |
| period = config.kda_mla_ratio + 1 |
| if (attention_idx + 1) % period: |
| return KimiDeltaAttentionAdapter(config, attention_idx) |
| return MultiheadLatentAttention(config, attention_idx) |
| if config.latent_attention: |
| return MultiheadLatentAttention(config, attention_idx) |
| if config.attention_variant == "shared_diff": |
| return SharedDiffCausalSelfAttention(config, attention_idx) |
| return CausalSelfAttention(config, attention_idx) |
|
|
|
|
| class GPT(nn.Module): |
| def __init__(self, config: Config) -> None: |
| super().__init__() |
| assert config.padded_vocab_size is not None |
| self.config = config |
|
|
| self.lm_head = ( |
| nn.Identity() |
| if config.mamba3_hierarchical_vocab |
| else nn.Linear(config.n_embd, config.padded_vocab_size, bias=config.lm_head_bias) |
| ) |
| if config.mamba3_hierarchical_vocab: |
| mixtures = config.mamba3_hierarchical_mixtures |
| self.mamba3_cluster_head = nn.Linear( |
| config.n_embd, mixtures * 64, bias=False |
| ) |
| self.mamba3_slot_head = nn.Linear( |
| config.n_embd, mixtures * 64, bias=False |
| ) |
| self.mamba3_mixture_head = ( |
| nn.Linear(config.n_embd, mixtures, bias=False) |
| if mixtures > 1 |
| else None |
| ) |
| multipliers = torch.tensor( |
| [1, 2053, 1597, 3135, 817, 3327, 1231, 2871], |
| dtype=torch.long, |
| )[:mixtures] |
| offsets = torch.tensor( |
| [0, 911, 1877, 283, 3491, 1453, 2579, 3677], |
| dtype=torch.long, |
| )[:mixtures] |
| vocabulary = torch.arange(4096, dtype=torch.long) |
| self.register_buffer( |
| "mamba3_vocab_multipliers", multipliers, persistent=False |
| ) |
| self.register_buffer( |
| "mamba3_vocab_offsets", offsets, persistent=False |
| ) |
| self.register_buffer( |
| "mamba3_vocab_permutations", |
| (multipliers[:, None] * vocabulary[None] + offsets[:, None]) |
| .bitwise_and(4095), |
| persistent=False, |
| ) |
| self.mamba3_position_embedding = ( |
| nn.Embedding(config.mamba3_position_table_size, config.n_embd) |
| if config.mamba3_position_table_size |
| else None |
| ) |
| self.mamba3_phase_embedding = ( |
| nn.Embedding(config.mamba3_phase_table_size, config.n_embd) |
| if config.mamba3_phase_table_size |
| else None |
| ) |
| self.mamba3_token_scalar = ( |
| nn.Embedding(config.mamba3_token_scalar_params, 1) |
| if config.mamba3_token_scalar_params |
| else None |
| ) |
| block_class = RMSBudgetedBlock if config.rms_budgeted_block else Block |
| if config.mamba3_enabled: |
| from litgpt.mamba3_hybrid import Mamba3MultiscreenBlock |
|
|
| blocks = nn.ModuleList(Mamba3MultiscreenBlock(config, block_idx) for block_idx in range(config.n_layer)) |
| elif config.rwkv7_enabled: |
| from litgpt.rwkv7 import RWKV7HybridBlock |
|
|
| if not config.rwkv7_hybrid_multiscreen: |
| raise NotImplementedError("The prototype currently exposes the RWKV-7 + Multiscreen hybrid.") |
| blocks = nn.ModuleList(RWKV7HybridBlock(config, block_idx) for block_idx in range(config.n_layer)) |
| elif config.multiscreen_enabled: |
| blocks = nn.ModuleList(MultiscreenBlock(config, block_idx) for block_idx in range(config.n_layer)) |
| elif config.multiscreen_layer_interval: |
| blocks = nn.ModuleList( |
| SelectiveRecallBlock(config, block_idx) |
| for block_idx in range(config.n_layer) |
| ) |
| elif config.sandwich_coefficient: |
| k = config.sandwich_coefficient |
| architecture = "s" * k + "sf" * (config.n_layer - k) + "f" * k |
| blocks = nn.ModuleList( |
| SandwichSublayer(config, sublayer_idx, kind) for sublayer_idx, kind in enumerate(architecture) |
| ) |
| else: |
| blocks = nn.ModuleList(block_class(config, block_idx) for block_idx in range(config.n_layer)) |
| depth_memory = nn.ModuleDict() |
| if config.depth_memory_mode != "none": |
| for sublayer_idx in range(len(blocks)): |
| depth = sublayer_idx + 1 |
| if depth >= config.depth_memory_start and depth % config.depth_memory_interval == 0: |
| depth_memory[str(sublayer_idx)] = AdaptiveDepthMemory(config) |
| self.transformer = nn.ModuleDict( |
| dict( |
| wte=nn.Embedding( |
| config.padded_vocab_size * config.mamba3_embedding_banks, |
| config.n_embd, |
| ), |
| h=blocks, |
| depth_memory=depth_memory, |
| ln_f=( |
| nn.Identity() |
| if config.multiscreen_enabled |
| else nn.LayerNorm( |
| config.n_embd, |
| eps=config.norm_eps, |
| bias=not config.rwkv7_exact_10m_norms, |
| ) |
| if config.rwkv7_enabled and config.rwkv7_exact_10m_norms |
| else config.norm_class(config.n_embd, eps=config.norm_eps) |
| ), |
| ) |
| ) |
| mtp_config = config |
| if config.mtp_num_layers and config.sliding_window_indices is not None: |
| mtp_config = copy(config) |
| period = config.sliding_window_indices |
| mtp_config.sliding_window_indices = [ |
| *period, |
| *(period[index % len(period)] for index in range(config.mtp_num_layers)), |
| ] |
| mtp_block_class = MultiscreenBlock if config.multiscreen_enabled else Block |
| self.mtp_blocks = nn.ModuleList( |
| mtp_block_class(mtp_config, config.n_layer + index) |
| for index in range(config.mtp_num_layers) |
| ) |
| self.mtp_enorm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) |
| if config.mtp_num_layers |
| else None |
| ) |
| self.mtp_hnorm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) |
| if config.mtp_num_layers |
| else None |
| ) |
| self.mtp_eh_proj = ( |
| nn.Linear(2 * config.n_embd, config.n_embd, bias=False) |
| if config.mtp_num_layers |
| else None |
| ) |
| self.mtp_eagle3_feature_proj = ( |
| nn.Linear(3 * config.n_embd, config.n_embd, bias=False) |
| if config.mtp_eagle3 |
| else None |
| ) |
| if config.attn_residual_num_blocks or config.attn_residual_block_size: |
| num_sublayers = len(blocks) |
| num_reads = num_sublayers + 1 |
| self.attn_residual_norms = nn.ModuleList( |
| config.norm_class(config.n_embd, eps=config.norm_eps) |
| for _ in range(num_reads) |
| ) |
| self.attn_residual_queries = nn.ModuleList( |
| nn.Linear(config.n_embd, 1, bias=False) |
| for _ in range(num_reads) |
| ) |
| self.attn_residual_block_size = ( |
| config.attn_residual_block_size |
| or num_sublayers // config.attn_residual_num_blocks |
| ) |
| else: |
| self.attn_residual_norms = nn.ModuleList() |
| self.attn_residual_queries = nn.ModuleList() |
| self.attn_residual_block_size = 0 |
| if config.multiscreen_enabled: |
| self.multiscreen_embedding_scale = nn.Parameter(torch.zeros(())) |
| |
| |
| |
| |
| |
| |
| |
| |
| self.multiscreen_readout_scale = nn.Parameter( |
| torch.tensor(math.log(4.0)) |
| ) |
| self.register_buffer("depth_memory_scale", torch.tensor(1.0), persistent=False) |
| self.model_match_scale = ( |
| nn.Parameter(torch.zeros(config.model_match_adapter_params)) |
| if config.model_match_adapter_params |
| else None |
| ) |
| self.mask_cache: torch.Tensor | None = None |
| self.max_seq_length = self.config.block_size |
|
|
| def _apply_model_match_scale(self, x: torch.Tensor) -> torch.Tensor: |
| if self.model_match_scale is not None: |
| scale = self.model_match_scale |
| if scale.numel() < self.config.n_embd: |
| scale = F.pad(scale, (0, self.config.n_embd - scale.numel())) |
| elif scale.numel() > self.config.n_embd: |
| rows = math.ceil(scale.numel() / self.config.n_embd) |
| scale = F.pad( |
| scale, |
| (0, rows * self.config.n_embd - scale.numel()), |
| ).view(rows, self.config.n_embd).mean(dim=0) |
| x = x * (1.0 + 0.01 * torch.tanh(scale)) |
| return x |
|
|
| def _matched_final_norm(self, x: torch.Tensor) -> torch.Tensor: |
| return self._apply_model_match_scale(self.transformer.ln_f(x)) |
|
|
| def _attention_residual_read( |
| self, |
| blocks: list[torch.Tensor], |
| partial_block: torch.Tensor, |
| norm: nn.Module, |
| query: nn.Module, |
| ) -> torch.Tensor: |
| """Kimi Block AttnRes inter-block softmax aggregation.""" |
| values = torch.stack([*blocks, partial_block], dim=0) |
| keys = norm(values) |
| logits = query(keys).squeeze(-1) |
| weights = F.softmax(logits, dim=0, dtype=torch.float).to(values.dtype) |
| return torch.einsum("nbt,nbtd->btd", weights, values) |
|
|
| def prepare_sliding_window_masks(self, sequence_length: int, device: torch.device) -> None: |
| """Build block-sparse causal masks before ``torch.compile`` traces the model.""" |
| for module in self.modules(): |
| if isinstance(module, CausalSelfAttention): |
| module.prepare_sliding_window_mask(sequence_length, device) |
|
|
| def set_sliding_window_enabled(self, enabled: bool) -> int: |
| """Toggle configured local-attention layers without changing parameters.""" |
| changed = 0 |
| for module in self.modules(): |
| if not isinstance(module, CausalSelfAttention): |
| continue |
| next_value = module.configured_sliding_window_attention and enabled |
| if module.apply_sliding_window_attention != next_value: |
| module.apply_sliding_window_attention = next_value |
| changed += 1 |
| return changed |
|
|
| @property |
| def max_seq_length(self) -> int: |
| return self._max_seq_length |
|
|
| @max_seq_length.setter |
| def max_seq_length(self, value: int) -> None: |
| """ |
| When doing inference, the sequences used might be shorter than the model's context length. |
| This allows setting a smaller number to avoid allocating unused memory |
| """ |
| if value > self.config.block_size: |
| raise ValueError( |
| f"Cannot attend to {value}, block size is only {self.config.block_size}." |
| " This is likely because the input text exceeds the supported context length of this model." |
| ) |
| self._max_seq_length = value |
| if not hasattr(self, "cos"): |
| |
| cos, sin = self.rope_cache() |
| self.register_buffer("cos", cos, persistent=False) |
| self.register_buffer("sin", sin, persistent=False) |
| |
| elif value != self.cos.size(0): |
| self.cos, self.sin = self.rope_cache(device=self.cos.device) |
| |
| |
| if self.mask_cache is not None and self.mask_cache.shape[-1] < value: |
| print( |
| f"Warning: KV cache has length {self.mask_cache.shape[-1]} < {value} = max_seq_length. Call 'set_kv_cache' before doing any forwards!" |
| ) |
|
|
| def reset_parameters(self) -> None: |
| |
| self.cos, self.sin = self.rope_cache(device=self.cos.device) |
|
|
| def _init_weights(self, module: nn.Module) -> None: |
| """Meant to be used with `gpt.apply(gpt._init_weights)`.""" |
| if module is self.mtp_eagle3_feature_proj: |
| |
| |
| with torch.no_grad(): |
| module.weight.zero_() |
| module.weight[:, -self.config.n_embd :].copy_( |
| torch.eye( |
| self.config.n_embd, |
| device=module.weight.device, |
| dtype=module.weight.dtype, |
| ) |
| ) |
| elif isinstance(module, GroupedTopkRouter): |
| torch.nn.init.normal_(module.weight.data, mean=0.0, std=0.02) |
| elif isinstance(module, nn.Linear): |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) |
| if module.bias is not None: |
| torch.nn.init.zeros_(module.bias) |
| elif isinstance(module, nn.Embedding): |
| torch.nn.init.normal_(module.weight, mean=0.0, std=0.02) |
|
|
| def forward( |
| self, |
| idx: torch.Tensor, |
| input_pos: torch.Tensor | None = None, |
| return_hidden: bool = False, |
| return_mtp_features: bool = False, |
| input_pos_maxp1: int | None = None, |
| lm_head_chunk_size: int = 0, |
| targets: torch.Tensor | None = None, |
| ) -> torch.Tensor | list[torch.Tensor]: |
| """ |
| If `input_pos` is provided, the KV cache uses K and V vectors for |
| positions smaller than entries in `input_pos`. For efficiency, pass |
| `input_pos_maxp1` as `max(input_pos) + 1` if already available from |
| your forward algorithm. This slices the KV cache buffers and speeds |
| up multi-head attention. |
| |
| Without `input_pos_maxp1`, the computation uses the full KV cache |
| (`max_seq_length`) with masking applied. Note that inferring |
| `input_pos_maxp1` from `input_pos` causes graph breaks and prevents |
| compilation. |
| |
| Args: |
| idx: Token indices of input sequences, shape `(B, T)`, where `B` |
| is batch size. |
| input_pos: Optional. Positions of input tokens. The default is |
| `arange(T)`. Can have shape `(T,)` or `(B, T)` (batched index). |
| input_pos_maxp1: Optional. See above. |
| lm_head_chunk_size: Optional. If `lm_head_chunk_size > 0`, the final |
| `lm_head` computation is done in chunks of this size. |
| |
| Returns: |
| Logit outputs, shape `(B, T, config.padded_vocab_size)`. If |
| `lm_head_chunk_size > 0`, this is a list of chunks of shape |
| `(B, lm_head_chunk_size, config.padded_vocab_size)`, the final |
| entry can be shorter. |
| |
| """ |
| if return_hidden and return_mtp_features: |
| raise ValueError("Request either the final hidden state or EAGLE-3 features, not both.") |
| if return_mtp_features and not self.config.mtp_eagle3: |
| raise ValueError("return_mtp_features requires mtp_eagle3=True.") |
|
|
| T = idx.size(1) |
| if self.max_seq_length < T: |
| raise ValueError(f"Cannot forward sequence of length {T}, max seq length is only {self.max_seq_length}.") |
|
|
| if input_pos is not None: |
| if input_pos.dim() > 2: |
| |
| raise ValueError(f"input_pos must have 1 or 2 dimensions, input_pos.shape = {input_pos.shape}") |
| if input_pos.shape[-1] != T: |
| raise ValueError(f"input_pos.shape[-1] = {input_pos.shape[-1]} != {T} = idx.shape[1], must be the same") |
| cos = batched_index_select(self.cos, 0, input_pos) |
| sin = batched_index_select(self.sin, 0, input_pos) |
| if input_pos.dim() == 1: |
| cos = cos.unsqueeze(0) |
| sin = sin.unsqueeze(0) |
| if self.mask_cache is None: |
| raise TypeError("You need to call `gpt.set_kv_cache()`") |
| mask = batched_index_select(self.mask_cache, 2, input_pos) |
| if mask.dim() > 4: |
| |
| |
| mask = mask.view(*(mask.shape[0:1] + mask.shape[2:])) |
| if input_pos_maxp1 is not None: |
| |
| if input_pos_maxp1 > self.max_seq_length: |
| raise ValueError(f"Positions in 'input_pos' must be in [0,{self.max_seq_length})") |
| mask = mask[..., :input_pos_maxp1] |
| else: |
| |
| cos = self.cos[:T].unsqueeze(0) |
| sin = self.sin[:T].unsqueeze(0) |
| |
| mask = None |
| input_pos_maxp1 = None |
|
|
| if self.config.multiscreen_enabled and input_pos is not None: |
| raise NotImplementedError("Multiscreen KV-cache decode is not implemented; use full-sequence evaluation.") |
|
|
| if self.config.multiscreen_enabled: |
| embedding = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| x = F.embedding(idx, embedding) * self.multiscreen_embedding_scale.exp() |
| elif self.config.mamba3_hierarchical_vocab: |
| positions = torch.arange(T, device=idx.device) |
| bank = positions.remainder(self.config.mamba3_embedding_banks) |
| striped_idx = idx + bank[None] * self.config.padded_vocab_size |
| x = self.transformer.wte(striped_idx) |
| if self.mamba3_position_embedding is not None: |
| x = x + self.mamba3_position_embedding( |
| positions.remainder(self.config.mamba3_position_table_size) |
| )[None] |
| if self.mamba3_phase_embedding is not None: |
| x = x + self.mamba3_phase_embedding( |
| positions.remainder(self.config.mamba3_phase_table_size) |
| )[None] |
| if self.mamba3_token_scalar is not None: |
| x = x + self.mamba3_token_scalar( |
| idx.remainder(self.config.mamba3_token_scalar_params) |
| ) |
| else: |
| x = self.transformer.wte(idx) |
| if self.config.scale_embeddings: |
| x = x * torch.tensor(self.config.n_embd**0.5, dtype=x.dtype) |
|
|
| first_layer_values: torch.Tensor | None = None |
| depth_memory_states = [x] |
| if ( |
| self.config.multiscreen_enabled |
| and self.config.multiscreen_activation_checkpointing |
| and self.training |
| and torch.is_grad_enabled() |
| ): |
| if self.config.rope_indices is not None or self.config.value_residual_mix > 0.0: |
| raise NotImplementedError( |
| "Segmented Multiscreen checkpointing does not support per-layer RoPE " |
| "indices or value residual mixing." |
| ) |
| group_size = max(1, self.config.multiscreen_checkpoint_group_size) |
| blocks = tuple(self.transformer.h) |
| for start in range(0, len(blocks), group_size): |
| segment_blocks = blocks[start : start + group_size] |
|
|
| def run_segment( |
| segment_input: torch.Tensor, |
| segment: tuple[nn.Module, ...] = segment_blocks, |
| ) -> torch.Tensor: |
| for segment_block in segment: |
| segment_input = segment_block( |
| segment_input, cos, sin, None, None, None |
| ) |
| return segment_input |
|
|
| x = checkpoint( |
| run_segment, |
| x, |
| |
| |
| |
| |
| use_reentrant=True, |
| preserve_rng_state=False, |
| ) |
| x = F.normalize(x, dim=-1, eps=1e-6) * self.multiscreen_readout_scale.exp() |
| x = self._apply_model_match_scale(x) |
| weight = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| if targets is not None and self.config.multiscreen_chunked_lm_loss: |
| from litgpt.multiscreen_loss import chunked_linear_cross_entropy |
|
|
| loss = chunked_linear_cross_entropy( |
| x, |
| weight, |
| targets, |
| self.config.multiscreen_lm_loss_chunk_tokens, |
| ) |
| return (loss, x) if return_hidden else loss |
| if lm_head_chunk_size > 0: |
| return [F.linear(x_i, weight) for x_i in x.split(lm_head_chunk_size, dim=1)] |
| logits = F.linear(x, weight) |
| return (logits, x) if return_hidden else logits |
|
|
| mamba3_checkpointed = ( |
| self.config.mamba3_enabled |
| and self.config.mamba3_activation_checkpointing |
| and self.training |
| and torch.is_grad_enabled() |
| ) |
| if mamba3_checkpointed: |
| group_size = self.config.mamba3_checkpoint_group_size |
| blocks = tuple(self.transformer.h) |
| for start in range(0, len(blocks), group_size): |
| segment_blocks = blocks[start : start + group_size] |
|
|
| def run_mamba3_segment( |
| segment_input: torch.Tensor, |
| segment: tuple[nn.Module, ...] = segment_blocks, |
| ) -> torch.Tensor: |
| for segment_block in segment: |
| segment_input = segment_block( |
| segment_input, cos, sin, None, None, None |
| ) |
| return segment_input |
|
|
| x = checkpoint( |
| run_mamba3_segment, |
| x, |
| |
| |
| |
| use_reentrant=True, |
| preserve_rng_state=False, |
| ) |
|
|
| rwkv_first_value: torch.Tensor | None = None |
| attn_residual_blocks: list[torch.Tensor] | None = None |
| attn_residual_partial: torch.Tensor | None = None |
| if self.config.attn_residual_num_blocks or self.config.attn_residual_block_size: |
| |
| |
| attn_residual_blocks = [x] |
| attn_residual_partial = x |
| active_blocks = self.transformer.h if not mamba3_checkpointed else () |
| attn_residual_norms = ( |
| self.attn_residual_norms[:-1] |
| if attn_residual_blocks is not None |
| else (None,) * len(active_blocks) |
| ) |
| attn_residual_queries = ( |
| self.attn_residual_queries[:-1] |
| if attn_residual_blocks is not None |
| else (None,) * len(active_blocks) |
| ) |
| for block_idx, (block, attn_residual_norm, attn_residual_query) in enumerate( |
| zip(active_blocks, attn_residual_norms, attn_residual_queries) |
| ): |
| if self.config.rope_indices is not None: |
| block_cos = cos[..., self.config.rope_indices[block_idx]] |
| block_sin = sin[..., self.config.rope_indices[block_idx]] |
| else: |
| block_cos = cos |
| block_sin = sin |
| if attn_residual_blocks is not None: |
| assert attn_residual_partial is not None |
| |
| |
| |
| |
| attn_residual_values = torch.stack( |
| [*attn_residual_blocks, attn_residual_partial], |
| dim=0, |
| ) |
| attn_residual_keys = attn_residual_norm(attn_residual_values) |
| attn_residual_logits = attn_residual_query( |
| attn_residual_keys |
| ).squeeze(-1) |
| attn_residual_weights = F.softmax( |
| attn_residual_logits, |
| dim=0, |
| dtype=torch.float, |
| ).to(attn_residual_values.dtype) |
| h = torch.einsum( |
| "nbt,nbtd->btd", |
| attn_residual_weights, |
| attn_residual_values, |
| ) |
| |
| |
| if block_idx and block_idx % self.attn_residual_block_size == 0: |
| attn_residual_blocks.append(attn_residual_partial) |
| attn_residual_partial = None |
| update = block( |
| h, |
| block_cos, |
| block_sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| ) - h |
| attn_residual_partial = ( |
| update |
| if attn_residual_partial is None |
| else attn_residual_partial + update |
| ) |
| x = attn_residual_partial |
| elif self.config.rwkv7_enabled: |
| x, rwkv_first_value, _ = block( |
| x, |
| block_cos, |
| block_sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| first_value=rwkv_first_value, |
| ) |
| elif self.config.value_residual_mix > 0.0: |
| x, current_values = block( |
| x, |
| block_cos, |
| block_sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| value_residual=first_layer_values, |
| ) |
| if first_layer_values is None: |
| first_layer_values = current_values |
| else: |
| x = block(x, block_cos, block_sin, mask, input_pos, input_pos_maxp1) |
| depth_memory_key = str(block_idx) |
| if depth_memory_key in self.transformer.depth_memory: |
| if self.config.depth_memory_mode == "adaptive": |
| x = self.transformer.depth_memory[depth_memory_key]( |
| x, depth_memory_states, self.depth_memory_scale |
| ) |
| if self.config.depth_memory_score_scale == "recurrent": |
| |
| |
| depth_memory_states = [x] |
| else: |
| depth_memory_states.append(x) |
| mtp_features: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None |
| if attn_residual_blocks is not None: |
| assert attn_residual_partial is not None |
| completed_attnres_blocks = [ |
| *attn_residual_blocks[1:], |
| attn_residual_partial, |
| ] |
| if return_mtp_features: |
| if len(completed_attnres_blocks) < 4: |
| raise RuntimeError("EAGLE-3 requires at least four completed AttnRes blocks.") |
| |
| mtp_features = ( |
| completed_attnres_blocks[0], |
| completed_attnres_blocks[3], |
| completed_attnres_blocks[-1], |
| ) |
| attn_residual_values = torch.stack( |
| [*attn_residual_blocks, attn_residual_partial], |
| dim=0, |
| ) |
| attn_residual_keys = self.attn_residual_norms[-1]( |
| attn_residual_values |
| ) |
| attn_residual_logits = self.attn_residual_queries[-1]( |
| attn_residual_keys |
| ).squeeze(-1) |
| attn_residual_weights = F.softmax( |
| attn_residual_logits, |
| dim=0, |
| dtype=torch.float, |
| ).to(attn_residual_values.dtype) |
| x = torch.einsum( |
| "nbt,nbtd->btd", |
| attn_residual_weights, |
| attn_residual_values, |
| ) |
| if self.config.multiscreen_enabled: |
| x = F.normalize(x, dim=-1, eps=1e-6) * self.multiscreen_readout_scale.exp() |
| x = self._apply_model_match_scale(x) |
| weight = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| if targets is not None and self.config.multiscreen_chunked_lm_loss: |
| from litgpt.multiscreen_loss import chunked_linear_cross_entropy |
|
|
| loss = chunked_linear_cross_entropy( |
| x, |
| weight, |
| targets, |
| self.config.multiscreen_lm_loss_chunk_tokens, |
| ) |
| return (loss, x) if return_hidden else loss |
| if lm_head_chunk_size > 0: |
| return [F.linear(x_i, weight) for x_i in x.split(lm_head_chunk_size, dim=1)] |
| logits = F.linear(x, weight) |
| return (logits, x) if return_hidden else logits |
|
|
| mtp_hidden = x |
| x = self._matched_final_norm(x) |
| if self.config.mamba3_hierarchical_vocab: |
| logits_or_loss = self._hierarchical_vocab_output(x, targets) |
| if targets is not None: |
| return logits_or_loss |
| logits = logits_or_loss |
| return (logits, mtp_hidden) if return_hidden else logits |
| if ( |
| targets is not None |
| and self.config.multiscreen_chunked_lm_loss |
| ): |
| if self.config.final_logit_softcapping is not None: |
| raise NotImplementedError( |
| "Chunked linear CE does not support logit softcapping." |
| ) |
| from litgpt.multiscreen_loss import chunked_linear_cross_entropy |
|
|
| loss = chunked_linear_cross_entropy( |
| x, |
| self.lm_head.weight, |
| targets, |
| self.config.multiscreen_lm_loss_chunk_tokens, |
| ) |
| return (loss, mtp_hidden) if return_hidden else loss |
| clamp_head = ( |
| partial(do_softcapping, thresh=self.config.final_logit_softcapping) |
| if self.config.final_logit_softcapping is not None |
| else nn.Identity() |
| ) |
| if lm_head_chunk_size > 0: |
| return [clamp_head(self.lm_head(x_i)) for x_i in x.split(lm_head_chunk_size, dim=1)] |
| else: |
| logits = clamp_head(self.lm_head(x)) |
| if return_mtp_features: |
| assert mtp_features is not None |
| return logits, mtp_features |
| return (logits, mtp_hidden) if return_hidden else logits |
|
|
| def forward_rwkv7_step( |
| self, |
| idx: torch.Tensor, |
| states: list[Any] | None = None, |
| ) -> tuple[torch.Tensor, list[Any]]: |
| """Decode one token with fixed-size RWKV and local-screening state.""" |
| if not self.config.rwkv7_enabled: |
| raise RuntimeError("forward_rwkv7_step requires rwkv7_enabled.") |
| if idx.ndim != 2 or idx.size(1) != 1: |
| raise ValueError("forward_rwkv7_step expects token indices shaped (batch, 1).") |
| if states is not None and len(states) != len(self.transformer.h): |
| raise ValueError("One recurrent state is required per hybrid block.") |
| x = self.transformer.wte(idx) |
| if self.config.scale_embeddings: |
| x = x * torch.tensor(self.config.n_embd**0.5, dtype=x.dtype, device=x.device) |
| next_states = [] |
| first_value = None |
| for index, block in enumerate(self.transformer.h): |
| layer_state = None if states is None else states[index] |
| x, first_value, layer_state = block.forward_step(x, first_value, layer_state) |
| next_states.append(layer_state) |
| logits = self.lm_head(self._matched_final_norm(x)) |
| return logits, next_states |
|
|
| def forward_multiscreen_step( |
| self, |
| idx: torch.Tensor, |
| states: list[Any] | None = None, |
| ) -> tuple[torch.Tensor, list[Any]]: |
| """Decode one token with bounded per-layer Multiscreen key/value caches.""" |
| if not self.config.multiscreen_enabled or self.config.mamba3_enabled: |
| raise RuntimeError("forward_multiscreen_step requires pure Multiscreen.") |
| if idx.ndim != 2 or idx.size(1) != 1: |
| raise ValueError("forward_multiscreen_step expects token indices shaped (batch, 1).") |
| if states is not None and len(states) != len(self.transformer.h): |
| raise ValueError("One screening state is required per Multiscreen block.") |
|
|
| embedding = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| x = F.embedding(idx, embedding) * self.multiscreen_embedding_scale.exp() |
| if self.config.scale_embeddings: |
| x = x * torch.tensor(self.config.n_embd**0.5, dtype=x.dtype, device=x.device) |
|
|
| next_states = [] |
| for index, block in enumerate(self.transformer.h): |
| layer_state = None if states is None else states[index] |
| x, layer_state = block.forward_step(x, layer_state) |
| next_states.append(layer_state) |
|
|
| x = F.normalize(x, dim=-1, eps=1e-6) * self.multiscreen_readout_scale.exp() |
| x = self._apply_model_match_scale(x) |
| weight = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| return F.linear(x, weight), next_states |
|
|
| def forward_mamba3_step( |
| self, |
| idx: torch.Tensor, |
| states: list[Any] | None = None, |
| ) -> tuple[torch.Tensor, list[Any]]: |
| """Decode one token with fixed-size Mamba-3 and local-screening state.""" |
| if not self.config.mamba3_enabled: |
| raise RuntimeError("forward_mamba3_step requires mamba3_enabled.") |
| if idx.ndim != 2 or idx.size(1) != 1: |
| raise ValueError("forward_mamba3_step expects token indices shaped (batch, 1).") |
| if states is not None and len(states) != len(self.transformer.h): |
| raise ValueError("One recurrent state is required per hybrid block.") |
| if self.config.mamba3_hierarchical_vocab: |
| position = 0 if states is None else states[0].next_position |
| bank = position % self.config.mamba3_embedding_banks |
| x = self.transformer.wte(idx + bank * self.config.padded_vocab_size) |
| if self.mamba3_position_embedding is not None: |
| position_idx = torch.tensor( |
| [position % self.config.mamba3_position_table_size], |
| device=idx.device, |
| ) |
| x = x + self.mamba3_position_embedding(position_idx)[None] |
| if self.mamba3_phase_embedding is not None: |
| phase_idx = torch.tensor( |
| [position % self.config.mamba3_phase_table_size], |
| device=idx.device, |
| ) |
| x = x + self.mamba3_phase_embedding(phase_idx)[None] |
| if self.mamba3_token_scalar is not None: |
| x = x + self.mamba3_token_scalar( |
| idx.remainder(self.config.mamba3_token_scalar_params) |
| ) |
| else: |
| x = self.transformer.wte(idx) |
| next_states = [] |
| for index, block in enumerate(self.transformer.h): |
| layer_state = None if states is None else states[index] |
| x, layer_state = block.forward_step(x, layer_state) |
| next_states.append(layer_state) |
| x = self._matched_final_norm(x) |
| if self.config.mamba3_hierarchical_vocab: |
| logits = self._hierarchical_vocab_output(x, None) |
| return logits, next_states |
| return self.lm_head(x), next_states |
|
|
| def _hierarchical_vocab_output( |
| self, |
| hidden: torch.Tensor, |
| targets: torch.Tensor | None, |
| ) -> torch.Tensor: |
| mixtures = self.config.mamba3_hierarchical_mixtures |
| cluster = self.mamba3_cluster_head(hidden).view( |
| *hidden.shape[:-1], mixtures, 64 |
| ) |
| slot = self.mamba3_slot_head(hidden).view( |
| *hidden.shape[:-1], mixtures, 64 |
| ) |
| if mixtures == 1: |
| if targets is not None: |
| cluster_targets = torch.div(targets, 64, rounding_mode="floor") |
| slot_targets = targets.remainder(64) |
| return F.cross_entropy( |
| cluster.flatten(0, -2).float(), |
| cluster_targets.flatten(), |
| ) + F.cross_entropy( |
| slot.flatten(0, -2).float(), |
| slot_targets.flatten(), |
| ) |
| cluster = cluster.squeeze(-2) |
| slot = slot.squeeze(-2) |
| return (cluster.unsqueeze(-1) + slot.unsqueeze(-2)).flatten(-2) |
|
|
| cluster_log_probability = F.log_softmax(cluster.float(), dim=-1) |
| slot_log_probability = F.log_softmax(slot.float(), dim=-1) |
| mixture_log_probability = F.log_softmax( |
| self.mamba3_mixture_head(hidden).float(), dim=-1 |
| ) |
| if targets is not None: |
| mapped = ( |
| targets[..., None] * self.mamba3_vocab_multipliers |
| + self.mamba3_vocab_offsets |
| ).bitwise_and(4095) |
| cluster_target = torch.div(mapped, 64, rounding_mode="floor") |
| slot_target = mapped.remainder(64) |
| cluster_target_logp = cluster_log_probability.gather( |
| -1, cluster_target.unsqueeze(-1) |
| ).squeeze(-1) |
| slot_target_logp = slot_log_probability.gather( |
| -1, slot_target.unsqueeze(-1) |
| ).squeeze(-1) |
| token_log_probability = torch.logsumexp( |
| mixture_log_probability |
| + cluster_target_logp |
| + slot_target_logp, |
| dim=-1, |
| ) |
| return -token_log_probability.mean() |
|
|
| component_log_probability = ( |
| cluster_log_probability.unsqueeze(-1) |
| + slot_log_probability.unsqueeze(-2) |
| ).flatten(-2) |
| gather_index = self.mamba3_vocab_permutations.view( |
| *((1,) * (component_log_probability.ndim - 2)), |
| mixtures, |
| 4096, |
| ).expand_as(component_log_probability) |
| component_log_probability = component_log_probability.gather( |
| -1, gather_index |
| ) |
| return torch.logsumexp( |
| mixture_log_probability.unsqueeze(-1) + component_log_probability, |
| dim=-2, |
| ) |
|
|
| def mtp_forward( |
| self, |
| hidden, |
| next_tokens, |
| return_hidden: bool = False, |
| targets: torch.Tensor | None = None, |
| ): |
| T = hidden.size(1) |
| if self.config.multiscreen_enabled: |
| embedding_weight = F.normalize( |
| self.transformer.wte.weight, |
| dim=-1, |
| eps=1e-6, |
| ) |
| embedding = ( |
| F.embedding(next_tokens, embedding_weight) |
| * self.multiscreen_embedding_scale.exp() |
| ) |
| else: |
| embedding = self.transformer.wte(next_tokens) |
| |
| x = self.mtp_eh_proj( |
| torch.cat((self.mtp_hnorm(hidden), self.mtp_enorm(embedding)), dim=-1) |
| ) |
| cos = self.cos[:T].unsqueeze(0) |
| sin = self.sin[:T].unsqueeze(0) |
| for block in self.mtp_blocks: |
| x = block(x, cos, sin) |
| if self.config.multiscreen_enabled: |
| x = F.normalize(x, dim=-1, eps=1e-6) * self.multiscreen_readout_scale.exp() |
| x = self._apply_model_match_scale(x) |
| weight = F.normalize(self.transformer.wte.weight, dim=-1, eps=1e-6) |
| if targets is not None and self.config.multiscreen_chunked_lm_loss: |
| from litgpt.multiscreen_loss import chunked_linear_cross_entropy |
|
|
| return chunked_linear_cross_entropy( |
| x, |
| weight, |
| targets, |
| self.config.multiscreen_lm_loss_chunk_tokens, |
| ) |
| logits = F.linear(x, weight) |
| else: |
| x = self._matched_final_norm(x) |
| if targets is not None and self.config.multiscreen_chunked_lm_loss: |
| from litgpt.multiscreen_loss import chunked_linear_cross_entropy |
|
|
| return chunked_linear_cross_entropy( |
| x, |
| self.lm_head.weight, |
| targets, |
| self.config.multiscreen_lm_loss_chunk_tokens, |
| ) |
| logits = self.lm_head(x) |
| return (logits, x) if return_hidden else logits |
|
|
| def mtp_forward_multiscreen_step( |
| self, |
| hidden: torch.Tensor, |
| next_tokens: torch.Tensor, |
| states: list[Any] | None = None, |
| return_hidden: bool = False, |
| ) -> tuple[torch.Tensor, list[Any]] | tuple[ |
| torch.Tensor, |
| torch.Tensor, |
| list[Any], |
| ]: |
| """Advance the MTP drafter with bounded Multiscreen recurrent state.""" |
| if not self.config.multiscreen_enabled or self.config.mamba3_enabled: |
| raise RuntimeError( |
| "mtp_forward_multiscreen_step requires pure Multiscreen." |
| ) |
| if len(self.mtp_blocks) != 1: |
| raise RuntimeError( |
| "mtp_forward_multiscreen_step requires exactly one MTP layer." |
| ) |
| if hidden.shape[:2] != next_tokens.shape or next_tokens.size(1) != 1: |
| raise ValueError( |
| "MTP hidden states and tokens must both have shape (batch, 1, ...)." |
| ) |
| if states is not None and len(states) != len(self.mtp_blocks): |
| raise ValueError("One recurrent state is required per MTP block.") |
|
|
| embedding_weight = F.normalize( |
| self.transformer.wte.weight, |
| dim=-1, |
| eps=1e-6, |
| ) |
| embedding = ( |
| F.embedding(next_tokens, embedding_weight) |
| * self.multiscreen_embedding_scale.exp() |
| ) |
| x = self.mtp_eh_proj( |
| torch.cat( |
| (self.mtp_hnorm(hidden), self.mtp_enorm(embedding)), |
| dim=-1, |
| ) |
| ) |
| next_states = [] |
| for index, block in enumerate(self.mtp_blocks): |
| layer_state = None if states is None else states[index] |
| x, layer_state = block.forward_step(x, layer_state) |
| next_states.append(layer_state) |
| draft_hidden = x |
| x = F.normalize(x, dim=-1, eps=1e-6) * self.multiscreen_readout_scale.exp() |
| x = self._apply_model_match_scale(x) |
| logits = F.linear(x, embedding_weight) |
| if return_hidden: |
| return logits, draft_hidden, next_states |
| return logits, next_states |
|
|
| def mtp_eagle3_forward( |
| self, |
| features: tuple[torch.Tensor, torch.Tensor, torch.Tensor], |
| next_tokens: torch.Tensor, |
| return_hidden: bool = False, |
| ): |
| """Run the Kimi K3 EAGLE-3 drafter from three AttnRes block features.""" |
| if self.mtp_eagle3_feature_proj is None: |
| raise RuntimeError("mtp_eagle3_forward requires mtp_eagle3=True.") |
| if len(features) != 3: |
| raise ValueError("EAGLE-3 requires exactly low-, mid-, and high-level features.") |
| hidden = self.mtp_eagle3_feature_proj(torch.cat(features, dim=-1)) |
| return self.mtp_forward(hidden, next_tokens, return_hidden=return_hidden) |
|
|
| def mtp_eagle3_forward_step( |
| self, |
| features: tuple[torch.Tensor, torch.Tensor, torch.Tensor], |
| next_tokens: torch.Tensor, |
| input_pos: torch.Tensor, |
| input_pos_maxp1: int, |
| return_hidden: bool = False, |
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
| """Advance EAGLE-3 using the cached pretrained MTP module.""" |
| if self.mtp_eagle3_feature_proj is None: |
| raise RuntimeError("mtp_eagle3_forward_step requires mtp_eagle3=True.") |
| if len(features) != 3: |
| raise ValueError("EAGLE-3 requires exactly low-, mid-, and high-level features.") |
| hidden = self.mtp_eagle3_feature_proj(torch.cat(features, dim=-1)) |
| return self.mtp_forward_step( |
| hidden, |
| next_tokens, |
| input_pos, |
| input_pos_maxp1, |
| return_hidden=return_hidden, |
| ) |
|
|
| def mtp_forward_step( |
| self, |
| hidden: torch.Tensor, |
| next_tokens: torch.Tensor, |
| input_pos: torch.Tensor, |
| input_pos_maxp1: int, |
| return_hidden: bool = False, |
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
| """Advance the one-depth DeepSeek MTP module with its own KV cache.""" |
| if len(self.mtp_blocks) != 1: |
| raise RuntimeError("mtp_forward_step requires exactly one MTP module.") |
| if hidden.shape[:2] != next_tokens.shape or next_tokens.size(1) != input_pos.shape[-1]: |
| raise ValueError("MTP hidden states, tokens, and positions must have matching sequence lengths.") |
| if self.mask_cache is None: |
| raise TypeError("Call set_kv_cache() before cached MTP decoding.") |
| embedding = self.transformer.wte(next_tokens) |
| x = self.mtp_eh_proj( |
| torch.cat((self.mtp_hnorm(hidden), self.mtp_enorm(embedding)), dim=-1) |
| ) |
| cos = batched_index_select(self.cos, 0, input_pos) |
| sin = batched_index_select(self.sin, 0, input_pos) |
| if input_pos.dim() == 1: |
| cos = cos.unsqueeze(0) |
| sin = sin.unsqueeze(0) |
| mask = batched_index_select(self.mask_cache, 2, input_pos) |
| if mask.dim() > 4: |
| mask = mask.view(*(mask.shape[0:1] + mask.shape[2:])) |
| mask = mask[..., :input_pos_maxp1] |
| for block in self.mtp_blocks: |
| x = block( |
| x, |
| cos, |
| sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| ) |
| logits = self.lm_head(self._matched_final_norm(x)) |
| return (logits, x) if return_hidden else logits |
|
|
| @classmethod |
| |
| def from_name(cls, name: str, **kwargs: Any) -> Self: |
| return cls(Config.from_name(name, **kwargs)) |
|
|
| def rope_cache(self, device: torch.device | None = None) -> tuple[torch.Tensor, torch.Tensor]: |
| if self.config.rope_adjustments is None: |
| extra_config = None |
|
|
| else: |
| |
| llama3_params = ["low_freq_factor", "high_freq_factor"] |
| yarn_params = ["beta_fast", "beta_slow"] |
|
|
| has_llama3 = any(param in self.config.rope_adjustments for param in llama3_params) |
| has_yarn = any(param in self.config.rope_adjustments for param in yarn_params) |
|
|
| if has_llama3 and has_yarn: |
| raise ValueError( |
| "RoPE adjustments cannot contain both Llama3 parameters (low_freq_factor, high_freq_factor) " |
| "and YaRN parameters (beta_fast, beta_slow). These are mutually exclusive." |
| ) |
|
|
| |
| if has_llama3: |
| adjusted_params_required = ["factor", "low_freq_factor", "high_freq_factor", "original_max_seq_len"] |
| params_present = [param in self.config.rope_adjustments for param in adjusted_params_required] |
| if all(params_present): |
| extra_config = {name: self.config.rope_adjustments[name] for name in adjusted_params_required} |
| else: |
| missing_params = [ |
| param for param, present in zip(adjusted_params_required, params_present) if not present |
| ] |
| raise ValueError( |
| f"The following Llama3 RoPE parameters are missing in rope_adjustments: {', '.join(missing_params)}. " |
| "All Llama3 parameters must be specified together." |
| ) |
|
|
| |
| elif has_yarn: |
| |
| |
| yarn_required_params = ["factor", "beta_fast", "beta_slow", "original_max_seq_len"] |
| params_present = [param in self.config.rope_adjustments for param in yarn_required_params] |
|
|
| if not all(params_present): |
| missing_params = [ |
| param for param, present in zip(yarn_required_params, params_present) if not present |
| ] |
| raise ValueError( |
| f"The following YaRN RoPE parameters are missing in rope_adjustments: {', '.join(missing_params)}. " |
| "All YaRN required parameters must be specified together." |
| ) |
|
|
| extra_config = {name: self.config.rope_adjustments[name] for name in yarn_required_params} |
|
|
| |
| for param in ["mscale", "mscale_all_dim"]: |
| if param in self.config.rope_adjustments: |
| extra_config[param] = self.config.rope_adjustments[param] |
|
|
| |
| elif "factor" in self.config.rope_adjustments: |
| |
| adjusted_params_required = ["factor"] |
| extra_config = {name: self.config.rope_adjustments[name] for name in adjusted_params_required} |
| else: |
| extra_config = None |
|
|
| return build_rope_cache( |
| seq_len=self.max_seq_length, |
| n_elem=self.config.rope_n_elem, |
| device=device, |
| condense_ratio=self.config.rope_condense_ratio, |
| base=self.config.rope_base, |
| extra_config=extra_config, |
| rope_local_base_freq=self.config.rope_local_base_freq, |
| ) |
|
|
| def rope_cache_length(self) -> int: |
| """ |
| Extract the head dimension (n_elem) from RoPE cache regardless of shape. |
| |
| The RoPE cache can have different shapes depending on model configuration: |
| - Standard RoPE: (seq_len, n_elem) - 2D tensor |
| - Dual RoPE (local/global): (seq_len, n_elem, 2) - 3D tensor |
| |
| Returns: |
| int: n_elem (head dimension for RoPE) |
| """ |
| return self.cos.size(1) |
|
|
| def set_kv_cache( |
| self, |
| batch_size: int, |
| max_seq_length: int | None = None, |
| rope_cache_length: int | None = None, |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| ) -> None: |
| if rope_cache_length is None: |
| rope_cache_length = self.rope_cache_length() |
|
|
| if max_seq_length is None: |
| max_seq_length = self.max_seq_length |
|
|
| |
| for block in self.transformer.h: |
| if block.attn is None: |
| continue |
| block.attn.kv_cache = block.attn.build_kv_cache( |
| batch_size, |
| max_seq_length, |
| rope_cache_length, |
| device, |
| dtype, |
| ) |
| for block in self.mtp_blocks: |
| block.attn.kv_cache = block.attn.build_kv_cache( |
| batch_size, |
| max_seq_length, |
| rope_cache_length, |
| device, |
| dtype, |
| ) |
|
|
| if self.mask_cache is None or self.mask_cache.size(3) != max_seq_length: |
| |
| |
| self.mask_cache = build_mask_cache(max_seq_length, device) |
|
|
| def clear_kv_cache(self) -> None: |
| self.mask_cache = None |
| for block in self.transformer.h: |
| if block.attn is not None: |
| block.attn.kv_cache = None |
| for block in self.mtp_blocks: |
| block.attn.kv_cache = None |
|
|
|
|
| class AdaptiveDepthMemory(nn.Module): |
| """Bounded, token-adaptive retrieval from earlier residual states. |
| |
| Retrieval weights are content-addressed independently for every token. |
| A zero-initialized channel gate makes the adaptive path exactly identical |
| to the control at initialization while allowing a live first-step gradient. |
| Earlier states are RMS-matched to the current stream before interpolation, |
| preventing depth-dependent residual scale from dominating retrieval. |
| """ |
|
|
| def __init__(self, config: Config) -> None: |
| super().__init__() |
| self.max_sources = config.depth_memory_max_sources |
| self.max_commit = config.depth_memory_max_commit |
| self.score_scale = config.depth_memory_score_scale |
| |
| self.channel_gate = nn.Parameter(torch.zeros(config.n_embd)) |
| self.source_bias = nn.Parameter(torch.zeros(self.max_sources)) |
| self.log_temperature = nn.Parameter(torch.zeros(())) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| states: list[torch.Tensor], |
| scale: torch.Tensor | float = 1.0, |
| ) -> torch.Tensor: |
| sources = torch.stack(states[-self.max_sources :], dim=-2) |
| x_float = x.float() |
| sources_float = sources.float() |
|
|
| source_count = sources.size(-2) |
| bias = self.source_bias[-source_count:].float() |
| if self.score_scale in {"factorized", "recurrent"}: |
| |
| |
| |
| if self.score_scale == "recurrent": |
| retrieved = sources_float[..., -1, :] |
| else: |
| weights = torch.softmax(bias, dim=-1) |
| retrieved = (weights.unsqueeze(-1) * sources_float).sum(dim=-2) |
| current_rms = x_float.pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| retrieved_rms = retrieved.pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| retrieved = retrieved * (current_rms / retrieved_rms) |
| correction = (retrieved - x_float).to(dtype=x.dtype) |
| agreement = torch.tanh( |
| x_float * retrieved / (current_rms * current_rms).clamp_min(1e-6) |
| ).to(dtype=x.dtype) |
| modulation = 1.0 + 0.5 * torch.tanh(self.log_temperature).to(dtype=x.dtype) * agreement |
| gate = self.max_commit * torch.tanh(self.channel_gate).to(dtype=x.dtype) * modulation |
| return x + torch.as_tensor(scale, device=x.device, dtype=x.dtype) * gate * correction |
|
|
| query_unit = F.normalize(x_float, dim=-1, eps=1e-6) |
| source_unit = F.normalize(sources_float, dim=-1, eps=1e-6) |
| scores = (source_unit * query_unit.unsqueeze(-2)).sum(dim=-1) |
| if self.score_scale == "inverse_sqrt": |
| |
| |
| scores = scores / math.sqrt(x.size(-1)) |
| temperature = F.softplus(self.log_temperature.float()) + 0.5 |
| weights = torch.softmax(scores * temperature + bias, dim=-1) |
|
|
| retrieved = (weights.unsqueeze(-1) * sources_float).sum(dim=-2) |
| current_rms = x_float.pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| retrieved_rms = retrieved.pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| retrieved = retrieved * (current_rms / retrieved_rms) |
| correction = (retrieved - x_float).to(dtype=x.dtype) |
| gate = self.max_commit * torch.tanh(self.channel_gate).to(dtype=x.dtype) |
| return x + torch.as_tensor(scale, device=x.device, dtype=x.dtype) * gate * correction |
|
|
|
|
| class _PackedMultiscreenProjection(torch.autograd.Function): |
| """One GEMM over four original parameters, with gradients returned separately.""" |
|
|
| @staticmethod |
| def forward(ctx, x, packed_weight, *weights): |
| ctx.save_for_backward(x, packed_weight) |
| ctx.shapes = tuple(weight.shape for weight in weights) |
| return F.linear(x, packed_weight) |
|
|
| @staticmethod |
| def backward(ctx, grad_output): |
| x, packed_weight = ctx.saved_tensors |
| x_flat = x.reshape(-1, x.size(-1)) |
| grad_flat = grad_output.reshape(-1, grad_output.size(-1)) |
| grad_x = torch.mm(grad_flat, packed_weight).reshape_as(x) |
| grad_packed = torch.mm(grad_flat.transpose(0, 1), x_flat) |
| gradients = [] |
| offset = 0 |
| for heads, hidden, features in ctx.shapes: |
| width = heads * features |
| gradient = ( |
| grad_packed[offset : offset + width] |
| .view(heads, features, hidden) |
| .permute(0, 2, 1) |
| .contiguous() |
| ) |
| gradients.append(gradient) |
| offset += width |
| return grad_x, None, *gradients |
|
|
|
|
| class ExactMultiscreenResidualAdapter(nn.Module): |
| """Compiler-fusible active calibration for a small exact budget gap.""" |
|
|
| def __init__(self, width: int, parameter_budget: int) -> None: |
| super().__init__() |
| scale_size = min(width, parameter_budget) |
| bias_size = parameter_budget - scale_size |
| self.width = width |
| self.scale = nn.Parameter(torch.zeros(scale_size)) |
| self.bias = nn.Parameter(torch.zeros(bias_size)) |
| if sum(parameter.numel() for parameter in self.parameters()) != parameter_budget: |
| raise AssertionError("Multiscreen adapter did not consume its exact budget.") |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| scale = F.pad(self.scale, (0, self.width - self.scale.numel())) |
| bias = F.pad(self.bias, (0, self.width - self.bias.numel())) |
| return x * (0.01 * torch.tanh(scale)) + bias |
|
|
|
|
| class BudgetedMultiscreenResidualAdapter(nn.Module): |
| """Useful low-rank residual that consumes an arbitrary exact budget.""" |
|
|
| def __init__(self, width: int, parameter_budget: int) -> None: |
| super().__init__() |
| rank, remainder = divmod(parameter_budget, 2 * width) |
| if rank <= 0: |
| raise ValueError("Budgeted adapter requires at least 2 * width parameters.") |
| self.width = width |
| self.up = nn.Parameter(torch.empty(width, rank)) |
| self.down = nn.Parameter(torch.zeros(rank, width)) |
| scale_size = min(width, remainder) |
| self.scale = nn.Parameter(torch.zeros(scale_size)) |
| self.bias = nn.Parameter(torch.zeros(remainder - scale_size)) |
| nn.init.orthogonal_(self.up) |
| if sum(parameter.numel() for parameter in self.parameters()) != parameter_budget: |
| raise AssertionError("Budgeted adapter did not consume its exact budget.") |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| low_rank = F.silu(x @ self.up) @ self.down |
| scale = F.pad(self.scale, (0, self.width - self.scale.numel())) |
| bias = F.pad(self.bias, (0, self.width - self.bias.numel())) |
| return low_rank + 0.01 * x * torch.tanh(scale) + bias |
|
|
|
|
| class MultiscreenBlock(nn.Module): |
| """A paper-faithful gated screening layer from arXiv:2604.01178. |
| |
| The query loop is only a memory-bounding implementation detail. It computes |
| the same dense causal screening equation as the unchunked reference. |
| """ |
|
|
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
| d = config.n_embd |
| h = config.multiscreen_num_heads |
| dk = config.multiscreen_key_dim |
| dv = config.multiscreen_value_dim |
| self.num_heads = h |
| self.num_layers = config.n_layer |
| self.key_dim = dk |
| self.value_dim = dv |
| self.window_threshold = float(config.multiscreen_window_threshold) |
| self.query_chunk_size = config.multiscreen_query_chunk_size |
| self.landmark_stride = config.multiscreen_landmark_stride |
| self.triton_inference = config.multiscreen_triton_inference |
| self.fused_projections = config.multiscreen_fused_projections |
| self.swe_context_length = config.multiscreen_swe_context_length |
| self.hard_max_window = config.multiscreen_hard_max_window |
| self.match_adapter = None |
| if config.multiscreen_match_adapter_params and block_idx == config.n_layer - 1: |
| adapter_budget = config.multiscreen_match_adapter_params |
| self.match_adapter = ( |
| ExactMultiscreenResidualAdapter(d, adapter_budget) |
| if adapter_budget <= 2 * d |
| else BudgetedMultiscreenResidualAdapter(d, adapter_budget) |
| ) |
|
|
| self.query_weight = nn.Parameter(torch.empty(h, d, dk)) |
| self.key_weight = nn.Parameter(torch.empty(h, d, dk)) |
| self.value_weight = nn.Parameter(torch.empty(h, d, dv)) |
| self.gate_weight = nn.Parameter(torch.empty(h, d, dv)) |
| self.output_weight = nn.Parameter(torch.empty(h, dv, d)) |
| self.log_window_minus_one = nn.Parameter(torch.empty(h)) |
| self.acceptance_logit = nn.Parameter(torch.zeros(h)) |
| self.log_output_scale = nn.Parameter(torch.empty(h)) |
| self.mlp_norm = ( |
| config.norm_class(d, eps=config.norm_eps) if config.multiscreen_mlp_enabled else None |
| ) |
| self.mlp = config.mlp_class(config) if config.multiscreen_mlp_enabled else None |
|
|
| self.reset_parameters() |
|
|
| @torch.no_grad() |
| def reset_parameters(self) -> None: |
| nn.init.normal_(self.query_weight, std=0.1 / math.sqrt(self.key_dim)) |
| nn.init.normal_(self.key_weight, std=0.1 / math.sqrt(self.key_dim)) |
| nn.init.normal_(self.value_weight, std=0.1 / math.sqrt(self.value_dim)) |
| nn.init.normal_(self.gate_weight, std=0.1) |
| nn.init.normal_(self.output_weight, std=0.1 / math.sqrt(self.output_weight.shape[-1])) |
| self.log_window_minus_one.copy_( |
| torch.linspace(0.0, math.log(self.window_threshold), self.num_heads) |
| ) |
| self.acceptance_logit.zero_() |
| self.log_output_scale.fill_(-0.5 * math.log(self.num_heads * self.num_layers)) |
|
|
| @staticmethod |
| def _mipe(x: torch.Tensor, angle: torch.Tensor) -> torch.Tensor: |
| first, second = x[..., 0], x[..., 1] |
| cos, sin = angle.cos(), angle.sin() |
| rotated_pair = torch.stack( |
| (first * cos - second * sin, first * sin + second * cos), |
| dim=-1, |
| ) |
| |
| |
| |
| return torch.cat((rotated_pair, x[..., 2:]), dim=-1) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| ) -> torch.Tensor: |
| del cos, sin, mask, input_pos, input_pos_maxp1 |
| _, sequence_length, _ = x.shape |
| if self.fused_projections: |
| from litgpt.multiscreen_projection_triton import direct_multiscreen_projection |
|
|
| h, dk, dv = self.num_heads, self.key_dim, self.value_dim |
| projected = direct_multiscreen_projection( |
| x, |
| self.query_weight, |
| self.key_weight, |
| self.value_weight, |
| self.gate_weight, |
| ) |
| query, key, value, gate = projected.split( |
| (h * dk, h * dk, h * dv, h * dv), dim=-1 |
| ) |
| query = query.view(*query.shape[:2], h, dk).transpose(1, 2) |
| key = key.view(*key.shape[:2], h, dk).transpose(1, 2) |
| value = value.view(*value.shape[:2], h, dv).transpose(1, 2) |
| gate = gate.view(*gate.shape[:2], h, dv).transpose(1, 2) |
| else: |
| query = torch.einsum("btd,hdk->bhtk", x, self.query_weight) |
| key = torch.einsum("btd,hdk->bhtk", x, self.key_weight) |
| value = torch.einsum("btd,hdv->bhtv", x, self.value_weight) |
| gate = torch.einsum("btd,hdv->bhtv", x, self.gate_weight) |
| query = F.normalize(query, dim=-1, eps=1e-6) |
| key = F.normalize(key, dim=-1, eps=1e-6) |
| value = F.normalize(value, dim=-1, eps=1e-6) |
|
|
| learned_window = self.log_window_minus_one.exp() + 1.0 |
| if self.hard_max_window > 0: |
| learned_window = learned_window.clamp_max(float(self.hard_max_window)) |
| use_swe = not torch.is_grad_enabled() and self.swe_context_length > 0 |
| if use_swe: |
| |
| |
| window = torch.where( |
| learned_window > self.swe_context_length, |
| torch.full_like(learned_window, -1.0), |
| learned_window, |
| ) |
| else: |
| window = learned_window |
| finite_window = window > 0.0 |
| safe_window = torch.where(finite_window, window, torch.ones_like(window)) |
| gamma = torch.where( |
| finite_window & (window < self.window_threshold), |
| 0.5 * (torch.cos(math.pi * safe_window / self.window_threshold) + 1.0), |
| torch.zeros_like(window), |
| ) |
| positions = torch.arange(sequence_length, device=x.device, dtype=torch.float32) |
| query_angle = (math.pi * gamma / safe_window)[:, None] * positions[None, :] |
| query = self._mipe(query, query_angle[None].to(query.dtype)) |
| key_positions_1d = positions[:: self.landmark_stride] |
| key = key[:, :, :: self.landmark_stride] |
| value = value[:, :, :: self.landmark_stride] |
| key_angle = ( |
| (math.pi * gamma / safe_window)[:, None] * key_positions_1d[None, :] |
| ) |
| key = self._mipe(key, key_angle[None].to(key.dtype)) |
|
|
| acceptance_width = torch.sigmoid(self.acceptance_logit).clamp_min(1e-6) |
| use_triton = ( |
| self.triton_inference |
| and query.is_cuda |
| and query.dtype in {torch.bfloat16, torch.float32} |
| and self.key_dim == 16 |
| and self.value_dim in {32, 64, 128} |
| and self.landmark_stride == 1 |
| ) |
| if use_triton: |
| from litgpt.multiscreen_triton import screening_aggregate_triton |
|
|
| aggregated = screening_aggregate_triton( |
| query.contiguous(), key.contiguous(), value.contiguous(), acceptance_width, window |
| ) |
| else: |
| chunks = [] |
| key_positions = key_positions_1d[None, None, None, :] |
| for start in range(0, sequence_length, self.query_chunk_size): |
| end = min(start + self.query_chunk_size, sequence_length) |
| similarity = torch.einsum("bhqd,bhkd->bhqk", query[:, :, start:end], key) |
| relevance = F.relu( |
| 1.0 - (1.0 - similarity.float()) / acceptance_width.float()[None, :, None, None] |
| ).square() |
| query_positions = positions[start:end][None, None, :, None] |
| distance = key_positions - query_positions |
| infinite = ~finite_window[None, :, None, None] |
| valid = (distance <= 0.0) & ( |
| infinite | (distance > -safe_window[None, :, None, None]) |
| ) |
| finite_softmask = 0.5 * ( |
| torch.cos( |
| math.pi * distance / safe_window[None, :, None, None] |
| ) |
| + 1.0 |
| ) |
| softmask = torch.where(infinite, torch.ones_like(finite_softmask), finite_softmask) |
| relevance = relevance * torch.where(valid, softmask, torch.zeros_like(softmask)) |
| chunks.append( |
| torch.einsum( |
| "bhqk,bhkv->bhqv", |
| relevance.to(value.dtype), |
| value, |
| ) |
| ) |
| aggregated = torch.cat(chunks, dim=2) |
| norm = aggregated.float().norm(dim=-1, keepdim=True) |
| aggregated = aggregated * (norm.tanh() / norm.clamp_min(1e-6)).to(aggregated.dtype) |
| gated = aggregated * torch.tanh(F.silu(gate)) |
| gated = gated * self.log_output_scale.exp()[None, :, None, None] |
| update = torch.einsum("bhtv,hvd->btd", gated, self.output_weight) |
| x = x + update |
| if self.mlp is not None: |
| x = x + self.mlp(self.mlp_norm(x)) |
| if self.match_adapter is not None: |
| x = x + self.match_adapter(x) |
| return x |
|
|
| def forward_step( |
| self, |
| x: torch.Tensor, |
| state: tuple[torch.Tensor, torch.Tensor, int] | None = None, |
| ) -> tuple[torch.Tensor, tuple[torch.Tensor, torch.Tensor, int]]: |
| """Decode one token while retaining only the largest learned finite window.""" |
| if x.ndim != 3 or x.size(1) != 1: |
| raise ValueError("MultiscreenBlock.forward_step expects shape (batch, 1, width).") |
| if self.swe_context_length > 0: |
| raise NotImplementedError( |
| "Bounded cached decode does not support screening-window expansion." |
| ) |
|
|
| if self.fused_projections: |
| from litgpt.multiscreen_projection_triton import direct_multiscreen_projection |
|
|
| h, dk, dv = self.num_heads, self.key_dim, self.value_dim |
| projected = direct_multiscreen_projection( |
| x, |
| self.query_weight, |
| self.key_weight, |
| self.value_weight, |
| self.gate_weight, |
| ) |
| query, key, value, gate = projected.split( |
| (h * dk, h * dk, h * dv, h * dv), dim=-1 |
| ) |
| query = query.view(*query.shape[:2], h, dk).transpose(1, 2) |
| key = key.view(*key.shape[:2], h, dk).transpose(1, 2) |
| value = value.view(*value.shape[:2], h, dv).transpose(1, 2) |
| gate = gate.view(*gate.shape[:2], h, dv).transpose(1, 2) |
| else: |
| query = torch.einsum("btd,hdk->bhtk", x, self.query_weight) |
| key = torch.einsum("btd,hdk->bhtk", x, self.key_weight) |
| value = torch.einsum("btd,hdv->bhtv", x, self.value_weight) |
| gate = torch.einsum("btd,hdv->bhtv", x, self.gate_weight) |
|
|
| query = F.normalize(query, dim=-1, eps=1e-6) |
| key = F.normalize(key, dim=-1, eps=1e-6) |
| value = F.normalize(value, dim=-1, eps=1e-6) |
| learned_window = self.log_window_minus_one.exp() + 1.0 |
| if self.hard_max_window > 0: |
| learned_window = learned_window.clamp_max(float(self.hard_max_window)) |
| safe_window = learned_window.clamp_min(1.0) |
| gamma = torch.where( |
| learned_window < self.window_threshold, |
| 0.5 * (torch.cos(math.pi * safe_window / self.window_threshold) + 1.0), |
| torch.zeros_like(learned_window), |
| ) |
|
|
| position = 0 if state is None else state[2] |
| angle = (math.pi * gamma / safe_window * position)[None, :, None].to(query.dtype) |
| query = self._mipe(query, angle) |
| key = self._mipe(key, angle) |
|
|
| if state is not None: |
| key = torch.cat((state[0], key), dim=2) |
| value = torch.cat((state[1], value), dim=2) |
| cache_tokens = max(1, int(torch.ceil(safe_window.max()).item())) |
| if key.size(2) > cache_tokens: |
| key = key[:, :, -cache_tokens:] |
| value = value[:, :, -cache_tokens:] |
|
|
| history = key.size(2) |
| distance = torch.arange( |
| 1 - history, |
| 1, |
| device=x.device, |
| dtype=torch.float32, |
| )[None, None, None, :] |
| similarity = torch.einsum("bhqd,bhkd->bhqk", query, key) |
| acceptance_width = torch.sigmoid(self.acceptance_logit).clamp_min(1e-6) |
| relevance = F.relu( |
| 1.0 - (1.0 - similarity.float()) / acceptance_width.float()[None, :, None, None] |
| ).square() |
| valid = distance > -safe_window[None, :, None, None] |
| softmask = 0.5 * ( |
| torch.cos(math.pi * distance / safe_window[None, :, None, None]) + 1.0 |
| ) |
| relevance = relevance * torch.where(valid, softmask, torch.zeros_like(softmask)) |
| aggregated = torch.einsum("bhqk,bhkv->bhqv", relevance, value) |
|
|
| norm = aggregated.float().norm(dim=-1, keepdim=True) |
| aggregated = aggregated * (norm.tanh() / norm.clamp_min(1e-6)).to(aggregated.dtype) |
| gated = aggregated * torch.tanh(F.silu(gate)) |
| gated = gated * self.log_output_scale.exp()[None, :, None, None] |
| update = torch.einsum("bhtv,hvd->btd", gated, self.output_weight) |
| x = x + update |
| if self.mlp is not None: |
| x = x + self.mlp(self.mlp_norm(x)) |
| if self.match_adapter is not None: |
| x = x + self.match_adapter(x) |
| return x, (key, value, position + 1) |
|
|
|
|
| class KimiDeltaAttentionAdapter(nn.Module): |
| """LitGPT adapter around Moonshot/FLA's official KDA implementation.""" |
|
|
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
| try: |
| from fla.layers import KimiDeltaAttention |
| except ImportError as error: |
| raise ImportError( |
| "KDA requires the official `fla-core`/`flash-linear-attention` package." |
| ) from error |
| self.attn = KimiDeltaAttention( |
| hidden_size=config.n_embd, |
| expand_v=1, |
| head_dim=config.kda_head_dim, |
| num_heads=config.kda_num_heads, |
| num_v_heads=config.kda_num_heads, |
| mode="chunk", |
| use_short_conv=True, |
| conv_size=config.kda_short_conv_kernel_size, |
| conv_bias=False, |
| safe_gate=config.kda_safe_gate, |
| lower_bound=config.kda_lower_bound if config.kda_safe_gate else None, |
| layer_idx=block_idx, |
| norm_eps=config.norm_eps, |
| ) |
| if config.kda_full_rank_output_gate: |
| |
| |
| |
| |
| self.attn.g_proj = nn.Linear( |
| config.n_embd, |
| self.attn.value_dim, |
| bias=False, |
| ) |
| self.kv_cache = None |
|
|
| def build_kv_cache( |
| self, |
| batch_size: int, |
| max_seq_length: int, |
| rope_cache_length: int, |
| device: torch.device | None, |
| dtype: torch.dtype | None, |
| ): |
| """Create the official FLA recurrent cache used by KDA decoding.""" |
| del batch_size, max_seq_length, rope_cache_length, device, dtype |
| from fla.models.utils import Cache |
|
|
| return Cache() |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| ) -> torch.Tensor: |
| use_cache = input_pos is not None |
| output, _, cache = self.attn( |
| hidden_states=x, |
| attention_mask=None, |
| past_key_values=self.kv_cache if use_cache else None, |
| use_cache=use_cache, |
| output_attentions=False, |
| ) |
| if use_cache: |
| self.kv_cache = cache |
| return output |
|
|
|
|
| class Block(nn.Module): |
| def __init__( |
| self, |
| config: Config, |
| block_idx: int, |
| ) -> None: |
| super().__init__() |
| if not config.parallel_residual and config.shared_attention_norm: |
| raise NotImplementedError( |
| "No checkpoint amongst the ones we support uses this configuration" |
| " (non-parallel residual and shared attention norm)." |
| ) |
|
|
| self.norm_1 = nn.Identity() if not config.norm_1 else config.norm_class(config.n_embd, eps=config.norm_eps) |
| self.attn = build_attention(config, block_idx) |
| self.post_attention_norm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) if config.post_attention_norm else nn.Identity() |
| ) |
| self.norm_2 = ( |
| nn.Identity() |
| if not config.norm_2 |
| else (None if config.shared_attention_norm else config.norm_class(config.n_embd, eps=config.norm_eps)) |
| ) |
| mlp_config = config |
| if config.mlp_taper_schedule != "none": |
| mlp_config = copy(config) |
| mlp_config.intermediate_size = config.tapered_mlp_intermediate_sizes()[block_idx] |
| self.mlp = mlp_config.mlp_class(mlp_config) |
| self.mlp_intermediate_size = mlp_config.intermediate_size |
| if config.first_k_dense_replace is not None and block_idx < config.first_k_dense_replace: |
| self.mlp = LLaMAMLP(config) |
| if hasattr(self.mlp, "set_block_index"): |
| self.mlp.set_block_index(block_idx, config.n_layer) |
| self.post_mlp_norm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) if config.post_mlp_norm else nn.Identity() |
| ) |
|
|
| self.grouped_mlp_rotation_shift = 0 |
| is_grouped_mlp = ( |
| config.mlp_class_name.startswith("TileRouted") or config.mlp_class_name == "HiddenBlockDSwiGLUMLP" |
| ) and not isinstance(self.mlp, LLaMAMLP) |
| if is_grouped_mlp and config.grouped_mlp_channel_rotation: |
| stride = config.grouped_mlp_channel_rotation_stride |
| if stride is None: |
| stride = max(1, config.n_embd // max(1, config.sparse_mlp_num_groups)) |
| self.grouped_mlp_rotation_shift = (block_idx * stride) % config.n_embd |
|
|
| self.grouped_mlp_mixer: nn.Module | None = None |
| if ( |
| is_grouped_mlp |
| and config.grouped_mlp_mix_every_n_layers > 0 |
| and (block_idx + 1) % config.grouped_mlp_mix_every_n_layers == 0 |
| ): |
| if config.grouped_mlp_mix_rank > 0: |
| self.grouped_mlp_mixer = nn.Sequential( |
| nn.Linear(config.n_embd, config.grouped_mlp_mix_rank, bias=False), |
| nn.Linear(config.grouped_mlp_mix_rank, config.n_embd, bias=False), |
| ) |
| else: |
| self.grouped_mlp_mixer = nn.Linear(config.n_embd, config.n_embd, bias=False) |
| mix_alpha = torch.tensor(float(config.grouped_mlp_mix_alpha)) |
| if config.grouped_mlp_mix_alpha_learnable: |
| self.grouped_mlp_mix_alpha = nn.Parameter(mix_alpha) |
| else: |
| self.register_buffer("grouped_mlp_mix_alpha", mix_alpha, persistent=False) |
|
|
| self.config = config |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| value_residual: torch.Tensor | None = None, |
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
| """ |
| Non-parallel residual Parallel residual |
| ┌─ x ┌─ x ──────────────────┐ Note: if `shared_attention_norm` is True, |
| │ ↓ │ ↓ ↓ the output from `norm_1` is reused |
| │ norm_1 │ norm_1 ───────► norm_2 |
| │ ↓ │ ↓ ↓ |
| │ attn │ attn MLP |
| │ ↓ │ ↓ ↓ |
| | post_attn_norm | post_attn_norm post_mlp_norm |
| | ↓ | ↓ ↓ |
| ┌─ └► + └► + ◄─────────────────┘ |
| | ↓ |
| │ norm_2 |
| │ ↓ |
| │ MLP |
| │ ↓ |
| | post_mlp_norm |
| | ↓ |
| └───► + |
| """ |
|
|
| x_normed = self.norm_1(x) |
| if self.config.value_residual_mix > 0.0: |
| attention_output, current_values = self.attn( |
| x_normed, |
| cos, |
| sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| value_residual=value_residual, |
| ) |
| else: |
| attention_output = self.attn(x_normed, cos, sin, mask, input_pos, input_pos_maxp1) |
| attention_output = self.post_attention_norm(attention_output) |
|
|
| if self.config.parallel_residual: |
| if not self.config.shared_attention_norm: |
| x_normed = self.norm_2(x) |
| x = attention_output + x |
| else: |
| x = attention_output + x |
| x_normed = self.norm_2(x) |
|
|
| mlp_input = x_normed |
| if self.grouped_mlp_rotation_shift: |
| mlp_input = torch.roll(mlp_input, shifts=self.grouped_mlp_rotation_shift, dims=-1) |
| mlp_output = self.mlp(mlp_input) |
| if self.grouped_mlp_rotation_shift: |
| mlp_output = torch.roll(mlp_output, shifts=-self.grouped_mlp_rotation_shift, dims=-1) |
| if self.grouped_mlp_mixer is not None: |
| mix_alpha = self.grouped_mlp_mix_alpha.to(device=mlp_output.device, dtype=mlp_output.dtype) |
| mlp_output = mlp_output + mix_alpha * self.grouped_mlp_mixer(mlp_output) |
|
|
| output = self.post_mlp_norm(mlp_output) + x |
| if self.config.value_residual_mix > 0.0: |
| return output, current_values |
| return output |
|
|
|
|
| class SelectiveRecallBlock(nn.Module): |
| """Fast Transformer block with a periodic causal landmark screen.""" |
|
|
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
| self.base = Block(config, block_idx) |
| self.screen_enabled = ( |
| (block_idx + 1) % config.multiscreen_layer_interval == 0 |
| ) |
| self.screen_norm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) |
| if self.screen_enabled |
| else None |
| ) |
| if self.screen_enabled: |
| screen_config = copy(config) |
| screen_config.multiscreen_mlp_enabled = False |
| self.screen = MultiscreenBlock(screen_config, block_idx) |
| else: |
| self.screen = None |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| value_residual: torch.Tensor | None = None, |
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
| base_output = self.base( |
| x, |
| cos, |
| sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| value_residual, |
| ) |
| if isinstance(base_output, tuple): |
| x, current_values = base_output |
| else: |
| x, current_values = base_output, None |
| if self.screen is not None: |
| normalized = self.screen_norm(x) |
| x = x + ( |
| self.screen( |
| normalized, |
| cos, |
| sin, |
| mask, |
| input_pos, |
| input_pos_maxp1, |
| ) |
| - normalized |
| ) |
| if current_values is not None: |
| return x, current_values |
| return x |
|
|
|
|
| class SandwichSublayer(nn.Module): |
| """One attention or FFN sublayer in the official Sandwich ordering. |
| |
| The source architecture is ``s^k (sf)^(L-k) f^k`` from |
| https://github.com/ofirpress/sandwich_transformer. This class only adapts |
| that ordering to LitGPT's pre-norm residual convention. |
| """ |
|
|
| def __init__(self, config: Config, sublayer_idx: int, kind: str) -> None: |
| super().__init__() |
| if kind not in {"s", "f"}: |
| raise ValueError(f"Unknown Sandwich sublayer kind: {kind!r}") |
| self.kind = kind |
| self.config = config |
| self.sublayer_idx = sublayer_idx |
| logical_depth = min(sublayer_idx // 2, config.n_layer - 1) |
| if kind == "s": |
| |
| |
| |
| |
| |
| |
| k = config.sandwich_coefficient |
| attention_idx = sublayer_idx if sublayer_idx < k else k + (sublayer_idx - k) // 2 |
| self.norm = config.norm_class(config.n_embd, eps=config.norm_eps) |
| self.attn = build_attention(config, attention_idx) |
| self.mlp = None |
| self.post_norm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) |
| if config.post_attention_norm |
| else nn.Identity() |
| ) |
| else: |
| self.norm = config.norm_class(config.n_embd, eps=config.norm_eps) |
| self.attn = None |
| self.mlp = config.mlp_class(config) |
| if hasattr(self.mlp, "set_block_index"): |
| self.mlp.set_block_index(logical_depth, config.n_layer) |
| self.post_norm = ( |
| config.norm_class(config.n_embd, eps=config.norm_eps) if config.post_mlp_norm else nn.Identity() |
| ) |
| adaptive_start = 2 * config.n_layer - config.sandwich_adaptive_commit |
| if kind == "f" and sublayer_idx >= adaptive_start: |
| |
| |
| self.adaptive_commit_router = nn.Parameter(torch.zeros(config.n_embd)) |
| else: |
| self.register_parameter("adaptive_commit_router", None) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| value_residual: torch.Tensor | None = None, |
| ) -> torch.Tensor: |
| x_normed = self.norm(x) |
| if self.kind == "s": |
| update = self.attn(x_normed, cos, sin, mask, input_pos, input_pos_maxp1) |
| else: |
| update = self.mlp(x_normed) |
| update = self.post_norm(update) |
| if self.adaptive_commit_router is not None: |
| router_logit = F.linear( |
| x_normed.float(), |
| self.adaptive_commit_router.float().unsqueeze(0), |
| ) |
| commit = (2.0 * torch.sigmoid(router_logit)).to(dtype=update.dtype) |
| update = commit * update |
| return x + update |
|
|
|
|
| class RMSBudgetedBlock(Block): |
| """Transformer block with a protected residual stream and bounded commits. |
| |
| Strong attention/MLP computation may happen in the branch state, but the |
| write back to the main residual stream is clamped by an update/input RMS |
| budget. The block is opt-in through ``Config.rms_budgeted_block`` so the |
| proven dense and grouped baselines keep their exact execution path. |
| """ |
|
|
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__(config, block_idx) |
| self.block_idx = block_idx |
| self.rms_budget_dual_stream = bool(config.rms_budget_dual_stream) |
| self.rms_budget_attn_mlp = bool(config.rms_budget_attn_mlp) |
| self.rms_budget_direction_aware = bool(config.rms_budget_direction_aware) |
| self.rms_budget_work_scale = float(config.rms_budget_work_scale) |
| self.rms_budget_min_scale = float(config.rms_budget_min_scale) |
| self.rms_budget_exact_target = bool(config.rms_budget_exact_target) |
| self.rms_budget_target_min = float(config.rms_budget_target_min) |
| self.rms_budget_target_max = float(config.rms_budget_target_max) |
| target = float(config.rms_budget_target_init) |
| target = min(max(target, self.rms_budget_target_min + 1e-6), self.rms_budget_target_max - 1e-6) |
| if config.rms_budget_target_learnable: |
| ratio = (target - self.rms_budget_target_min) / (self.rms_budget_target_max - self.rms_budget_target_min) |
| self.rms_budget_target_raw = nn.Parameter(torch.tensor(math.log(ratio / (1.0 - ratio)))) |
| else: |
| self.register_buffer("rms_budget_target", torch.tensor(target), persistent=False) |
| if self.rms_budget_attn_mlp: |
| self.budget_gate_weight = nn.Parameter(torch.zeros(2, config.n_embd)) |
| self.budget_gate_bias = nn.Parameter(torch.zeros(2)) |
| self.last_residual_ratio: torch.Tensor | None = None |
| self.last_residual_scale: torch.Tensor | None = None |
| self.last_attn_budget_mean: torch.Tensor | None = None |
| self.last_mlp_budget_mean: torch.Tensor | None = None |
|
|
| def _target_ratio(self, x: torch.Tensor) -> torch.Tensor: |
| if hasattr(self, "rms_budget_target_raw"): |
| raw = self.rms_budget_target_raw.to(device=x.device, dtype=torch.float32) |
| target = self.rms_budget_target_min + (self.rms_budget_target_max - self.rms_budget_target_min) * torch.sigmoid(raw) |
| return target.to(dtype=x.dtype) |
| return self.rms_budget_target.to(device=x.device, dtype=x.dtype) |
|
|
| def _direction_filter(self, x: torch.Tensor, update: torch.Tensor) -> torch.Tensor: |
| if not self.rms_budget_direction_aware: |
| return update |
| x_float = x.float() |
| update_float = update.float() |
| denom = x_float.pow(2).sum(dim=-1, keepdim=True).clamp_min(1e-8) |
| coeff = (update_float * x_float).sum(dim=-1, keepdim=True) / denom |
| anti_coeff = coeff.clamp(max=0.0).to(dtype=update.dtype) |
| return update - anti_coeff * x |
|
|
| def _combine_updates(self, x: torch.Tensor, attention_output: torch.Tensor, mlp_output: torch.Tensor) -> torch.Tensor: |
| if not self.rms_budget_attn_mlp: |
| if not torch.is_grad_enabled(): |
| one = x.new_tensor(1.0) |
| self.last_attn_budget_mean = one |
| self.last_mlp_budget_mean = one |
| return attention_output + mlp_output |
| x_rms = x.float().pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-8) |
| x_normed = (x.float() / x_rms).to(dtype=x.dtype) |
| logits = F.linear( |
| x_normed, |
| self.budget_gate_weight.to(device=x.device, dtype=x.dtype), |
| self.budget_gate_bias.to(device=x.device, dtype=x.dtype), |
| ) |
| weights = torch.softmax(logits.float(), dim=-1).to(dtype=x.dtype) * 2.0 |
| if not torch.is_grad_enabled(): |
| self.last_attn_budget_mean = weights[..., 0].detach().mean() |
| self.last_mlp_budget_mean = weights[..., 1].detach().mean() |
| return attention_output * weights[..., 0:1] + mlp_output * weights[..., 1:2] |
|
|
| def _budget_update(self, x: torch.Tensor, update: torch.Tensor) -> torch.Tensor: |
| update = self._direction_filter(x, update) |
| input_rms = x.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| update_rms = update.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| target = self._target_ratio(x).float() |
| desired = target * input_rms |
| scale = desired / update_rms.clamp_min(1e-8) |
| if not self.rms_budget_exact_target: |
| scale = scale.clamp(max=1.0) |
| if self.rms_budget_min_scale > 0.0: |
| scale = scale.clamp(min=self.rms_budget_min_scale) |
| if not torch.is_grad_enabled(): |
| ratio = update_rms / input_rms.clamp_min(1e-8) |
| self.last_residual_ratio = ratio.detach().mean() |
| self.last_residual_scale = scale.detach().mean() |
| return update * scale.to(device=update.device, dtype=update.dtype) |
|
|
| def _mlp_forward_with_hooks(self, mlp_input: torch.Tensor) -> torch.Tensor: |
| if self.grouped_mlp_rotation_shift: |
| mlp_input = torch.roll(mlp_input, shifts=self.grouped_mlp_rotation_shift, dims=-1) |
| mlp_output = self.mlp(mlp_input) |
| if self.grouped_mlp_rotation_shift: |
| mlp_output = torch.roll(mlp_output, shifts=-self.grouped_mlp_rotation_shift, dims=-1) |
| if self.grouped_mlp_mixer is not None: |
| mix_alpha = self.grouped_mlp_mix_alpha.to(device=mlp_output.device, dtype=mlp_output.dtype) |
| mlp_output = mlp_output + mix_alpha * self.grouped_mlp_mixer(mlp_output) |
| return self.post_mlp_norm(mlp_output) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| ) -> torch.Tensor: |
| x_lm = x |
| x_normed = self.norm_1(x_lm) |
| attention_output = self.attn(x_normed, cos, sin, mask, input_pos, input_pos_maxp1) |
| attention_output = self.post_attention_norm(attention_output) |
|
|
| if self.config.parallel_residual: |
| if self.rms_budget_dual_stream: |
| mlp_source = x_lm + self.rms_budget_work_scale * attention_output |
| else: |
| mlp_source = x_lm |
| mlp_input = x_normed if self.config.shared_attention_norm else self.norm_2(mlp_source) |
| else: |
| work_scale = self.rms_budget_work_scale if self.rms_budget_dual_stream else 1.0 |
| mlp_input = self.norm_2(x_lm + work_scale * attention_output) |
|
|
| mlp_output = self._mlp_forward_with_hooks(mlp_input) |
| update = self._combine_updates(x_lm, attention_output, mlp_output) |
| return x_lm + self._budget_update(x_lm, update) |
|
|
|
|
| class _CausalLinearMemory(torch.autograd.Function): |
| """Normalized causal linear memory with an explicit reverse-scan backward.""" |
|
|
| @staticmethod |
| def forward( |
| ctx, |
| query: torch.Tensor, |
| key: torch.Tensor, |
| value: torch.Tensor, |
| ) -> torch.Tensor: |
| kv_state = torch.cumsum( |
| key.unsqueeze(-1) * value.unsqueeze(-2), |
| dim=1, |
| ) |
| key_state = torch.cumsum(key, dim=1) |
| numerator = torch.einsum("btr,btrh->bth", query, kv_state) |
| denominator_raw = torch.einsum("btr,btr->bt", query, key_state) |
| denominator = denominator_raw.clamp_min(1e-6) |
| ctx.save_for_backward( |
| query, |
| key, |
| value, |
| kv_state, |
| key_state, |
| numerator, |
| denominator, |
| denominator_raw, |
| ) |
| return numerator / denominator.unsqueeze(-1) |
|
|
| @staticmethod |
| def backward(ctx, grad_output: torch.Tensor): |
| ( |
| query, |
| key, |
| value, |
| kv_state, |
| key_state, |
| numerator, |
| denominator, |
| denominator_raw, |
| ) = ctx.saved_tensors |
| grad_numerator = grad_output / denominator.unsqueeze(-1) |
| grad_denominator = -( |
| grad_output * numerator |
| ).sum(dim=-1) / denominator.square() |
| grad_denominator = grad_denominator * (denominator_raw > 1e-6) |
|
|
| grad_query = torch.einsum( |
| "btrh,bth->btr", kv_state, grad_numerator |
| ) |
| grad_query = grad_query + key_state * grad_denominator.unsqueeze(-1) |
|
|
| state_contribution = ( |
| query.unsqueeze(-1) * grad_numerator.unsqueeze(-2) |
| ) |
| grad_state = torch.flip( |
| torch.cumsum(torch.flip(state_contribution, dims=(1,)), dim=1), |
| dims=(1,), |
| ) |
| key_contribution = query * grad_denominator.unsqueeze(-1) |
| grad_key_state = torch.flip( |
| torch.cumsum(torch.flip(key_contribution, dims=(1,)), dim=1), |
| dims=(1,), |
| ) |
| grad_key = torch.einsum( |
| "btrh,bth->btr", grad_state, value |
| ) + grad_key_state |
| grad_value = torch.einsum("btrh,btr->bth", grad_state, key) |
| return grad_query, grad_key, grad_value |
|
|
|
|
| class CausalSelfAttention(nn.Module): |
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
| self.attention_output_gate = config.attention_output_gate |
| if self.attention_output_gate == "headwise": |
| self.gate_size = config.n_head |
| elif self.attention_output_gate == "elementwise": |
| self.gate_size = config.n_head * config.head_size |
| else: |
| self.gate_size = 0 |
| |
| self.qkv = nn.Linear( |
| config.n_embd, |
| (config.n_head + 2 * config.n_query_groups) * config.head_size + self.gate_size, |
| bias=config.bias or config.attn_bias, |
| ) |
| |
| self.proj = nn.Linear(config.head_size * config.n_head, config.n_embd, bias=config.bias) |
| self.dual_path_linear_enabled = ( |
| config.dual_path_linear_enabled |
| and (block_idx + 1) % config.dual_path_linear_interval == 0 |
| ) |
| self.dual_path_linear_rank = config.dual_path_linear_rank |
| self.dual_path_linear_explicit_backward = ( |
| config.dual_path_linear_explicit_backward |
| ) |
| self.dual_path_gate = ( |
| nn.Parameter( |
| torch.full( |
| (config.n_head,), |
| float(config.dual_path_linear_gate_init), |
| ) |
| ) |
| if self.dual_path_linear_enabled |
| else None |
| ) |
| |
| self.kv_cache: KVCache | MLACompressedKVCache | None = None |
| self.apply_sliding_window_attention = False |
| self.configured_sliding_window_attention = False |
| self.sliding_window_block_mask: BlockMask | None = None |
| if config.sliding_window_size is not None and config.sliding_window_indices is not None: |
| self.configured_sliding_window_attention = bool(config.sliding_window_indices[block_idx]) |
| self.apply_sliding_window_attention = self.configured_sliding_window_attention |
|
|
| if config.norm_qk: |
| norm_q_size = config.n_head * config.head_size if config.norm_qk_type == "olmo2" else config.head_size |
| norm_k_size = ( |
| config.n_query_groups * config.head_size if config.norm_qk_type == "olmo2" else config.head_size |
| ) |
| self.norm_q = config.norm_class(norm_q_size, eps=config.norm_eps) |
| self.norm_k = config.norm_class(norm_k_size, eps=config.norm_eps) |
| else: |
| self.norm_q = self.norm_k = None |
|
|
| if config.rope_adjustments is not None: |
| mscale_all_dim = config.rope_adjustments.get("mscale_all_dim", None) |
| scaling_factor = config.rope_adjustments.get("factor", None) |
| if mscale_all_dim and scaling_factor: |
| self.mscale = yarn_get_mscale(scaling_factor, mscale_all_dim) |
| else: |
| self.mscale = 1.0 |
| else: |
| self.mscale = 1.0 |
|
|
| self.config = config |
| self.block_idx = block_idx |
|
|
| def prepare_sliding_window_mask(self, sequence_length: int, device: torch.device) -> None: |
| if not self.configured_sliding_window_attention: |
| return |
| window_size = int(self.config.sliding_window_size) |
|
|
| def sliding_causal_mask( |
| batch_index: torch.Tensor, |
| head_index: torch.Tensor, |
| query_index: torch.Tensor, |
| key_index: torch.Tensor, |
| ) -> torch.Tensor: |
| del batch_index, head_index |
| distance = query_index - key_index |
| return (distance >= 0) & (distance < window_size) |
|
|
| self.sliding_window_block_mask = create_block_mask( |
| sliding_causal_mask, |
| B=None, |
| H=None, |
| Q_LEN=sequence_length, |
| KV_LEN=sequence_length, |
| device=device, |
| ) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| value_residual: torch.Tensor | None = None, |
| ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| head_size = self.config.head_size |
| n_head = self.config.n_head |
| n_query_groups = self.config.n_query_groups |
| rope_n_elem = self.config.rope_n_elem |
| B, T, C = x.size() |
|
|
| |
| |
| qkv = self.qkv(x) |
|
|
| |
| |
| query_size = n_head * head_size |
| query_projection_size = query_size + self.gate_size |
| key_size = value_size = n_query_groups * head_size |
| |
| q_projection, k, v = qkv.split((query_projection_size, key_size, value_size), dim=-1) |
| gate_score: torch.Tensor | None = None |
| if self.attention_output_gate != "none": |
| queries_per_group = n_head // n_query_groups |
| q_projection = q_projection.view(B, T, n_query_groups, -1) |
| gate_features = ( |
| queries_per_group |
| if self.attention_output_gate == "headwise" |
| else queries_per_group * head_size |
| ) |
| q, gate_score = q_projection.split((queries_per_group * head_size, gate_features), dim=-1) |
| q = q.reshape(B, T, n_head, head_size) |
| if self.attention_output_gate == "headwise": |
| gate_score = gate_score.reshape(B, T, n_head, 1) |
| else: |
| gate_score = gate_score.reshape(B, T, n_head, head_size) |
| else: |
| q = q_projection |
|
|
| current_values = v |
| if value_residual is not None: |
| mix = self.config.value_residual_mix |
| v = mix * value_residual + (1.0 - mix) * v |
|
|
| if self.config.norm_qk and self.config.norm_qk_type == "olmo2": |
| q = self.norm_q(q) |
| k = self.norm_k(k) |
|
|
| |
| |
|
|
| |
| |
| if self.attention_output_gate == "none": |
| q = q.view(B, T, n_head, head_size) |
| k = k.view(B, T, n_query_groups, head_size) |
| v = v.view(B, T, n_query_groups, head_size) |
|
|
| |
| |
| |
| q = q.transpose(1, 2) |
| k = k.transpose(1, 2) |
| v = v.transpose(1, 2) |
| v_current = v |
|
|
| if self.config.norm_qk and self.config.norm_qk_type == "default": |
| q = self.norm_q(q) |
| k = self.norm_k(k) |
|
|
| |
| use_rope = ( |
| rope_n_elem > 0 |
| and ( |
| self.config.no_rope_layer_interval == 0 |
| or (self.block_idx + 1) % self.config.no_rope_layer_interval != 0 |
| ) |
| ) |
| if use_rope: |
| if self.config.rope_interleave: |
| q_roped = apply_rope_interleave(q[..., :rope_n_elem], cos, sin) |
| k_roped = apply_rope_interleave(k[..., :rope_n_elem], cos, sin) |
| else: |
| q_roped = apply_rope(q[..., :rope_n_elem], cos, sin) |
| k_roped = apply_rope(k[..., :rope_n_elem], cos, sin) |
| q = torch.cat((q_roped, q[..., rope_n_elem:]), dim=-1) |
| k = torch.cat((k_roped, k[..., rope_n_elem:]), dim=-1) |
|
|
| |
| if input_pos is not None: |
| if not isinstance(self.kv_cache, KVCache): |
| raise TypeError("You need to call `gpt.set_kv_cache()`") |
| k, v = self.kv_cache(input_pos, k, v) |
|
|
| if input_pos_maxp1 is not None: |
| |
| k = k[..., :input_pos_maxp1, :] |
| v = v[..., :input_pos_maxp1, :] |
| if self.apply_sliding_window_attention: |
| |
| |
| |
| |
| |
| key_position = torch.arange(k.size(2), device=input_pos.device) |
| window_size = int(self.config.sliding_window_size) |
| if input_pos.dim() == 1: |
| distance = input_pos[:, None] - key_position[None, :] |
| mask = ((distance >= 0) & (distance < window_size))[None, None, :, :] |
| else: |
| distance = input_pos[:, :, None] - key_position[None, None, :] |
| mask = ((distance >= 0) & (distance < window_size))[:, None, :, :] |
| |
| |
|
|
| use_flex_window = ( |
| self.apply_sliding_window_attention |
| and input_pos is None |
| and self.sliding_window_block_mask is not None |
| and self.sliding_window_block_mask.shape[-1] == T |
| and self.config.attention_logit_softcapping is None |
| ) |
|
|
| |
| |
| |
| if n_query_groups != n_head and (input_pos is None or n_query_groups != 1) and not use_flex_window: |
| q_per_kv = n_head // n_query_groups |
| k = k.repeat_interleave(q_per_kv, dim=1) |
| v = v.repeat_interleave(q_per_kv, dim=1) |
|
|
| if self.apply_sliding_window_attention and not use_flex_window: |
| """ |
| Global Window Sliding window Sliding window |
| attention mask + bias = attention mask |
| ┌────────────────────────┐ ┌───────────────────────┐ ┌─────────────────────────┐ |
| │ True False False False │ │ True True True True │ │ True False False False │ |
| │ True True False False │ │ True True True True │ │ True True False False │ |
| │ True True True False │ │ False True True True │ │ False True True False │ |
| │ True True True True │ │ False False True True │ │ False False True True │ |
| └────────────────────────┘ └───────────────────────┘ └─────────────────────────┘ |
| """ |
| if input_pos is None: |
| if mask is None: |
| mask = torch.ones(T, T, dtype=q.dtype, device=q.device).triu(diagonal=1) |
| mask.masked_fill_(mask.bool(), float("-inf")) |
| mask = mask.view(1, 1, *mask.shape) |
|
|
| sliding_window_mask = torch.full((T, T), float("-inf"), dtype=q.dtype, device=q.device) |
| for i in range(T): |
| window_start = max(0, i - self.config.sliding_window_size + 1) |
| sliding_window_mask[i, window_start : i + 1] = 0.0 |
| sliding_window_mask = sliding_window_mask.view(1, 1, T, T) |
| mask = sliding_window_mask |
|
|
| |
| |
| |
| if use_flex_window: |
| scale = 1.0 / math.sqrt(self.config.attention_scores_scalar or self.config.head_size) |
| scale = scale * self.mscale * self.mscale |
| y = flex_attention( |
| q, |
| k, |
| v, |
| block_mask=self.sliding_window_block_mask, |
| scale=scale, |
| enable_gqa=n_query_groups != n_head, |
| ).transpose(1, 2) |
| else: |
| y = self.scaled_dot_product_attention(q, k, v, mask) |
| if self.dual_path_linear_enabled: |
| |
| |
| |
| rank = self.dual_path_linear_rank |
| q_feature = F.elu(q[..., :rank].mean(dim=1).float()) + 1.0 |
| k_feature = F.elu(k[:, 0, :, :rank].float()) + 1.0 |
| value_feature = v_current[:, 0].float() |
| if self.dual_path_linear_explicit_backward: |
| global_value = _CausalLinearMemory.apply( |
| q_feature, |
| k_feature, |
| value_feature, |
| ) |
| else: |
| kv_state = torch.cumsum( |
| k_feature.unsqueeze(-1) * value_feature.unsqueeze(-2), |
| dim=1, |
| ) |
| key_state = torch.cumsum(k_feature, dim=1) |
| numerator = torch.einsum( |
| "btr,btrh->bth", q_feature, kv_state |
| ) |
| denominator = torch.einsum( |
| "btr,btr->bt", q_feature, key_state |
| ) |
| global_value = numerator / denominator.clamp_min( |
| 1e-6 |
| ).unsqueeze(-1) |
| global_value = global_value.to(dtype=y.dtype) |
| gate = torch.tanh(self.dual_path_gate).to(dtype=y.dtype) |
| y = y + global_value[:, :, None, :] * gate[None, None, :, None] |
| if gate_score is not None: |
| y = y * torch.sigmoid(gate_score) |
| if self.config.xsa_projection: |
| if v.size(2) == T: |
| v_projection_base = v.transpose(1, 2) |
| else: |
| q_per_kv = n_head // n_query_groups |
| v_projection_base = v_current.repeat_interleave(q_per_kv, dim=1).transpose(1, 2) |
| v_projection_base = F.normalize(v_projection_base, dim=-1) |
| y = y - (y * v_projection_base).sum(dim=-1, keepdim=True) * v_projection_base |
|
|
| |
| y = y.reshape(B, T, head_size * n_head) |
|
|
| |
| output = self.proj(y) |
| if self.config.value_residual_mix > 0.0: |
| return output, current_values |
| return output |
|
|
| def scaled_dot_product_attention( |
| self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, mask: torch.Tensor | None = None |
| ) -> torch.Tensor: |
| scale = 1.0 / math.sqrt(self.config.attention_scores_scalar or self.config.head_size) |
| scale = scale * self.mscale * self.mscale |
|
|
| |
| if self.config.attention_logit_softcapping is not None: |
| scores = q @ k.mT * scale |
| scores = do_softcapping(scores, self.config.attention_logit_softcapping) |
| if mask is None: |
| mask = torch.ones(q.size(2), q.size(2), dtype=q.dtype, device=q.device).triu(diagonal=1) |
| mask.masked_fill_(mask.bool(), torch.finfo(q.dtype).min) |
| scores = scores + mask |
| scores = F.softmax(scores, dim=-1, dtype=torch.float).to(dtype=q.dtype) |
| y = scores @ v |
| else: |
| y = F.scaled_dot_product_attention( |
| q, k, v, attn_mask=mask, dropout_p=0.0, scale=scale, is_causal=mask is None |
| ) |
| return y.transpose(1, 2) |
|
|
| def build_kv_cache( |
| self, |
| batch_size: int, |
| max_seq_length: int, |
| rope_cache_length: int | None = None, |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| ) -> "KVCache": |
| |
| |
| |
| effective_cache_size = max_seq_length |
|
|
| v_shape = (batch_size, self.config.n_query_groups, effective_cache_size, self.config.head_size) |
|
|
| if rope_cache_length is None: |
| if self.config.rotary_percentage != 1.0: |
| raise TypeError( |
| "Please pass the `rope_cache_length` parameter. " |
| "Use `rope_cache_length=model.rope_cache_length()` to extract it automatically." |
| ) |
| k_shape = v_shape |
| else: |
| k_shape = ( |
| batch_size, |
| self.config.n_query_groups, |
| effective_cache_size, |
| rope_cache_length + self.config.head_size - self.config.rope_n_elem, |
| ) |
|
|
| return KVCache( |
| k_shape, |
| v_shape, |
| device=device, |
| dtype=dtype, |
| is_sliding_window=self.apply_sliding_window_attention, |
| sliding_window_size=self.config.sliding_window_size if self.apply_sliding_window_attention else None, |
| ) |
|
|
| def _load_from_state_dict(self, state_dict: dict, prefix: str, *args: Any, **kwargs: Any) -> None: |
| """For compatibility with legacy checkpoints.""" |
|
|
| for attr in ("weight", "bias"): |
| legacy_key = f"{prefix}attn.{attr}" |
| current_key = f"{prefix}qkv.{attr}" |
| if legacy_key in state_dict: |
| state_dict[current_key] = qkv_reassemble(state_dict.pop(legacy_key), self.config) |
|
|
| super()._load_from_state_dict(state_dict, prefix, *args, **kwargs) |
|
|
|
|
| class SharedDiffCausalSelfAttention(nn.Module): |
| """Shared DIFF attention from arXiv:2501.17900. |
| |
| The differential attention and lambda parameterization follow Microsoft's |
| reference DIFF implementation. Q and K use the Shared DIFF paper's shared |
| base projection plus two trainable low-rank updates. |
| """ |
|
|
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
| self.config = config |
| self.block_idx = block_idx |
| self.num_heads = config.n_head // 2 |
| |
| |
| self.num_kv_heads = config.n_query_groups // 2 |
| self.head_dim = config.head_size |
| self.q_size = self.num_heads * self.head_dim |
| self.k_size = self.num_kv_heads * self.head_dim |
| self.v_size = self.num_kv_heads * 2 * self.head_dim |
| rank = config.shared_diff_rank |
|
|
| self.q_base = nn.Linear(config.n_embd, self.q_size, bias=config.attn_bias) |
| self.k_base = nn.Linear(config.n_embd, self.k_size, bias=config.attn_bias) |
| self.v_proj = nn.Linear(config.n_embd, self.v_size, bias=config.attn_bias) |
| self.proj = nn.Linear(config.n_embd, config.n_embd, bias=config.bias) |
|
|
| self.q_lora_a = nn.Parameter(torch.empty(2, self.num_heads, config.n_embd, rank)) |
| self.q_lora_b = nn.Parameter(torch.empty(2, self.num_heads, rank, self.head_dim)) |
| self.k_lora_a = nn.Parameter(torch.empty(2, self.num_kv_heads, config.n_embd, rank)) |
| self.k_lora_b = nn.Parameter(torch.empty(2, self.num_kv_heads, rank, self.head_dim)) |
| for parameter in (self.q_lora_a, self.q_lora_b, self.k_lora_a, self.k_lora_b): |
| nn.init.normal_(parameter, mean=0.0, std=0.02) |
|
|
| self.lambda_init = 0.8 - 0.6 * math.exp(-0.3 * block_idx) |
| self.lambda_q1 = nn.Parameter(torch.empty(self.head_dim).normal_(mean=0.0, std=0.1)) |
| self.lambda_k1 = nn.Parameter(torch.empty(self.head_dim).normal_(mean=0.0, std=0.1)) |
| self.lambda_q2 = nn.Parameter(torch.empty(self.head_dim).normal_(mean=0.0, std=0.1)) |
| self.lambda_k2 = nn.Parameter(torch.empty(self.head_dim).normal_(mean=0.0, std=0.1)) |
| self.subln = RMSNorm(2 * self.head_dim, eps=config.norm_eps) |
| self.kv_cache: KVCache | None = None |
|
|
| if config.rope_adjustments is not None: |
| mscale_all_dim = config.rope_adjustments.get("mscale_all_dim") |
| scaling_factor = config.rope_adjustments.get("factor") |
| self.mscale = yarn_get_mscale(scaling_factor, mscale_all_dim) if mscale_all_dim and scaling_factor else 1.0 |
| else: |
| self.mscale = 1.0 |
|
|
| @staticmethod |
| def _low_rank_pair(x: torch.Tensor, a: torch.Tensor, b: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: |
| hidden = torch.einsum("btc,shcr->btshr", x, a) |
| updates = torch.einsum("btshr,shrd->btshd", hidden, b) |
| return updates[:, :, 0], updates[:, :, 1] |
|
|
| def _apply_rope(self, tensor: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor: |
| rope_n_elem = self.config.rope_n_elem |
| if self.config.rope_interleave: |
| roped = apply_rope_interleave(tensor[..., :rope_n_elem], cos, sin) |
| else: |
| roped = apply_rope(tensor[..., :rope_n_elem], cos, sin) |
| return torch.cat((roped, tensor[..., rope_n_elem:]), dim=-1) |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| value_residual: torch.Tensor | None = None, |
| ) -> torch.Tensor: |
| if value_residual is not None: |
| raise ValueError("Shared DIFF does not support value residuals.") |
|
|
| batch_size, sequence_length, _ = x.shape |
| q_delta1, q_delta2 = self._low_rank_pair(x, self.q_lora_a, self.q_lora_b) |
| k_delta1, k_delta2 = self._low_rank_pair(x, self.k_lora_a, self.k_lora_b) |
| q_base = self.q_base(x) |
| k_base = self.k_base(x) |
| q_base = q_base.view(batch_size, sequence_length, self.num_heads, self.head_dim) |
| k_base = k_base.view(batch_size, sequence_length, self.num_kv_heads, self.head_dim) |
| q1 = q_base + q_delta1 |
| q2 = q_base + q_delta2 |
| k1 = k_base + k_delta1 |
| k2 = k_base + k_delta2 |
| v = self.v_proj(x).view(batch_size, sequence_length, self.num_kv_heads, 2 * self.head_dim) |
|
|
| q1 = self._apply_rope(q1.transpose(1, 2), cos, sin) |
| q2 = self._apply_rope(q2.transpose(1, 2), cos, sin) |
| k1 = self._apply_rope(k1.transpose(1, 2), cos, sin) |
| k2 = self._apply_rope(k2.transpose(1, 2), cos, sin) |
| v = v.transpose(1, 2) |
|
|
| if input_pos is not None: |
| if not isinstance(self.kv_cache, KVCache): |
| raise TypeError("You need to call `gpt.set_kv_cache()`") |
| cached_k, v = self.kv_cache(input_pos, torch.cat((k1, k2), dim=1), v) |
| k1, k2 = cached_k.split(self.num_kv_heads, dim=1) |
| if input_pos_maxp1 is not None: |
| k1 = k1[..., :input_pos_maxp1, :] |
| k2 = k2[..., :input_pos_maxp1, :] |
| v = v[..., :input_pos_maxp1, :] |
|
|
| if self.num_kv_heads != self.num_heads: |
| repeats = self.num_heads // self.num_kv_heads |
| k1 = k1.repeat_interleave(repeats, dim=1) |
| k2 = k2.repeat_interleave(repeats, dim=1) |
| v = v.repeat_interleave(repeats, dim=1) |
|
|
| scale = self.mscale * self.mscale / math.sqrt(self.head_dim) |
| is_causal = mask is None |
| attn1 = F.scaled_dot_product_attention(q1, k1, v, attn_mask=mask, dropout_p=0.0, scale=scale, is_causal=is_causal) |
| attn2 = F.scaled_dot_product_attention(q2, k2, v, attn_mask=mask, dropout_p=0.0, scale=scale, is_causal=is_causal) |
| lambda1 = torch.exp(torch.sum(self.lambda_q1 * self.lambda_k1).float()).to(dtype=x.dtype) |
| lambda2 = torch.exp(torch.sum(self.lambda_q2 * self.lambda_k2).float()).to(dtype=x.dtype) |
| lambda_full = lambda1 - lambda2 + self.lambda_init |
| output = self.subln(attn1 - lambda_full * attn2) * (1.0 - self.lambda_init) |
| output = output.transpose(1, 2).reshape(batch_size, sequence_length, self.config.n_embd) |
| return self.proj(output) |
|
|
| def build_kv_cache( |
| self, |
| batch_size: int, |
| max_seq_length: int, |
| rope_cache_length: int | None = None, |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| ) -> "KVCache": |
| if rope_cache_length is None: |
| if self.config.rotary_percentage != 1.0: |
| raise TypeError("Please pass rope_cache_length when rotary_percentage != 1.0") |
| key_dim = self.head_dim |
| else: |
| key_dim = rope_cache_length + self.head_dim - self.config.rope_n_elem |
| k_shape = (batch_size, 2 * self.num_kv_heads, max_seq_length, key_dim) |
| v_shape = (batch_size, self.num_kv_heads, max_seq_length, 2 * self.head_dim) |
| return KVCache(k_shape, v_shape, device=device, dtype=dtype) |
|
|
|
|
| class MultiheadLatentAttention(nn.Module): |
| def __init__(self, config: Config, block_idx: int) -> None: |
| super().__init__() |
|
|
| self.q_a_proj = nn.Linear(config.n_embd, config.q_lora_rank, bias=config.attn_bias) |
| self.q_a_norm = RMSNorm(config.q_lora_rank, eps=config.norm_eps) |
| self.q_b_proj = nn.Linear(config.q_lora_rank, config.n_head * config.qk_head_dim, bias=config.bias) |
|
|
| self.kv_a_proj_with_mqa = nn.Linear( |
| config.n_embd, config.kv_lora_rank + config.qk_rope_head_dim, bias=config.attn_bias |
| ) |
| self.kv_a_norm = RMSNorm(config.kv_lora_rank, eps=config.norm_eps) |
| self.kv_b_proj = nn.Linear( |
| config.kv_lora_rank, |
| config.n_query_groups * (config.qk_nope_head_dim + config.v_head_dim), |
| bias=config.bias, |
| ) |
|
|
| |
| self.proj = nn.Linear(config.n_head * config.v_head_dim, config.n_embd, bias=config.bias) |
| self.output_gate = ( |
| nn.Linear( |
| config.n_embd, |
| config.n_head * config.v_head_dim, |
| bias=False, |
| ) |
| if config.mla_use_output_gate |
| else None |
| ) |
| |
| self.kv_cache: KVCache | None = None |
|
|
| if config.rope_adjustments is not None: |
| mscale_all_dim = config.rope_adjustments.get("mscale_all_dim", None) |
| scaling_factor = config.rope_adjustments.get("factor", None) |
| if mscale_all_dim and scaling_factor: |
| self.mscale = yarn_get_mscale(scaling_factor, mscale_all_dim) |
| else: |
| self.mscale = 1.0 |
| else: |
| self.mscale = 1.0 |
|
|
| self.config = config |
| self.block_idx = block_idx |
|
|
| def forward( |
| self, |
| x: torch.Tensor, |
| cos: torch.Tensor, |
| sin: torch.Tensor, |
| mask: torch.Tensor | None = None, |
| input_pos: torch.Tensor | None = None, |
| input_pos_maxp1: int | None = None, |
| ) -> torch.Tensor: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| B, T, C = x.size() |
|
|
| q = self.q_b_proj(self.q_a_norm(self.q_a_proj(x))) |
| q = q.view(B, T, -1, self.config.qk_head_dim) |
| q = q.transpose(1, 2) |
| q_pass, q_rot = torch.split(q, [self.config.qk_nope_head_dim, self.config.qk_rope_head_dim], dim=-1) |
|
|
| compressed_kv = self.kv_a_proj_with_mqa(x) |
| compressed_latent, k_rot = torch.split( |
| compressed_kv, [self.config.kv_lora_rank, self.config.qk_rope_head_dim], dim=-1 |
| ) |
| compressed_latent = self.kv_a_norm(compressed_latent) |
|
|
| k_rot = k_rot.view(B, 1, T, self.config.qk_rope_head_dim) |
| if self.config.mla_use_nope: |
| q_roped = q_rot |
| k_roped = k_rot |
| elif self.config.rope_interleave: |
| q_roped = apply_rope_interleave(q_rot, cos, sin) |
| k_roped = apply_rope_interleave(k_rot, cos, sin) |
| else: |
| q_roped = apply_rope(q_rot, cos, sin) |
| k_roped = apply_rope(k_rot, cos, sin) |
|
|
| if input_pos is not None and isinstance(self.kv_cache, MLACompressedKVCache): |
| |
| |
| |
| return self.compressed_cache_attention( |
| q_pass, |
| q_roped, |
| compressed_latent, |
| k_roped, |
| input_pos, |
| input_pos_maxp1, |
| mask, |
| x, |
| ) |
|
|
| k_pass = self.kv_b_proj(compressed_latent) |
| k_pass = k_pass.view(B, T, self.config.n_query_groups, -1) |
| k_pass = k_pass.transpose(1, 2) |
|
|
| k_pass, v = torch.split(k_pass, [self.config.qk_nope_head_dim, self.config.v_head_dim], dim=-1) |
| k_roped = k_roped.expand(*k_pass.shape[:-1], -1) |
|
|
| q = torch.cat((q_pass, q_roped), dim=-1) |
| k = torch.cat((k_pass, k_roped), dim=-1) |
|
|
| |
| if input_pos is not None and isinstance(self.kv_cache, KVCache): |
| if not isinstance(self.kv_cache, KVCache): |
| raise TypeError("You need to call `gpt.set_kv_cache()`") |
| k, v = self.kv_cache(input_pos, k, v) |
| if input_pos_maxp1 is not None: |
| |
| k = k[..., :input_pos_maxp1, :] |
| v = v[..., :input_pos_maxp1, :] |
| |
| |
|
|
| |
| |
| |
| if self.config.n_query_groups != self.config.n_head and ( |
| input_pos is None or self.config.n_query_groups != 1 or T > 1 |
| ): |
| q_per_kv = self.config.n_head // self.config.n_query_groups |
| k = k.repeat_interleave(q_per_kv, dim=1) |
| v = v.repeat_interleave(q_per_kv, dim=1) |
|
|
| |
| |
| |
| y = self.scaled_dot_product_attention(q, k, v, mask) |
|
|
| |
| y = y.reshape(B, T, self.config.n_head * self.config.v_head_dim) |
| if self.output_gate is not None: |
| y = torch.sigmoid(self.output_gate(x)) * y |
|
|
| |
| return self.proj(y) |
|
|
| def compressed_cache_attention( |
| self, |
| q_pass: torch.Tensor, |
| q_roped: torch.Tensor, |
| compressed_latent: torch.Tensor, |
| k_roped: torch.Tensor, |
| input_pos: torch.Tensor, |
| input_pos_maxp1: int | None, |
| mask: torch.Tensor | None, |
| gate_input: torch.Tensor, |
| ) -> torch.Tensor: |
| """Decode from compressed MLA state without materializing per-head K/V.""" |
| if self.kv_b_proj.bias is not None or self.proj.bias is not None: |
| raise NotImplementedError("Compressed MLA cache currently requires bias=False.") |
| latent_cache, rope_cache = self.kv_cache(input_pos, compressed_latent, k_roped) |
| if input_pos_maxp1 is not None: |
| latent_cache = latent_cache[:, :input_pos_maxp1, :] |
| rope_cache = rope_cache[..., :input_pos_maxp1, :] |
| if mask is not None: |
| mask = mask[..., :input_pos_maxp1] |
|
|
| h = self.config.n_head |
| g = self.config.n_query_groups |
| q_per_group = h // g |
| r = self.config.kv_lora_rank |
| k_dim = self.config.qk_nope_head_dim |
| v_dim = self.config.v_head_dim |
| kv_weight = self.kv_b_proj.weight.view(g, k_dim + v_dim, r) |
| k_up = kv_weight[:, :k_dim, :].repeat_interleave(q_per_group, dim=0) |
| v_up = kv_weight[:, k_dim:, :].repeat_interleave(q_per_group, dim=0) |
| latent_q = torch.einsum("bhtd,hdr->bhtr", q_pass, k_up) |
| scores = torch.einsum("bhtr,bsr->bhts", latent_q, latent_cache) |
| scores = scores + torch.einsum("bhtd,bnsd->bhts", q_roped, rope_cache) |
| scale = self.mscale * self.mscale / math.sqrt( |
| self.config.attention_scores_scalar or self.config.qk_head_dim |
| ) |
| scores = scores * scale |
| if mask is not None: |
| scores = scores.masked_fill(~mask, torch.finfo(scores.dtype).min) if mask.dtype == torch.bool else scores + mask |
| weights = F.softmax(scores, dim=-1, dtype=torch.float).to(dtype=scores.dtype) |
| latent_context = torch.einsum("bhts,bsr->bhtr", weights, latent_cache) |
| if self.output_gate is not None: |
| context = torch.einsum("bhtr,hvr->bhtv", latent_context, v_up) |
| context = context.transpose(1, 2).reshape( |
| gate_input.size(0), |
| gate_input.size(1), |
| h * v_dim, |
| ) |
| context = torch.sigmoid(self.output_gate(gate_input)) * context |
| return self.proj(context) |
| out_weight = self.proj.weight.view(self.config.n_embd, h, v_dim) |
| absorbed_v_o = torch.einsum("ohv,hvr->hor", out_weight, v_up) |
| return torch.einsum("bhtr,hor->bto", latent_context, absorbed_v_o) |
|
|
| def scaled_dot_product_attention( |
| self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, mask: torch.Tensor | None = None |
| ) -> torch.Tensor: |
| scale = 1.0 / math.sqrt(self.config.attention_scores_scalar or self.config.qk_head_dim) |
| scale = scale * self.mscale * self.mscale |
|
|
| |
| if self.config.attention_logit_softcapping is not None: |
| scores = q @ k.mT * scale |
| scores = do_softcapping(scores, self.config.attention_logit_softcapping) |
| if mask is None: |
| mask = torch.ones(q.size(2), q.size(2), dtype=q.dtype, device=q.device).triu(diagonal=1) |
| mask.masked_fill_(mask.bool(), torch.finfo(q.dtype).min) |
| scores = scores + mask |
| scores = F.softmax(scores, dim=-1, dtype=torch.float).to(dtype=q.dtype) |
| y = scores @ v |
| else: |
| y = F.scaled_dot_product_attention( |
| q, k, v, attn_mask=mask, dropout_p=0.0, scale=scale, is_causal=mask is None |
| ) |
| return y.transpose(1, 2) |
|
|
| def build_kv_cache( |
| self, |
| batch_size: int, |
| max_seq_length: int, |
| rope_cache_length: int | None = None, |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| ) -> "MLACompressedKVCache": |
| latent_shape = (batch_size, max_seq_length, self.config.kv_lora_rank) |
| rope_shape = (batch_size, 1, max_seq_length, self.config.qk_rope_head_dim) |
|
|
| if rope_cache_length is not None: |
| print("Warning: `rope_cache_length` has no effect on MultiheadLatentAttention!") |
| if self.config.rotary_percentage != 1.0: |
| print("Warning: `rotary_percentage` has no effect on MultiheadLatentAttention!") |
|
|
| return MLACompressedKVCache(latent_shape, rope_shape, device=device, dtype=dtype) |
|
|
|
|
| class GptNeoxMLP(nn.Module): |
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.fc = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.proj = nn.Linear(self.intermediate_size, config.n_embd, bias=config.bias) |
| self.config = config |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x = self.fc(x) |
| x = F.gelu(x, approximate=self.config.gelu_approximate) |
| return self.proj(x) |
|
|
|
|
| def powlu_gate(x: torch.Tensor, m: float) -> torch.Tensor: |
| positive_mask = x > 0 |
| positive = torch.where(positive_mask, x, torch.ones_like(x)) |
| exponent = float(m) / (torch.sqrt(positive) + 1.0) |
| positive_gate = torch.pow(positive, exponent) * torch.sigmoid(x) |
| return torch.where(positive_mask, positive_gate, F.silu(x)) |
|
|
|
|
| class LLaMAMLP(nn.Module): |
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.fc_1 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.fc_2 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.proj = nn.Linear(self.intermediate_size, config.n_embd, bias=config.bias) |
| self.config = config |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x_fc_1 = self.fc_1(x) |
| x_fc_2 = self.fc_2(x) |
| x = F.silu(x_fc_1) * x_fc_2 |
| return self.proj(x) |
|
|
|
|
| class KimiSiTUGLUMLP(LLaMAMLP): |
| """Kimi K3 Eq. 12 dense SiTU-GLU. |
| |
| Both multiplicative branches are smoothly capped in FP32 using the |
| constants released by Moonshot (beta_gate=4, beta_up=25). |
| """ |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| gate = self.fc_1(x).float() |
| up = self.fc_2(x).float() |
| beta_gate = self.config.kimi_situ_beta |
| beta_up = self.config.kimi_situ_linear_beta |
| gate = beta_gate * torch.tanh(gate / beta_gate) * torch.sigmoid(gate) |
| up = beta_up * torch.tanh(up / beta_up) |
| return self.proj((gate * up).to(x.dtype)) |
|
|
|
|
| class _KimiSiTUExpert(nn.Module): |
| def __init__(self, input_size: int, intermediate_size: int, config: Config) -> None: |
| super().__init__() |
| self.gate = nn.Linear(input_size, intermediate_size, bias=False) |
| self.up = nn.Linear(input_size, intermediate_size, bias=False) |
| self.down = nn.Linear(intermediate_size, input_size, bias=False) |
| self.beta_gate = config.kimi_situ_beta |
| self.beta_up = config.kimi_situ_linear_beta |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| gate = self.gate(x).float() |
| up = self.up(x).float() |
| gate = self.beta_gate * torch.tanh(gate / self.beta_gate) * torch.sigmoid(gate) |
| up = self.beta_up * torch.tanh(up / self.beta_up) |
| return self.down((gate * up).to(x.dtype)) |
|
|
|
|
| class KimiStableLatentMoEMLP(nn.Module): |
| """Training-capable Kimi K3 Stable LatentMoE (Eqs. 11-14). |
| |
| The routed experts operate at latent width, their weighted sum is |
| RMS-normalized before the full-width up projection, two full-width shared |
| SiTU experts are always active, and the next batch's dispatch bias is |
| updated with exact single-device Quantile Balancing. |
| """ |
|
|
| def __init__(self, config: Config) -> None: |
| super().__init__() |
| d = config.n_embd |
| latent = config.kimi_latent_moe_latent_size |
| n_experts = config.kimi_latent_moe_num_experts |
| routed_hidden = config.kimi_latent_moe_expert_intermediate_size |
| shared_hidden = ( |
| config.kimi_latent_moe_shared_intermediate_size |
| * config.kimi_latent_moe_num_shared_experts |
| ) |
| self.config = config |
| self.router = nn.Linear(d, n_experts, bias=False) |
| self.routed_down = nn.Linear(d, latent, bias=False) |
| self.routed_up = nn.Linear(latent, d, bias=False) |
| self.routed_norm = config.norm_class(latent, eps=config.norm_eps) |
| |
| |
| |
| |
| self.routed_gate_weight = nn.Parameter( |
| torch.empty(n_experts, routed_hidden, latent) |
| ) |
| self.routed_up_weight = nn.Parameter( |
| torch.empty(n_experts, routed_hidden, latent) |
| ) |
| self.routed_down_weight = nn.Parameter( |
| torch.empty(n_experts, latent, routed_hidden) |
| ) |
| for parameter in ( |
| self.routed_gate_weight, |
| self.routed_up_weight, |
| self.routed_down_weight, |
| ): |
| nn.init.normal_(parameter, mean=0.0, std=0.02) |
| |
| |
| |
| self.shared_experts = _KimiSiTUExpert(d, shared_hidden, config) |
| self.register_buffer("quantile_bias", torch.zeros(n_experts)) |
|
|
| @torch.no_grad() |
| def _next_quantile_bias( |
| self, |
| scores: torch.Tensor, |
| biased_scores: torch.Tensor, |
| ) -> torch.Tensor: |
| k = self.config.kimi_latent_moe_top_k |
| experts = self.config.kimi_latent_moe_num_experts |
| bins = self.config.kimi_latent_moe_qb_bins |
| cutoff = biased_scores.topk(k + 1, dim=-1, sorted=True).values[:, k] |
| |
| |
| required_bias = cutoff[:, None] - scores |
| lower = self.quantile_bias.min().float() - 1.0 |
| upper = self.quantile_bias.max().float() + 1.0 |
| bin_width = (upper - lower) / bins |
| bin_index = torch.floor( |
| (required_bias - lower) / bin_width |
| ).to(torch.long).clamp_(0, bins - 1) |
| expert_offset = ( |
| torch.arange(experts, device=scores.device, dtype=torch.long) * bins |
| ) |
| flat_index = (bin_index + expert_offset).reshape(-1) |
| histogram = torch.zeros( |
| experts * bins, |
| device=scores.device, |
| dtype=torch.int32, |
| ) |
| histogram.scatter_add_( |
| 0, |
| flat_index, |
| torch.ones_like(flat_index, dtype=torch.int32), |
| ) |
| histogram = histogram.view(experts, bins) |
| cumulative = histogram.cumsum(dim=1) |
| target_load = (scores.size(0) * k + experts - 1) // experts |
| selected_bin = (cumulative >= target_load).to(torch.int32).argmax(dim=1) |
| selected_count = histogram.gather(1, selected_bin[:, None]).squeeze(1) |
| prior_bin = (selected_bin - 1).clamp_min(0) |
| prior_count = cumulative.gather(1, prior_bin[:, None]).squeeze(1) |
| prior_count = torch.where( |
| selected_bin == 0, |
| torch.zeros_like(prior_count), |
| prior_count, |
| ) |
| fraction = ( |
| (target_load - prior_count).float() |
| / selected_count.clamp_min(1).float() |
| ).clamp_(0.0, 1.0) |
| next_bias = lower + (selected_bin.float() + fraction) * bin_width |
| return next_bias - next_bias.mean() |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| shape = x.shape |
| flat = x.reshape(-1, shape[-1]) |
| scores = torch.sigmoid(self.router(flat).float()) |
| biased_scores = scores + self.quantile_bias.float() |
| k = self.config.kimi_latent_moe_top_k |
| indices = biased_scores.topk(k, dim=-1, sorted=False).indices |
| weights = scores.gather(1, indices) |
| weights = weights / weights.sum(dim=-1, keepdim=True).clamp_min(1e-20) |
|
|
| latent = self.routed_down(flat) |
| token_count, route_count = indices.shape |
| route_input = latent[:, None, :].expand( |
| token_count, |
| route_count, |
| latent.size(-1), |
| ).reshape(-1, latent.size(-1)) |
| flat_indices = indices.reshape(-1) |
| gate = torch.bmm( |
| self.routed_gate_weight[flat_indices], |
| route_input.unsqueeze(-1), |
| ).squeeze(-1).float() |
| up = torch.bmm( |
| self.routed_up_weight[flat_indices], |
| route_input.unsqueeze(-1), |
| ).squeeze(-1).float() |
| gate = ( |
| self.config.kimi_situ_beta |
| * torch.tanh(gate / self.config.kimi_situ_beta) |
| * torch.sigmoid(gate) |
| ) |
| up = self.config.kimi_situ_linear_beta * torch.tanh( |
| up / self.config.kimi_situ_linear_beta |
| ) |
| expert_hidden = (gate * up).to(x.dtype) |
| expert_output = torch.bmm( |
| self.routed_down_weight[flat_indices], |
| expert_hidden.unsqueeze(-1), |
| ).squeeze(-1) |
| expert_output = expert_output * weights.reshape(-1, 1).to(x.dtype) |
| routed = torch.zeros_like(latent) |
| routed.index_add_( |
| 0, |
| torch.arange(token_count, device=x.device).repeat_interleave( |
| route_count |
| ), |
| expert_output, |
| ) |
|
|
| if self.training and self.config.kimi_latent_moe_quantile_balancing: |
| self.quantile_bias.copy_( |
| self._next_quantile_bias(scores.detach(), biased_scores.detach()) |
| ) |
| routed = self.routed_up(self.routed_norm(routed)) |
| shared = self.shared_experts(flat) |
| return (shared + routed).view(shape) |
|
|
|
|
| class LLaMAPowLUMLP(LLaMAMLP): |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x_fc_1 = self.fc_1(x) |
| x_fc_2 = self.fc_2(x) |
| x = powlu_gate(x_fc_1, self.config.powlu_m) * x_fc_2 |
| return self.proj(x) |
|
|
|
|
| class DSwiGLUMLP(nn.Module): |
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.fc_1 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.fc_2 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.fc_3 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.proj = nn.Linear(self.intermediate_size, config.n_embd, bias=config.bias) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
|
|
| def _hard_topk_gate(self, logits: torch.Tensor) -> torch.Tensor: |
| active = max(1, min(self.intermediate_size, int(round(self.intermediate_size * self.config.drelu_target_active)))) |
| threshold = torch.topk(logits.float(), k=active, dim=-1).values[..., -1:].detach() |
| centered = logits.float() - threshold |
| soft = torch.sigmoid(self.config.drelu_gate_sharpness * centered).to(dtype=logits.dtype) |
| hard = (centered >= 0).to(dtype=logits.dtype) |
| return hard + soft - soft.detach() |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| representation = F.silu(self.fc_1(x)) * self.fc_2(x) |
| gate = self._hard_topk_gate(self.fc_3(x)) |
| self.last_gate_sparsity = (gate.detach() == 0).float().mean() |
| return self.proj(representation * gate) |
|
|
|
|
| class AdaptiveDSwiGLUMLP(nn.Module): |
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.fc_1 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.fc_2 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.fc_3 = nn.Linear(config.n_embd, self.intermediate_size, bias=config.bias) |
| self.threshold = nn.Linear(config.n_embd, 1, bias=True) |
| self.proj = nn.Linear(self.intermediate_size, config.n_embd, bias=config.bias) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| init = min(max(config.drelu_threshold_init, 1e-4), 1 - 1e-4) |
| torch.nn.init.zeros_(self.threshold.weight) |
| torch.nn.init.constant_(self.threshold.bias, math.log(init / (1 - init))) |
|
|
| def _adaptive_gate(self, logits: torch.Tensor, x: torch.Tensor) -> torch.Tensor: |
| probabilities = torch.sigmoid(logits.float()) |
| threshold = torch.sigmoid(self.threshold(x).float()) |
| centered = probabilities - threshold |
| soft = torch.sigmoid(self.config.drelu_gate_sharpness * centered).to(dtype=logits.dtype) |
| hard = (centered >= 0).to(dtype=logits.dtype) |
| self.last_threshold_mean = threshold.detach().mean() |
| return hard + soft - soft.detach() |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| representation = F.silu(self.fc_1(x)) * self.fc_2(x) |
| gate = self._adaptive_gate(self.fc_3(x), x) |
| self.last_gate_sparsity = (gate.detach() == 0).float().mean() |
| return self.proj(representation * gate) |
|
|
|
|
| class BlockSparseAdaptiveDSwiGLUMLP(nn.Module): |
| """Adaptive dSwiGLU with structured conditional compute. |
| |
| The dense dSwiGLU variants compute every SwiGLU channel and then zero out |
| activations. This variant splits the intermediate MLP into groups and only |
| runs the selected groups for each token. The Python implementation is meant |
| for laptop-scale research and correctness; production speedups need a fused |
| grouped kernel. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.min_active_groups = config.sparse_mlp_min_active_groups |
| self.max_active_groups = config.sparse_mlp_max_active_groups |
| if self.intermediate_size % self.num_groups != 0: |
| raise ValueError("intermediate_size must be divisible by sparse_mlp_num_groups") |
| self.group_size = self.intermediate_size // self.num_groups |
| if config.bias: |
| raise ValueError("BlockSparseAdaptiveDSwiGLUMLP currently expects bias=False for grouped GEMM.") |
| self.up_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.group_size)) |
| self.value_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.group_size)) |
| self.down_weight = nn.Parameter(torch.empty(self.num_groups, self.group_size, config.n_embd)) |
| self.router = nn.Linear(config.n_embd, self.num_groups, bias=True) |
| self.threshold = nn.Linear(config.n_embd, 1, bias=True) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| init = min(max(config.drelu_threshold_init, 1e-4), 1 - 1e-4) |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
| self.router.reset_parameters() |
| torch.nn.init.zeros_(self.threshold.weight) |
| init = min(max(self.config.drelu_threshold_init, 1e-4), 1 - 1e-4) |
| torch.nn.init.constant_(self.threshold.bias, math.log(init / (1 - init))) |
|
|
| def _active_group_mask(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| probabilities = torch.sigmoid(self.router(x).float()) |
| threshold = torch.sigmoid(self.threshold(x).float()) |
| _, top_indices = torch.topk(probabilities, k=self.max_active_groups, dim=-1) |
| top_mask = torch.zeros_like(probabilities, dtype=torch.bool) |
| top_mask.scatter_(-1, top_indices, True) |
| threshold_mask = probabilities >= threshold |
| active_mask = threshold_mask & top_mask |
|
|
| active_count = active_mask.sum(dim=-1) |
| if self.min_active_groups > 0: |
| _, min_indices = torch.topk(probabilities, k=self.min_active_groups, dim=-1) |
| min_mask = torch.zeros_like(active_mask) |
| min_mask.scatter_(-1, min_indices, True) |
| active_mask = active_mask | (active_count.unsqueeze(-1) < self.min_active_groups) & min_mask |
|
|
| self.last_gate_sparsity = 1.0 - active_mask.detach().float().mean() |
| self.last_threshold_mean = threshold.detach().mean() |
| self.last_active_groups_mean = active_mask.detach().float().sum(dim=-1).mean() |
| return active_mask, probabilities.to(dtype=x.dtype), threshold.to(dtype=x.dtype) |
|
|
| def _exact_topk_pairs(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| probabilities = torch.sigmoid(self.router(x).float()) |
| top_values, top_indices = torch.topk(probabilities, k=self.max_active_groups, dim=-1) |
| self.last_gate_sparsity = x.new_tensor(1.0 - self.max_active_groups / self.num_groups) |
| self.last_threshold_mean = torch.sigmoid(self.threshold(x).float()).detach().mean() |
| self.last_active_groups_mean = x.new_tensor(float(self.max_active_groups)) |
| token_idx = torch.arange(x.shape[0], device=x.device).repeat_interleave(self.max_active_groups) |
| return token_idx, top_indices.reshape(-1), top_values.reshape(-1).to(dtype=x.dtype) |
|
|
| @torch._dynamo.disable |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| output = x_flat.new_zeros(x_flat.shape) |
|
|
| if self.min_active_groups == self.max_active_groups: |
| token_idx, group_idx, scale_values = self._exact_topk_pairs(x_flat) |
| else: |
| active_mask, probabilities, _ = self._active_group_mask(x_flat) |
| active_pairs = torch.nonzero(active_mask, as_tuple=False) |
| if active_pairs.numel() == 0: |
| return output.reshape(original_shape) |
| token_idx = active_pairs[:, 0] |
| group_idx = active_pairs[:, 1] |
| scale_values = probabilities[token_idx, group_idx] |
|
|
| order = torch.argsort(group_idx) |
| token_idx = token_idx.index_select(0, order) |
| group_idx = group_idx.index_select(0, order) |
| scale_values = scale_values.index_select(0, order) |
| selected_x = x_flat.index_select(0, token_idx).contiguous() |
| counts = torch.bincount(group_idx, minlength=self.num_groups) |
| offsets = torch.cumsum(counts, dim=0).to(dtype=torch.int32) |
| if not selected_x.is_cuda: |
| for group_idx_int in range(self.num_groups): |
| pair_idx = torch.nonzero(group_idx == group_idx_int, as_tuple=False).flatten() |
| if pair_idx.numel() == 0: |
| continue |
| group_token_idx = token_idx.index_select(0, pair_idx) |
| x_group = x_flat.index_select(0, group_token_idx) |
| hidden = F.silu(x_group @ self.up_weight[group_idx_int]) * (x_group @ self.value_weight[group_idx_int]) |
| projected = hidden @ self.down_weight[group_idx_int] |
| scale = scale_values.index_select(0, pair_idx).unsqueeze(-1) |
| output.index_add_(0, group_token_idx, projected * scale.to(dtype=projected.dtype)) |
| return output.reshape(original_shape) |
|
|
| grouped_dtype = torch.bfloat16 if selected_x.is_cuda else selected_x.dtype |
| selected_x_gemm = selected_x.to(dtype=grouped_dtype) |
| up_weight = self.up_weight.to(dtype=grouped_dtype) |
| value_weight = self.value_weight.to(dtype=grouped_dtype) |
| down_weight = self.down_weight.to(dtype=grouped_dtype) |
|
|
| up = torch._grouped_mm(selected_x_gemm, up_weight, offsets) |
| value = torch._grouped_mm(selected_x_gemm, value_weight, offsets) |
| hidden = F.silu(up) * value |
| projected = torch._grouped_mm(hidden, down_weight, offsets).to(dtype=output.dtype) |
| scale = scale_values.unsqueeze(-1).to(dtype=projected.dtype) |
| output.index_add_(0, token_idx, projected * scale) |
|
|
| return output.reshape(original_shape) |
|
|
|
|
| class HiddenBlockDSwiGLUMLP(nn.Module): |
| """Parameter-matched hidden-channel block SwiGLU. |
| |
| Unlike ``TileRoutedDSwiGLUMLP``, which gives every group the full residual |
| stream and partitions the intermediate channels, this module partitions the |
| residual stream itself into blocks. Each block owns a local SwiGLU with the |
| full configured intermediate width, so total parameter count and matmul work |
| are close to a dense dSwiGLU at the same ``intermediate_size``. |
| |
| ``grouped_mlp_stack_alpha`` turns the parallel blocks into a small cascade: |
| each later block can see a scaled residual update from the previous block. |
| The existing block-level channel rotation hook can rotate hidden channels |
| across transformer layers, preventing the same channels from being trapped |
| in the same local block for the whole network. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_blocks = config.sparse_mlp_num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| if self.active_groups != self.num_blocks: |
| raise ValueError("HiddenBlockDSwiGLUMLP is a full-active block variant.") |
| if config.n_embd % self.num_blocks != 0: |
| raise ValueError("n_embd must be divisible by sparse_mlp_num_groups") |
| if config.bias: |
| raise ValueError("HiddenBlockDSwiGLUMLP currently expects bias=False.") |
| self.block_hidden_size = config.n_embd // self.num_blocks |
| self.up_weight = nn.Parameter(torch.empty(self.num_blocks, self.block_hidden_size, self.intermediate_size)) |
| self.value_weight = nn.Parameter(torch.empty(self.num_blocks, self.block_hidden_size, self.intermediate_size)) |
| self.down_weight = nn.Parameter(torch.empty(self.num_blocks, self.intermediate_size, self.block_hidden_size)) |
| stack_alpha = torch.tensor(float(config.grouped_mlp_stack_alpha)) |
| if config.grouped_mlp_stack_alpha_learnable: |
| self.stack_alpha = nn.Parameter(stack_alpha) |
| else: |
| self.register_buffer("stack_alpha", stack_alpha, persistent=False) |
| output_scale = torch.tensor(float(config.grouped_mlp_stack_output_scale)) |
| if config.grouped_mlp_stack_output_scale_learnable: |
| self.stack_output_scale = nn.Parameter(output_scale) |
| else: |
| self.register_buffer("stack_output_scale", output_scale, persistent=False) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| std *= float(getattr(self.config, "grouped_mlp_stack_init_scale", 1.0)) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| x_blocks = x_flat.reshape(x_flat.shape[0], self.num_blocks, self.block_hidden_size) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| deltas: list[torch.Tensor] = [] |
| carry: torch.Tensor | None = None |
| for block_idx in range(self.num_blocks): |
| state = x_blocks[:, block_idx, :] |
| if carry is not None: |
| state = state + stack_alpha * carry |
| hidden = F.silu(state @ self.up_weight[block_idx]) * (state @ self.value_weight[block_idx]) |
| carry = hidden @ self.down_weight[block_idx] |
| deltas.append(carry) |
| output = torch.stack(deltas, dim=1).reshape(original_shape) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| output_scale = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| return output * output_scale |
|
|
|
|
| class TileRoutedDSwiGLUMLP(nn.Module): |
| """Tile-routed grouped dSwiGLU. |
| |
| This is the hardware-aligned sparse MLP experiment. It keeps one active MLP |
| group per token, but routes contiguous token tiles instead of individual |
| tokens. The forward is expressed as batched GEMMs so backward uses efficient |
| dense kernels instead of per-token atomic accumulation. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| if self.intermediate_size % self.num_groups != 0: |
| raise ValueError("intermediate_size must be divisible by sparse_mlp_num_groups") |
| if config.bias: |
| raise ValueError("TileRoutedDSwiGLUMLP currently expects bias=False.") |
| self.group_size = self.intermediate_size // self.num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| self.up_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.group_size)) |
| self.value_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.group_size)) |
| self.down_weight = nn.Parameter(torch.empty(self.num_groups, self.group_size, config.n_embd)) |
| self.shared_path: LLaMAMLP | None = None |
| if config.grouped_mlp_shared_intermediate_size > 0: |
| self.shared_path = LLaMAMLP(config, intermediate_size=config.grouped_mlp_shared_intermediate_size) |
| shared_alpha = torch.tensor(float(config.grouped_mlp_shared_alpha)) |
| if config.grouped_mlp_shared_alpha_learnable: |
| self.shared_alpha = nn.Parameter(shared_alpha) |
| else: |
| self.register_buffer("shared_alpha", shared_alpha, persistent=False) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
|
|
| def _apply_shared_path(self, x: torch.Tensor, grouped_output: torch.Tensor) -> torch.Tensor: |
| if self.shared_path is None: |
| return grouped_output |
| shared_alpha = self.shared_alpha.to(device=grouped_output.device, dtype=grouped_output.dtype) |
| return grouped_output + shared_alpha * self.shared_path(x) |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| return F.silu(up) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| |
| |
| |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| output = (self._gate(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
| return output.reshape(original_shape) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| if self.active_groups == self.num_groups: |
| output = self._forward_full_active(x, original_shape, x_flat) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return self._apply_shared_path(x, output) |
|
|
| usable_tokens = x_flat.shape[0] - (x_flat.shape[0] % self.num_groups) |
| output = x_flat.new_empty(x_flat.shape) |
|
|
| if usable_tokens: |
| x_tiles = x_flat[:usable_tokens].reshape(self.num_groups, usable_tokens // self.num_groups, x_flat.shape[-1]) |
| out_tiles = torch.zeros_like(x_tiles) |
| for route_offset in range(self.active_groups): |
| if route_offset == 0: |
| up_weight = self.up_weight |
| value_weight = self.value_weight |
| down_weight = self.down_weight |
| else: |
| up_weight = torch.roll(self.up_weight, shifts=-route_offset, dims=0) |
| value_weight = torch.roll(self.value_weight, shifts=-route_offset, dims=0) |
| down_weight = torch.roll(self.down_weight, shifts=-route_offset, dims=0) |
| up = torch.bmm(x_tiles, up_weight) |
| value = torch.bmm(x_tiles, value_weight) |
| out_tiles = out_tiles + torch.bmm(self._gate(up) * value, down_weight) |
| output[:usable_tokens] = out_tiles.reshape(usable_tokens, x_flat.shape[-1]) |
|
|
| if usable_tokens < x_flat.shape[0]: |
| tail = x_flat[usable_tokens:] |
| tail_out = torch.zeros_like(tail) |
| for route_offset in range(self.active_groups): |
| hidden = self._gate(tail @ self.up_weight[route_offset]) * (tail @ self.value_weight[route_offset]) |
| tail_out = tail_out + hidden @ self.down_weight[route_offset] |
| output[usable_tokens:] = tail_out |
|
|
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(1.0 - self.active_groups / self.num_groups) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return self._apply_shared_path(x, output.reshape(original_shape)) |
|
|
|
|
| class TileRoutedActivationDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped GLU with a configurable gate activation.""" |
|
|
| gate_kind = "silu" |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| if self.gate_kind == "gelu": |
| return F.gelu(up, approximate=self.config.gelu_approximate) |
| if self.gate_kind == "relu": |
| return F.relu(up) |
| if self.gate_kind == "squared_relu": |
| relu = F.relu(up) |
| return relu * relu |
| if self.gate_kind == "sigmoid": |
| return torch.sigmoid(up) |
| if self.gate_kind == "powlu": |
| return powlu_gate(up, self.config.powlu_m) |
| return F.silu(up) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = self._gate(x_flat @ up_weight) * (x_flat @ value_weight) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGEGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| gate_kind = "gelu" |
|
|
|
|
| class TileRoutedReGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| gate_kind = "relu" |
|
|
|
|
| class TileRoutedSquaredReGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| gate_kind = "squared_relu" |
|
|
|
|
| class TileRoutedSigmoidGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| gate_kind = "sigmoid" |
|
|
|
|
| class TileRoutedPowLUGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| gate_kind = "powlu" |
|
|
|
|
| class TileRoutedEulerGLUMLP(TileRoutedActivationDSwiGLUMLP): |
| """Grouped SwiGLU with a normalized Euler maximum envelope. |
| |
| For ``p = softplus(u)``, ``p ** (1 / p)`` reaches its unique global |
| maximum at ``p = e``. Normalizing by ``e ** (1 / e)`` bounds the positive |
| modulation by one while SiLU retains the signed gate behavior. |
| """ |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| return F.silu(up) * envelope |
|
|
|
|
| class TileRoutedEulerMishBlendGLUMLP(TileRoutedEulerGLUMLP): |
| """Euler gate with a zero-start, per-group Mish residual. |
| |
| The gate begins as the fixed Euler gate exactly. Each contiguous 4/4 |
| group can learn a bounded interpolation toward Mish, preserving the proven |
| positive Euler envelope while exposing Mish's smooth negative curvature. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Euler-Mish blend requires all groups to be active.") |
| self.mish_residual = nn.Parameter(torch.zeros(self.num_groups)) |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| euler_gate = F.silu(up) * envelope |
| mish_gate = up * torch.tanh(p).to(dtype=up.dtype) |
| mix = torch.tanh(self.mish_residual).repeat_interleave(self.group_size).to(dtype=up.dtype) |
| return euler_gate + mix * (mish_gate - euler_gate) |
|
|
|
|
| class TileRoutedContextualValueRotationEulerGLUMLP(TileRoutedEulerGLUMLP): |
| """Euler 4/4 MLP with contextual orthogonal value rotations. |
| |
| The 1664-channel intermediate is interpreted as 13 aligned tiles, each |
| containing four 32-channel branches. Four response-conditioned butterfly |
| rotations mix the branches before the existing dense down projection. |
| All controller coefficients start at zero, making this exactly the fixed |
| Euler gate at initialization while retaining nonzero controller gradients. |
| """ |
|
|
| _tile_size = 32 |
| _rotation_pairs = ((0, 1), (2, 3), (0, 2), (1, 3)) |
| _max_half_angle = math.tan(math.pi / 24.0) |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups or self.num_groups != 4: |
| raise ValueError("Contextual value rotation requires full-active grouped 4/4.") |
| if self.group_size % self._tile_size: |
| raise ValueError("Contextual value rotation requires a group size divisible by 32.") |
| self.num_value_tiles = self.group_size // self._tile_size |
| self.cvr_energy_coeff = nn.Parameter(torch.zeros(len(self._rotation_pairs))) |
| self.cvr_agreement_coeff = nn.Parameter(torch.zeros(len(self._rotation_pairs))) |
|
|
| def _rotate_pair( |
| self, |
| left: torch.Tensor, |
| right: torch.Tensor, |
| pair_index: int, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| left_float = left.float() |
| right_float = right.float() |
| epsilon = torch.finfo(torch.float32).eps |
| left_energy = left_float.square().mean(dim=-1).clamp_min(epsilon) |
| right_energy = right_float.square().mean(dim=-1).clamp_min(epsilon) |
| energy_contrast = torch.tanh(0.5 * (torch.log(left_energy) - torch.log(right_energy))) |
| agreement = (left_float * right_float).mean(dim=-1) * torch.rsqrt(left_energy * right_energy) |
| agreement = agreement.clamp(-1.0, 1.0) |
| controller = ( |
| self.cvr_energy_coeff[pair_index].float() * energy_contrast |
| + self.cvr_agreement_coeff[pair_index].float() * agreement |
| ) |
| half_angle = self._max_half_angle * torch.tanh(controller) |
| half_angle_squared = half_angle.square() |
| denominator = 1.0 + half_angle_squared |
| cosine = ((1.0 - half_angle_squared) / denominator).to(dtype=left.dtype).unsqueeze(-1) |
| sine = ((2.0 * half_angle) / denominator).to(dtype=left.dtype).unsqueeze(-1) |
| return cosine * left - sine * right, sine * left + cosine * right |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape( |
| x_flat.shape[0], self.num_groups, self.num_value_tiles, self._tile_size |
| ) |
| groups = list(hidden.unbind(dim=1)) |
| for pair_index, (left_index, right_index) in enumerate(self._rotation_pairs): |
| groups[left_index], groups[right_index] = self._rotate_pair( |
| groups[left_index], groups[right_index], pair_index |
| ) |
| hidden = torch.stack(groups, dim=1).reshape(x_flat.shape[0], self.intermediate_size) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedContextAdaptiveEulerTemperatureGLUMLP(TileRoutedEulerGLUMLP): |
| """Euler 4/4 gate with a bounded, response-conditioned temperature. |
| |
| The normalized Euler log-envelope is non-positive and has its unique zero |
| at ``softplus(up) = e``. Each group learns how strongly to sharpen one |
| side of that maximum while relaxing the other, using the bounded signed |
| displacement from ``e`` as local context. Zero parameters reproduce the |
| fixed Euler gate exactly, and the temperature always stays in [0.5, 1.5], |
| so the envelope remains bounded by one with the same global maximum. |
| """ |
|
|
| _max_temperature_delta = 0.5 |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups or self.num_groups != 4: |
| raise ValueError("Context-adaptive Euler temperature requires full-active grouped 4/4.") |
| self.caet_temperature_raw = nn.Parameter(torch.zeros(self.num_groups)) |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| if up.shape[-1] != self.intermediate_size: |
| raise RuntimeError("Context-adaptive Euler expects the full active intermediate dimension.") |
| up_float = up.float() |
| p = F.softplus(up_float).clamp_min(torch.finfo(torch.float32).tiny) |
| log_envelope = torch.log(p) / p - (1.0 / math.e) |
| response = (p - math.e) / (p + math.e) |
| group_delta = self._max_temperature_delta * torch.tanh(self.caet_temperature_raw.float()) |
| channel_delta = group_delta.repeat_interleave(self.group_size) |
| temperature = 1.0 + channel_delta * response |
| envelope = torch.exp(log_envelope * temperature).to(dtype=up.dtype) |
| return F.silu(up) * envelope |
|
|
|
|
| class TileRoutedAdaptiveEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped SwiGLU with a learned SiLU/Euler choice per group. |
| |
| The 4/4 path stores channels in four contiguous groups. Each group receives |
| an independent differentiable selector between ordinary SiLU and the |
| normalized Euler envelope. This adds four scalars per layer, not another |
| MLP projection, so it can express activation specialization without |
| changing the model's MLP width or matrix-multiply count. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("TileRoutedAdaptiveEulerGLUMLP requires all groups to be active.") |
| initial_euler_probability = 0.75 |
| initial_logit = math.log(initial_euler_probability / (1.0 - initial_euler_probability)) |
| self.euler_mix_logits = nn.Parameter(torch.full((self.num_groups,), initial_logit)) |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| if up.shape[-1] != self.intermediate_size: |
| raise RuntimeError("Adaptive Euler gating expects the full active intermediate dimension.") |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| euler_weight = torch.sigmoid(self.euler_mix_logits).repeat_interleave(self.group_size) |
| return F.silu(up) * (1.0 + euler_weight.to(dtype=up.dtype) * (envelope - 1.0)) |
|
|
|
|
| class TileRoutedDiverseEulerGLUMLP(TileRoutedAdaptiveEulerGLUMLP): |
| """Adaptive Euler gating with deliberately diverse group initializations.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| probabilities = torch.linspace(0.125, 0.875, self.num_groups) |
| with torch.no_grad(): |
| self.euler_mix_logits.copy_(torch.logit(probabilities)) |
|
|
|
|
| class TileRoutedGroupTokenAttentionEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler-gated 4/4 MLP with token-conditioned attention between groups.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Group-token attention requires all groups to be active.") |
| self.group_query_scale = nn.Parameter(torch.ones(self.num_groups)) |
| self.group_key_scale = nn.Parameter(torch.ones(self.num_groups)) |
| self.group_value_scale = nn.Parameter(torch.ones(self.num_groups)) |
| self.group_attention_bias = nn.Parameter(torch.zeros(self.num_groups, self.num_groups)) |
| self.group_attention_gain = nn.Parameter(torch.full((self.num_groups,), 0.1)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape(-1, self.num_groups, self.group_size) |
| summaries = hidden.float().mean(dim=-1) |
| query = summaries * self.group_query_scale.float() |
| key = summaries * self.group_key_scale.float() |
| scores = query.unsqueeze(-1) * key.unsqueeze(-2) + self.group_attention_bias.float() |
| attention = F.softmax(scores, dim=-1) |
| context = (attention @ (summaries * self.group_value_scale.float()).unsqueeze(-1)).squeeze(-1) |
| gains = 1.0 + torch.tanh(context * self.group_attention_gain.float()) |
| hidden = hidden * gains.to(dtype=hidden.dtype).unsqueeze(-1) |
| return (hidden.reshape(-1, self.intermediate_size) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedHierarchicalEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler-gated 4/4 MLP with a learned 2x2 macro-group stage.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.num_groups != 4 or self.active_groups != self.num_groups: |
| raise ValueError("Hierarchical Euler MLP is specialized for full-active grouped 4/4.") |
| self.macro_mix_delta = nn.Parameter(torch.zeros(2, 2)) |
| self.macro_gain = nn.Parameter(torch.full((2, 2), 0.1)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape(-1, 2, 2, self.group_size) |
| macro = hidden.float().mean(dim=2) |
| mix = torch.eye(2, device=macro.device, dtype=macro.dtype) + self.macro_mix_delta.float() |
| macro = torch.einsum('tpg,pq->tqg', macro, mix) |
| macro_summary = macro.mean(dim=-1) |
| macro_gains = 1.0 + torch.tanh( |
| macro_summary.unsqueeze(-1) * self.macro_gain.float().unsqueeze(0) |
| ) |
| hidden = hidden * macro_gains.to(dtype=hidden.dtype).unsqueeze(-1) |
| return (hidden.reshape(-1, self.intermediate_size) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedTokenExpertEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Token- and group-routed mixture of SiLU, Euler, and GELU gates.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Token expert routing requires all groups to be active.") |
| self.expert_router_slope = nn.Parameter(torch.zeros(self.num_groups, 3)) |
| initial_bias = torch.tensor([-2.2, 2.9, -2.2]) |
| self.expert_router_bias = nn.Parameter(initial_bias.repeat(self.num_groups, 1)) |
|
|
| def _gate(self, up: torch.Tensor) -> torch.Tensor: |
| if up.shape[-1] != self.intermediate_size: |
| raise RuntimeError("Token expert routing expects the full active intermediate dimension.") |
| grouped_up = up.reshape(-1, self.num_groups, self.group_size) |
| summaries = grouped_up.float().mean(dim=-1) |
| logits = summaries.unsqueeze(-1) * self.expert_router_slope.float() + self.expert_router_bias.float() |
| weights = F.softmax(logits, dim=-1) |
| p = F.softplus(grouped_up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| silu_gate = F.silu(grouped_up) |
| gates = torch.stack((silu_gate, silu_gate * envelope, F.gelu(grouped_up)), dim=-1) |
| return (gates * weights.to(dtype=up.dtype).unsqueeze(-2)).sum(dim=-1).reshape_as(up) |
|
|
|
|
| class TileRoutedDynamicGroupMixerEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler-gated 4/4 MLP with a token-conditioned 4x4 group mixer.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.num_groups != 4 or self.active_groups != self.num_groups: |
| raise ValueError("Dynamic group mixing is specialized for full-active grouped 4/4.") |
| self.mixer_slope = nn.Parameter(torch.zeros(4, 4)) |
| self.mixer_bias = nn.Parameter(torch.full((4, 4), -3.0)) |
| with torch.no_grad(): |
| self.mixer_bias.diagonal().fill_(3.0) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape(-1, 4, self.group_size) |
| summaries = hidden.float().mean(dim=-1) |
| logits = self.mixer_bias.float().unsqueeze(0) + summaries.unsqueeze(-1) * self.mixer_slope.float() |
| mixer = F.softmax(logits, dim=-1) |
| hidden = torch.einsum('tij,tjg->tig', mixer.to(dtype=hidden.dtype), hidden) |
| return (hidden.reshape(-1, self.intermediate_size) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGroupCompetitionEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler-gated 4/4 MLP with token-wise conserved group energy.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Group competition requires all groups to be active.") |
| self.competition_temperature_raw = nn.Parameter(torch.tensor(0.0)) |
| self.competition_strength_raw = nn.Parameter(torch.tensor(-2.0)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape(-1, self.num_groups, self.group_size) |
| energy = hidden.float().square().mean(dim=-1).add(1e-6).log() |
| temperature = F.softplus(self.competition_temperature_raw.float()) |
| allocation = F.softmax(energy * temperature, dim=-1) * self.num_groups |
| strength = torch.sigmoid(self.competition_strength_raw.float()) |
| gains = 1.0 + strength * (allocation - 1.0) |
| hidden = hidden * gains.to(dtype=hidden.dtype).unsqueeze(-1) |
| return (hidden.reshape(-1, self.intermediate_size) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedEulerGroupStateMemoryGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler-gated full-active grouped MLP with token-conditioned group state.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.group_state_memory_gain_max = float(config.group_state_memory_gain_max) |
| self.group_state_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, 1)) |
|
|
| def _apply_group_state_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| group_mean = hidden_groups.float().mean(dim=-1, keepdim=True) |
| group_rms = hidden_groups.float().square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
| gain = self.group_state_memory_gain_max * torch.tanh(self.group_state_memory_gain_raw).view(1, self.num_groups, 1) |
| return (hidden_groups * (1.0 + gain.to(dtype=hidden_groups.dtype) * state)).reshape_as(hidden) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = F.silu(up) * envelope * (x_flat @ value_weight) |
| hidden = self._apply_group_state_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedEulerFusedGroupMixGLUMLP(TileRoutedDSwiGLUMLP): |
| """Fixed Euler 4/4 MLP with group mixing folded into down weights. |
| |
| Mixing hidden groups before the down projection is algebraically identical |
| to mixing the four down-weight tiles. Folding it avoids a token-side |
| einsum and retains the dense full-active GEMM path. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| logits = torch.full((self.num_groups, self.num_groups), -6.0) |
| logits.fill_diagonal_(6.0) |
| self.group_mix_logits = nn.Parameter(logits) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = F.silu(up) * envelope * value |
| mix = F.softmax(self.group_mix_logits.float(), dim=-1).to(dtype=hidden.dtype) |
| effective_down = torch.einsum('og,osh->gsh', mix, self.down_weight) |
| return (hidden @ effective_down.reshape(self.intermediate_size, hidden_size)).reshape(original_shape) |
|
|
|
|
| class TileRoutedAdaptiveOperatorFieldEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler 4/4 MLP with a token-conditioned group operator field. |
| |
| A small controller reads the full residual token and selects a convex |
| combination of 4x4 operator bases. Because the operator changes per token, |
| it cannot be folded into the down projection like a static group mixer. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.num_groups != 4 or self.active_groups != self.num_groups: |
| raise ValueError("Adaptive operator field is specialized for full-active grouped 4/4.") |
| controller_dim = 16 |
| num_bases = 4 |
| self.operator_controller = nn.Linear(config.n_embd, controller_dim, bias=False) |
| self.operator_router = nn.Linear(controller_dim, num_bases, bias=False) |
| self.operator_bases = nn.Parameter(torch.empty(num_bases, self.num_groups, self.num_groups)) |
| self.operator_strength_raw = nn.Parameter(torch.tensor(-2.2)) |
| nn.init.normal_(self.operator_controller.weight, mean=0.0, std=0.02) |
| nn.init.normal_(self.operator_router.weight, mean=0.0, std=0.02) |
| nn.init.normal_(self.operator_bases, mean=0.0, std=0.02) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| hidden = (F.silu(up) * envelope * value).reshape(-1, self.num_groups, self.group_size) |
| controller = F.rms_norm(x_flat.float(), (x_flat.shape[-1],)).to(dtype=x_flat.dtype) |
| router = F.softmax(self.operator_router(F.silu(self.operator_controller(controller))).float(), dim=-1) |
| delta = torch.einsum('tk,kij->tij', router, self.operator_bases.float()) |
| strength = torch.sigmoid(self.operator_strength_raw.float()) |
| identity = torch.eye(self.num_groups, device=hidden.device, dtype=torch.float32) |
| operator = identity.unsqueeze(0) + strength * torch.tanh(delta) |
| hidden = torch.bmm(operator.to(dtype=hidden.dtype), hidden) |
| return (hidden.reshape(-1, self.intermediate_size) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedAdaptiveBasisEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler 4/4 MLP with token-dependent low-rank feature construction. |
| |
| A controller selects one of two low-rank bases that update the up and value |
| features before the Euler gate. This changes the token's feature basis and |
| is not equivalent to a post-activation group mixer. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Adaptive basis Euler MLP requires all groups to be active.") |
| rank = 4 |
| num_bases = 2 |
| self.basis_input = nn.Parameter(torch.empty(config.n_embd, rank)) |
| self.basis_up = nn.Parameter(torch.empty(num_bases, rank, self.intermediate_size)) |
| self.basis_value = nn.Parameter(torch.empty(num_bases, rank, self.intermediate_size)) |
| self.basis_router = nn.Linear(config.n_embd, num_bases, bias=False) |
| self.basis_strength_raw = nn.Parameter(torch.tensor(-1.4)) |
| nn.init.normal_(self.basis_input, mean=0.0, std=0.02) |
| nn.init.normal_(self.basis_up, mean=0.0, std=0.02) |
| nn.init.normal_(self.basis_value, mean=0.0, std=0.02) |
| nn.init.zeros_(self.basis_router.weight) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| normalized_x = F.rms_norm(x_flat.float(), (x_flat.shape[-1],)).to(dtype=x_flat.dtype) |
| router = F.softmax(self.basis_router(normalized_x).float(), dim=-1) |
| latent = x_flat @ self.basis_input.to(dtype=x_flat.dtype) |
| basis_up = torch.einsum('tk,kri->tri', router, self.basis_up.to(dtype=x_flat.dtype)) |
| basis_value = torch.einsum('tk,kri->tri', router, self.basis_value.to(dtype=x_flat.dtype)) |
| strength = torch.sigmoid(self.basis_strength_raw.float()).to(dtype=x_flat.dtype) |
| up = x_flat @ up_weight + strength * torch.bmm(latent.unsqueeze(1), basis_up).squeeze(1) |
| value = x_flat @ value_weight + strength * torch.bmm(latent.unsqueeze(1), basis_value).squeeze(1) |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| return ((F.silu(up) * envelope * value) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedFusedAdaptiveBasisEulerGLUMLP(TileRoutedAdaptiveBasisEulerGLUMLP): |
| """Adaptive basis Euler MLP without materializing token-specific bases.""" |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| normalized_x = F.rms_norm(x_flat.float(), (x_flat.shape[-1],)).to(dtype=x_flat.dtype) |
| router = F.softmax(self.basis_router(normalized_x).float(), dim=-1).to(dtype=x_flat.dtype) |
| latent = x_flat @ self.basis_input.to(dtype=x_flat.dtype) |
| delta_up = torch.zeros(x_flat.shape[0], self.intermediate_size, device=x_flat.device, dtype=x_flat.dtype) |
| delta_value = torch.zeros_like(delta_up) |
| for basis_idx in range(self.basis_up.shape[0]): |
| weighted_latent = latent * router[:, basis_idx : basis_idx + 1] |
| delta_up = delta_up + weighted_latent @ self.basis_up[basis_idx].to(dtype=x_flat.dtype) |
| delta_value = delta_value + weighted_latent @ self.basis_value[basis_idx].to(dtype=x_flat.dtype) |
| strength = torch.sigmoid(self.basis_strength_raw.float()).to(dtype=x_flat.dtype) |
| up = x_flat @ up_weight + strength * delta_up |
| value = x_flat @ value_weight + strength * delta_value |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| return ((F.silu(up) * envelope * value) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedAdaptiveDirectionEulerGLUMLP(TileRoutedDSwiGLUMLP): |
| """Euler 4/4 MLP with a token-selected rank-one feature direction. |
| |
| Two learned directions are mixed by a controller, then multiplied by a |
| token-specific scalar projection. This is a conditional low-rank update to |
| the up/value features without materializing token-specific matrices. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("Adaptive direction Euler MLP requires all groups to be active.") |
| num_directions = 2 |
| self.direction_input = nn.Parameter(torch.empty(config.n_embd)) |
| self.direction_up = nn.Parameter(torch.empty(num_directions, self.intermediate_size)) |
| self.direction_value = nn.Parameter(torch.empty(num_directions, self.intermediate_size)) |
| self.direction_router = nn.Linear(config.n_embd, num_directions, bias=False) |
| self.direction_strength_raw = nn.Parameter(torch.tensor(-2.0)) |
| nn.init.normal_(self.direction_input, mean=0.0, std=0.02) |
| nn.init.normal_(self.direction_up, mean=0.0, std=0.02) |
| nn.init.normal_(self.direction_value, mean=0.0, std=0.02) |
| nn.init.zeros_(self.direction_router.weight) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| normalized_x = F.rms_norm(x_flat.float(), (x_flat.shape[-1],)).to(dtype=x_flat.dtype) |
| router = F.softmax(self.direction_router(normalized_x).float(), dim=-1).to(dtype=x_flat.dtype) |
| token_scalar = (x_flat @ self.direction_input.to(dtype=x_flat.dtype)).unsqueeze(-1) |
| up_direction = router @ self.direction_up.to(dtype=x_flat.dtype) |
| value_direction = router @ self.direction_value.to(dtype=x_flat.dtype) |
| strength = torch.sigmoid(self.direction_strength_raw.float()).to(dtype=x_flat.dtype) |
| up = x_flat @ up_weight + strength * token_scalar * up_direction |
| value = x_flat @ value_weight + strength * token_scalar * value_direction |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| return ((F.silu(up) * envelope * value) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedAdaptiveDirectionEulerFusedGLUMLP(TileRoutedAdaptiveDirectionEulerGLUMLP): |
| """Checkpoint-compatible two-route fused form of Adaptive Direction.""" |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| normalized_x = F.rms_norm(x_flat.float(), (x_flat.shape[-1],)).to(dtype=x_flat.dtype) |
| router_weight = self.direction_router.weight.to(dtype=x_flat.dtype) |
| route_first = torch.sigmoid(normalized_x @ (router_weight[0] - router_weight[1])) |
| token_scalar = (x_flat @ self.direction_input.to(dtype=x_flat.dtype)).unsqueeze(-1) |
| up_base = self.direction_up[1].to(dtype=x_flat.dtype) |
| value_base = self.direction_value[1].to(dtype=x_flat.dtype) |
| up_direction = up_base + route_first.unsqueeze(-1) * (self.direction_up[0].to(dtype=x_flat.dtype) - up_base) |
| value_direction = value_base + route_first.unsqueeze(-1) * (self.direction_value[0].to(dtype=x_flat.dtype) - value_base) |
| strength = torch.sigmoid(self.direction_strength_raw.float()).to(dtype=x_flat.dtype) |
| up = x_flat @ up_weight + strength * token_scalar * up_direction |
| value = x_flat @ value_weight + strength * token_scalar * value_direction |
| p = F.softplus(up.float()).clamp_min(torch.finfo(torch.float32).tiny) |
| envelope = torch.exp(torch.log(p) / p - (1.0 / math.e)).to(dtype=up.dtype) |
| return ((F.silu(up) * envelope * value) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGateLawDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Vectorized grouped4 GLU with a compile-friendly gate law. |
| |
| The full-active grouped 4/4 path stays as the same three dense GEMMs as |
| ``TileRoutedDSwiGLUMLP``. Subclasses only add cheap elementwise transforms |
| between the up/value GEMMs and the down GEMM. |
| """ |
|
|
| use_powrat_gate = False |
| use_sin_gate = False |
| use_spon_shift = False |
| default_spon_value = False |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.gate_alpha_max = float(config.grouped_gate_alpha_max) |
| self.gate_sin_eps = float(config.grouped_gate_sin_eps) |
| self.gate_sin_freq = float(config.grouped_gate_sin_freq) |
| self.gate_spon_max = float(config.grouped_gate_spon_max) |
| self.gate_layer_schedule = str(config.grouped_gate_layer_schedule) |
| self.spon_value_enabled = bool(config.grouped_gate_spon_value or self.default_spon_value) |
| self._gate_schedule_initialized = False |
| if self.use_powrat_gate: |
| self.gate_alpha_raw = nn.Parameter(torch.empty(self.num_groups, 1, 1)) |
| self.gate_beta = nn.Parameter(torch.full((self.num_groups, 1, 1), float(config.grouped_gate_beta_init))) |
| self._fill_alpha(float(config.grouped_gate_alpha_init)) |
| if self.use_spon_shift: |
| self.spon_up_shift = nn.Parameter(torch.zeros(self.intermediate_size)) |
| self.spon_value_shift = nn.Parameter(torch.zeros(self.intermediate_size)) |
| self.last_gate_alpha_mean: torch.Tensor | None = None |
| self.last_gate_beta_mean: torch.Tensor | None = None |
| self.last_spon_up_absmean: torch.Tensor | None = None |
| self.last_spon_value_absmean: torch.Tensor | None = None |
| self.last_gate_outlier_rate: torch.Tensor | None = None |
| self.last_hidden_outlier_rate: torch.Tensor | None = None |
|
|
| def _fill_alpha(self, alpha: float) -> None: |
| alpha = max(-0.999 * self.gate_alpha_max, min(0.999 * self.gate_alpha_max, alpha)) |
| raw = math.atanh(alpha / self.gate_alpha_max) |
| with torch.no_grad(): |
| self.gate_alpha_raw.fill_(raw) |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| if self.use_powrat_gate and not self._gate_schedule_initialized: |
| self._fill_alpha(self._scheduled_alpha(block_idx, n_layer)) |
| self._gate_schedule_initialized = True |
|
|
| def _scheduled_alpha(self, block_idx: int, n_layer: int) -> float: |
| if self.gate_layer_schedule == "constant": |
| return float(self.config.grouped_gate_alpha_init) |
| if self.gate_layer_schedule == "late_positive": |
| if n_layer <= 1: |
| return self.gate_alpha_max |
| pos = block_idx / max(1, n_layer - 1) |
| return 0.0 if pos < 0.67 else self.gate_alpha_max * (pos - 0.67) / 0.33 |
| observed = [-0.074, -0.090, -0.089, -0.090, -0.084, -0.084, -0.060, -0.043, -0.020, 0.107, 0.235, 0.351] |
| if n_layer <= 1: |
| value = observed[-1] |
| else: |
| pos = block_idx * (len(observed) - 1) / max(1, n_layer - 1) |
| lo = int(math.floor(pos)) |
| hi = min(len(observed) - 1, lo + 1) |
| frac = pos - lo |
| value = observed[lo] * (1.0 - frac) + observed[hi] * frac |
| scale = self.gate_alpha_max / max(abs(v) for v in observed) |
| return max(-self.gate_alpha_max, min(self.gate_alpha_max, value * scale)) |
|
|
| def _dense_weights(self, hidden_size: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return up_weight, value_weight, down_weight |
|
|
| def _spon_biases(self, dtype: torch.dtype) -> tuple[torch.Tensor | None, torch.Tensor | None]: |
| if not self.use_spon_shift or self.gate_spon_max == 0.0: |
| return None, None |
| up_bias = self.spon_up_shift.to(dtype=dtype) |
| value_bias = self.spon_value_shift.to(dtype=dtype) if self.spon_value_enabled else None |
| return up_bias, value_bias |
|
|
| def _gate_law(self, up: torch.Tensor) -> torch.Tensor: |
| if not self.use_powrat_gate: |
| return F.silu(up) |
| up_groups = up.reshape(up.shape[0], self.num_groups, self.group_size) |
| alpha = self.gate_alpha_max * torch.tanh( |
| self.gate_alpha_raw.to(device=up.device, dtype=up.dtype) |
| ).view(1, self.num_groups, 1) |
| beta = self.gate_beta.to(device=up.device, dtype=up.dtype).abs().clamp_min(1e-4).view(1, self.num_groups, 1) |
| modifier = 1.0 + alpha * up_groups * torch.rsqrt(1.0 + beta * up_groups.square()) |
| gate = F.silu(up_groups) * modifier |
| if self.use_sin_gate and self.gate_sin_eps > 0.0: |
| gate = gate * (1.0 + self.gate_sin_eps * torch.sin(self.gate_sin_freq * up_groups)) |
| return gate.reshape_as(up) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight, value_weight, down_weight = self._dense_weights(hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| up_bias, value_bias = self._spon_biases(x_flat.dtype) |
| if up_bias is not None: |
| up.add_(up_bias) |
| if value_bias is not None: |
| value.add_(value_bias) |
| gate = self._gate_law(up) |
| hidden = gate * value |
| output = hidden @ down_weight |
| if not torch.is_grad_enabled(): |
| if self.use_powrat_gate: |
| alpha = self.gate_alpha_max * torch.tanh(self.gate_alpha_raw.detach().float()) |
| self.last_gate_alpha_mean = alpha.mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_gate_beta_mean = self.gate_beta.detach().float().abs().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| if self.use_spon_shift: |
| self.last_spon_up_absmean = self.spon_up_shift.detach().float().abs().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_spon_value_absmean = self.spon_value_shift.detach().float().abs().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_gate_outlier_rate = (gate.detach().float().abs() > 8.0).float().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_hidden_outlier_rate = (hidden.detach().float().abs() > 8.0).float().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedPowRatGLUMLP(TileRoutedGateLawDSwiGLUMLP): |
| use_powrat_gate = True |
|
|
|
|
| class TileRoutedSinPowRatGLUMLP(TileRoutedGateLawDSwiGLUMLP): |
| use_powrat_gate = True |
| use_sin_gate = True |
|
|
|
|
| class TileRoutedSPONGLUMLP(TileRoutedGateLawDSwiGLUMLP): |
| use_spon_shift = True |
|
|
|
|
| class TileRoutedTrainOnlySPONGLUMLP(TileRoutedSPONGLUMLP): |
| """Use SPON as a training-time activation perturbation with free inference.""" |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| if not torch.is_grad_enabled(): |
| return TileRoutedDSwiGLUMLP._forward_full_active(self, x, original_shape, x_flat) |
| return super()._forward_full_active(x, original_shape, x_flat) |
|
|
|
|
| class TileRoutedSPONPowRatGLUMLP(TileRoutedGateLawDSwiGLUMLP): |
| use_powrat_gate = True |
| use_spon_shift = True |
|
|
|
|
| class TileRoutedAttentionMemoryGateDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Grouped4 GLU whose gate is steered by a small learned memory attention. |
| |
| The main compute path keeps grouped4's full-active dense GEMMs: |
| up/value/down are still single matmuls. The fixed SwiGLU nonlinearity is |
| replaced by ``up * sigmoid(up + memory_delta)`` where ``memory_delta`` is a |
| per-group token-conditioned lookup over learned hidden memory slots. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.attn_gate_rank = int(config.attn_gate_rank) |
| self.attn_gate_slots = int(config.attn_gate_slots) |
| self.attn_gate_alpha_max = float(config.attn_gate_alpha_max) |
| self.attn_gate_temperature = float(config.attn_gate_temperature) |
| self.attn_q_weight = nn.Parameter(torch.empty(self.num_groups, self.group_size, self.attn_gate_rank)) |
| self.attn_k = nn.Parameter(torch.empty(self.num_groups, self.attn_gate_slots, self.attn_gate_rank)) |
| self.attn_v = nn.Parameter(torch.empty(self.num_groups, self.attn_gate_slots, self.group_size)) |
| self.attn_alpha_raw = nn.Parameter(torch.empty(self.num_groups, 1, 1)) |
| self._reset_attention_memory(float(config.attn_gate_alpha_init)) |
| self.last_attn_gate_alpha_mean: torch.Tensor | None = None |
| self.last_attn_gate_entropy: torch.Tensor | None = None |
| self.last_attn_gate_delta_rms: torch.Tensor | None = None |
| self.last_attn_gate_up_rms: torch.Tensor | None = None |
|
|
| def _reset_attention_memory(self, alpha_init: float) -> None: |
| nn.init.normal_(self.attn_q_weight, mean=0.0, std=self.group_size**-0.5) |
| nn.init.normal_(self.attn_k, mean=0.0, std=self.attn_gate_rank**-0.5) |
| nn.init.normal_(self.attn_v, mean=0.0, std=0.01) |
| alpha = max(-0.999 * self.attn_gate_alpha_max, min(0.999 * self.attn_gate_alpha_max, alpha_init)) |
| raw = math.atanh(alpha / self.attn_gate_alpha_max) if self.attn_gate_alpha_max > 0.0 else 0.0 |
| with torch.no_grad(): |
| self.attn_alpha_raw.fill_(raw) |
|
|
| def _dense_weights(self, hidden_size: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return up_weight, value_weight, down_weight |
|
|
| def _memory_delta(self, up_groups: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: |
| dtype = up_groups.dtype |
| up_norm = up_groups * torch.rsqrt(up_groups.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + 1e-6) |
| q = torch.einsum("ngc,gcr->ngr", up_norm, self.attn_q_weight.to(device=up_groups.device, dtype=dtype)) |
| k = self.attn_k.to(device=up_groups.device, dtype=dtype) |
| logits = torch.einsum("ngr,gsr->ngs", q, k) |
| logits = logits * (self.attn_gate_rank**-0.5) / max(self.attn_gate_temperature, 1e-4) |
| attn = torch.softmax(logits.float(), dim=-1).to(dtype=dtype) |
| delta = torch.einsum("ngs,gsc->ngc", attn, self.attn_v.to(device=up_groups.device, dtype=dtype)) |
| return delta, attn |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight, value_weight, down_weight = self._dense_weights(hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| up_groups = up.reshape(up.shape[0], self.num_groups, self.group_size) |
| delta, attn = self._memory_delta(up_groups) |
| alpha = self.attn_gate_alpha_max * torch.tanh( |
| self.attn_alpha_raw.to(device=up.device, dtype=up.dtype) |
| ).view(1, self.num_groups, 1) |
| gate_groups = up_groups * torch.sigmoid(up_groups + alpha * delta) |
| hidden = gate_groups.reshape_as(up) * value |
| output = hidden @ down_weight |
| if not torch.is_grad_enabled(): |
| entropy = -(attn.float().clamp_min(1e-8) * attn.float().clamp_min(1e-8).log()).sum(dim=-1).mean() |
| self.last_attn_gate_alpha_mean = alpha.detach().float().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_attn_gate_entropy = entropy.to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_attn_gate_delta_rms = delta.detach().float().square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_attn_gate_up_rms = up_groups.detach().float().square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedInnerAttentionFFNMLP(TileRoutedDSwiGLUMLP): |
| """Activation-free grouped FFN with an attention block inside the FFN. |
| |
| This removes pointwise gate activations from the full-active path. The |
| hidden groups become four tiny tokens: ``up`` builds q/k, ``value`` builds |
| values, group attention mixes values, and the result is down-projected. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.inner_attn_rank = int(config.inner_attn_rank) |
| self.inner_attn_alpha_max = float(config.inner_attn_alpha_max) |
| self.inner_attn_temperature = float(config.inner_attn_temperature) |
| self.inner_q_weight = nn.Parameter(torch.empty(self.num_groups, self.group_size, self.inner_attn_rank)) |
| self.inner_k_weight = nn.Parameter(torch.empty(self.num_groups, self.group_size, self.inner_attn_rank)) |
| self.inner_attn_bias = nn.Parameter(torch.empty(self.num_groups, self.num_groups)) |
| self.inner_alpha_raw = nn.Parameter(torch.empty(self.num_groups, 1, 1)) |
| self._reset_inner_attention(float(config.inner_attn_alpha_init)) |
| self.last_inner_attn_alpha_mean: torch.Tensor | None = None |
| self.last_inner_attn_entropy: torch.Tensor | None = None |
| self.last_inner_attn_offdiag: torch.Tensor | None = None |
| self.last_inner_attn_update_rms: torch.Tensor | None = None |
| self.last_inner_attn_value_rms: torch.Tensor | None = None |
|
|
| def _reset_inner_attention(self, alpha_init: float) -> None: |
| nn.init.normal_(self.inner_q_weight, mean=0.0, std=self.group_size**-0.5) |
| nn.init.normal_(self.inner_k_weight, mean=0.0, std=self.group_size**-0.5) |
| with torch.no_grad(): |
| self.inner_attn_bias.fill_(-2.0) |
| self.inner_attn_bias.diagonal().fill_(2.0) |
| alpha = max(-0.999 * self.inner_attn_alpha_max, min(0.999 * self.inner_attn_alpha_max, alpha_init)) |
| raw = math.atanh(alpha / self.inner_attn_alpha_max) if self.inner_attn_alpha_max > 0.0 else 0.0 |
| with torch.no_grad(): |
| self.inner_alpha_raw.fill_(raw) |
|
|
| def _dense_weights(self, hidden_size: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return up_weight, value_weight, down_weight |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight, value_weight, down_weight = self._dense_weights(hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| up_groups = up.reshape(up.shape[0], self.num_groups, self.group_size) |
| value_groups = value.reshape(value.shape[0], self.num_groups, self.group_size) |
|
|
| dtype = up_groups.dtype |
| up_norm = up_groups * torch.rsqrt(up_groups.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + 1e-6) |
| q = torch.einsum("ngc,gcr->ngr", up_norm, self.inner_q_weight.to(device=up.device, dtype=dtype)) |
| k = torch.einsum("ngc,gcr->ngr", up_norm, self.inner_k_weight.to(device=up.device, dtype=dtype)) |
| logits = torch.einsum("ngr,nhr->ngh", q, k) |
| logits = logits * (self.inner_attn_rank**-0.5) / max(self.inner_attn_temperature, 1e-4) |
| logits = logits + self.inner_attn_bias.to(device=up.device, dtype=dtype).view(1, self.num_groups, self.num_groups) |
| attn = torch.softmax(logits.float(), dim=-1).to(dtype=dtype) |
| mixed = torch.einsum("ngh,nhc->ngc", attn, value_groups) |
| alpha = self.inner_attn_alpha_max * torch.tanh( |
| self.inner_alpha_raw.to(device=up.device, dtype=dtype) |
| ).view(1, self.num_groups, 1) |
| hidden_groups = value_groups + alpha * (mixed - value_groups) |
| output = hidden_groups.reshape_as(value) @ down_weight |
|
|
| if not torch.is_grad_enabled(): |
| probs = attn.detach().float().clamp_min(1e-8) |
| entropy = -(probs * probs.log()).sum(dim=-1).mean() |
| diag = torch.diagonal(attn.detach().float(), dim1=-2, dim2=-1).mean() |
| update = (mixed - value_groups).detach().float() |
| value_float = value_groups.detach().float() |
| self.last_inner_attn_alpha_mean = alpha.detach().float().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_inner_attn_entropy = entropy.to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_inner_attn_offdiag = (1.0 - diag).to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_inner_attn_update_rms = update.square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_inner_attn_value_rms = value_float.square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| return output.reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedBilinearMemoryStepFFNMLP(TileRoutedDSwiGLUMLP): |
| """Grouped FFN with a small bounded inner update loop instead of a gate. |
| |
| This is not a pointwise activation replacement. The FFN makes group hidden |
| states, repeatedly applies a bilinear product correction, RMS-bounds that |
| correction, shares a small mean-bus between groups, and only then writes |
| through the down projection. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.bilinear_memory_depth = int(config.bilinear_memory_depth) |
| self.bilinear_memory_alpha_max = float(config.bilinear_memory_alpha_max) |
| self.bilinear_memory_target_ratio = float(config.bilinear_memory_target_ratio) |
| self.bilinear_memory_bus_mix = float(config.bilinear_memory_bus_mix) |
| self.bilinear_memory_state_update = float(config.bilinear_memory_state_update) |
| self.memory_scale = nn.Parameter(torch.empty(1, self.num_groups, self.group_size)) |
| self.inner_alpha_raw = nn.Parameter(torch.empty(self.num_groups, 1, 1)) |
| self._reset_bilinear_memory(float(config.bilinear_memory_alpha_init)) |
| self.last_bilinear_alpha_mean: torch.Tensor | None = None |
| self.last_bilinear_update_ratio: torch.Tensor | None = None |
| self.last_bilinear_hidden_rms: torch.Tensor | None = None |
| self.last_bilinear_state_rms: torch.Tensor | None = None |
|
|
| def _reset_bilinear_memory(self, alpha_init: float) -> None: |
| nn.init.normal_(self.memory_scale, mean=1.0, std=0.02) |
| alpha = max(-0.999 * self.bilinear_memory_alpha_max, min(0.999 * self.bilinear_memory_alpha_max, alpha_init)) |
| raw = math.atanh(alpha / self.bilinear_memory_alpha_max) if self.bilinear_memory_alpha_max > 0.0 else 0.0 |
| with torch.no_grad(): |
| self.inner_alpha_raw.fill_(raw) |
|
|
| def _dense_weights(self, hidden_size: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return up_weight, value_weight, down_weight |
|
|
| @staticmethod |
| def _rms_norm(x: torch.Tensor) -> torch.Tensor: |
| return x * torch.rsqrt(x.float().square().mean(dim=-1, keepdim=True).to(dtype=x.dtype) + 1e-6) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight, value_weight, down_weight = self._dense_weights(hidden_size) |
| up = x_flat @ up_weight |
| value = x_flat @ value_weight |
| state = self._rms_norm(up.reshape(up.shape[0], self.num_groups, self.group_size)) |
| hidden = value.reshape(value.shape[0], self.num_groups, self.group_size) |
| alpha = self.bilinear_memory_alpha_max * torch.tanh( |
| self.inner_alpha_raw.to(device=up.device, dtype=up.dtype) |
| ).view(1, self.num_groups, 1) |
| memory = self.memory_scale.to(device=up.device, dtype=up.dtype) |
| last_ratio = hidden.new_tensor(0.0) |
|
|
| for _ in range(self.bilinear_memory_depth): |
| correction = state * hidden * memory |
| if self.bilinear_memory_bus_mix > 0.0: |
| bus = correction.mean(dim=1, keepdim=True) |
| correction = correction + self.bilinear_memory_bus_mix * (bus - correction) |
| hidden_rms = hidden.float().square().mean(dim=-1, keepdim=True).sqrt().to(dtype=hidden.dtype).clamp_min(1e-6) |
| correction_rms = correction.float().square().mean(dim=-1, keepdim=True).sqrt().to(dtype=hidden.dtype).clamp_min(1e-6) |
| scale = (self.bilinear_memory_target_ratio * hidden_rms / correction_rms).detach() |
| correction = correction * scale |
| hidden = hidden + alpha * correction |
| if self.bilinear_memory_state_update > 0.0: |
| state = self._rms_norm(state + self.bilinear_memory_state_update * correction) |
| if not torch.is_grad_enabled(): |
| last_ratio = (correction.detach().float().square().mean().sqrt() / hidden.detach().float().square().mean().sqrt().clamp_min(1e-6)).to(dtype=hidden.dtype) |
|
|
| output = hidden.reshape_as(value) @ down_weight |
| if not torch.is_grad_enabled(): |
| self.last_bilinear_alpha_mean = alpha.detach().float().mean().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_bilinear_update_ratio = last_ratio.to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_bilinear_hidden_rms = hidden.detach().float().square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| self.last_bilinear_state_rms = state.detach().float().square().mean().sqrt().to(device=x_flat.device, dtype=x_flat.dtype) |
| return output.reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedRationalGateDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """SwiGLU plus a tiny learned rational gate correction. |
| |
| Inspired by KAT/KAN-style learnable activations, but initialized as exactly |
| SwiGLU so the model starts from the known-good grouped path. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.rational_num = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
| self.rational_den = nn.Parameter(torch.ones(self.num_groups, 1, 1)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| output = torch.zeros_like(x_flat) |
| for group_idx in range(self.num_groups): |
| up = x_flat @ self.up_weight[group_idx] |
| value = x_flat @ self.value_weight[group_idx] |
| correction = self.rational_num[group_idx] * up.square() / (1.0 + self.rational_den[group_idx].abs() * up.abs()) |
| hidden = (F.silu(up) + correction) * value |
| output = output + hidden @ self.down_weight[group_idx] |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedExpGateDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """SwiGLU with a learnable stabilized exponential gate multiplier.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.exp_gate_alpha = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| output = torch.zeros_like(x_flat) |
| for group_idx in range(self.num_groups): |
| up = x_flat @ self.up_weight[group_idx] |
| value = x_flat @ self.value_weight[group_idx] |
| multiplier = torch.exp(self.exp_gate_alpha[group_idx].to(dtype=up.dtype) * torch.tanh(up)) |
| hidden = F.silu(up) * multiplier * value |
| output = output + hidden @ self.down_weight[group_idx] |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedHiddenRMSNormDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Zero-init hidden RMS correction before the down projection.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.hidden_norm_alpha = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| output = torch.zeros_like(x_flat) |
| for group_idx in range(self.num_groups): |
| hidden = F.silu(x_flat @ self.up_weight[group_idx]) * (x_flat @ self.value_weight[group_idx]) |
| rms = hidden.float().pow(2).mean(dim=-1, keepdim=True).add(self.config.norm_eps).rsqrt().to(dtype=hidden.dtype) |
| hidden_normed = hidden * rms |
| alpha = self.hidden_norm_alpha[group_idx].to(device=hidden.device, dtype=hidden.dtype).clamp(-1.0, 1.0) |
| hidden = hidden + alpha * (hidden_normed - hidden) |
| output = output + hidden @ self.down_weight[group_idx] |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedLateTokenHiddenRMSPreserveDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Late, token-gated hidden RMS correction with the grouped fastpath preserved.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.late_layer_start = int(config.grouped_mlp_hidden_rms_late_layer_start) |
| self.alpha_max = float(config.grouped_mlp_hidden_rms_alpha_max) |
| alpha_init = max( |
| -0.999 * self.alpha_max, |
| min(0.999 * self.alpha_max, float(config.grouped_mlp_hidden_rms_alpha_init)), |
| ) |
| raw_alpha = math.atanh(alpha_init / self.alpha_max) |
| self.hidden_norm_alpha_raw = nn.Parameter(torch.full((self.num_groups, 1, 1), raw_alpha)) |
| self.hidden_norm_gate = nn.Linear(config.n_embd, self.num_groups, bias=True) |
| nn.init.zeros_(self.hidden_norm_gate.weight) |
| gate_init = max(1e-4, min(1.0 - 1e-4, float(config.grouped_mlp_hidden_rms_gate_init))) |
| nn.init.constant_(self.hidden_norm_gate.bias, math.log(gate_init / (1.0 - gate_init))) |
| self.hidden_norm_output_scale = float(config.grouped_mlp_hidden_rms_output_scale) |
| self.hidden_norm_token_gate = bool(config.grouped_mlp_hidden_rms_token_gate) |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| if self.block_idx < self.late_layer_start or self.hidden_norm_output_scale == 0.0: |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| rms = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True).add(self.config.norm_eps).rsqrt() |
| hidden_normed = hidden_groups * rms.to(dtype=hidden_groups.dtype) |
| alpha = self.alpha_max * torch.tanh( |
| self.hidden_norm_alpha_raw.to(device=hidden.device, dtype=hidden.dtype) |
| ).view(1, self.num_groups, 1) |
| if self.hidden_norm_token_gate: |
| gate = torch.sigmoid(self.hidden_norm_gate(x_flat).float()).to(dtype=hidden.dtype).unsqueeze(-1) |
| else: |
| gate = 1.0 |
| correction = (hidden_normed - hidden_groups) * (alpha * gate * self.hidden_norm_output_scale) |
| corrected_hidden = (hidden_groups + correction).reshape(hidden.shape[0], self.intermediate_size) |
| return (corrected_hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedRMSMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped SwiGLU with normalized hidden group memory exchange. |
| |
| The base 4/4 grouped SwiGLU is preserved. Hidden group activations are RMS |
| normalized, softly mixed across groups, then blended back before the down |
| projection. This moves communication into hidden scratch space instead of |
| adding an unbounded late residual correction. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| logits = torch.full((self.num_groups, self.num_groups), float(config.rms_memory_mix_offdiag_logit)) |
| logits.fill_diagonal_(float(config.rms_memory_mix_identity_logit)) |
| self.group_memory_mix_logits = nn.Parameter(logits) |
| self.rms_memory_alpha_max = float(config.rms_memory_alpha_max) |
| self.hidden_memory_alpha_raw = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
| target = float(config.rms_memory_target_init) |
| target_min = float(config.rms_memory_target_min) |
| target_max = float(config.rms_memory_target_max) |
| target = min(max(target, target_min + 1e-6), target_max - 1e-6) |
| ratio = (target - target_min) / (target_max - target_min) |
| self.hidden_rms_target_raw = nn.Parameter(torch.full((self.num_groups, 1, 1), math.log(ratio / (1.0 - ratio)))) |
| self.rms_memory_target_min = target_min |
| self.rms_memory_target_max = target_max |
| self.rms_memory_token_gate = bool(config.rms_memory_token_gate) |
| if self.rms_memory_token_gate: |
| self.memory_gate_weight = nn.Parameter(torch.zeros(self.num_groups, config.n_embd)) |
| gate_init = min(max(float(config.rms_memory_gate_init), 1e-4), 1.0 - 1e-4) |
| self.memory_gate_bias = nn.Parameter(torch.full((self.num_groups,), math.log(gate_init / (1.0 - gate_init)))) |
| self.rms_weight_reparam = bool(config.rms_weight_reparam) |
| if self.rms_weight_reparam: |
| self.up_weight_gain = nn.Parameter(torch.ones(self.num_groups, 1, 1)) |
| self.value_weight_gain = nn.Parameter(torch.ones(self.num_groups, 1, 1)) |
| self.down_weight_gain = nn.Parameter(torch.ones(self.num_groups, 1, 1)) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self._scheduled_alpha_initialized = False |
| self.last_hidden_rms_before: torch.Tensor | None = None |
| self.last_hidden_rms_after: torch.Tensor | None = None |
| self.last_memory_alpha_mean: torch.Tensor | None = None |
| self.last_memory_mix_offdiag: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| if not self._scheduled_alpha_initialized: |
| alpha = self._scheduled_alpha(block_idx, n_layer) |
| alpha = max(-0.999 * self.rms_memory_alpha_max, min(0.999 * self.rms_memory_alpha_max, alpha)) |
| with torch.no_grad(): |
| self.hidden_memory_alpha_raw.fill_(math.atanh(alpha / self.rms_memory_alpha_max)) |
| self._scheduled_alpha_initialized = True |
|
|
| def _scheduled_alpha(self, block_idx: int, n_layer: int) -> float: |
| observed = [-0.074, -0.090, -0.089, -0.090, -0.084, -0.084, -0.060, -0.043, -0.020, 0.107, 0.235, 0.351] |
| if n_layer <= 1: |
| return observed[-1] |
| pos = block_idx * (len(observed) - 1) / max(1, n_layer - 1) |
| lo = int(math.floor(pos)) |
| hi = min(len(observed) - 1, lo + 1) |
| frac = pos - lo |
| return observed[lo] * (1.0 - frac) + observed[hi] * frac |
|
|
| def _target_rms(self, hidden: torch.Tensor) -> torch.Tensor: |
| raw = self.hidden_rms_target_raw.to(device=hidden.device, dtype=torch.float32) |
| target = self.rms_memory_target_min + (self.rms_memory_target_max - self.rms_memory_target_min) * torch.sigmoid(raw) |
| return target.to(dtype=hidden.dtype).view(1, self.num_groups, 1) |
|
|
| def _maybe_reparam_weight(self, weight: torch.Tensor, gain: torch.Tensor | None, target_std: float) -> torch.Tensor: |
| if not self.rms_weight_reparam or gain is None: |
| return weight |
| rms = weight.float().pow(2).mean(dim=(-2, -1), keepdim=True).sqrt().clamp_min(1e-8) |
| scale = (target_std / rms).to(device=weight.device, dtype=weight.dtype) |
| return weight * scale * gain.to(device=weight.device, dtype=weight.dtype) |
|
|
| def _weights(self) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| down_std = 1.0 / math.sqrt(self.config.n_embd) / self.config.n_layer |
| up = self._maybe_reparam_weight(self.up_weight, getattr(self, "up_weight_gain", None), std) |
| value = self._maybe_reparam_weight(self.value_weight, getattr(self, "value_weight_gain", None), std) |
| down = self._maybe_reparam_weight(self.down_weight, getattr(self, "down_weight_gain", None), down_std) |
| return up, value, down |
|
|
| def _token_gate(self, x_flat: torch.Tensor) -> torch.Tensor | float: |
| if not self.rms_memory_token_gate: |
| return 1.0 |
| x_rms = x_flat.float().pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-8) |
| x_normed = (x_flat.float() / x_rms).to(dtype=x_flat.dtype) |
| logits = F.linear( |
| x_normed, |
| self.memory_gate_weight.to(device=x_flat.device, dtype=x_flat.dtype), |
| self.memory_gate_bias.to(device=x_flat.device, dtype=x_flat.dtype), |
| ) |
| return torch.sigmoid(logits.float()).to(dtype=x_flat.dtype).unsqueeze(-1) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight, value_weight, down_weight = self._weights() |
| up_dense = up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_dense = value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| hidden = F.silu(x_flat @ up_dense) * (x_flat @ value_dense) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_rms = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| hidden_normed = hidden_groups * torch.rsqrt( |
| hidden_groups.float().pow(2).mean(dim=-1, keepdim=True) + self.config.norm_eps |
| ).to(dtype=hidden_groups.dtype) |
| hidden_normed = hidden_normed * self._target_rms(hidden_groups) |
| mix = torch.softmax(self.group_memory_mix_logits.float(), dim=-1).to(device=hidden.device, dtype=hidden.dtype) |
| mixed = torch.einsum("og,ngs->nos", mix, hidden_normed) |
| alpha = self.rms_memory_alpha_max * torch.tanh( |
| self.hidden_memory_alpha_raw.to(device=hidden.device, dtype=hidden.dtype) |
| ).view(1, self.num_groups, 1) |
| gate = self._token_gate(x_flat) |
| hidden_groups = hidden_groups + alpha * gate * (mixed - hidden_groups) |
| down_dense = down_weight.reshape(self.intermediate_size, hidden_size) |
| output = hidden_groups.reshape(hidden.shape[0], self.intermediate_size) @ down_dense |
| if not torch.is_grad_enabled(): |
| self.last_hidden_rms_before = hidden_rms.detach().mean() |
| self.last_hidden_rms_after = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True).sqrt().detach().mean() |
| self.last_memory_alpha_mean = alpha.detach().mean() |
| eye = torch.eye(self.num_groups, device=mix.device, dtype=torch.bool) |
| self.last_memory_mix_offdiag = mix.masked_select(~eye).detach().mean() |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFastRMSMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Speed-first hidden RMS memory for full-active grouped SwiGLU. |
| |
| The early layers run the exact grouped4 dense-GEMM fastpath. Enabled late |
| layers keep the same up/value/down GEMMs, but insert a cheap hidden-space |
| mean-bus exchange before the down projection. The exchange preserves each |
| group's hidden RMS, so it changes direction more than magnitude. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.fast_rms_memory_schedule = str(config.fast_rms_memory_schedule) |
| self.fast_rms_memory_alpha_max = float(config.fast_rms_memory_alpha_max) |
| self.fast_rms_memory_use_rms_norm = bool(config.fast_rms_memory_use_rms_norm) |
| self.hidden_memory_alpha_raw = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
| target = float(config.rms_memory_target_init) |
| target_min = float(config.rms_memory_target_min) |
| target_max = float(config.rms_memory_target_max) |
| target = min(max(target, target_min + 1e-6), target_max - 1e-6) |
| ratio = (target - target_min) / (target_max - target_min) |
| self.hidden_rms_target_raw = nn.Parameter(torch.full((self.num_groups, 1, 1), math.log(ratio / (1.0 - ratio)))) |
| self.rms_memory_target_min = target_min |
| self.rms_memory_target_max = target_max |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.memory_enabled = False |
| self._scheduled_alpha_initialized = False |
| self.last_hidden_rms_before: torch.Tensor | None = None |
| self.last_hidden_rms_after: torch.Tensor | None = None |
| self.last_memory_alpha_mean: torch.Tensor | None = None |
| self.last_memory_bus_rms: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| if not self._scheduled_alpha_initialized: |
| alpha = self._scheduled_alpha(block_idx, n_layer) |
| self.memory_enabled = abs(alpha) > 0.0 |
| alpha = max(-0.999 * self.fast_rms_memory_alpha_max, min(0.999 * self.fast_rms_memory_alpha_max, alpha)) |
| with torch.no_grad(): |
| self.hidden_memory_alpha_raw.fill_(math.atanh(alpha / self.fast_rms_memory_alpha_max)) |
| self._scheduled_alpha_initialized = True |
|
|
| def _scheduled_alpha(self, block_idx: int, n_layer: int) -> float: |
| if self.fast_rms_memory_schedule == "late8_meanbus": |
| if n_layer == 12: |
| schedule = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.05, 0.10, 0.18, 0.25] |
| return schedule[block_idx] |
| start = max(0, n_layer - 4) |
| late = [0.05, 0.10, 0.18, 0.25] |
| return late[block_idx - start] if block_idx >= start else 0.0 |
| if self.fast_rms_memory_schedule == "late9_meanbus": |
| if n_layer == 12: |
| schedule = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.08, 0.16, 0.25] |
| return schedule[block_idx] |
| start = max(0, n_layer - 3) |
| late = [0.08, 0.16, 0.25] |
| return late[block_idx - start] if block_idx >= start else 0.0 |
| if self.fast_rms_memory_schedule == "all_tiny_meanbus": |
| observed = [-0.074, -0.090, -0.089, -0.090, -0.084, -0.084, -0.060, -0.043, -0.020, 0.107, 0.235, 0.351] |
| if n_layer <= 1: |
| value = observed[-1] |
| else: |
| pos = block_idx * (len(observed) - 1) / max(1, n_layer - 1) |
| lo = int(math.floor(pos)) |
| hi = min(len(observed) - 1, lo + 1) |
| frac = pos - lo |
| value = observed[lo] * (1.0 - frac) + observed[hi] * frac |
| return max(-0.03, min(0.08, value)) |
| raise ValueError(f"unknown fast_rms_memory_schedule={self.fast_rms_memory_schedule!r}") |
|
|
| def _target_scale(self, hidden: torch.Tensor) -> torch.Tensor: |
| raw = self.hidden_rms_target_raw.to(device=hidden.device, dtype=torch.float32) |
| target = self.rms_memory_target_min + (self.rms_memory_target_max - self.rms_memory_target_min) * torch.sigmoid(raw) |
| return target.to(dtype=hidden.dtype).view(1, self.num_groups, 1) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| if not self.memory_enabled: |
| return super()._forward_full_active(x, original_shape, x_flat) |
|
|
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| alpha = self.fast_rms_memory_alpha_max * torch.tanh( |
| self.hidden_memory_alpha_raw.to(device=hidden.device, dtype=hidden.dtype) |
| ) |
| if not self.fast_rms_memory_use_rms_norm: |
| |
| |
| alpha_weight = alpha.to(device=self.down_weight.device, dtype=self.down_weight.dtype) |
| shared_down = (alpha_weight * self.down_weight).mean(dim=0, keepdim=True) |
| effective_down = (1.0 - alpha_weight) * self.down_weight + shared_down |
| output = hidden @ effective_down.reshape(self.intermediate_size, hidden_size) |
| if not torch.is_grad_enabled(): |
| self.last_hidden_rms_before = hidden.float().pow(2).mean(dim=-1).sqrt().detach().mean() |
| self.last_hidden_rms_after = self.last_hidden_rms_before |
| self.last_memory_alpha_mean = alpha.detach().mean() |
| self.last_memory_bus_rms = hidden.new_tensor(0.0) |
| return output.reshape(original_shape) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
|
|
| hidden_var = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True) |
| hidden_rms = torch.sqrt(hidden_var + self.config.norm_eps) |
| hidden_normed = hidden_groups * torch.rsqrt(hidden_var + self.config.norm_eps).to(dtype=hidden_groups.dtype) |
| bus = hidden_normed.mean(dim=1, keepdim=True) |
| bus_var = bus.float().pow(2).mean(dim=-1, keepdim=True) |
| bus = bus * torch.rsqrt(bus_var + self.config.norm_eps).to(dtype=bus.dtype) |
| memory = bus * hidden_rms.to(dtype=hidden_groups.dtype) * self._target_scale(hidden_groups) |
| alpha = alpha.view(1, self.num_groups, 1) |
| hidden_groups = hidden_groups + alpha * (memory - hidden_groups) |
|
|
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| output = hidden_groups.reshape(hidden.shape[0], self.intermediate_size) @ down_weight |
| if not torch.is_grad_enabled(): |
| self.last_hidden_rms_before = hidden_rms.detach().mean() |
| self.last_hidden_rms_after = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True).sqrt().detach().mean() |
| self.last_memory_alpha_mean = alpha.detach().mean() |
| self.last_memory_bus_rms = bus.float().pow(2).mean(dim=-1, keepdim=True).sqrt().detach().mean() |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedCoAdaptHiddenControllerDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Late-only train-time hidden controller for grouped4. |
| |
| This keeps grouped4's full-active up/value/down GEMM path. In late layers, |
| a zero-init low-rank controller can add a tiny RMS-bounded hidden-space |
| correction before the down projection. The design is the train-time, |
| co-adapted version of the oracle-positive hidden write probe; at |
| initialization it is exactly the grouped4 fastpath. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.controller_rank = int(config.hidden_controller_rank) |
| self.controller_rho_frac = float(config.hidden_controller_rho_frac) |
| self.controller_late_layer_start = int(config.hidden_controller_late_layer_start) |
| self.controller_down = nn.Parameter(torch.empty(self.num_groups, self.group_size, self.controller_rank)) |
| self.controller_up = nn.Parameter(torch.zeros(self.num_groups, self.controller_rank, self.group_size)) |
| nn.init.normal_(self.controller_down, mean=0.0, std=self.group_size**-0.5) |
| self.last_hidden_controller_ratio: torch.Tensor | None = None |
| self.last_hidden_controller_raw_rms: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| enabled = self._controller_enabled() |
| self.controller_down.requires_grad_(enabled) |
| self.controller_up.requires_grad_(enabled) |
|
|
| def _controller_enabled(self) -> bool: |
| return self.controller_rho_frac > 0.0 and self.block_idx >= self.controller_late_layer_start |
|
|
| def _hidden_controller(self, hidden_groups: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| dtype = hidden_groups.dtype |
| hidden_rms = hidden_groups.float().square().mean(dim=-1, keepdim=True).sqrt().to(dtype=dtype) |
| h_norm = hidden_groups * torch.rsqrt( |
| hidden_groups.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + self.config.norm_eps |
| ) |
| low = torch.einsum("ngs,gsr->ngr", h_norm, self.controller_down.to(dtype=dtype)) |
| raw = torch.einsum("ngr,grs->ngs", torch.tanh(low), self.controller_up.to(dtype=dtype)) |
| raw_rms = torch.sqrt(raw.float().square().mean(dim=-1, keepdim=True) + self.config.norm_eps) |
| raw_rms = raw_rms.to(dtype=dtype) |
| target = self.controller_rho_frac * hidden_rms |
| correction = raw * (target / raw_rms).clamp(max=1.0) |
| ratio = correction.float().square().mean().sqrt() / hidden_groups.float().square().mean().sqrt().clamp_min(1e-8) |
| return hidden_groups + correction, ratio, raw_rms.detach().mean() |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| if not self._controller_enabled(): |
| return super()._forward_full_active(x, original_shape, x_flat) |
|
|
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_groups, ratio, raw_rms = self._hidden_controller(hidden_groups) |
| if not torch.is_grad_enabled(): |
| self.last_hidden_controller_ratio = ratio.detach() |
| self.last_hidden_controller_raw_rms = raw_rms.detach() |
| output = hidden_groups.reshape(hidden.shape[0], self.intermediate_size) @ down_weight |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedHiddenDirectionMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Late-only hidden key/value direction memory for grouped4. |
| |
| Each group owns a tiny bank of correction directions. Hidden activations |
| query the bank, select a value direction with softmax, and commit only an |
| RMS-bounded correction before the down projection. Values are zero-init, so |
| the model starts exactly as grouped4 and must learn useful directions during |
| pretraining instead of receiving an unbounded residual update. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.memory_slots = int(config.hidden_direction_memory_slots) |
| self.memory_rho_frac = float(config.hidden_direction_memory_rho_frac) |
| self.memory_late_layer_start = int(config.hidden_direction_memory_late_layer_start) |
| self.memory_temperature = float(config.hidden_direction_memory_temperature) |
| self.memory_key = nn.Parameter(torch.empty(self.num_groups, self.group_size, self.memory_slots)) |
| self.memory_value = nn.Parameter(torch.zeros(self.num_groups, self.memory_slots, self.group_size)) |
| nn.init.normal_(self.memory_key, mean=0.0, std=self.group_size**-0.5) |
| self.last_direction_memory_entropy: torch.Tensor | None = None |
| self.last_direction_memory_ratio: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| enabled = self._memory_enabled() |
| self.memory_key.requires_grad_(enabled) |
| self.memory_value.requires_grad_(enabled) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.memory_rho_frac > 0.0 and self.block_idx >= self.memory_late_layer_start |
|
|
| def _direction_memory(self, hidden_groups: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: |
| dtype = hidden_groups.dtype |
| hidden_rms = hidden_groups.float().square().mean(dim=-1, keepdim=True).sqrt().to(dtype=dtype) |
| h_norm = hidden_groups * torch.rsqrt( |
| hidden_groups.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + self.config.norm_eps |
| ) |
| key = self.memory_key.to(dtype=dtype) |
| value = self.memory_value.to(dtype=dtype) |
| logits = torch.einsum("ngs,gsm->ngm", h_norm, key) |
| logits = logits / (math.sqrt(self.group_size) * self.memory_temperature) |
| weights = torch.softmax(logits.float(), dim=-1).to(dtype=dtype) |
| raw = torch.einsum("ngm,gms->ngs", weights, value) |
| raw_rms = torch.sqrt(raw.float().square().mean(dim=-1, keepdim=True) + self.config.norm_eps).to(dtype=dtype) |
| target = self.memory_rho_frac * hidden_rms |
| correction = raw * (target / raw_rms).clamp(max=1.0) |
| ratio = correction.float().square().mean().sqrt() / hidden_groups.float().square().mean().sqrt().clamp_min(1e-8) |
| entropy = -(weights.float() * weights.float().clamp_min(1e-8).log()).sum(dim=-1).mean() |
| return hidden_groups + correction, ratio, entropy |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return super()._forward_full_active(x, original_shape, x_flat) |
|
|
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_groups, ratio, entropy = self._direction_memory(hidden_groups) |
| if not torch.is_grad_enabled(): |
| self.last_direction_memory_ratio = ratio.detach() |
| self.last_direction_memory_entropy = entropy.detach() |
| output = hidden_groups.reshape(hidden.shape[0], self.intermediate_size) @ down_weight |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedScalarDirectionMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Speed-first hidden direction memory with no extra hidden matmul. |
| |
| Each group stores one learned direction. A token reads that memory by |
| cosine-like alignment with its normalized hidden state, then writes a tiny |
| RMS-capped signed correction before the down projection. This is less |
| expressive than a key/value bank but keeps the extra work to reductions and |
| elementwise ops in late layers. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.scalar_memory_rho_frac = float(config.scalar_direction_memory_rho_frac) |
| self.scalar_memory_late_layer_start = int(config.scalar_direction_memory_late_layer_start) |
| self.scalar_memory_direction = nn.Parameter(torch.empty(self.num_groups, self.group_size)) |
| self.scalar_memory_alpha_raw = nn.Parameter(torch.zeros(self.num_groups, 1, 1)) |
| nn.init.normal_(self.scalar_memory_direction, mean=0.0, std=1.0) |
| self.last_scalar_memory_alpha: torch.Tensor | None = None |
| self.last_scalar_memory_gate_abs: torch.Tensor | None = None |
| self.last_scalar_memory_ratio: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| enabled = self._memory_enabled() |
| self.scalar_memory_direction.requires_grad_(enabled) |
| self.scalar_memory_alpha_raw.requires_grad_(enabled) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.scalar_memory_rho_frac > 0.0 and self.block_idx >= self.scalar_memory_late_layer_start |
|
|
| def _scalar_memory(self, hidden_groups: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: |
| dtype = hidden_groups.dtype |
| hidden_rms = hidden_groups.float().square().mean(dim=-1, keepdim=True).sqrt().to(dtype=dtype) |
| h_norm = hidden_groups * torch.rsqrt( |
| hidden_groups.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + self.config.norm_eps |
| ) |
| direction = self.scalar_memory_direction.to(dtype=dtype) |
| direction = direction * torch.rsqrt(direction.float().square().mean(dim=-1, keepdim=True).to(dtype=dtype) + self.config.norm_eps) |
| direction = direction.unsqueeze(0) |
| gate = torch.tanh((h_norm * direction).mean(dim=-1, keepdim=True)) |
| alpha = torch.tanh(self.scalar_memory_alpha_raw.to(dtype=dtype)).view(1, self.num_groups, 1) |
| correction = alpha * gate * direction * (self.scalar_memory_rho_frac * hidden_rms) |
| ratio = correction.float().square().mean().sqrt() / hidden_groups.float().square().mean().sqrt().clamp_min(1e-8) |
| return hidden_groups + correction, ratio, alpha.detach().abs().mean(), gate.detach().abs().mean() |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return super()._forward_full_active(x, original_shape, x_flat) |
|
|
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_groups, ratio, alpha_abs, gate_abs = self._scalar_memory(hidden_groups) |
| if not torch.is_grad_enabled(): |
| self.last_scalar_memory_ratio = ratio.detach() |
| self.last_scalar_memory_alpha = alpha_abs.detach() |
| self.last_scalar_memory_gate_abs = gate_abs.detach() |
| output = hidden_groups.reshape(hidden.shape[0], self.intermediate_size) @ down_weight |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedChannelMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Foldable FFN memory-value gain for grouped4. |
| |
| FFN hidden channels act like keys; down-projection rows are the memory |
| values. This class learns a bounded per-channel value strength. It starts |
| exactly as grouped4 and can be folded into ``down_weight`` for inference, |
| so it changes what the FFN remembers without adding a new token-time |
| reasoning path. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.channel_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, self.group_size, 1)) |
| self.last_channel_memory_gain_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.channel_memory_gain_raw.requires_grad_(self._memory_enabled()) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _effective_down_weight(self) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return self.down_weight |
| gain = self.channel_memory_gain_max * torch.tanh( |
| self.channel_memory_gain_raw.to(device=self.down_weight.device, dtype=self.down_weight.dtype) |
| ) |
| if not torch.is_grad_enabled(): |
| self.last_channel_memory_gain_abs = gain.detach().abs().mean() |
| return self.down_weight * (1.0 + gain) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self._effective_down_weight().reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
|
|
| class TileRoutedChannelStateMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Per-channel token-conditioned memory for grouped4. |
| |
| The previous scalar group-state memory could move argmaxes but only had one |
| learned gain per group. This variant keeps the same cheap group-state signal |
| but gives each hidden channel its own bounded value-control coefficient. |
| Zero gain is exactly grouped4. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.channel_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, self.group_size)) |
| self.last_channel_state_gain_abs: torch.Tensor | None = None |
| self.last_channel_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.channel_memory_gain_raw.requires_grad_(self._memory_enabled()) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _apply_channel_state_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| group_rms = hidden_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| group_state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
| gain = self.channel_memory_gain_max * torch.tanh( |
| self.channel_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).unsqueeze(0) |
| if not torch.is_grad_enabled(): |
| self.last_channel_state_gain_abs = gain.detach().abs().mean() |
| self.last_channel_state_abs = group_state.detach().abs().mean() |
| hidden_groups = hidden_groups * (1.0 + gain * group_state) |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_channel_state_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedCenteredChannelStateMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Mean-preserving per-channel token-conditioned memory for grouped4.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.channel_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, self.group_size)) |
| self.last_channel_state_gain_abs: torch.Tensor | None = None |
| self.last_channel_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.channel_memory_gain_raw.requires_grad_(self._memory_enabled()) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _apply_centered_channel_state_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| centered = hidden_groups - group_mean.to(dtype=hidden_groups.dtype) |
| group_rms = hidden_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| group_state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
| gain = self.channel_memory_gain_max * torch.tanh( |
| self.channel_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).unsqueeze(0) |
| if not torch.is_grad_enabled(): |
| self.last_channel_state_gain_abs = gain.detach().abs().mean() |
| self.last_channel_state_abs = group_state.detach().abs().mean() |
| hidden_groups = hidden_groups + centered * (gain * group_state) |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_centered_channel_state_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedCenteredChannelGroupStateMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Mean-preserving channel memory plus scalar group-state memory.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.group_state_memory_gain_max = float(config.group_state_memory_gain_max) |
| self.group_state_memory_late_layer_start = int(config.group_state_memory_late_layer_start) |
| self.channel_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, self.group_size)) |
| self.group_state_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, 1)) |
| self.last_channel_state_gain_abs: torch.Tensor | None = None |
| self.last_group_state_gain_abs: torch.Tensor | None = None |
| self.last_group_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.channel_memory_gain_raw.requires_grad_(self._channel_memory_enabled()) |
| self.group_state_memory_gain_raw.requires_grad_(self._group_state_memory_enabled()) |
|
|
| def _channel_memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _group_state_memory_enabled(self) -> bool: |
| return self.group_state_memory_gain_max > 0.0 and self.block_idx >= self.group_state_memory_late_layer_start |
|
|
| def _apply_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._channel_memory_enabled() and not self._group_state_memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| group_rms = hidden_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| group_state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
|
|
| if self._channel_memory_enabled(): |
| centered = hidden_groups - group_mean.to(dtype=hidden_groups.dtype) |
| channel_gain = self.channel_memory_gain_max * torch.tanh( |
| self.channel_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).unsqueeze(0) |
| if not torch.is_grad_enabled(): |
| self.last_channel_state_gain_abs = channel_gain.detach().abs().mean() |
| hidden_groups = hidden_groups + centered * (channel_gain * group_state) |
|
|
| if self._group_state_memory_enabled(): |
| group_gain = self.group_state_memory_gain_max * torch.tanh( |
| self.group_state_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).view(1, self.num_groups, 1) |
| if not torch.is_grad_enabled(): |
| self.last_group_state_gain_abs = group_gain.detach().abs().mean() |
| self.last_group_state_abs = group_state.detach().abs().mean() |
| hidden_groups = hidden_groups * (1.0 + group_gain * group_state) |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedKeyedChannelMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Prototype-keyed token-conditioned memory for grouped4. |
| |
| This reads a richer token signal than group mean/RMS: each group compares |
| its normalized centered hidden state to learned prototype keys, then uses |
| zero-init bounded value vectors to modulate centered channels. With zero |
| values the function is exactly grouped4. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.keyed_channel_memory_rank = int(config.keyed_channel_memory_rank) |
| key = torch.randn(self.num_groups, self.keyed_channel_memory_rank, self.group_size) |
| key = key * (self.group_size ** -0.5) |
| self.keyed_memory_key = nn.Parameter(key) |
| self.keyed_memory_value_raw = nn.Parameter( |
| torch.zeros(self.num_groups, self.keyed_channel_memory_rank, self.group_size) |
| ) |
| self.last_keyed_memory_gain_abs: torch.Tensor | None = None |
| self.last_keyed_memory_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| enabled = self._memory_enabled() |
| self.keyed_memory_key.requires_grad_(enabled) |
| self.keyed_memory_value_raw.requires_grad_(enabled) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _apply_keyed_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| centered = hidden_groups - group_mean.to(dtype=hidden_groups.dtype) |
| centered_float = centered.float() |
| centered_rms = centered_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| centered_norm = centered_float / centered_rms |
|
|
| key = self.keyed_memory_key.to(device=hidden.device, dtype=torch.float32) |
| key = key / key.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| state = torch.tanh((centered_norm.unsqueeze(2) * key.unsqueeze(0)).mean(dim=-1)) |
| value = self.channel_memory_gain_max * torch.tanh( |
| self.keyed_memory_value_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ) |
| channel_gain = (state.to(dtype=hidden_groups.dtype).unsqueeze(-1) * value.unsqueeze(0)).sum(dim=2) |
| channel_gain = channel_gain * (self.keyed_channel_memory_rank ** -0.5) |
| if not torch.is_grad_enabled(): |
| self.last_keyed_memory_gain_abs = channel_gain.detach().abs().mean() |
| self.last_keyed_memory_state_abs = state.detach().abs().mean() |
| hidden_groups = hidden_groups + centered * channel_gain |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_keyed_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGroupStateMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Token-conditioned bounded group memory for grouped4. |
| |
| This keeps grouped4's full-active GEMM path, then lets each hidden group |
| scale its own channels from a signed normalized group state. Zero gain is |
| exactly grouped4, while nonzero gain gives a cheap token-dependent memory |
| write before the down projection. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.group_state_memory_gain_max = float(config.group_state_memory_gain_max) |
| self.group_state_memory_late_layer_start = int(config.group_state_memory_late_layer_start) |
| self.group_state_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, 1)) |
| self.last_group_state_gain_abs: torch.Tensor | None = None |
| self.last_group_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.group_state_memory_gain_raw.requires_grad_(self._memory_enabled()) |
|
|
| def _memory_enabled(self) -> bool: |
| return self.group_state_memory_gain_max > 0.0 and self.block_idx >= self.group_state_memory_late_layer_start |
|
|
| def _apply_group_state_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| group_rms = hidden_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| group_state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
| gain = self.group_state_memory_gain_max * torch.tanh( |
| self.group_state_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).view(1, self.num_groups, 1) |
| if not torch.is_grad_enabled(): |
| self.last_group_state_gain_abs = gain.detach().abs().mean() |
| self.last_group_state_abs = group_state.detach().abs().mean() |
| hidden_groups = hidden_groups * (1.0 + gain * group_state) |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_group_state_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedChannelGroupStateMemoryDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Combined foldable channel values plus token-conditioned group memory.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.block_idx = 0 |
| self.n_layer = config.n_layer |
| self.channel_memory_gain_max = float(config.channel_memory_gain_max) |
| self.channel_memory_late_layer_start = int(config.channel_memory_late_layer_start) |
| self.group_state_memory_gain_max = float(config.group_state_memory_gain_max) |
| self.group_state_memory_late_layer_start = int(config.group_state_memory_late_layer_start) |
| self.channel_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, self.group_size, 1)) |
| self.group_state_memory_gain_raw = nn.Parameter(torch.zeros(self.num_groups, 1)) |
| self.last_channel_memory_gain_abs: torch.Tensor | None = None |
| self.last_group_state_gain_abs: torch.Tensor | None = None |
| self.last_group_state_abs: torch.Tensor | None = None |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
| self.channel_memory_gain_raw.requires_grad_(self._channel_memory_enabled()) |
| self.group_state_memory_gain_raw.requires_grad_(self._group_state_memory_enabled()) |
|
|
| def _channel_memory_enabled(self) -> bool: |
| return self.channel_memory_gain_max > 0.0 and self.block_idx >= self.channel_memory_late_layer_start |
|
|
| def _group_state_memory_enabled(self) -> bool: |
| return self.group_state_memory_gain_max > 0.0 and self.block_idx >= self.group_state_memory_late_layer_start |
|
|
| def _effective_down_weight(self) -> torch.Tensor: |
| if not self._channel_memory_enabled(): |
| return self.down_weight |
| gain = self.channel_memory_gain_max * torch.tanh( |
| self.channel_memory_gain_raw.to(device=self.down_weight.device, dtype=self.down_weight.dtype) |
| ) |
| if not torch.is_grad_enabled(): |
| self.last_channel_memory_gain_abs = gain.detach().abs().mean() |
| return self.down_weight * (1.0 + gain) |
|
|
| def _apply_group_state_memory(self, hidden: torch.Tensor) -> torch.Tensor: |
| if not self._group_state_memory_enabled(): |
| return hidden |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| hidden_float = hidden_groups.float() |
| group_mean = hidden_float.mean(dim=-1, keepdim=True) |
| group_rms = hidden_float.square().mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-6) |
| group_state = torch.tanh(group_mean / group_rms).to(dtype=hidden_groups.dtype) |
| gain = self.group_state_memory_gain_max * torch.tanh( |
| self.group_state_memory_gain_raw.to(device=hidden.device, dtype=hidden_groups.dtype) |
| ).view(1, self.num_groups, 1) |
| if not torch.is_grad_enabled(): |
| self.last_group_state_gain_abs = gain.detach().abs().mean() |
| self.last_group_state_abs = group_state.detach().abs().mean() |
| hidden_groups = hidden_groups * (1.0 + gain * group_state) |
| return hidden_groups.reshape(hidden.shape) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self._effective_down_weight().reshape(self.intermediate_size, hidden_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden = self._apply_group_state_memory(hidden) |
| return (hidden @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGRNDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Grouped hidden Global Response Normalization before the down projection.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.grn_gamma = nn.Parameter(torch.zeros(self.num_groups, self.group_size)) |
| self.grn_beta = nn.Parameter(torch.zeros(self.num_groups, self.group_size)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| group_norm = hidden_groups.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| response = group_norm / group_norm.mean(dim=1, keepdim=True).clamp_min(1e-6) |
| gamma = self.grn_gamma.to(device=hidden.device, dtype=hidden.dtype).unsqueeze(0) |
| beta = self.grn_beta.to(device=hidden.device, dtype=hidden.dtype).unsqueeze(0) |
| hidden_groups = hidden_groups + gamma * (hidden_groups * response.to(dtype=hidden.dtype)) + beta |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return (hidden_groups.reshape_as(hidden) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedGroupMixDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Learn a tiny soft group-to-group hidden mixer before the down weights.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| logits = torch.full((self.num_groups, self.num_groups), -6.0) |
| logits.fill_diagonal_(6.0) |
| self.group_mix_logits = nn.Parameter(logits) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| mix = torch.softmax(self.group_mix_logits.to(device=hidden.device, dtype=torch.float32), dim=-1).to(dtype=hidden.dtype) |
| hidden_groups = torch.einsum("og,ngs->nos", mix, hidden_groups) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return (hidden_groups.reshape_as(hidden) @ down_weight).reshape(original_shape) |
|
|
|
|
| class TileRoutedTokenGroupGateDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Token-dependent competition between full-active groups.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.group_gate_alpha = nn.Parameter(torch.tensor(0.05)) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = original_shape[-1] |
| up_weight = self.up_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.value_weight.permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| hidden = F.silu(x_flat @ up_weight) * (x_flat @ value_weight) |
| hidden_groups = hidden.reshape(hidden.shape[0], self.num_groups, self.group_size) |
| scores = hidden_groups.float().pow(2).mean(dim=-1).sqrt() |
| gates = torch.softmax(scores, dim=-1).to(dtype=hidden.dtype).unsqueeze(-1) * self.num_groups |
| alpha = self.group_gate_alpha.to(device=hidden.device, dtype=hidden.dtype).clamp(-0.5, 0.5) |
| hidden_groups = hidden_groups * (1.0 + alpha * (gates - 1.0)) |
| down_weight = self.down_weight.reshape(self.intermediate_size, hidden_size) |
| return (hidden_groups.reshape_as(hidden) @ down_weight).reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedBlockNormDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped MLP with block-local RMS normalization per group. |
| |
| Hard hidden blocks damaged LM quality because each FFN block lost access to |
| most of the residual stream. This softer variant keeps the standard |
| full-hidden grouped MLP: every intermediate group still sees all hidden |
| channels. The non-absorbed part is an input-dependent local RMS normalization |
| over hidden-channel blocks before each group path. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| if config.n_embd % self.num_groups != 0: |
| raise ValueError("n_embd must be divisible by sparse_mlp_num_groups") |
| self.block_hidden_size = config.n_embd // self.num_groups |
| self.group_norm_weight = nn.Parameter(torch.ones(self.num_groups, config.n_embd)) |
|
|
| def _block_norm(self, x_flat: torch.Tensor, group_idx: int) -> torch.Tensor: |
| x_blocks = x_flat.reshape(x_flat.shape[0], self.num_groups, self.block_hidden_size) |
| scale = torch.rsqrt(x_blocks.pow(2).mean(dim=-1, keepdim=True) + self.config.norm_eps) |
| x_normed = (x_blocks * scale).reshape_as(x_flat) |
| weight = self.group_norm_weight[group_idx].to(device=x_flat.device, dtype=x_flat.dtype) |
| return x_normed * weight |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| output = torch.zeros_like(x_flat) |
| for group_idx in range(self.num_groups): |
| group_input = self._block_norm(x_flat, group_idx) |
| hidden = F.silu(group_input @ self.up_weight[group_idx]) * (group_input @ self.value_weight[group_idx]) |
| output = output + hidden @ self.down_weight[group_idx] |
| return output.reshape(original_shape) |
|
|
|
|
|
|
| class TileRoutedStackedDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped dSwiGLU with serial group communication. |
| |
| At ``grouped_mlp_stack_alpha == 0`` this is equivalent to full-active |
| grouped 4/4. With positive alpha, each group sees the residual state |
| updated by earlier group outputs, making the groups a small ordered |
| sub-FFN instead of independent parallel subspaces. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| stack_alpha = torch.tensor(float(config.grouped_mlp_stack_alpha)) |
| if config.grouped_mlp_stack_alpha_learnable: |
| self.stack_alpha = nn.Parameter(stack_alpha) |
| else: |
| self.register_buffer("stack_alpha", stack_alpha, persistent=False) |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| output = torch.zeros_like(x_flat) |
| state = x_flat |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for group_idx in range(self.num_groups): |
| hidden = F.silu(state @ self.up_weight[group_idx]) * (state @ self.value_weight[group_idx]) |
| delta = hidden @ self.down_weight[group_idx] |
| output = output + delta |
| if group_idx + 1 < self.num_groups: |
| state = state + stack_alpha * delta |
| return output.reshape(original_shape) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| if self.active_groups != self.num_groups: |
| return super().forward(x) |
| output = self._forward_full_active(x, original_shape, x_flat) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return self._apply_shared_path(x, output) |
|
|
|
|
|
|
| class TileRoutedChunkedStackedDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full-active grouped MLP with serial stage chunks inside existing groups. |
| |
| This keeps the exact ``TileRoutedDSwiGLUMLP`` parameter layout. The |
| intermediate channels in each group are split into ``grouped_mlp_stack_depth`` |
| chunks. At alpha=0, summing all chunks is exactly the normal grouped MLP. |
| Non-zero alpha lets later chunks see a small residual correction from earlier |
| chunks, testing the ARC margin effect without changing initialization layout. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.stack_depth = config.grouped_mlp_stack_depth |
| if self.stack_depth < 1: |
| raise ValueError("grouped_mlp_stack_depth must be >= 1") |
| if self.group_size % self.stack_depth != 0: |
| raise ValueError("group_size must be divisible by grouped_mlp_stack_depth") |
| self.stage_group_size = self.group_size // self.stack_depth |
| self.stage_intermediate_size = self.stage_group_size * self.num_groups |
| stack_alpha = torch.tensor(float(config.grouped_mlp_stack_alpha)) |
| if config.grouped_mlp_stack_alpha_learnable: |
| self.stack_alpha = nn.Parameter(stack_alpha) |
| else: |
| self.register_buffer("stack_alpha", stack_alpha, persistent=False) |
| output_scale = torch.tensor(float(config.grouped_mlp_stack_output_scale)) |
| if config.grouped_mlp_stack_output_scale_learnable: |
| self.stack_output_scale = nn.Parameter(output_scale) |
| else: |
| self.register_buffer("stack_output_scale", output_scale, persistent=False) |
|
|
| def _stage_forward(self, stage_idx: int, state: torch.Tensor) -> torch.Tensor: |
| hidden_size = state.shape[-1] |
| start = stage_idx * self.stage_group_size |
| stop = (stage_idx + 1) * self.stage_group_size |
| up_weight = self.up_weight[:, :, start:stop].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| value_weight = self.value_weight[:, :, start:stop].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| down_weight = self.down_weight[:, start:stop, :].reshape(self.stage_intermediate_size, hidden_size) |
| return (F.silu(state @ up_weight) * (state @ value_weight)) @ down_weight |
|
|
| def _forward_full_active(self, x: torch.Tensor, original_shape: torch.Size, x_flat: torch.Tensor) -> torch.Tensor: |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| output = output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state = state + stack_alpha * delta |
| output_scale = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| return (output * output_scale).reshape(original_shape) |
|
|
|
|
| class TileRoutedGatedStackedDSwiGLUMLP(nn.Module): |
| """Grouped MLP main path plus a gated independent split-stage branch. |
| |
| The total intermediate budget is conserved: |
| |
| - ``intermediate_size - grouped_mlp_contrastive_intermediate_size`` goes to |
| a normal full-active grouped MLP main path. |
| - ``grouped_mlp_contrastive_intermediate_size`` goes to a split-stage |
| branch, the architecture that produced stronger ARC margins. |
| |
| This tests whether the split-stage branch is useful as a bounded |
| contrastive/ranking perturbation instead of replacing the whole MLP. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| self.stack_depth = config.grouped_mlp_stack_depth |
| self.branch_intermediate_size = int(config.grouped_mlp_contrastive_intermediate_size) |
| if self.active_groups != self.num_groups: |
| raise ValueError("TileRoutedGatedStackedDSwiGLUMLP is a full-active grouped variant.") |
| if self.stack_depth < 1: |
| raise ValueError("grouped_mlp_stack_depth must be >= 1") |
| if config.bias: |
| raise ValueError("TileRoutedGatedStackedDSwiGLUMLP currently expects bias=False.") |
| if self.branch_intermediate_size <= 0: |
| raise ValueError("grouped_mlp_contrastive_intermediate_size must be > 0") |
| if self.branch_intermediate_size >= self.intermediate_size: |
| raise ValueError("contrastive branch must be smaller than intermediate_size") |
| self.main_intermediate_size = self.intermediate_size - self.branch_intermediate_size |
| if self.main_intermediate_size % self.num_groups != 0: |
| raise ValueError("main intermediate must divide across sparse_mlp_num_groups") |
| if self.branch_intermediate_size % (self.num_groups * self.stack_depth) != 0: |
| raise ValueError("contrastive branch intermediate must divide across groups * stack depth") |
| self.main_group_size = self.main_intermediate_size // self.num_groups |
| self.branch_stage_intermediate_size = self.branch_intermediate_size // self.stack_depth |
| self.branch_group_size = self.branch_stage_intermediate_size // self.num_groups |
|
|
| self.main_up_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.main_group_size)) |
| self.main_value_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.main_group_size)) |
| self.main_down_weight = nn.Parameter(torch.empty(self.num_groups, self.main_group_size, config.n_embd)) |
| self.branch_up_weight = nn.Parameter( |
| torch.empty(self.stack_depth, self.num_groups, config.n_embd, self.branch_group_size) |
| ) |
| self.branch_value_weight = nn.Parameter( |
| torch.empty(self.stack_depth, self.num_groups, config.n_embd, self.branch_group_size) |
| ) |
| self.branch_down_weight = nn.Parameter( |
| torch.empty(self.stack_depth, self.num_groups, self.branch_group_size, config.n_embd) |
| ) |
|
|
| stack_alpha = torch.tensor(float(config.grouped_mlp_stack_alpha)) |
| if config.grouped_mlp_stack_alpha_learnable: |
| self.stack_alpha = nn.Parameter(stack_alpha) |
| else: |
| self.register_buffer("stack_alpha", stack_alpha, persistent=False) |
|
|
| gate_max = float(config.grouped_mlp_contrastive_gate_max) |
| if gate_max <= 0: |
| raise ValueError("grouped_mlp_contrastive_gate_max must be > 0") |
| self.contrastive_gate_max = gate_max |
| gate = float(config.grouped_mlp_contrastive_gate) |
| if config.grouped_mlp_contrastive_gate_learnable: |
| ratio = max(-0.999, min(0.999, gate / gate_max)) |
| self.raw_contrastive_gate = nn.Parameter(torch.tensor(math.atanh(ratio))) |
| else: |
| self.register_buffer("contrastive_gate", torch.tensor(gate), persistent=False) |
|
|
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.main_up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.main_value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.main_down_weight, mean=0.0, std=std) |
| branch_std = std * float(getattr(self.config, "grouped_mlp_stack_init_scale", 1.0)) |
| nn.init.normal_(self.branch_up_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_value_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_down_weight, mean=0.0, std=branch_std) |
|
|
| def _main_forward(self, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = x_flat.shape[-1] |
| up_weight = self.main_up_weight.permute(1, 0, 2).reshape(hidden_size, self.main_intermediate_size) |
| value_weight = self.main_value_weight.permute(1, 0, 2).reshape(hidden_size, self.main_intermediate_size) |
| down_weight = self.main_down_weight.reshape(self.main_intermediate_size, hidden_size) |
| return (F.silu(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
|
|
| def _branch_stage_forward(self, stage_idx: int, state: torch.Tensor) -> torch.Tensor: |
| hidden_size = state.shape[-1] |
| up_weight = self.branch_up_weight[stage_idx].permute(1, 0, 2).reshape( |
| hidden_size, self.branch_stage_intermediate_size |
| ) |
| value_weight = self.branch_value_weight[stage_idx].permute(1, 0, 2).reshape( |
| hidden_size, self.branch_stage_intermediate_size |
| ) |
| down_weight = self.branch_down_weight[stage_idx].reshape(self.branch_stage_intermediate_size, hidden_size) |
| return (F.silu(state @ up_weight) * (state @ value_weight)) @ down_weight |
|
|
| def _branch_forward(self, x_flat: torch.Tensor) -> torch.Tensor: |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._branch_stage_forward(stage_idx, state) |
| output = output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state = state + stack_alpha * delta |
| return output |
|
|
| def _gate(self, x_flat: torch.Tensor) -> torch.Tensor: |
| if hasattr(self, "raw_contrastive_gate"): |
| gate = self.contrastive_gate_max * torch.tanh(self.raw_contrastive_gate) |
| else: |
| gate = self.contrastive_gate |
| return gate.to(device=x_flat.device, dtype=x_flat.dtype) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| output = self._main_forward(x_flat) + self._gate(x_flat) * self._branch_forward(x_flat) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainGatedStackedDSwiGLUMLP(TileRoutedGatedStackedDSwiGLUMLP): |
| """Full grouped MLP plus a ReZero-style split-stack residual branch. |
| |
| ``TileRoutedGatedStackedDSwiGLUMLP`` conserves total intermediate width by |
| moving channels from the normal grouped MLP into the split-stack branch. That |
| is useful as a fixed-budget control, but it weakens the LM path whenever the |
| branch gate starts small. This variant keeps the full grouped MLP width and |
| adds the split-stack branch on top behind the same bounded learnable gate. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.main_intermediate_size = self.intermediate_size |
| self.main_group_size = self.intermediate_size // self.num_groups |
| self.main_up_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.main_group_size)) |
| self.main_value_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.main_group_size)) |
| self.main_down_weight = nn.Parameter(torch.empty(self.num_groups, self.main_group_size, config.n_embd)) |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.main_up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.main_value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.main_down_weight, mean=0.0, std=std) |
| branch_std = std * float(getattr(self.config, "grouped_mlp_stack_init_scale", 1.0)) |
| nn.init.normal_(self.branch_up_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_value_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_down_weight, mean=0.0, std=branch_std) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self._branch_forward(x_flat) |
| output = main_output + self._gate(x_flat) * branch_output |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainRMSBoundedGatedStackedDSwiGLUMLP(TileRoutedFullMainGatedStackedDSwiGLUMLP): |
| """Full-main gated split-stack with final RMS residual scaling. |
| |
| The full-main gated screen showed that a small branch gate alone does not |
| prevent the whole MLP update from growing to split-stack scale. This variant |
| interprets ``grouped_mlp_stack_output_scale`` as the target output/input RMS |
| ratio for the complete MLP output. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| output_scale = torch.tensor(float(config.grouped_mlp_stack_output_scale)) |
| if config.grouped_mlp_stack_output_scale_learnable: |
| self.stack_output_scale = nn.Parameter(output_scale) |
| else: |
| self.register_buffer("stack_output_scale", output_scale, persistent=False) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self._branch_forward(x_flat) |
| output = main_output + self._gate(x_flat) * branch_output |
| target_ratio = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| input_rms = x_flat.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| output_rms = output.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| scale = target_ratio * input_rms / output_rms.clamp_min(torch.finfo(output.dtype).eps) |
| output = output * scale |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP( |
| TileRoutedFullMainRMSBoundedGatedStackedDSwiGLUMLP |
| ): |
| def _rms_bound_output(self, x_flat: torch.Tensor, output: torch.Tensor) -> torch.Tensor: |
| target_ratio = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| input_rms = x_flat.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| output_rms = output.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| scale = target_ratio * input_rms / output_rms.clamp_min(torch.finfo(output.dtype).eps) |
| return output * scale |
|
|
| def _match_token_rms(self, source: torch.Tensor, reference: torch.Tensor) -> torch.Tensor: |
| source_rms = source.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| reference_rms = reference.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| scale = reference_rms / source_rms.clamp_min(1e-8) |
| return source * scale.to(device=source.device, dtype=source.dtype) |
|
|
|
|
| class TileRoutedFullMainOrthogonalRMSGatedStackedDSwiGLUMLP( |
| TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP |
| ): |
| """Add only the branch component that is orthogonal to the main grouped update.""" |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self._branch_forward(x_flat) |
| dot = (branch_output.float() * main_output.float()).sum(dim=-1, keepdim=True) |
| denom = main_output.float().pow(2).sum(dim=-1, keepdim=True).clamp_min(1e-8) |
| branch_output = branch_output - (dot / denom).to(dtype=branch_output.dtype) * main_output |
| branch_output = self._match_token_rms(branch_output, main_output) |
| output = main_output + self._gate(x_flat) * branch_output |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainAlignedRMSGatedStackedDSwiGLUMLP( |
| TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP |
| ): |
| """Convert the branch into a bounded token-wise modulation of the main update.""" |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self._branch_forward(x_flat) |
| dot = (branch_output.float() * main_output.float()).sum(dim=-1, keepdim=True) |
| denom = main_output.float().pow(2).sum(dim=-1, keepdim=True).clamp_min(1e-8) |
| coeff = (dot / denom).clamp(min=-1.0, max=1.0).to(dtype=main_output.dtype) |
| output = main_output * (1.0 + self._gate(x_flat) * coeff) |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainNormedBranchRMSGatedStackedDSwiGLUMLP( |
| TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP |
| ): |
| """Normalize branch token RMS to the main update before the gated add.""" |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self._match_token_rms(self._branch_forward(x_flat), main_output) |
| output = main_output + self._gate(x_flat) * branch_output |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedFullMainCompressedRMSGatedStackedDSwiGLUMLP( |
| TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP |
| ): |
| """Compress the split branch through a small dense bottleneck before adding it.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| rank = int(config.grouped_mlp_mix_rank) if config.grouped_mlp_mix_rank > 0 else max(1, config.n_embd // 4) |
| self.branch_compress = nn.Linear(config.n_embd, rank, bias=False) |
| self.branch_expand = nn.Linear(rank, config.n_embd, bias=False) |
| nn.init.normal_(self.branch_compress.weight, mean=0.0, std=0.02) |
| nn.init.normal_(self.branch_expand.weight, mean=0.0, std=0.02) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| branch_output = self.branch_expand(torch.tanh(self.branch_compress(self._branch_forward(x_flat)))) |
| branch_output = self._match_token_rms(branch_output, main_output) |
| output = main_output + self._gate(x_flat) * branch_output |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedLateOnlyRMSGatedStackedDSwiGLUMLP(TileRoutedFullMainDirectionalRMSGatedStackedDSwiGLUMLP): |
| """Use the branch only in later transformer blocks.""" |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_output = self._main_forward(x_flat) |
| start_layer = int(getattr(self.config, "grouped_mlp_branch_start_layer", 0)) |
| if getattr(self, "block_idx", 0) >= start_layer: |
| branch_output = self._match_token_rms(self._branch_forward(x_flat), main_output) |
| output = main_output + self._gate(x_flat) * branch_output |
| else: |
| output = main_output |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedAdditiveLateTokenRMSGatedDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Full grouped 4/4 MLP plus a tiny late token-conditioned branch. |
| |
| This keeps the base grouped MLP parameter names and shapes unchanged so a |
| trained ``TileRoutedDSwiGLUMLP`` checkpoint can initialize the full LM path |
| exactly. Only the additive branch and its token gate are new. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| branch_intermediate_size = int(config.grouped_mlp_contrastive_intermediate_size) |
| if branch_intermediate_size <= 0: |
| raise ValueError("grouped_mlp_contrastive_intermediate_size must be > 0") |
| if branch_intermediate_size % self.num_groups != 0: |
| raise ValueError("contrastive branch intermediate must divide across sparse_mlp_num_groups") |
| self.branch_intermediate_size = branch_intermediate_size |
| self.branch_group_size = branch_intermediate_size // self.num_groups |
| self.branch_up_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.branch_group_size)) |
| self.branch_value_weight = nn.Parameter(torch.empty(self.num_groups, config.n_embd, self.branch_group_size)) |
| self.branch_down_weight = nn.Parameter(torch.empty(self.num_groups, self.branch_group_size, config.n_embd)) |
| self.token_gate = nn.Linear(config.n_embd, 1, bias=True) |
| gate_max = float(config.grouped_mlp_contrastive_gate_max) |
| if gate_max <= 0: |
| raise ValueError("grouped_mlp_contrastive_gate_max must be > 0") |
| self.contrastive_gate_max = gate_max |
| gate = float(config.grouped_mlp_contrastive_gate) |
| ratio = max(-0.999, min(0.999, gate / gate_max)) |
| self.initial_gate_raw = math.atanh(ratio) |
| self.block_idx = 0 |
| self.n_layer = 1 |
| self.reset_branch_parameters() |
|
|
| def reset_branch_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| branch_std = std * float(getattr(self.config, "grouped_mlp_stack_init_scale", 1.0)) |
| nn.init.normal_(self.branch_up_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_value_weight, mean=0.0, std=branch_std) |
| nn.init.normal_(self.branch_down_weight, mean=0.0, std=branch_std) |
| nn.init.zeros_(self.token_gate.weight) |
| nn.init.constant_(self.token_gate.bias, self.initial_gate_raw) |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.n_layer = n_layer |
|
|
| def _branch_forward(self, x_flat: torch.Tensor) -> torch.Tensor: |
| hidden_size = x_flat.shape[-1] |
| up_weight = self.branch_up_weight.permute(1, 0, 2).reshape(hidden_size, self.branch_intermediate_size) |
| value_weight = self.branch_value_weight.permute(1, 0, 2).reshape(hidden_size, self.branch_intermediate_size) |
| down_weight = self.branch_down_weight.reshape(self.branch_intermediate_size, hidden_size) |
| return (F.silu(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
|
|
| def _match_token_rms(self, source: torch.Tensor, reference: torch.Tensor) -> torch.Tensor: |
| source_rms = source.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| reference_rms = reference.float().pow(2).mean(dim=-1, keepdim=True).sqrt() |
| scale = reference_rms / source_rms.clamp_min(1e-8) |
| return source * scale.to(device=source.device, dtype=source.dtype) |
|
|
| def _token_gate(self, x_flat: torch.Tensor) -> torch.Tensor: |
| x_rms = x_flat.float().pow(2).mean(dim=-1, keepdim=True).sqrt().clamp_min(1e-8) |
| x_normed = (x_flat.float() / x_rms).to(dtype=x_flat.dtype) |
| raw_gate = self.token_gate(x_normed) |
| return self.contrastive_gate_max * torch.tanh(raw_gate).to(dtype=x_flat.dtype) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| main_output = super().forward(x) |
| start_layer = int(getattr(self.config, "grouped_mlp_branch_start_layer", 0)) |
| if self.block_idx < start_layer: |
| return main_output |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| main_flat = main_output.reshape(-1, original_shape[-1]) |
| branch_output = self._match_token_rms(self._branch_forward(x_flat), main_flat) |
| output = main_flat + self._token_gate(x_flat) * branch_output |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedStackedLayersDSwiGLUMLP(nn.Module): |
| """Compute-matched stack of full-active grouped 4/4 MLP stages. |
| |
| The total intermediate budget is split across ``grouped_mlp_stack_depth`` |
| stages. Each stage is a full-active grouped 4/4 MLP. Earlier stages update |
| the state consumed by later stages, giving group layers a serial path for |
| communication without increasing total MLP parameters or matmul FLOPs. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.stack_depth = config.grouped_mlp_stack_depth |
| if self.stack_depth < 1: |
| raise ValueError("grouped_mlp_stack_depth must be >= 1") |
| if config.sparse_mlp_max_active_groups != self.num_groups: |
| raise ValueError("TileRoutedStackedLayersDSwiGLUMLP is a full-active 4/4 variant.") |
| if self.intermediate_size % (self.num_groups * self.stack_depth) != 0: |
| raise ValueError("intermediate_size must divide across stack_depth * sparse_mlp_num_groups") |
| if config.bias: |
| raise ValueError("TileRoutedStackedLayersDSwiGLUMLP currently expects bias=False.") |
| self.stage_intermediate_size = self.intermediate_size // self.stack_depth |
| self.group_size = self.stage_intermediate_size // self.num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| self.up_weight = nn.Parameter(torch.empty(self.stack_depth, self.num_groups, config.n_embd, self.group_size)) |
| self.value_weight = nn.Parameter(torch.empty(self.stack_depth, self.num_groups, config.n_embd, self.group_size)) |
| self.down_weight = nn.Parameter(torch.empty(self.stack_depth, self.num_groups, self.group_size, config.n_embd)) |
| stack_alpha = torch.tensor(float(config.grouped_mlp_stack_alpha)) |
| if config.grouped_mlp_stack_alpha_learnable: |
| self.stack_alpha = nn.Parameter(stack_alpha) |
| else: |
| self.register_buffer("stack_alpha", stack_alpha, persistent=False) |
| output_scale = torch.tensor(float(config.grouped_mlp_stack_output_scale)) |
| if config.grouped_mlp_stack_output_scale_learnable: |
| self.stack_output_scale = nn.Parameter(output_scale) |
| else: |
| self.register_buffer("stack_output_scale", output_scale, persistent=False) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| std *= float(getattr(self.config, "grouped_mlp_stack_init_scale", 1.0)) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
|
|
| def _stage_forward(self, stage_idx: int, state: torch.Tensor) -> torch.Tensor: |
| hidden_size = state.shape[-1] |
| up_weight = self.up_weight[stage_idx].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| value_weight = self.value_weight[stage_idx].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| down_weight = self.down_weight[stage_idx].reshape(self.stage_intermediate_size, hidden_size) |
| return (F.silu(state @ up_weight) * (state @ value_weight)) @ down_weight |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| output = output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state = state + stack_alpha * delta |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| output_scale = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| return (output * output_scale).reshape(original_shape) |
|
|
|
|
| class TileRoutedRMSBoundedStackedLayersDSwiGLUMLP(TileRoutedStackedLayersDSwiGLUMLP): |
| """Stacked grouped layers with bounded residual update RMS. |
| |
| The unbounded split-stack d2 alpha -0.03 improves some choice benchmarks |
| but produces MLP updates about four times stronger than dense/grouped MLPs. |
| Here ``grouped_mlp_stack_output_scale`` is interpreted as a target |
| output/input RMS ratio, keeping the serial group computation while limiting |
| how hard it can perturb the residual stream. |
| """ |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| output = output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state = state + stack_alpha * delta |
| target_ratio = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| input_rms = x_flat.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| output_rms = output.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| scale = target_ratio * input_rms / output_rms.clamp_min(torch.finfo(output.dtype).eps) |
| output = output * scale |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedBlockLayeredNormDSwiGLUMLP(nn.Module): |
| """Deep MLP with block-local RMS normalization at each layer. |
| |
| Unlike split-stack which uses serial state updates, this has real layers |
| where the output of one layer feeds into the next layer. Each layer has |
| block-normalized grouped MLPs. This tests whether deep blocknorm layers |
| improve LM quality without the serial state perturbation of split-stack. |
| |
| The depth is controlled by `grouped_mlp_stack_depth`, but each layer |
| is a full feedforward pass, not a state update. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.num_layers = config.grouped_mlp_stack_depth |
| self.config = config |
| |
| if config.n_embd % self.num_groups != 0: |
| raise ValueError("n_embd must be divisible by sparse_mlp_num_groups") |
| self.block_hidden_size = config.n_embd // self.num_groups |
| self.group_size = self.intermediate_size // self.num_groups |
| |
| |
| self.layer_up_weight = nn.Parameter(torch.empty(self.num_layers, self.num_groups, config.n_embd, self.group_size)) |
| self.layer_value_weight = nn.Parameter(torch.empty(self.num_layers, self.num_groups, config.n_embd, self.group_size)) |
| self.layer_down_weight = nn.Parameter(torch.empty(self.num_layers, self.num_groups, self.group_size, config.n_embd)) |
| self.layer_norm_weight = nn.Parameter(torch.ones(self.num_layers, self.num_groups, config.n_embd)) |
| |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.layer_up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.layer_value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.layer_down_weight, mean=0.0, std=std) |
|
|
| def _block_norm(self, x_flat: torch.Tensor, layer_idx: int, group_idx: int) -> torch.Tensor: |
| x_blocks = x_flat.reshape(x_flat.shape[0], self.num_groups, self.block_hidden_size) |
| scale = torch.rsqrt(x_blocks.pow(2).mean(dim=-1, keepdim=True) + self.config.norm_eps) |
| x_normed = (x_blocks * scale).reshape_as(x_flat) |
| weight = self.layer_norm_weight[layer_idx, group_idx].to(device=x_flat.device, dtype=x_flat.dtype) |
| return x_normed * weight |
|
|
| def _layer_forward(self, x_flat: torch.Tensor, layer_idx: int) -> torch.Tensor: |
| hidden_size = x_flat.shape[-1] |
| |
| |
| up_weight = self.layer_up_weight[layer_idx].permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| value_weight = self.layer_value_weight[layer_idx].permute(1, 0, 2).reshape(hidden_size, self.intermediate_size) |
| down_weight = self.layer_down_weight[layer_idx].reshape(self.intermediate_size, hidden_size) |
| |
| |
| output = torch.zeros_like(x_flat) |
| for group_idx in range(self.num_groups): |
| group_input = self._block_norm(x_flat, layer_idx, group_idx) |
| hidden = F.silu(group_input @ up_weight) * (group_input @ value_weight) |
| output = output + hidden @ down_weight |
| |
| return output |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| |
| |
| for layer_idx in range(self.num_layers): |
| x_flat = self._layer_forward(x_flat, layer_idx) |
| |
| return x_flat.reshape(original_shape) |
|
|
|
|
| class TileRoutedRegulatedStackedLayersDSwiGLUMLP(TileRoutedRMSBoundedStackedLayersDSwiGLUMLP): |
| def _match_global_rms(self, source: torch.Tensor, reference: torch.Tensor) -> torch.Tensor: |
| source_rms = source.float().pow(2).mean().sqrt() |
| reference_rms = reference.float().pow(2).mean().sqrt() |
| scale = reference_rms / source_rms.clamp_min(1e-8) |
| return source * scale.to(device=source.device, dtype=source.dtype) |
|
|
| def _rms_bound_output(self, x_flat: torch.Tensor, output: torch.Tensor) -> torch.Tensor: |
| target_ratio = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| input_rms = x_flat.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| output_rms = output.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| scale = target_ratio * input_rms / output_rms.clamp_min(torch.finfo(output.dtype).eps) |
| return output * scale |
|
|
|
|
| class TileRoutedStageNormRMSBoundedStackedLayersDSwiGLUMLP(TileRoutedRegulatedStackedLayersDSwiGLUMLP): |
| """Normalize each inter-stage state update before the next stage reads it.""" |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| output = output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state_delta = self._match_global_rms(delta, state) |
| state = state + stack_alpha * state_delta |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedWeightedStageRMSBoundedStackedLayersDSwiGLUMLP(TileRoutedRegulatedStackedLayersDSwiGLUMLP): |
| """Learn a soft blend over stage deltas instead of summing stages equally.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.raw_stage_weight = nn.Parameter(torch.zeros(self.stack_depth)) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| weights = F.softmax(self.raw_stage_weight.float(), dim=0).to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| output = output + weights[stage_idx] * self.stack_depth * delta |
| if stage_idx + 1 < self.stack_depth: |
| state_delta = self._match_global_rms(delta, state) |
| state = state + stack_alpha * state_delta |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedMomentumRMSBoundedStackedLayersDSwiGLUMLP(TileRoutedRegulatedStackedLayersDSwiGLUMLP): |
| """Use a momentum stream over stage deltas before updating later stages.""" |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| beta = min(max(float(config.grouped_mlp_mix_alpha), 1e-4), 1 - 1e-4) |
| self.raw_momentum_beta = nn.Parameter(torch.tensor(math.log(beta / (1.0 - beta)))) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| state = x_flat |
| output = torch.zeros_like(x_flat) |
| momentum: torch.Tensor | None = None |
| beta = torch.sigmoid(self.raw_momentum_beta).to(device=x_flat.device, dtype=x_flat.dtype) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| momentum = delta if momentum is None else beta * momentum + (1.0 - beta) * delta |
| output = output + momentum |
| if stage_idx + 1 < self.stack_depth: |
| state_delta = self._match_global_rms(momentum, state) |
| state = state + stack_alpha * state_delta |
| output = self._rms_bound_output(x_flat, output) |
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedLateLayerStackedLayersDSwiGLUMLP(TileRoutedStackedLayersDSwiGLUMLP): |
| """Split-stack only active in late layers where ranking matters most. |
| |
| Early layers use standard grouped 4/4 MLP for clean LM learning. |
| Late layers (config.splitstack_late_layer_start and above) use split-stack |
| for stronger answer-choice ranking signal. This follows the momentum merge |
| result where late8 5% injection preserved LM quality while improving OpenBookQA. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.late_layer_start = getattr(config, "splitstack_late_layer_start", 0) |
| self.block_idx = 0 |
| self.is_late_layer = False |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
| self.is_late_layer = block_idx >= self.late_layer_start |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| if not self.is_late_layer: |
| |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| hidden_size = x_flat.shape[-1] |
| up_weight = self.up_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| value_weight = self.value_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| down_weight = self.down_weight[0].reshape(self.stage_intermediate_size, hidden_size) |
| output = (F.silu(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
| output_scale = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| return (output * output_scale).reshape(original_shape) |
| else: |
| |
| return super().forward(x) |
|
|
|
|
| class TileRoutedGatedStackedLayersDSwiGLUMLP(TileRoutedRMSBoundedStackedLayersDSwiGLUMLP): |
| """Split-stack with learned per-layer contribution gate. |
| |
| A scalar gate controls how much split-stack contributes vs a baseline |
| grouped MLP. This allows the model to learn when strong residual perturbations |
| are helpful (choice ranking) vs harmful (general LM modeling). |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| initial_gate = getattr(config, "splitstack_initial_gate", 0.1) |
| self.raw_gate = nn.Parameter(torch.tensor(math.log(initial_gate / (1.0 - initial_gate)))) |
| self.block_idx = 0 |
|
|
| def set_block_index(self, block_idx: int, n_layer: int) -> None: |
| self.block_idx = block_idx |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
|
|
| |
| hidden_size = x_flat.shape[-1] |
| up_weight = self.up_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| value_weight = self.value_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| down_weight = self.down_weight[0].reshape(self.stage_intermediate_size, hidden_size) |
| baseline_output = (F.silu(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
|
|
| |
| state = x_flat |
| stack_output = torch.zeros_like(x_flat) |
| stack_alpha = self.stack_alpha.to(device=x_flat.device, dtype=x_flat.dtype) |
| for stage_idx in range(self.stack_depth): |
| delta = self._stage_forward(stage_idx, state) |
| stack_output = stack_output + delta |
| if stage_idx + 1 < self.stack_depth: |
| state = state + stack_alpha * delta |
|
|
| |
| target_ratio = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| input_rms = x_flat.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| stack_rms = stack_output.float().pow(2).mean().sqrt().to(dtype=x_flat.dtype) |
| scale = target_ratio * input_rms / stack_rms.clamp_min(torch.finfo(stack_output.dtype).eps) |
| stack_output = stack_output * scale |
|
|
| |
| gate = torch.sigmoid(self.raw_gate).to(device=x_flat.device, dtype=x_flat.dtype) |
| output = (1.0 - gate) * baseline_output + gate * stack_output |
|
|
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(0.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
|
|
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedAttentionOutputStackedLayersDSwiGLUMLP(TileRoutedStackedLayersDSwiGLUMLP): |
| """Apply split-stack to attention output instead of MLP input. |
| |
| The split-stack processes the attention residual stream, then the standard |
| grouped MLP processes the combined stream. This moves the strong residual |
| perturbation to the attention path where it may be less disruptive to |
| general LM modeling while still providing ranking signal. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| self.attention_stack_alpha = getattr(config, "splitstack_attention_alpha", -0.03) |
| self.attention_stack_alpha_tensor = torch.tensor(self.attention_stack_alpha) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| |
| |
| |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| hidden_size = x_flat.shape[-1] |
| up_weight = self.up_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| value_weight = self.value_weight[0].permute(1, 0, 2).reshape(hidden_size, self.stage_intermediate_size) |
| down_weight = self.down_weight[0].reshape(self.stage_intermediate_size, hidden_size) |
| output = (F.silu(x_flat @ up_weight) * (x_flat @ value_weight)) @ down_weight |
| output_scale = self.stack_output_scale.to(device=x_flat.device, dtype=x_flat.dtype) |
| return (output * output_scale).reshape(original_shape) |
|
|
|
|
| class TileRoutedStaticDSwiGLUMLP(TileRoutedDSwiGLUMLP): |
| """Static tile-routed dSwiGLU without the per-route Python math loop. |
| |
| The active route pattern is fixed: tile group `g` uses expert groups |
| `g, g + 1, ...` modulo `num_groups`. This keeps the same structural routing |
| as `TileRoutedDSwiGLUMLP`, but executes the active routes as one batched |
| matmul stack instead of rolling weights and launching one MLP path per |
| route. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__(config, intermediate_size) |
| route_indices = [ |
| (torch.arange(self.num_groups, dtype=torch.long) + route_offset) % self.num_groups |
| for route_offset in range(self.active_groups) |
| ] |
| self.register_buffer("route_indices", torch.stack(route_indices, dim=0), persistent=False) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, original_shape[-1]) |
| usable_tokens = x_flat.shape[0] - (x_flat.shape[0] % self.num_groups) |
| output = x_flat.new_empty(x_flat.shape) |
|
|
| if usable_tokens: |
| tokens_per_group = usable_tokens // self.num_groups |
| x_tiles = x_flat[:usable_tokens].reshape(self.num_groups, tokens_per_group, x_flat.shape[-1]) |
| route_idx = self.route_indices.to(device=x_flat.device) |
| route_up = self.up_weight.index_select(0, route_idx.reshape(-1)).reshape( |
| self.active_groups, self.num_groups, x_flat.shape[-1], self.group_size |
| ) |
| route_value = self.value_weight.index_select(0, route_idx.reshape(-1)).reshape( |
| self.active_groups, self.num_groups, x_flat.shape[-1], self.group_size |
| ) |
| route_down = self.down_weight.index_select(0, route_idx.reshape(-1)).reshape( |
| self.active_groups, self.num_groups, self.group_size, x_flat.shape[-1] |
| ) |
| x_routes = x_tiles.unsqueeze(0).expand(self.active_groups, -1, -1, -1) |
| up = torch.matmul(x_routes, route_up) |
| value = torch.matmul(x_routes, route_value) |
| out_tiles = torch.matmul(F.silu(up) * value, route_down).sum(dim=0) |
| output[:usable_tokens] = out_tiles.reshape(usable_tokens, x_flat.shape[-1]) |
|
|
| if usable_tokens < x_flat.shape[0]: |
| tail = x_flat[usable_tokens:] |
| tail_out = torch.zeros_like(tail) |
| for route_offset in range(self.active_groups): |
| group_id = route_offset % self.num_groups |
| hidden = F.silu(tail @ self.up_weight[group_id]) * (tail @ self.value_weight[group_id]) |
| tail_out = tail_out + hidden @ self.down_weight[group_id] |
| output[usable_tokens:] = tail_out |
|
|
| if not torch.is_grad_enabled(): |
| self.last_gate_sparsity = x_flat.new_tensor(1.0 - self.active_groups / self.num_groups) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(float(self.active_groups)) |
| return self._apply_shared_path(x, output.reshape(original_shape)) |
|
|
|
|
| class TileRoutedDSwiGLUMLPStaticA2(nn.Module): |
| """Hard-specialized fixed-route tile dSwiGLU for the 1M model. |
| |
| This variant intentionally trades flexibility for a simpler execution |
| shape: |
| |
| - hidden size 128 |
| - intermediate size 1024 |
| - 8 fixed tile groups |
| - exactly 2 active fixed routes per tile |
| |
| There is no router, threshold, argsort, gather/scatter, or torch.roll in |
| the forward path. Route selection is encoded directly in the parameter |
| layout: `[active_route, tile_group, hidden, group_size]`. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| if config.bias: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticA2 expects bias=False.") |
| if config.n_embd != 128: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticA2 is specialized for hidden size 128.") |
| if self.intermediate_size != 1024: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticA2 is specialized for intermediate size 1024.") |
| if self.num_groups != 8: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticA2 is specialized for 8 groups.") |
| if self.active_groups != 2 or config.sparse_mlp_min_active_groups != 2: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticA2 is specialized for active groups 2.") |
|
|
| self.group_size = self.intermediate_size // self.num_groups |
| self.up_weight = nn.Parameter(torch.empty(2, 8, 128, self.group_size)) |
| self.value_weight = nn.Parameter(torch.empty(2, 8, 128, self.group_size)) |
| self.down_weight = nn.Parameter(torch.empty(2, 8, self.group_size, 128)) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, 128) |
| usable_tokens = x_flat.shape[0] - (x_flat.shape[0] % 8) |
| output = x_flat.new_empty(x_flat.shape) |
|
|
| if usable_tokens: |
| tokens_per_group = usable_tokens // 8 |
| x_tiles = x_flat[:usable_tokens].reshape(8, tokens_per_group, 128) |
| x_routes = x_tiles.unsqueeze(0) |
| up = torch.matmul(x_routes, self.up_weight) |
| value = torch.matmul(x_routes, self.value_weight) |
| out_tiles = torch.matmul(F.silu(up) * value, self.down_weight).sum(dim=0) |
| output[:usable_tokens] = out_tiles.reshape(usable_tokens, 128) |
|
|
| if usable_tokens < x_flat.shape[0]: |
| tail = x_flat[usable_tokens:] |
| tail_routes = tail.view(1, 1, -1, 128) |
| up = torch.matmul(tail_routes, self.up_weight[:, :1]) |
| value = torch.matmul(tail_routes, self.value_weight[:, :1]) |
| tail_out = torch.matmul(F.silu(up) * value, self.down_weight[:, :1]).sum(dim=(0, 1)) |
| output[usable_tokens:] = tail_out |
|
|
| self.last_gate_sparsity = x_flat.new_tensor(0.75) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(2.0) |
| return output.reshape(original_shape) |
|
|
|
|
| class TileRoutedDSwiGLUMLPStaticGPTS(nn.Module): |
| """Hard-specialized fixed-route tile dSwiGLU for GPT-S-5M shape. |
| |
| Exact shape: |
| - hidden size 192 |
| - intermediate size 672 |
| - 6 fixed tile groups |
| - 2 active fixed routes per tile |
| |
| This intentionally uses a route-major parameter layout to avoid dynamic |
| routing overhead during training: no router, threshold, argsort, |
| gather/scatter, or torch.roll in the forward path. |
| """ |
|
|
| def __init__(self, config: Config, intermediate_size: int | None = None) -> None: |
| super().__init__() |
| self.intermediate_size = intermediate_size or config.intermediate_size |
| self.num_groups = config.sparse_mlp_num_groups |
| self.active_groups = config.sparse_mlp_max_active_groups |
| if config.bias: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticGPTS expects bias=False.") |
| if config.n_embd != 192: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticGPTS is specialized for hidden size 192.") |
| if self.intermediate_size != 672: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticGPTS is specialized for intermediate size 672.") |
| if self.num_groups != 6: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticGPTS is specialized for 6 groups.") |
| if self.active_groups != 2 or config.sparse_mlp_min_active_groups != 2: |
| raise ValueError("TileRoutedDSwiGLUMLPStaticGPTS is specialized for active groups 2.") |
|
|
| self.group_size = self.intermediate_size // self.num_groups |
| self.up_weight = nn.Parameter(torch.empty(2, 6, 192, self.group_size)) |
| self.value_weight = nn.Parameter(torch.empty(2, 6, 192, self.group_size)) |
| self.down_weight = nn.Parameter(torch.empty(2, 6, self.group_size, 192)) |
| self.config = config |
| self.last_gate_sparsity: torch.Tensor | None = None |
| self.last_threshold_mean: torch.Tensor | None = None |
| self.last_active_groups_mean: torch.Tensor | None = None |
| self.reset_parameters() |
|
|
| def reset_parameters(self) -> None: |
| std = math.sqrt(2.0 / 5 / self.config.n_embd) |
| nn.init.normal_(self.up_weight, mean=0.0, std=std) |
| nn.init.normal_(self.value_weight, mean=0.0, std=std) |
| nn.init.normal_(self.down_weight, mean=0.0, std=std) |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| original_shape = x.shape |
| x_flat = x.reshape(-1, 192) |
| usable_tokens = x_flat.shape[0] - (x_flat.shape[0] % 6) |
| output = x_flat.new_empty(x_flat.shape) |
|
|
| if usable_tokens: |
| tokens_per_group = usable_tokens // 6 |
| x_tiles = x_flat[:usable_tokens].reshape(6, tokens_per_group, 192) |
| x_routes = x_tiles.unsqueeze(0) |
| up = torch.matmul(x_routes, self.up_weight) |
| value = torch.matmul(x_routes, self.value_weight) |
| out_tiles = torch.matmul(F.silu(up) * value, self.down_weight).sum(dim=0) |
| output[:usable_tokens] = out_tiles.reshape(usable_tokens, 192) |
|
|
| if usable_tokens < x_flat.shape[0]: |
| tail = x_flat[usable_tokens:] |
| tail_routes = tail.view(1, 1, -1, 192) |
| up = torch.matmul(tail_routes, self.up_weight[:, :1]) |
| value = torch.matmul(tail_routes, self.value_weight[:, :1]) |
| tail_out = torch.matmul(F.silu(up) * value, self.down_weight[:, :1]).sum(dim=(0, 1)) |
| output[usable_tokens:] = tail_out |
|
|
| self.last_gate_sparsity = x_flat.new_tensor(2.0 / 3.0) |
| self.last_threshold_mean = x_flat.new_tensor(0.0) |
| self.last_active_groups_mean = x_flat.new_tensor(2.0) |
| return output.reshape(original_shape) |
|
|
|
|
| class GemmaMLP(LLaMAMLP): |
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| x_fc_1 = self.fc_1(x) |
| x_fc_2 = self.fc_2(x) |
| x = F.gelu(x_fc_1, approximate=self.config.gelu_approximate) * x_fc_2 |
| return self.proj(x) |
|
|
|
|
| class LLaMAMoE(nn.Module): |
| def __init__(self, config: Config) -> None: |
| super().__init__() |
| self.gate = ( |
| nn.Linear(config.n_embd, config.n_expert, bias=False) |
| if not config.n_expert_groups |
| else GroupedTopkRouter(config) |
| ) |
| self.experts = nn.ModuleList( |
| LLaMAMLP(config, intermediate_size=config.moe_intermediate_size) for _ in range(config.n_expert) |
| ) |
| if config.n_shared_expert: |
| self.shared_experts = LLaMAMLP( |
| config, intermediate_size=config.moe_intermediate_size * config.n_shared_expert |
| ) |
| self.config = config |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| """ |
| Derived from: https://github.com/mistralai/mistral-src/blob/b46d6/moe_one_file_ref.py#L203-L219 |
| See also figure 1 in https://arxiv.org/abs/2211.15841 |
| """ |
| B, T, C = x.size() |
| residual_x = x.clone() |
| x = x.view(-1, C) |
| if not self.config.n_expert_groups: |
| router = self.gate(x) |
| probs, indices = torch.topk(router, self.config.n_expert_per_token) |
| probs = probs.softmax(dim=1, dtype=torch.float).to(dtype=x.dtype) |
| else: |
| probs, indices = self.gate(x) |
| if self.config.routed_scaling_factor != 1.0: |
| probs = probs * self.config.routed_scaling_factor |
| masks = indices.unsqueeze(-1) == torch.arange(self.config.n_expert, device=x.device) |
| masks = masks.permute(2, 0, 1) |
| y = torch.zeros_like(x) |
| for mask, expert in zip(masks, self.experts): |
| token_idx, expert_idx = torch.where(mask) |
| y[token_idx] += probs[token_idx, expert_idx, None] * expert(x[token_idx]) |
|
|
| y = y.view(B, T, C) |
| if self.config.n_shared_expert: |
| y = y + self.shared_experts(residual_x) |
| return y |
|
|
|
|
| class GroupedTopkRouter(nn.Module): |
| """ |
| Derived from: https://github.com/huggingface/transformers/blob/main/src/transformers/models/deepseek_v3/modeling_deepseek_v3.py. |
| DeepseekV3TopkRouter class. |
| """ |
|
|
| def __init__(self, config: Config) -> None: |
| super().__init__() |
| self.config = config |
| self.weight = nn.Parameter(torch.empty(config.n_expert, config.n_embd)) |
| self.register_buffer("e_score_correction_bias", torch.zeros(config.n_expert)) |
|
|
| @torch.no_grad() |
| def get_topk_indices(self, scores: torch.Tensor) -> torch.Tensor: |
| scores_for_choice = scores.view(-1, self.config.n_expert) + self.e_score_correction_bias.unsqueeze(0) |
| group_scores = ( |
| scores_for_choice.view(-1, self.config.n_expert_groups, self.config.n_expert // self.config.n_expert_groups) |
| .topk(self.config.n_topk_scores_per_group, dim=-1)[0] |
| .sum(dim=-1) |
| ) |
|
|
| group_idx = torch.topk(group_scores, k=self.config.n_topk_groups, dim=-1, sorted=False)[1] |
| group_mask = torch.zeros_like(group_scores) |
| group_mask.scatter_(1, group_idx, 1) |
| score_mask = ( |
| group_mask.unsqueeze(-1) |
| .expand(-1, self.config.n_expert_groups, self.config.n_expert // self.config.n_expert_groups) |
| .reshape(-1, self.config.n_expert) |
| ) |
| scores_for_choice = scores_for_choice.masked_fill(~score_mask.bool(), 0.0) |
| topk_indices = torch.topk(scores_for_choice, k=self.config.n_expert_per_token, dim=-1, sorted=False)[1] |
| return topk_indices |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| router_logits = F.linear(x.type(torch.float32), self.weight.type(torch.float32)) |
| scores = router_logits.sigmoid() |
| topk_indices = self.get_topk_indices(scores) |
| topk_weights = scores.gather(1, topk_indices) |
| if self.config.norm_topk_prob: |
| denominator = topk_weights.sum(dim=-1, keepdim=True) + 1e-20 |
| topk_weights /= denominator |
| return topk_weights, topk_indices |
|
|
|
|
| |
| def yarn_get_mscale(scale=1, mscale=1): |
| if scale <= 1: |
| return 1.0 |
| return 0.1 * mscale * math.log(scale) + 1.0 |
|
|
|
|
| def build_rope_cache( |
| seq_len: int, |
| n_elem: int, |
| device: torch.device | None = None, |
| base: int = 10000, |
| condense_ratio: int = 1, |
| extra_config: dict | None = None, |
| rope_local_base_freq: float | None = None, |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| """ |
| Enhanced Transformer with Rotary Position Embedding. |
| |
| Args: |
| seq_len (int): Sequence length. |
| n_elem (int): Number of elements (head dimension). |
| device (torch.device, optional): Device for tensor allocations. |
| base (int, optional): Base for computing inverse frequencies. |
| condense_ratio (int, optional): Ratio to condense the position indices. |
| extra_config (dict, optional): Configuration parameters for frequency adjustments (used by Llama 3.1 and 3.2) |
| |
| Returns: |
| Tuple[torch.Tensor, torch.Tensor]: Cosine and sine caches for RoPE. |
| Shapes are `(seq_len, n_elem)`. |
| """ |
|
|
| |
| theta = 1.0 / (base ** (torch.arange(0, n_elem, 2, device=device).float() / n_elem)) |
|
|
| |
| attention_scaling = 1.0 |
|
|
| if extra_config is not None: |
| factor = extra_config["factor"] |
| |
| if "beta_fast" in extra_config or "beta_slow" in extra_config: |
| |
| beta_fast = extra_config["beta_fast"] |
| beta_slow = extra_config["beta_slow"] |
| original_max_seq_len = extra_config["original_max_seq_len"] |
|
|
| |
| mscale = extra_config.get("mscale") |
| mscale_all_dim = extra_config.get("mscale_all_dim") |
| if mscale and mscale_all_dim: |
| attention_scaling = yarn_get_mscale(factor, mscale) / yarn_get_mscale(factor, mscale_all_dim) |
| elif mscale_all_dim: |
| attention_scaling = yarn_get_mscale(factor, mscale_all_dim) |
| elif mscale: |
| attention_scaling = yarn_get_mscale(factor, mscale) |
| |
|
|
| |
| pos_freqs = base ** (torch.arange(0, n_elem, 2, device=device).float() / n_elem) |
| theta_extrapolation = 1.0 / pos_freqs |
| theta_interpolation = 1.0 / (factor * pos_freqs) |
|
|
| |
| |
| def find_correction_dim(num_rotations, dim, base_val, max_pos): |
| return (dim * math.log(max_pos / (num_rotations * 2 * math.pi))) / (2 * math.log(base_val)) |
|
|
| low_dim = find_correction_dim(beta_fast, n_elem, base, original_max_seq_len) |
| high_dim = find_correction_dim(beta_slow, n_elem, base, original_max_seq_len) |
|
|
| |
| if extra_config.get("truncate", True): |
| low_dim = math.floor(low_dim) |
| high_dim = math.ceil(high_dim) |
|
|
| low_dim = max(low_dim, 0) |
| high_dim = min(high_dim, n_elem // 2 - 1) |
|
|
| |
| dim_range = torch.arange(n_elem // 2, device=device, dtype=torch.float32) |
| if low_dim == high_dim: |
| high_dim += 0.001 |
|
|
| linear_func = (dim_range - low_dim) / (high_dim - low_dim) |
| ramp_func = torch.clamp(linear_func, 0.0, 1.0) |
|
|
| |
| |
| theta_extrapolation_factor = ramp_func |
| theta = ( |
| theta_interpolation * (1 - theta_extrapolation_factor) |
| + theta_extrapolation * theta_extrapolation_factor |
| ) |
| elif "original_max_seq_len" in extra_config: |
| |
| orig_context_len = extra_config["original_max_seq_len"] |
| low_freq_factor = extra_config["low_freq_factor"] |
| high_freq_factor = extra_config["high_freq_factor"] |
|
|
| wavelen = 2 * torch.pi / theta |
| ratio = orig_context_len / wavelen |
| smooth_factor = (ratio - low_freq_factor) / (high_freq_factor - low_freq_factor) |
| smooth_factor = torch.clamp(smooth_factor, min=0.0, max=1.0) |
|
|
| |
| adjusted_theta = (1 - smooth_factor) * (theta / factor) + smooth_factor * theta |
| theta = adjusted_theta |
| else: |
| |
| theta = theta / factor |
|
|
| |
| seq_idx = torch.arange(seq_len, device=device).float() / condense_ratio |
|
|
| |
| idx_theta = torch.outer(seq_idx, theta).repeat(1, 2) |
| |
| |
| |
| |
| |
| |
| |
| if idx_theta.shape[-1] > n_elem > 1: |
| idx_theta = idx_theta[..., :n_elem] |
|
|
| |
| |
| if rope_local_base_freq is not None: |
| local_theta = 1.0 / (rope_local_base_freq ** (torch.arange(0, n_elem, 2, device=device).float() / n_elem)) |
| local_idx_theta = torch.outer(seq_idx, local_theta) |
| local_idx_theta = local_idx_theta.repeat(1, 2) |
| if local_idx_theta.shape[-1] > n_elem > 1: |
| local_idx_theta = local_idx_theta[..., :n_elem] |
|
|
| idx_theta = torch.stack((idx_theta, local_idx_theta), dim=-1) |
|
|
| cos = torch.cos(idx_theta) * attention_scaling |
| sin = torch.sin(idx_theta) * attention_scaling |
| return cos, sin |
|
|
|
|
| def batched_index_select(t, dim, idx): |
| """index_select for batched index and unbatched t""" |
| if idx.dim() == 1: |
| return torch.index_select(t, dim, idx) |
|
|
| *batch_shape, idx_size = idx.shape |
| res = torch.index_select(t, dim, idx.reshape(-1)) |
| |
| res = res.view(*t.shape[:dim], -1, idx_size, *t.shape[dim + 1 :]) |
| if dim > 0: |
| |
| dims = [dim] + list(range(res.dim())) |
| del dims[dim + 1] |
| res = res.permute(dims) |
| |
| res = res.view(*batch_shape, *res.shape[1:]) |
| return res |
|
|
|
|
| def batched_index_copy_(t, dim, idx, val): |
| """Index copy for batched t, idx, val""" |
|
|
| if t.device.type == "mps": |
| |
| if dim < 0: |
| dim = t.dim() + dim |
| if idx.dim() == 1: |
| idx_shape = [1] * val.dim() |
| idx_shape[dim] = -1 |
| idx_expanded = idx.view(*idx_shape) |
| idx_expanded = idx_expanded.expand_as(val) |
| t.scatter_(dim, idx_expanded, val) |
| return t |
|
|
| elif idx.dim() == 2: |
| assert dim != 0, "Cannot index the batch dimension" |
| batch_size = idx.size(0) |
| idx_size = idx.size(1) |
| assert batch_size == t.size(0) == val.size(0) |
|
|
| idx_shape = [batch_size] + [1] * (val.dim() - 1) |
| idx_shape[dim] = idx_size |
| idx_expanded = idx.view(*idx_shape) |
| idx_expanded = idx_expanded.expand_as(val) |
|
|
| t.scatter_(dim, idx_expanded, val) |
| return t |
| else: |
| raise NotImplementedError(f"idx.dim() == {idx.dim()} not supported") |
|
|
| else: |
| if idx.dim() == 1: |
| return t.index_copy_(dim, idx, val) |
|
|
| assert idx.dim() == 2, f"multiple batch dims not yet {idx.shape=}" |
| assert dim != 0, f"cannot index batch dim {dim=}" |
| batch_size, idx_size = idx.shape |
| assert batch_size == t.size(0) |
| assert batch_size == val.size(0) |
|
|
| |
| |
| |
| for i in range(batch_size): |
| unbatched_dim = dim if dim < 0 else dim - 1 |
| t[i].index_copy_(unbatched_dim, idx[i], val[i]) |
| return t |
|
|
|
|
| def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor: |
| """ |
| Applies RoPE transform to `x`. Note that `cos`, `sin` need to have a batch |
| dimension. |
| |
| Args: |
| x: Input tensor, `(B, ..., T, head_size)` |
| cos: Cached cosines, `(B, T, head_size)` or `(1, T, head_size)` |
| sin: Cached sines, `(B, T, head_size)` or `(1, T, head_size)` |
| |
| Returns: |
| Encoded tensor, `(B, ..., T, head_size)` |
| """ |
| if cos.dim() != 3: |
| raise ValueError(f"cos must be three-dimensional, but shape is {cos.shape}") |
| if cos.shape != sin.shape: |
| raise ValueError(f"cos, sin must have same shape, but cos.shape={cos.shape}, sin.shape={sin.shape}") |
| head_size_half = x.size(-1) // 2 |
| x1 = x[..., :head_size_half] |
| x2 = x[..., head_size_half:] |
| rotated = torch.cat((-x2, x1), dim=-1) |
| dims_diff = x.dim() - cos.dim() |
| if dims_diff > 0: |
| |
| new_shape = cos.shape[0:1] + (1,) * dims_diff + cos.shape[1:] |
| cos = cos.view(*new_shape) |
| sin = sin.view(*new_shape) |
|
|
| roped = (x * cos) + (rotated * sin) |
| return roped.to(dtype=x.dtype) |
|
|
|
|
| def apply_rope_interleave(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor: |
| """Apply rotary position embeddings with interleaved tensor layout. |
| |
| This version rearranges the input tensor to group even/odd indices separately |
| before applying the standard RoPE rotation, matching HuggingFace's |
| apply_rotary_pos_emb_interleave behavior. |
| |
| Args: |
| x: Input tensor of shape (..., seq_len, head_dim) |
| cos: Cosine component of shape (B, seq_len, head_dim) or (1, seq_len, head_dim) |
| sin: Sine component of shape (B, seq_len, head_dim) or (1, seq_len, head_dim) |
| |
| Returns: |
| Tensor with RoPE applied, same shape as input |
| """ |
| if cos.dim() != 3: |
| raise ValueError(f"cos must be three-dimensional, but shape is {cos.shape}") |
| if cos.shape != sin.shape: |
| raise ValueError(f"cos, sin must have same shape, but cos.shape={cos.shape}, sin.shape={sin.shape}") |
|
|
| |
| *batch_dims, d = x.shape |
| x = x.view(*batch_dims, d // 2, 2).transpose(-1, -2).reshape(*batch_dims, d) |
|
|
| |
| head_size_half = x.size(-1) // 2 |
| x1 = x[..., :head_size_half] |
| x2 = x[..., head_size_half:] |
| rotated = torch.cat((-x2, x1), dim=-1) |
|
|
| |
| dims_diff = x.dim() - cos.dim() |
| if dims_diff > 0: |
| new_shape = cos.shape[0:1] + (1,) * dims_diff + cos.shape[1:] |
| cos = cos.view(*new_shape) |
| sin = sin.view(*new_shape) |
|
|
| roped = (x * cos) + (rotated * sin) |
| return roped.to(dtype=x.dtype) |
|
|
|
|
| def do_softcapping(x: torch.Tensor, thresh: float) -> torch.Tensor: |
| return torch.tanh(x / thresh) * thresh |
|
|
|
|
| class MLACompressedKVCache(nn.Module): |
| """Cache normalized c^KV and the shared RoPE key instead of expanded K/V.""" |
|
|
| def __init__( |
| self, |
| latent_shape: tuple[int, int, int], |
| rope_shape: tuple[int, int, int, int], |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| ) -> None: |
| super().__init__() |
| self.register_buffer("latent", torch.zeros(latent_shape, device=device, dtype=dtype), persistent=False) |
| self.register_buffer("rope", torch.zeros(rope_shape, device=device, dtype=dtype), persistent=False) |
|
|
| def forward( |
| self, input_pos: torch.Tensor, latent: torch.Tensor, rope: torch.Tensor |
| ) -> tuple[torch.Tensor, torch.Tensor]: |
| if self.latent.dtype != latent.dtype: |
| self.latent = self.latent.to(latent.dtype) |
| if self.rope.dtype != rope.dtype: |
| self.rope = self.rope.to(rope.dtype) |
| bs = latent.size(0) |
| if input_pos.dim() == 1: |
| self.latent[:bs].index_copy_(1, input_pos, latent) |
| self.rope[:bs].index_copy_(2, input_pos, rope) |
| else: |
| batch = torch.arange(bs, device=input_pos.device)[:, None] |
| self.latent[batch, input_pos] = latent |
| self.rope[:bs, 0][batch, input_pos] = rope[:, 0] |
| return self.latent[:bs], self.rope[:bs] |
|
|
| @property |
| def bytes_per_token(self) -> int: |
| return (self.latent.shape[-1] + self.rope.shape[-1]) * self.latent.element_size() |
|
|
|
|
| class KVCache(nn.Module): |
| """ |
| Buffers `k`, `v` have shape |
| `(batch_size, n_query_groups, max_seq_length, head_size)`. |
| """ |
|
|
| def __init__( |
| self, |
| k_shape: tuple[int, int, int, int], |
| v_shape: tuple[int, int, int, int], |
| device: torch.device | None = None, |
| dtype: torch.dtype | None = None, |
| is_sliding_window: bool = False, |
| sliding_window_size: int | None = None, |
| ) -> None: |
| super().__init__() |
| self.register_buffer("k", torch.zeros(k_shape, device=device, dtype=dtype), persistent=False) |
| self.register_buffer("v", torch.zeros(v_shape, device=device, dtype=dtype), persistent=False) |
| self.is_sliding_window = is_sliding_window |
| self.sliding_window_size = sliding_window_size |
| self.max_cache_len = k_shape[2] |
|
|
| def forward(self, input_pos: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: |
| """ |
| Writes new values `k` and `v` into the cache at the positions specified |
| by `input_pos` along the sequence dimension (`max_seq_length`). The batch |
| size of `k` and `v` (`bs`) must be smaller or equal to `KVCache` batch |
| size. Returns the full buffers, adjusted to the batch size `bs`. |
| |
| Args: |
| input_pos: Position index, `(bs, T)` or `(T,)` |
| k: New values, `(bs, n_query_groups, T, head_size)` |
| v: New values, `(bs, n_query_groups, T, head_size)` |
| |
| Returns: |
| k_full, v_full, `(bs, n_query_groups, max_seq_length, head_size)` |
| |
| """ |
| |
| if self.k.dtype != k.dtype: |
| self.k = self.k.to(k.dtype) |
| if self.v.dtype != v.dtype: |
| self.v = self.v.to(v.dtype) |
| |
| bs = k.size(0) |
| if self.is_sliding_window: |
| |
| prefill_len = input_pos.shape[-1] |
| if prefill_len > self.max_cache_len: |
| raise ValueError( |
| f"Prefill length ({prefill_len}) exceeds the sliding window size ({self.max_cache_len}). " |
| f"This causes the ring-buffer KV cache to overwrite entries, but the attention mask is not " |
| f"rebuilt to reflect the true positions, which silently violates causality. " |
| f"Please use chunked prefill with chunk size <= {self.max_cache_len} to avoid this issue." |
| ) |
| cache_positions = input_pos % self.max_cache_len |
| k = batched_index_copy_(self.k[:bs, ...], -2, cache_positions, k) |
| v = batched_index_copy_(self.v[:bs, ...], -2, cache_positions, v) |
|
|
| max_pos = input_pos.max().item() |
| if max_pos < self.max_cache_len: |
| k = k[:, :, : max_pos + 1, :] |
| v = v[:, :, : max_pos + 1, :] |
| else: |
| |
| k = batched_index_copy_(self.k[:bs, ...], -2, input_pos, k) |
| v = batched_index_copy_(self.v[:bs, ...], -2, input_pos, v) |
|
|
| return k, v |
|
|
| def reset_parameters(self) -> None: |
| torch.nn.init.zeros_(self.k) |
| torch.nn.init.zeros_(self.v) |
|
|
|
|
| def build_mask_cache(max_seq_length: int, device: torch.device | None = None) -> torch.Tensor: |
| ones = torch.ones((max_seq_length, max_seq_length), device=device, dtype=torch.bool) |
| return torch.tril(ones).unsqueeze(0).unsqueeze(0) |
|
|
|
|
| class RMSNorm(torch.nn.Module): |
| """Root Mean Square Layer Normalization. |
| |
| Derived from https://github.com/bzhangGo/rmsnorm/blob/master/rmsnorm_torch.py. BSD 3-Clause License: |
| https://github.com/bzhangGo/rmsnorm/blob/master/LICENSE. |
| """ |
|
|
| def __init__(self, size: int, dim: int = -1, eps: float = 1e-6, add_unit_offset: bool = False) -> None: |
| super().__init__() |
| self.weight = torch.nn.Parameter(torch.ones(size)) |
| self.eps = eps |
| self.dim = dim |
| self.add_unit_offset = add_unit_offset |
|
|
| def forward(self, x: torch.Tensor) -> torch.Tensor: |
| dtype = x.dtype |
| x = x.float() |
| |
| norm_x = torch.mean(x * x, dim=self.dim, keepdim=True) |
| x_normed = x * torch.rsqrt(norm_x + self.eps) |
| weight = (1 + self.weight) if self.add_unit_offset else self.weight |
| return (x_normed * weight.float()).to(dtype=dtype) |
|
|
| def reset_parameters(self) -> None: |
| torch.nn.init.ones_(self.weight) |
|
|