import math import torch import torch.nn as nn import torch.nn.functional as F from transformers import PreTrainedModel from transformers.modeling_outputs import CausalLMOutput from .configuration_dumbmini import DumbMiniConfig class DumbLinearNext(nn.Module): def __init__(self, in_dim, out_dim): super().__init__() self.main_proj = nn.Linear(in_dim, out_dim) self.gate_proj = nn.Linear(in_dim, out_dim) self.extra_bias = nn.Parameter(torch.zeros(out_dim)) def forward(self, x): return (self.main_proj(x) * torch.sigmoid(self.gate_proj(x))) + self.extra_bias class TriDumbSwiGLU(nn.Module): def __init__(self, config, is_semantic_layer=False): super().__init__() mult = 1.5 if is_semantic_layer else 1.0 hidden_dim = int(config.intermediate_size * mult) self.w1 = nn.Linear(config.hidden_size, hidden_dim, bias=False) self.w2 = nn.Linear(config.hidden_size, hidden_dim, bias=False) self.w3 = nn.Linear(config.hidden_size, hidden_dim, bias=False) self.w_out = nn.Linear(hidden_dim, config.hidden_size, bias=False) def forward(self, x): return self.w_out(F.silu(self.w1(x)) * self.w2(x) * torch.tanh(self.w3(x))) class DumbGatedAttention(nn.Module): def __init__(self, config, layer_idx): super().__init__() self.num_heads = config.num_attention_heads self.head_dim = config.hidden_size // config.num_attention_heads self.is_structural = layer_idx < 8 self.qkv = nn.Linear(config.hidden_size, config.hidden_size * 3, bias=False) self.gate = nn.Linear(config.hidden_size, config.hidden_size) self.o_proj = nn.Linear(config.hidden_size, config.hidden_size, bias=False) def forward(self, x): b, s, d = x.shape qkv = self.qkv(x).reshape(b, s, 3, self.num_heads, self.head_dim).permute(2, 0, 3, 1, 4) q, k, v = qkv[0], qkv[1], qkv[2] attn = (q @ k.transpose(-2, -1)) * (1.0 / math.sqrt(self.head_dim)) mask = torch.triu(torch.ones(s, s, device=x.device), 1).bool() attn = attn.masked_fill(mask, float('-inf')) attn = F.softmax(attn, dim=-1) out = (attn @ v).transpose(1, 2).reshape(b, s, d) g = torch.sigmoid(self.gate(x)) out = out * g if self.is_structural else out * (0.5 + 0.5 * g) return self.o_proj(out) class DumbMiniLayer(nn.Module): def __init__(self, config, layer_idx): super().__init__() self.ln1 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) self.attn = DumbGatedAttention(config, layer_idx) self.ln2 = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) self.ffn = TriDumbSwiGLU(config, is_semantic_layer=(layer_idx >= 8)) def forward(self, x): x = x + self.attn(self.ln1(x)) x = x + self.ffn(self.ln2(x)) return x class DumbMiniModel(PreTrainedModel): config_class = DumbMiniConfig def __init__(self, config): super().__init__(config) self.embed_in = nn.Embedding(config.vocab_size, config.emb_split_dim) self.up_proj = DumbLinearNext(config.emb_split_dim, config.hidden_size) self.pos_embed = nn.Embedding(config.max_position_embeddings, config.hidden_size) self.layers = nn.ModuleList([DumbMiniLayer(config, i) for i in range(config.num_hidden_layers)]) self.ln_f = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) self.down_proj = nn.Linear(config.hidden_size, config.emb_split_dim, bias=False) self.post_init() def _init_weights(self, module): std = self.config.initializer_range if isinstance(module, nn.Linear): module.weight.data.normal_(mean=0.0, std=std) if module.bias is not None: module.bias.data.zero_() elif isinstance(module, nn.Embedding): module.weight.data.normal_(mean=0.0, std=std) elif isinstance(module, nn.LayerNorm): module.bias.data.zero_() module.weight.data.fill_(1.0) def forward(self, input_ids=None, labels=None, **kwargs): s_len = input_ids.shape[1] pos = torch.arange(s_len, device=input_ids.device).unsqueeze(0) x = self.up_proj(self.embed_in(input_ids)) + self.pos_embed(pos) for layer in self.layers: x = layer(x) x = self.down_proj(self.ln_f(x)) logits = F.linear(x, self.embed_in.weight) loss = None if labels is not None: shift_logits = logits[..., :-1, :].contiguous() shift_labels = labels[..., 1:].contiguous() loss = F.cross_entropy(shift_logits.view(-1, self.config.vocab_size), shift_labels.view(-1)) return CausalLMOutput( loss=loss, logits=logits ) def prepare_inputs_for_generation(self, input_ids, **kwargs): return {"input_ids": input_ids}