import math from dataclasses import dataclass from typing import Optional, Tuple, List, Union import torch import torch.nn as nn import torch.nn.functional as F from transformers import PreTrainedModel, PretrainedConfig from transformers import AutoConfig, AutoModelForCausalLM, AutoModel from transformers.modeling_outputs import ( BaseModelOutput, CausalLMOutput, ) @dataclass class BaseModelOutputWithAux(BaseModelOutput): aux_loss: Optional[torch.Tensor] = None def create_causal_mask(seq_len: int, device=None, dtype=torch.float32): mask = torch.full((seq_len, seq_len), torch.finfo(dtype).min, device=device, dtype=dtype) mask = torch.triu(mask, diagonal=1) return mask def create_sw_mask(seq_len: int, window_size: Optional[int], device=None, dtype=torch.float32): if not window_size: return None i = torch.arange(seq_len, device=device) j = torch.arange(seq_len, device=device) allowed = (j[None, :] <= i[:, None]) & (j[None, :] >= i[:, None] - (window_size - 1)) mask = torch.zeros((seq_len, seq_len), device=device, dtype=dtype) mask = mask.masked_fill(~allowed, torch.finfo(dtype).min) return mask class Linear(nn.Module): def __init__(self, in_dim: int, out_dim: int, b: bool = True): super().__init__() self.linear = nn.Linear(in_dim, out_dim, bias=b) def forward(self, x: torch.Tensor): return self.linear(x) class Layer_norm(nn.Module): def __init__(self, dim: int, eps: float = 1e-5, b: bool = True): super().__init__() self.layer_norm = nn.LayerNorm(dim, eps=eps, elementwise_affine=b) def forward(self, x: torch.Tensor): return self.layer_norm(x) class MLP(nn.Module): def __init__(self, in_dim: int, hidden_dim: int, dim_out: Optional[int] = None, b: bool = True): super().__init__() self.linear1 = nn.Linear(in_dim, hidden_dim, bias=b) out_dim = in_dim if dim_out is None else dim_out self.linear2 = nn.Linear(hidden_dim, out_dim, bias=b) @staticmethod def gelu_new(x: torch.Tensor): return 0.5 * x * (1 + torch.tanh(math.sqrt(2 / math.pi) * (x + 0.044715 * x ** 3))) def forward(self, x: torch.Tensor): x = self.linear1(x) x = self.gelu_new(x) x = self.linear2(x) return x class SelfAttention(nn.Module): def __init__(self, dim: int, n_heads: int, attn_dropout: float = 0.1): super().__init__() assert dim % n_heads == 0, "dim must be divisible by n_heads" self.dim = dim self.n_heads = n_heads self.head_dim = dim // n_heads self.Q = nn.Linear(dim, dim, bias=True) self.K = nn.Linear(dim, dim, bias=True) self.V = nn.Linear(dim, dim, bias=True) self.O = nn.Linear(dim, dim, bias=True) self.attn_dropout = nn.Dropout(attn_dropout) def forward(self, x: torch.Tensor, *, causal_mask: Optional[torch.Tensor] = None, key_padding_mask: Optional[torch.Tensor] = None): # x: [B,S,D] B, S, D = x.shape H = self.n_heads Dh = self.head_dim q = self.Q(x).view(B, S, H, Dh).transpose(1, 2) # [B,H,S,Dh] k = self.K(x).view(B, S, H, Dh).transpose(1, 2) v = self.V(x).view(B, S, H, Dh).transpose(1, 2) attn = (q @ k.transpose(-2, -1)) / math.sqrt(Dh) # [B,H,S,S] neg_inf = torch.finfo(attn.dtype).min if causal_mask is not None: attn = attn + causal_mask[None, None, :, :] if key_padding_mask is not None: # True at PAD keys attn = attn.masked_fill(key_padding_mask[:, None, None, :], neg_inf) attn = F.softmax(attn, dim=-1) attn = self.attn_dropout(attn) out = (attn @ v).transpose(1, 2).contiguous().view(B, S, D) if key_padding_mask is not None: query_mask = (~key_padding_mask).to(out.dtype).unsqueeze(-1) # [B,S,1] out = out * query_mask return self.O(out) class Layer_Block(nn.Module): def __init__(self, dim: int, n_heads: int, ffn_multp: int, dropout: float = 0.1, attn_dropout: float = 0.1): super().__init__() self.norm_1 = Layer_norm(dim) self.norm_2 = Layer_norm(dim) self.attention = SelfAttention(dim, n_heads, attn_dropout) self.feedforward = MLP(dim, dim * ffn_multp) self.dropout1 = nn.Dropout(dropout) self.dropout2 = nn.Dropout(dropout) def forward(self, x: torch.Tensor, *, causal_mask=None, key_padding_mask=None): residual = x x = self.norm_1(x) x = self.attention(x, causal_mask=causal_mask, key_padding_mask=key_padding_mask) x = self.dropout1(x) x = residual + x residual = x x = self.norm_2(x) x = self.feedforward(x) x = self.dropout2(x) x = residual + x return x class Top_k_Router(nn.Module): def __init__(self, dim: int, n_experts: int, k: int): super().__init__() self.n_experts = n_experts self.k = k self.router = Linear(dim, n_experts) def forward(self, x: torch.Tensor): # x: [T, D] logits = self.router(x) probs = F.softmax(logits, dim=-1) topk_prob, topk_idx = torch.topk(probs, k=self.k, dim=-1) topk_prob = topk_prob / (topk_prob.sum(dim=-1, keepdim=True) + 1e-9) p_mean = probs.mean(dim=0) T, E = probs.size() one_hot = F.one_hot(topk_idx, num_classes=E).sum(dim=1) f_mean = one_hot.float().mean(dim=0) / float(self.k) aux_loss = (E * (p_mean * f_mean).sum()) return topk_idx, topk_prob, aux_loss class MoE_Block(nn.Module): def __init__(self, dim_in: int, n_experts: int, k: int, dim_hidden: int, dim_out: Optional[int] = None): super().__init__() self.n_experts = n_experts self.k = k self.dim_out = dim_in if dim_out is None else dim_out self.router = Top_k_Router(dim_in, n_experts, k) self.experts = nn.ModuleList([nn.Linear(dim_in, self.dim_out, bias=True) for _ in range(n_experts)]) def forward(self, x: torch.Tensor): # x: [B,S,D] B, S, D = x.shape T = B * S x_flat = x.view(T, D) topk_idx, topk_prob, aux_loss = self.router(x_flat) # [T,k], [T,k] y_flat = x_flat.new_zeros((T, self.dim_out)) for slot in range(self.k): e_ids = topk_idx[:, slot] e_prob = topk_prob[:, slot].unsqueeze(-1) # [T,1] for e in range(self.n_experts): idx = (e_ids == e).nonzero(as_tuple=False).squeeze(1) if idx.numel() == 0: continue y_e = self.experts[e](x_flat.index_select(0, idx)) # [Ne, dim_out] y_flat.index_add_(0, idx, y_e * e_prob.index_select(0, idx)) return y_flat.view(B, S, self.dim_out), aux_loss class ParallelLayer(nn.Module): def __init__(self, dim: int, n_heads: int, n_blocks: int, k: int, ffn_multp: int, dropout: float = 0.1, attn_dropout: float = 0.1): super().__init__() self.dim = dim self.n_blocks = n_blocks self.blocks = nn.ModuleList([Layer_Block(dim, n_heads, ffn_multp, dropout, attn_dropout) for _ in range(n_blocks)]) self.router = Top_k_Router(dim, n_blocks, k) def forward(self, x: torch.Tensor, *, causal_mask=None, key_padding_mask=None): B, S, D = x.shape T = B * S outs = [blk(x, causal_mask=causal_mask, key_padding_mask=key_padding_mask) for blk in self.blocks] Y = torch.stack([y.view(T, D) for y in outs], dim=1) # [T, n_blocks, D] x_flat = x.view(T, D) topk_idx, topk_prob, aux_loss = self.router(x_flat) # [T,k], [T,k] gather_idx = topk_idx.unsqueeze(-1).expand(-1, -1, D) # [T,k,D] Y_topk = torch.gather(Y, dim=1, index=gather_idx) # [T,k,D] y_flat = (Y_topk * topk_prob.unsqueeze(-1)).sum(dim=1) # [T,D] return y_flat.view(B, S, D), aux_loss class MoEPLMConfig(PretrainedConfig): model_type = "moep_lm" def __init__( self, vocab_size: int = 16384, bos_token_id = 1, eos_token_id = 2, pad_token_id = 3, mask_token_id = 4, max_position_embeddings: int = 1024, n_layers: int = 12, ffn_multp: int = 4, dim: int = 348, n_heads: int = 6, # Parallel specifics n_parallel_blocks: int = 4, parallel_dim: int = 192, parallel_n_heads: int = 3, parallel_k: int = 2, sliding_window: Optional[int] = None, # MoE specifics n_experts: int = 4, moe_k: int = 2, # Dropouts / norms attn_pdrop: float = 0.1, embd_pdrop: float = 0.1, resid_pdrop: float = 0.1, layer_norm_epsilon: float = 1e-5, tie_word_embeddings: bool = True, **kwargs, ): super().__init__( vocab_size=vocab_size, pad_token_id=pad_token_id, bos_token_id=bos_token_id, eos_token_id=eos_token_id, **kwargs, ) # Special tokens self.mask_token_id = mask_token_id # Core dims self.vocab_size = vocab_size self.max_position_embeddings = max_position_embeddings self.n_layers = n_layers self.ffn_multp = ffn_multp self.dim = dim self.n_heads = n_heads # Parallel specifics self.n_parallel_blocks = n_parallel_blocks self.parallel_dim = parallel_dim self.parallel_n_heads = parallel_n_heads self.parallel_k = parallel_k self.sliding_window = sliding_window # MoE specifics self.n_experts = n_experts self.moe_k = moe_k # Dropouts / norms self.attn_pdrop = attn_pdrop self.embd_pdrop = embd_pdrop self.resid_pdrop = resid_pdrop self.layer_norm_epsilon = layer_norm_epsilon # Tying self.tie_word_embeddings = tie_word_embeddings class MoEPModel(PreTrainedModel): config_class = MoEPLMConfig base_model_prefix = "model" def __init__(self, config: MoEPLMConfig): super().__init__(config) self.config = config # --- embeddings --- self.embed_tokens = nn.Embedding(config.vocab_size, config.dim, padding_idx=config.pad_token_id) self.embed_positions = nn.Embedding(config.max_position_embeddings, config.dim) self.embd_dropout = nn.Dropout(config.embd_pdrop) self.layers = nn.ModuleList() # Large Layer self.layers.append(Layer_Block(config.dim, config.n_heads, config.ffn_multp, dropout=config.resid_pdrop, attn_dropout=config.attn_pdrop)) # MoE Linear self.layers.append(MoE_Block(config.dim, config.n_experts, config.moe_k, dim_hidden=config.dim * 2, dim_out=config.parallel_dim)) # Parallel Layers for _ in range(config.n_layers - 2): self.layers.append(ParallelLayer(config.parallel_dim, config.parallel_n_heads, config.n_parallel_blocks, config.parallel_k, config.ffn_multp, dropout=config.resid_pdrop, attn_dropout=config.attn_pdrop)) # MoE Linear self.layers.append(MoE_Block(config.parallel_dim, config.n_experts, config.moe_k, dim_hidden=config.parallel_dim * config.ffn_multp, dim_out=config.dim)) # Large Layer self.layers.append(Layer_Block(config.dim, config.n_heads, config.ffn_multp, dropout=config.resid_pdrop, attn_dropout=config.attn_pdrop)) self.norm = Layer_norm(config.dim) self.post_init() def get_input_embeddings(self): return self.embed_tokens def set_input_embeddings(self, new_embeddings): self.embed_tokens = new_embeddings def _init_weights(self, module): if isinstance(module, (nn.Linear, nn.Embedding)): nn.init.normal_(module.weight, mean=0.0, std=0.02) if isinstance(module, nn.Linear) and module.bias is not None: nn.init.zeros_(module.bias) def forward( self, input_ids: torch.LongTensor, attention_mask: Optional[torch.Tensor] = None, return_dict: bool = True, return_aux: Optional[bool] = False, return_aux_dict: Optional[bool] = False, **unused, ) -> Union[BaseModelOutputWithAux, BaseModelOutput, Tuple]: device = input_ids.device B, S = input_ids.shape key_padding = input_ids.eq(self.config.pad_token_id) if attention_mask is None else (attention_mask == 0) # embeddings x = self.embed_tokens(input_ids) mask_dtype = x.dtype causal = create_causal_mask(S, device=device, dtype=mask_dtype) sw = create_sw_mask(S, self.config.sliding_window, device=device, dtype=mask_dtype) sw_mask = causal if sw is None else torch.minimum(causal, sw) pos = torch.arange(S, device=device) pos = pos.unsqueeze(0).expand(B, S) x = x + self.embed_positions(pos) x = self.embd_dropout(x) aux_total = None # First large x = self.layers[0](x, causal_mask=causal, key_padding_mask=key_padding) # MoE shrink x, aux = self.layers[1](x) if aux is not None: aux_total = aux idx = 2 for i in range(self.config.n_layers - 2): use_sw = (self.config.sliding_window is not None) and (i % 2 == 1) mask = sw_mask if use_sw else causal x, aux = self.layers[idx](x, causal_mask=mask, key_padding_mask=key_padding) if aux is not None: aux_total += aux idx += 1 # MoE grow x, aux = self.layers[idx](x) if aux is not None: aux_total += aux idx += 1 # Second large x = self.layers[idx](x, causal_mask=causal, key_padding_mask=key_padding) last_hidden = self.norm(x) if not return_dict: return (last_hidden, aux_total) if return_aux else (last_hidden,) if return_aux_dict: return BaseModelOutputWithAux( last_hidden_state=last_hidden, hidden_states=None, attentions=None, aux_loss=aux_total, ) else: return BaseModelOutput( last_hidden_state=last_hidden, hidden_states=None, attentions=None, ) class MoEPForCausalLM(PreTrainedModel): config_class = MoEPLMConfig base_model_prefix = "model" def __init__(self, config: MoEPLMConfig): super().__init__(config) self.config = config self.model = MoEPModel(config) self.lm_head = nn.Linear(config.dim, config.vocab_size, bias=False) if config.tie_word_embeddings: self.lm_head.weight = self.model.embed_tokens.weight self.post_init() def get_input_embeddings(self): return self.model.get_input_embeddings() def set_input_embeddings(self, new_embeddings): self.model.set_input_embeddings(new_embeddings) if self.config.tie_word_embeddings: self.lm_head.weight = self.model.embed_tokens.weight def _init_weights(self, module): if isinstance(module, (nn.Linear, nn.Embedding)): nn.init.normal_(module.weight, mean=0.0, std=0.02) if isinstance(module, nn.Linear) and module.bias is not None: nn.init.zeros_(module.bias) def forward( self, input_ids: torch.LongTensor, attention_mask: Optional[torch.Tensor] = None, labels: Optional[torch.LongTensor] = None, return_dict: bool = True, return_aux: Optional[bool] = False, aux_loss_weight: Optional[float] = 0.2, **unused, ) -> Union[CausalLMOutput, Tuple]: if return_aux: base_out = self.model(input_ids=input_ids, attention_mask=attention_mask, return_aux_dict=True, return_aux=True) else: base_out = self.model(input_ids=input_ids, attention_mask=attention_mask) logits = self.lm_head(base_out.last_hidden_state).float() loss = None if labels is not None: shift_logits = logits[:, :-1, :].contiguous() shift_labels = labels[:, 1:].contiguous() pad_id = self.config.pad_token_id if pad_id is not None: shift_labels = shift_labels.masked_fill(shift_labels == pad_id, -100) loss = F.cross_entropy( shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1), ignore_index=-100, ) aux = getattr(base_out, "aux_loss", None) if aux is not None: loss = loss + aux_loss_weight * aux if not return_dict: return (loss, logits) if loss is not None else (logits,) return CausalLMOutput( loss=loss, logits=logits, hidden_states=base_out.hidden_states, attentions=base_out.attentions, ) AutoConfig.register("moep_lm", MoEPLMConfig) AutoModelForCausalLM.register(MoEPLMConfig, MoEPForCausalLM) AutoModel.register(MoEPLMConfig, MoEPModel)