Download moep_gemma.py from Jtapsa/moep_gemma: direct link, hf CLI and curl.
- Browser
- Download file 20.2 kB
-
https://huggingface.co/Jtapsa/moep_gemma/resolve/main/moep_gemma.py
- Command line
-
hf download hf://Jtapsa/moep_gemma/moep_gemma.py
-
curl -L -o moep_gemma.py https://huggingface.co/Jtapsa/moep_gemma/resolve/main/moep_gemma.py
20.2 kB
| 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, | |
| ) | |
| class BaseModelOutputWithAux(BaseModelOutput): | |
| aux_loss: Optional[torch.Tensor] = None | |
| one_hot: Optional[torch.Tensor] = None | |
| class CausalLMOutputWithAux(CausalLMOutput): | |
| aux_loss: Optional[torch.Tensor] = None | |
| one_hot: 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 = False): | |
| super().__init__() | |
| self.linear = nn.Linear(in_dim, out_dim, bias=b) | |
| def forward(self, x: torch.Tensor): | |
| return self.linear(x) | |
| class RMS_norm(nn.Module): | |
| def __init__(self, dim: int, eps: float = 1e-5, b=True): | |
| super().__init__() | |
| self.rms_norm = nn.RMSNorm(dim, eps=eps, elementwise_affine=b) | |
| def forward(self, x: torch.Tensor): | |
| return self.rms_norm(x) | |
| class GeGLU(nn.Module): | |
| def __init__(self, in_dim, hidden_dim, b=False): | |
| super().__init__() | |
| self.linear1 = nn.Linear(in_dim, hidden_dim, bias=b) | |
| self.linear2 = nn.Linear(in_dim, hidden_dim, bias=b) | |
| self.linear3 = nn.Linear(hidden_dim, in_dim, bias=b) | |
| def forward(self, x: torch.Tensor): | |
| gate = F.gelu(self.linear1(x), approximate='tanh') | |
| return self.linear3(gate * self.linear2(x)) | |
| # --- RoPE --- # | |
| def precompute_theta_pos_frequencies(emb_dim: int, seq_len: int, device: str, theta: float = 10000.0): | |
| assert emb_dim % 2 == 0, "Dimension must be divisible by 2" | |
| theta_numerator = torch.arange(0, emb_dim, 2, device=device).float() | |
| theta_values = 1.0 / (theta ** (theta_numerator / emb_dim)) | |
| m = torch.arange(seq_len, device=device) | |
| freqs_matrix = torch.outer(m, theta_values).float() | |
| freqs_complex = torch.polar(torch.ones_like(freqs_matrix, device=device), freqs_matrix) | |
| return freqs_complex | |
| def RoPE(x: torch.Tensor, freqs: torch.Tensor, index: int = -1): | |
| ndim = x.ndim | |
| assert 1 < ndim | |
| assert freqs.shape[-1] == x.shape[-1] | |
| if index != -1: | |
| # Ensure that index cannot be less than 1 --> (1 == first token position) | |
| # and more than freqs.shape[-2] --> (index > max sequence length) | |
| index = max(1, min(index, freqs.shape[-2])) | |
| freqs = freqs[(index -1), :].unsqueeze(0) | |
| if freqs.shape[-2] != x.shape[-2] and index == -1: | |
| # if in x seq_len != max lenght, drop unneeded m frequence positions | |
| freqs = freqs[:x.shape[-2], :] | |
| # from (seq_len, head_dim) to (1, 1, seq_len, head_dim) | |
| freqs = freqs.unsqueeze(0).unsqueeze(0) | |
| rotated_x = x * freqs | |
| return rotated_x | |
| def apply_rotary_embeddings(x: torch.Tensor, freqs_complex: torch.Tensor): | |
| x_reshaped = x.reshape(*x.shape[:-1], -1, 2) | |
| x_complex = torch.view_as_complex(x_reshaped.float()) | |
| x_rotated = RoPE(x_complex, freqs_complex) | |
| x_out = torch.view_as_real(x_rotated) | |
| x_out = x_out.reshape(*x.shape).type_as(x) | |
| return x_out | |
| class SelfAttention(nn.Module): | |
| def __init__(self, dim: int, n_heads: int): | |
| 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) | |
| self.K = nn.Linear(dim, dim) | |
| self.V = nn.Linear(dim, dim) | |
| self.O = nn.Linear(dim, dim) | |
| def forward(self, x: torch.Tensor, *, causal_mask: Optional[torch.Tensor] = None, key_padding_mask: Optional[torch.Tensor] = None, freqs: 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) | |
| q = apply_rotary_embeddings(q, freqs) | |
| k = apply_rotary_embeddings(k, freqs) | |
| 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) | |
| 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): | |
| super().__init__() | |
| self.norm_1 = RMS_norm(dim) | |
| self.norm_2 = RMS_norm(dim) | |
| self.attention = SelfAttention(dim, n_heads) | |
| self.hidden_dim = int(dim * 4) | |
| self.feedforward = GeGLU(dim, self.hidden_dim) | |
| def forward(self, x: torch.Tensor, *, causal_mask=None, key_padding_mask=None, freqs=None): | |
| residual = x | |
| x = self.norm_1(x) | |
| x = self.attention(x, causal_mask=causal_mask, key_padding_mask=key_padding_mask, freqs=freqs) | |
| x = residual + x | |
| residual = x | |
| x = self.norm_2(x) | |
| x = self.feedforward(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, one_hot | |
| 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, one_hot = 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, one_hot.view(B, S, self.n_experts) | |
| class ParallelLayer(nn.Module): | |
| def __init__(self, dim: int, n_heads: int, n_blocks: int, k: int): | |
| super().__init__() | |
| self.dim = dim | |
| self.n_blocks = n_blocks | |
| self.blocks = nn.ModuleList([Layer_Block(dim, n_heads) 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, freqs=None): | |
| B, S, D = x.shape | |
| T = B * S | |
| outs = [blk(x, causal_mask=causal_mask, key_padding_mask=key_padding_mask, freqs=freqs) 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, one_hot = self.router(x_flat) | |
| 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, one_hot.view(B, S, self.n_blocks) | |
| class MoEPgemmaLMConfig(PretrainedConfig): | |
| model_type = "moep_gemma_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_context_length: 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, | |
| # Norms | |
| 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_context_length = max_context_length | |
| 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 | |
| # norms | |
| self.layer_norm_epsilon = layer_norm_epsilon | |
| # Tying | |
| self.tie_word_embeddings = tie_word_embeddings | |
| class MoEPgemmaModel(PreTrainedModel): | |
| config_class = MoEPgemmaLMConfig | |
| base_model_prefix = "model" | |
| def __init__(self, config: MoEPgemmaLMConfig): | |
| super().__init__(config) | |
| self.config = config | |
| # --- embeddings --- | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.dim, padding_idx=config.pad_token_id) | |
| self.layers = nn.ModuleList() | |
| # Large Layer | |
| self.layers.append(Layer_Block(config.dim, | |
| config.n_heads)) | |
| # 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)) | |
| # 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)) | |
| self.norm = RMS_norm(config.dim) | |
| self.post_init() | |
| self.sliding_window = config.sliding_window | |
| self.freqs_p = None | |
| self.freqs = None | |
| 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 | |
| if self.freqs is None or self.freqs.device != device: | |
| self.freqs = precompute_theta_pos_frequencies( | |
| int(self.config.dim / self.config.n_heads), | |
| self.config.max_context_length, | |
| device=device | |
| ) | |
| if self.freqs_p is None or self.freqs_p.device != device: | |
| self.freqs_p = precompute_theta_pos_frequencies( | |
| int(self.config.parallel_dim / self.config.parallel_n_heads), | |
| self.config.max_context_length, | |
| device=device | |
| ) | |
| 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.sliding_window, device=self.device, dtype=mask_dtype) | |
| sw_mask = None if sw is None else torch.minimum(causal, sw) | |
| aux_total = None | |
| all_one_hots = [] | |
| freqs = self.freqs | |
| # First large | |
| x = self.layers[0](x, causal_mask=causal, key_padding_mask=key_padding, freqs=freqs) | |
| # MoE shrink | |
| x, aux, one_hot = self.layers[1](x) | |
| if aux is not None: | |
| aux_total = aux | |
| if one_hot is not None: | |
| all_one_hots.append(one_hot) | |
| freqs_p = self.freqs_p | |
| idx = 2 | |
| for i in range(self.config.n_layers - 2): | |
| use_sw = (self.sliding_window is not None) and (i % 2 == 1) | |
| mask = sw_mask if (use_sw and sw_mask is not None) else causal | |
| x, aux, one_hot = self.layers[idx](x, causal_mask=mask, key_padding_mask=key_padding, freqs=freqs_p) | |
| if aux is not None: | |
| aux_total += aux | |
| if one_hot is not None: | |
| all_one_hots.append(one_hot) | |
| idx += 1 | |
| # MoE grow | |
| x, aux, one_hot = self.layers[idx](x) | |
| if aux is not None: | |
| aux_total += aux | |
| if one_hot is not None: | |
| all_one_hots.append(one_hot) | |
| idx += 1 | |
| # Second large | |
| x = self.layers[idx](x, causal_mask=causal, key_padding_mask=key_padding, freqs=freqs) | |
| 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, | |
| one_hot=all_one_hots, | |
| ) | |
| else: | |
| return BaseModelOutput( | |
| last_hidden_state=last_hidden, | |
| hidden_states=None, | |
| attentions=None, | |
| ) | |
| class MoEPgemmaForCausalLM(PreTrainedModel): | |
| config_class = MoEPgemmaLMConfig | |
| base_model_prefix = "model" | |
| def __init__(self, config: MoEPgemmaLMConfig): | |
| super().__init__(config) | |
| self.config = config | |
| self.model = MoEPgemmaModel(config) | |
| self.lm_head = nn.Linear(config.dim, config.vocab_size, bias=False) | |
| self.post_init() | |
| if config.tie_word_embeddings: | |
| self.lm_head.weight = self.model.embed_tokens.weight | |
| 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) | |
| 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 return_aux: | |
| return CausalLMOutputWithAux( | |
| loss=loss, | |
| logits=logits, | |
| hidden_states=base_out.hidden_states, | |
| attentions=base_out.attentions, | |
| aux_loss=getattr(base_out, "aux_loss", None), | |
| one_hot=getattr(base_out, "one_hot", None), | |
| ) | |
| else: | |
| return CausalLMOutput( | |
| loss=loss, | |
| logits=logits, | |
| hidden_states=base_out.hidden_states, | |
| attentions=base_out.attentions, | |
| ) | |
| AutoConfig.register("moep_gemma_lm", MoEPgemmaLMConfig) | |
| AutoModelForCausalLM.register(MoEPgemmaLMConfig, MoEPgemmaForCausalLM) | |
| AutoModel.register(MoEPgemmaLMConfig, MoEPgemmaModel) | |