Download modeling_anka.py from Melih1234/ANKA-50M-RMW3: direct link, hf CLI and curl.
- Browser
- Download file 13.8 kB
-
https://huggingface.co/Melih1234/ANKA-50M-RMW3/resolve/main/modeling_anka.py
- Command line
-
hf download hf://Melih1234/ANKA-50M-RMW3/modeling_anka.py
-
curl -L -o modeling_anka.py https://huggingface.co/Melih1234/ANKA-50M-RMW3/resolve/main/modeling_anka.py
13.8 kB
| """ANKA-50M RMW3 model architecture. | |
| This module contains the exact architecture used by the submitted Base 5B | |
| checkpoint. It intentionally depends only on PyTorch. | |
| """ | |
| from dataclasses import asdict, dataclass | |
| from typing import Optional | |
| import math | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class RMW3Config: | |
| vocab_size: int = 16_384 | |
| d_model: int = 512 | |
| n_physical_layers: int = 13 | |
| recurrent_passes: int = 2 | |
| n_heads: int = 8 | |
| n_kv_heads: int = 2 | |
| d_ff: int = 1_280 | |
| reflect_rank: int = 32 | |
| morph_rank: int = 368 | |
| morph_stages: int = 4 | |
| morph_experts: int = 4 | |
| max_seq_len: int = 1_024 | |
| rope_theta: float = 10_000.0 | |
| norm_eps: float = 1e-5 | |
| def to_dict(self) -> dict: | |
| return asdict(self) | |
| class RMSNorm(nn.Module): | |
| def __init__(self, dimension: int, epsilon: float): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(dimension)) | |
| self.epsilon = epsilon | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| inverse_rms = torch.rsqrt( | |
| x.float().pow(2).mean(dim=-1, keepdim=True) + self.epsilon | |
| ) | |
| normalized = (x.float() * inverse_rms).to(x.dtype) | |
| return normalized * self.weight | |
| def build_rope_cache( | |
| maximum_sequence_length: int, | |
| head_dimension: int, | |
| theta: float, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| inverse_frequency = 1.0 / ( | |
| theta | |
| ** ( | |
| torch.arange(0, head_dimension, 2, dtype=torch.float32) | |
| / head_dimension | |
| ) | |
| ) | |
| positions = torch.arange(maximum_sequence_length, dtype=torch.float32) | |
| angles = torch.outer(positions, inverse_frequency) | |
| return angles.cos(), angles.sin() | |
| def apply_rope( | |
| x: torch.Tensor, | |
| cosine: torch.Tensor, | |
| sine: torch.Tensor, | |
| ) -> torch.Tensor: | |
| sequence_length = x.shape[-2] | |
| cosine = cosine[:sequence_length].to(device=x.device, dtype=x.dtype)[ | |
| None, None, :, : | |
| ] | |
| sine = sine[:sequence_length].to(device=x.device, dtype=x.dtype)[ | |
| None, None, :, : | |
| ] | |
| even = x[..., 0::2] | |
| odd = x[..., 1::2] | |
| rotated = torch.stack( | |
| (even * cosine - odd * sine, even * sine + odd * cosine), dim=-1 | |
| ) | |
| return rotated.flatten(-2) | |
| class GroupedQueryAttention(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.number_of_heads = config.n_heads | |
| self.number_of_kv_heads = config.n_kv_heads | |
| self.head_dimension = config.d_model // config.n_heads | |
| if config.d_model % config.n_heads != 0: | |
| raise ValueError("d_model must be divisible by n_heads") | |
| if config.n_heads % config.n_kv_heads != 0: | |
| raise ValueError("n_heads must be divisible by n_kv_heads") | |
| self.query_projection = nn.Linear( | |
| config.d_model, | |
| config.n_heads * self.head_dimension, | |
| bias=False, | |
| ) | |
| self.key_projection = nn.Linear( | |
| config.d_model, | |
| config.n_kv_heads * self.head_dimension, | |
| bias=False, | |
| ) | |
| self.value_projection = nn.Linear( | |
| config.d_model, | |
| config.n_kv_heads * self.head_dimension, | |
| bias=False, | |
| ) | |
| self.output_projection = nn.Linear( | |
| config.n_heads * self.head_dimension, | |
| config.d_model, | |
| bias=False, | |
| ) | |
| cosine, sine = build_rope_cache( | |
| config.max_seq_len, | |
| self.head_dimension, | |
| config.rope_theta, | |
| ) | |
| self.register_buffer("rope_cosine", cosine, persistent=False) | |
| self.register_buffer("rope_sine", sine, persistent=False) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| batch_size, sequence_length, _ = x.shape | |
| query = self.query_projection(x).view( | |
| batch_size, | |
| sequence_length, | |
| self.number_of_heads, | |
| self.head_dimension, | |
| ).transpose(1, 2) | |
| key = self.key_projection(x).view( | |
| batch_size, | |
| sequence_length, | |
| self.number_of_kv_heads, | |
| self.head_dimension, | |
| ).transpose(1, 2) | |
| value = self.value_projection(x).view( | |
| batch_size, | |
| sequence_length, | |
| self.number_of_kv_heads, | |
| self.head_dimension, | |
| ).transpose(1, 2) | |
| query = apply_rope(query, self.rope_cosine, self.rope_sine) | |
| key = apply_rope(key, self.rope_cosine, self.rope_sine) | |
| output = F.scaled_dot_product_attention( | |
| query, | |
| key, | |
| value, | |
| dropout_p=0.0, | |
| is_causal=True, | |
| enable_gqa=True, | |
| ) | |
| output = output.transpose(1, 2).contiguous().view( | |
| batch_size, sequence_length, -1 | |
| ) | |
| return self.output_projection(output) | |
| class SwiGLU(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.gate_projection = nn.Linear(config.d_model, config.d_ff, bias=False) | |
| self.up_projection = nn.Linear(config.d_model, config.d_ff, bias=False) | |
| self.down_projection = nn.Linear(config.d_ff, config.d_model, bias=False) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.down_projection( | |
| F.silu(self.gate_projection(x)) * self.up_projection(x) | |
| ) | |
| class RMW3Block(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.attention_norm = RMSNorm(config.d_model, config.norm_eps) | |
| self.attention = GroupedQueryAttention(config) | |
| self.ffn_norm = RMSNorm(config.d_model, config.norm_eps) | |
| self.ffn = SwiGLU(config) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| x = x + self.attention(self.attention_norm(x)) | |
| x = x + self.ffn(self.ffn_norm(x)) | |
| return x | |
| class ReflectUnit(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.down_projection = nn.Linear( | |
| config.d_model, config.reflect_rank, bias=False | |
| ) | |
| self.up_projection = nn.Linear( | |
| config.reflect_rank, config.d_model, bias=False | |
| ) | |
| self.gate_logit = nn.Parameter(torch.tensor(-2.1972246, dtype=torch.float32)) | |
| def forward( | |
| self, | |
| current: torch.Tensor, | |
| previous: torch.Tensor, | |
| ) -> torch.Tensor: | |
| discrepancy = current - previous | |
| correction = self.up_projection( | |
| F.silu(self.down_projection(discrepancy)) | |
| ) | |
| gate = torch.sigmoid(self.gate_logit).to(current.dtype) | |
| return current + gate * correction | |
| class MorphExpert(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.down_projection = nn.Linear( | |
| config.d_model, config.morph_rank, bias=False | |
| ) | |
| self.up_projection = nn.Linear( | |
| config.morph_rank, config.d_model, bias=False | |
| ) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.up_projection(F.silu(self.down_projection(x))) | |
| class SparseMorphStage(nn.Module): | |
| def __init__(self, config: RMW3Config): | |
| super().__init__() | |
| self.number_of_experts = config.morph_experts | |
| self.router = nn.Linear( | |
| config.d_model, config.morph_experts, bias=False | |
| ) | |
| self.experts = nn.ModuleList( | |
| [MorphExpert(config) for _ in range(config.morph_experts)] | |
| ) | |
| self.scale_logit = nn.Parameter(torch.tensor(-4.0, dtype=torch.float32)) | |
| self.last_loads: Optional[torch.Tensor] = None | |
| self.last_entropy: Optional[torch.Tensor] = None | |
| def forward(self, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]: | |
| original_shape = x.shape | |
| flat = x.reshape(-1, original_shape[-1]) | |
| probabilities = torch.softmax(self.router(flat).float(), dim=-1) | |
| choices = probabilities.argmax(dim=-1) | |
| output = torch.zeros_like(flat) | |
| loads = [] | |
| for expert_index, expert in enumerate(self.experts): | |
| selected_mask = choices == expert_index | |
| loads.append(selected_mask.float().mean()) | |
| indices = torch.nonzero(selected_mask, as_tuple=False).flatten() | |
| if indices.numel() == 0: | |
| continue | |
| selected_input = flat.index_select(0, indices) | |
| expert_output = expert(selected_input).to(output.dtype) | |
| selected_probability = probabilities.index_select(0, indices)[ | |
| :, expert_index | |
| ].to(output.dtype) | |
| straight_through = ( | |
| 1.0 + selected_probability - selected_probability.detach() | |
| ) | |
| output.index_copy_( | |
| 0, | |
| indices, | |
| expert_output * straight_through.unsqueeze(-1), | |
| ) | |
| load_tensor = torch.stack(loads) | |
| importance = probabilities.mean(dim=0) | |
| balance_loss = self.number_of_experts * torch.sum( | |
| load_tensor * importance | |
| ) | |
| entropy = -( | |
| probabilities * probabilities.clamp_min(1e-9).log() | |
| ).sum(dim=-1).mean() | |
| self.last_loads = load_tensor.detach() | |
| self.last_entropy = entropy.detach() | |
| scale = torch.sigmoid(self.scale_logit).to(output.dtype) | |
| return x + scale * output.view(original_shape), balance_loss | |
| class RMW3(nn.Module): | |
| def __init__(self, config: Optional[RMW3Config] = None): | |
| super().__init__() | |
| self.config = config if config is not None else RMW3Config() | |
| config = self.config | |
| self.token_embeddings = nn.Embedding(config.vocab_size, config.d_model) | |
| self.blocks = nn.ModuleList( | |
| [RMW3Block(config) for _ in range(config.n_physical_layers)] | |
| ) | |
| self.reflect_units = nn.ModuleList( | |
| [ReflectUnit(config) for _ in range(config.n_physical_layers)] | |
| ) | |
| self.morph_stages = nn.ModuleList( | |
| [SparseMorphStage(config) for _ in range(config.morph_stages)] | |
| ) | |
| self.final_norm = RMSNorm(config.d_model, config.norm_eps) | |
| self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False) | |
| self.apply(self._initialize_weights) | |
| self.lm_head.weight = self.token_embeddings.weight | |
| self._scale_residual_outputs() | |
| def _initialize_weights(module: nn.Module) -> None: | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| def _scale_residual_outputs(self) -> None: | |
| residual_scale = 1.0 / math.sqrt( | |
| 2 | |
| * self.config.n_physical_layers | |
| * self.config.recurrent_passes | |
| ) | |
| with torch.no_grad(): | |
| for block in self.blocks: | |
| block.attention.output_projection.weight.mul_(residual_scale) | |
| block.ffn.down_projection.weight.mul_(residual_scale) | |
| for reflect_unit in self.reflect_units: | |
| reflect_unit.up_projection.weight.mul_(residual_scale) | |
| for stage in self.morph_stages: | |
| for expert in stage.experts: | |
| expert.up_projection.weight.mul_(residual_scale) | |
| def forward( | |
| self, | |
| input_ids: torch.Tensor, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| if input_ids.ndim != 2: | |
| raise ValueError("input_ids must have shape [batch, sequence]") | |
| if input_ids.shape[1] > self.config.max_seq_len: | |
| raise ValueError( | |
| f"Sequence length {input_ids.shape[1]} exceeds " | |
| f"the limit {self.config.max_seq_len}" | |
| ) | |
| x = self.token_embeddings(input_ids) | |
| first_pass_states = [] | |
| balance_losses = [] | |
| effective_layer_index = 0 | |
| total_effective_layers = ( | |
| self.config.n_physical_layers * self.config.recurrent_passes | |
| ) | |
| for recurrent_pass in range(self.config.recurrent_passes): | |
| for layer_index, block in enumerate(self.blocks): | |
| if recurrent_pass == 1: | |
| x = self.reflect_units[layer_index]( | |
| x, first_pass_states[layer_index] | |
| ) | |
| x = block(x) | |
| if recurrent_pass == 0: | |
| first_pass_states.append(x) | |
| current_stage = min( | |
| self.config.morph_stages - 1, | |
| effective_layer_index | |
| * self.config.morph_stages | |
| // total_effective_layers, | |
| ) | |
| next_stage = min( | |
| self.config.morph_stages - 1, | |
| (effective_layer_index + 1) | |
| * self.config.morph_stages | |
| // total_effective_layers, | |
| ) | |
| stage_finished = ( | |
| next_stage != current_stage | |
| or effective_layer_index == total_effective_layers - 1 | |
| ) | |
| if stage_finished: | |
| x, balance_loss = self.morph_stages[current_stage](x) | |
| balance_losses.append(balance_loss) | |
| effective_layer_index += 1 | |
| logits = self.lm_head(self.final_norm(x)) | |
| auxiliary_loss = ( | |
| torch.stack(balance_losses).mean() | |
| if balance_losses | |
| else logits.new_zeros(()) | |
| ) | |
| return logits, auxiliary_loss | |
| RMW3_CONFIG = RMW3Config() | |
| def count_parameters(model: nn.Module) -> int: | |
| """Count unique trainable parameters, including tied embeddings once.""" | |
| return sum(parameter.numel() for parameter in model.parameters()) | |
| __all__ = ["RMW3", "RMW3Config", "RMW3_CONFIG", "count_parameters"] | |