from __future__ import annotations from typing import Optional import torch import torch.nn as nn import torch.nn.functional as F from torch import Tensor from config import ModelConfig class RMSNorm(nn.Module): def __init__(self, dim: int, eps: float = 1.0e-5): super().__init__() self.weight = nn.Parameter(torch.ones(dim)) self.eps = eps def forward(self, x: Tensor) -> Tensor: dtype = x.dtype x32 = x.float() rms = x32.pow(2).mean(dim=-1, keepdim=True).add_(self.eps).rsqrt_() out = (x32 * rms).to(dtype) return out * self.weight.to(dtype) class RotaryEmbedding(nn.Module): def __init__(self, head_dim: int, max_seq_len: int, theta: float = 10_000.0): super().__init__() self.head_dim = head_dim self.max_seq_len = max_seq_len self.theta = theta self._cached_len: int = 0 self._cos_cache: Optional[Tensor] = None self._sin_cache: Optional[Tensor] = None def _build_cache(self, seq_len: int, device, dtype): inv_freq = 1.0 / ( self.theta ** (torch.arange(0, self.head_dim, 2, dtype=torch.float32, device=device) / self.head_dim) ) t = torch.arange(seq_len, dtype=torch.float32, device=device) freqs = torch.outer(t, inv_freq) emb = torch.cat([freqs, freqs], dim=-1) self._cos_cache = emb.cos().to(dtype) self._sin_cache = emb.sin().to(dtype) self._cached_len = seq_len def forward(self, seq_len: int, device, dtype) -> tuple[Tensor, Tensor]: if ( self._cos_cache is None or seq_len > self._cached_len or self._cos_cache.device != device or self._cos_cache.dtype != dtype ): self._build_cache(max(seq_len, self.max_seq_len), device, dtype) return self._cos_cache[:seq_len], self._sin_cache[:seq_len] def _rotate_half(x: Tensor) -> Tensor: x1, x2 = x.chunk(2, dim=-1) return torch.cat([-x2, x1], dim=-1) def apply_rope(q: Tensor, k: Tensor, cos: Tensor, sin: Tensor) -> tuple[Tensor, Tensor]: cos = cos.unsqueeze(0).unsqueeze(0) sin = sin.unsqueeze(0).unsqueeze(0) q_rot = (q * cos) + (_rotate_half(q) * sin) k_rot = (k * cos) + (_rotate_half(k) * sin) return q_rot, k_rot class Attention(nn.Module): def __init__(self, cfg: ModelConfig, layer_idx: int): super().__init__() self.cfg = cfg self.layer_idx = layer_idx self.num_heads = cfg.num_heads self.num_kv_heads = cfg.num_kv_heads self.head_dim = cfg.head_dim self.kv_groups = cfg.kv_groups self.scale = self.head_dim ** -0.5 h, hd = cfg.hidden_size, self.head_dim self.q_proj = nn.Linear(h, self.num_heads * hd, bias=False) self.k_proj = nn.Linear(h, self.num_kv_heads * hd, bias=False) self.v_proj = nn.Linear(h, self.num_kv_heads * hd, bias=False) self.o_proj = nn.Linear(self.num_heads * hd, h, bias=False) if cfg.qk_norm: self.q_norm = RMSNorm(hd, eps=cfg.rms_norm_eps) self.k_norm = RMSNorm(hd, eps=cfg.rms_norm_eps) else: self.q_norm = nn.Identity() self.k_norm = nn.Identity() self.attn_dropout = cfg.attn_dropout def forward(self, x: Tensor, cos: Tensor, sin: Tensor) -> Tensor: B, S, _ = x.shape q = self.q_proj(x).view(B, S, self.num_heads, self.head_dim) k = self.k_proj(x).view(B, S, self.num_kv_heads, self.head_dim) v = self.v_proj(x).view(B, S, self.num_kv_heads, self.head_dim) q = self.q_norm(q) k = self.k_norm(k) q = q.transpose(1, 2) k = k.transpose(1, 2) v = v.transpose(1, 2) q, k = apply_rope(q, k, cos, sin) if self.kv_groups > 1: k = k.repeat_interleave(self.kv_groups, dim=1) v = v.repeat_interleave(self.kv_groups, dim=1) out = F.scaled_dot_product_attention( q, k, v, attn_mask=None, dropout_p=self.attn_dropout if self.training else 0.0, is_causal=True, ) out = out.transpose(1, 2).contiguous().view(B, S, self.num_heads * self.head_dim) return self.o_proj(out) class SwiGLU(nn.Module): def __init__(self, cfg: ModelConfig): super().__init__() h, i = cfg.hidden_size, cfg.intermediate_size self.gate_proj = nn.Linear(h, i, bias=False) self.up_proj = nn.Linear(h, i, bias=False) self.down_proj = nn.Linear(i, h, bias=False) def forward(self, x: Tensor) -> Tensor: return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x)) class Block(nn.Module): def __init__(self, cfg: ModelConfig, layer_idx: int): super().__init__() self.input_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps) self.attn = Attention(cfg, layer_idx) self.post_attn_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps) self.mlp = SwiGLU(cfg) self.resid_drop = nn.Dropout(cfg.resid_dropout) if cfg.resid_dropout > 0 else nn.Identity() def forward(self, x: Tensor, cos: Tensor, sin: Tensor) -> Tensor: x = x + self.resid_drop(self.attn(self.input_norm(x), cos, sin)) x = x + self.resid_drop(self.mlp(self.post_attn_norm(x))) return x class MarulLLM(nn.Module): def __init__(self, cfg: ModelConfig): super().__init__() self.cfg = cfg self.embed_tokens = nn.Embedding(cfg.vocab_size, cfg.hidden_size) self.rotary = RotaryEmbedding(cfg.head_dim, cfg.max_seq_len, cfg.rope_theta) self.layers = nn.ModuleList(Block(cfg, i) for i in range(cfg.num_layers)) self.final_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps) if cfg.tie_word_embeddings: self.lm_head = None else: self.lm_head = nn.Linear(cfg.hidden_size, cfg.vocab_size, bias=False) self.apply(self._init_weights) self._scale_residual_inits() self.num_params = sum(p.numel() for p in self.parameters()) self.num_params_trainable = sum(p.numel() for p in self.parameters() if p.requires_grad) embed_params = cfg.vocab_size * cfg.hidden_size self.num_params_non_embed = self.num_params - embed_params def _init_weights(self, module: nn.Module) -> None: std = self.cfg.initializer_range if isinstance(module, nn.Linear): nn.init.normal_(module.weight, mean=0.0, std=std) if module.bias is not None: nn.init.zeros_(module.bias) elif isinstance(module, nn.Embedding): nn.init.normal_(module.weight, mean=0.0, std=std) def _scale_residual_inits(self) -> None: scale = (2 * self.cfg.num_layers) ** -0.5 with torch.no_grad(): for block in self.layers: block.attn.o_proj.weight.mul_(scale) block.mlp.down_proj.weight.mul_(scale) def forward( self, input_ids: Tensor, targets: Optional[Tensor] = None, return_logits: bool = True, ) -> tuple[Optional[Tensor], Optional[Tensor]]: B, S = input_ids.shape assert S <= self.cfg.max_seq_len, ( f"dizi uzunluğu {S}, modelin bağlam sınırı {self.cfg.max_seq_len}") x = self.embed_tokens(input_ids) cos, sin = self.rotary(S, x.device, x.dtype) for block in self.layers: x = block(x, cos, sin) x = self.final_norm(x) if self.cfg.tie_word_embeddings: logits = F.linear(x, self.embed_tokens.weight) else: logits = self.lm_head(x) loss: Optional[Tensor] = None if targets is not None: flat_logits = logits.view(-1, logits.size(-1)) flat_targets = targets.view(-1) ce = F.cross_entropy( flat_logits, flat_targets, ignore_index=-100, reduction="mean" ) loss = ce if self.cfg.z_loss_coef > 0: mask = flat_targets != -100 lse = torch.logsumexp(flat_logits, dim=-1) if mask.any(): z = (lse[mask].float().pow(2)).mean() loss = loss + self.cfg.z_loss_coef * z if not return_logits and targets is not None: logits = None return logits, loss @torch.no_grad() def generate( self, input_ids: Tensor, max_new_tokens: int = 128, temperature: float = 0.6, top_k: int = 40, top_p: float = 0.88, repetition_penalty: float = 1.20, no_repeat_ngram_size: int = 4, min_p: float = 0.05, eos_token_id: Optional[int] = None, ) -> Tensor: self.eval() eos = eos_token_id if eos_token_id is not None else self.cfg.eos_token_id out = input_ids.clone() device = out.device for _ in range(max_new_tokens): ctx = out[:, -self.cfg.max_seq_len:] logits, _ = self.forward(ctx) logits = logits[:, -1, :].float() if repetition_penalty is not None and repetition_penalty != 1.0: for b in range(out.size(0)): seen = torch.unique(out[b]) vals = logits[b, seen] logits[b, seen] = torch.where( vals > 0, vals / repetition_penalty, vals * repetition_penalty ) if no_repeat_ngram_size and no_repeat_ngram_size > 0: n = no_repeat_ngram_size if out.size(1) >= n - 1: for b in range(out.size(0)): seq = out[b].tolist() ngrams: dict = {} for i in range(len(seq) - n + 1): prefix = tuple(seq[i : i + n - 1]) ngrams.setdefault(prefix, set()).add(seq[i + n - 1]) curr = tuple(seq[-(n - 1):]) if curr in ngrams: banned = torch.tensor(list(ngrams[curr]), device=device, dtype=torch.long) logits[b, banned] = float("-inf") if temperature is not None and temperature != 1.0: logits = logits / max(temperature, 1.0e-6) if top_k is not None and top_k > 0: v, _ = torch.topk(logits, k=min(top_k, logits.size(-1))) logits[logits < v[:, -1:]] = float("-inf") if min_p is not None and min_p > 0.0: probs_tmp = F.softmax(logits, dim=-1) max_probs, _ = probs_tmp.max(dim=-1, keepdim=True) logits = logits.masked_fill(probs_tmp < (max_probs * min_p), float("-inf")) if top_p is not None and 0.0 < top_p < 1.0: sorted_logits, sorted_idx = torch.sort(logits, descending=True, dim=-1) probs = F.softmax(sorted_logits, dim=-1) cumprobs = probs.cumsum(dim=-1) mask = cumprobs > top_p mask[..., 1:] = mask[..., :-1].clone() mask[..., 0] = False sorted_logits = sorted_logits.masked_fill(mask, float("-inf")) logits = torch.full_like(logits, float("-inf")).scatter(-1, sorted_idx, sorted_logits) probs = F.softmax(logits, dim=-1) next_token = torch.multinomial(probs, num_samples=1) out = torch.cat([out, next_token], dim=1) if eos is not None and (next_token == eos).all(): break return out