msit_gptbert_fresh / modeling_msit.py
SRJ5035's picture
Upload model weights and configuration
4a38582 verified
Raw
History Blame Contribute Delete
18.3 kB
import os
import math
import random
import inspect
from typing import Optional, Tuple, Dict, Any
from dataclasses import dataclass
import torch
import torch.nn as nn
import torch.nn.functional as F
from transformers import PretrainedConfig, PreTrainedModel, GenerationMixin, AutoConfig, AutoModel, AutoModelForCausalLM
from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
# ─────────────────────────────────────────────────────────────
# Configuration Classes
# ─────────────────────────────────────────────────────────────
@dataclass
class MSITGPTBERTConfig:
vocab_size: int = 16384
block_size: int = 512
d_model: int = 384
d_thin: int = 192
num_layers: int = 6
num_blocks: int = 6
capacity_factor: float = 2.0
dropout: float = 0.1
class MSITGPTBERTHFConfig(PretrainedConfig):
model_type = "msit_gptbert"
def __init__(
self,
vocab_size: int = 16384,
block_size: int = 512,
d_model: int = 384,
d_thin: int = 192,
num_layers: int = 6,
num_blocks: int = 6,
capacity_factor: float = 2.0,
dropout: float = 0.1,
**kwargs
):
kwargs.setdefault("is_decoder", True)
kwargs.setdefault("bos_token_id", 2) # [CLS]
kwargs.setdefault("eos_token_id", 3) # [SEP]
kwargs.setdefault("pad_token_id", 1) # [PAD]
self.vocab_size = vocab_size
self.block_size = block_size
self.d_model = d_model
self.d_thin = d_thin
self.num_layers = num_layers
self.num_blocks = num_blocks
self.capacity_factor = capacity_factor
self.dropout = dropout
# Attribute parity for classification heads
self.hidden_size = d_model
super().__init__(**kwargs)
# ─────────────────────────────────────────────────────────────
# ROPE HELPERS
# ─────────────────────────────────────────────────────────────
def _precompute_rope_freqs(head_dim: int, seq_len: int, device: torch.device, theta: float = 10000.0):
assert head_dim % 2 == 0, "head_dim must be divisible by 2 for RoPE"
inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
t = torch.arange(seq_len, device=device).float()
freqs = torch.outer(t, inv_freq)
emb = torch.cat((freqs, freqs), dim=-1)
return emb.cos(), emb.sin()
def _apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor):
L = x.size(2)
cos = cos[:L, :].unsqueeze(0).unsqueeze(1)
sin = sin[:L, :].unsqueeze(0).unsqueeze(1)
half_dim = x.size(-1) // 2
x1 = x[..., :half_dim]
x2 = x[..., half_dim:]
rotated_x = torch.cat((-x2, x1), dim=-1)
return (x * cos) + (rotated_x * sin)
# ─────────────────────────────────────────────────────────────
# 1. SLIDING WINDOW ATTENTION
# ─────────────────────────────────────────────────────────────
class SlidingWindowAttention(nn.Module):
def __init__(self, dim: int, num_heads: int, window_size=None):
super().__init__()
assert dim % num_heads == 0, "dim must be divisible by num_heads"
self.num_heads = num_heads
self.window_size = window_size
self.head_dim = dim // num_heads
self.q_proj = nn.Linear(dim, dim, bias=False)
self.k_proj = nn.Linear(dim, dim, bias=False)
self.v_proj = nn.Linear(dim, dim, bias=False)
self.o_proj = nn.Linear(dim, dim, bias=False)
def forward(self, x: torch.Tensor,
past_kv=None,
use_cache: bool = False,
bidirectional: bool = False):
B, L, D = x.size()
q = self.q_proj(x).view(B, L, self.num_heads, self.head_dim).transpose(1, 2)
k = self.k_proj(x).view(B, L, self.num_heads, self.head_dim).transpose(1, 2)
v = self.v_proj(x).view(B, L, self.num_heads, self.head_dim).transpose(1, 2)
if past_kv is not None:
past_k, past_v = past_kv
past_len = past_k.size(2)
q_cos, q_sin = _precompute_rope_freqs(self.head_dim, past_len + L, x.device)
q = _apply_rope(q, q_cos[past_len:, :], q_sin[past_len:, :])
k = _apply_rope(k, q_cos[past_len:, :], q_sin[past_len:, :])
k = torch.cat([past_k, k], dim=2)
v = torch.cat([past_v, v], dim=2)
else:
cos, sin = _precompute_rope_freqs(self.head_dim, L, x.device)
q = _apply_rope(q, cos, sin)
k = _apply_rope(k, cos, sin)
L_kv = k.size(2)
scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.head_dim)
if bidirectional:
if self.window_size is not None:
past_len = L_kv - L
pos_i = (past_len + torch.arange(L, device=x.device)).unsqueeze(1)
pos_j = torch.arange(L_kv, device=x.device).unsqueeze(0)
dist = torch.abs(pos_i - pos_j)
win_mask = dist < self.window_size
scores = scores.masked_fill(
~win_mask.unsqueeze(0).unsqueeze(0), float('-inf')
)
else:
past_len = L_kv - L
pos_i = (past_len + torch.arange(L, device=x.device)).unsqueeze(1)
pos_j = torch.arange(L_kv, device=x.device).unsqueeze(0)
dist = pos_i - pos_j
causal_mask = dist >= 0
if self.window_size is not None:
causal_mask = causal_mask & (dist < self.window_size)
scores = scores.masked_fill(
~causal_mask.unsqueeze(0).unsqueeze(0), float('-inf')
)
attn = torch.softmax(scores, dim=-1)
out = torch.matmul(attn, v)
out = out.transpose(1, 2).contiguous().view(B, L, D)
out = self.o_proj(out)
if use_cache:
if self.window_size is not None:
present_kv = (
k[:, :, -self.window_size:, :],
v[:, :, -self.window_size:, :]
)
else:
present_kv = (k, v)
else:
present_kv = None
return out, present_kv
# ─────────────────────────────────────────────────────────────
# 2. MSIT BRANCH BLOCK (Pre-Norm)
# ─────────────────────────────────────────────────────────────
class MSITBranchBlock(nn.Module):
def __init__(self, dim: int, num_heads: int, window_size):
super().__init__()
self.ln1 = nn.LayerNorm(dim)
self.attn = SlidingWindowAttention(dim, num_heads, window_size)
self.ln2 = nn.LayerNorm(dim)
self.ffn = nn.Sequential(
nn.Linear(dim, dim * 4, bias=False),
nn.GELU(),
nn.Linear(dim * 4, dim, bias=False),
)
def forward(self, x: torch.Tensor,
past_kv=None,
use_cache: bool = False,
bidirectional: bool = False):
attn_out, present_kv = self.attn(
self.ln1(x), past_kv, use_cache, bidirectional
)
x = x + attn_out
x = x + self.ffn(self.ln2(x))
return x, present_kv
# ─────────────────────────────────────────────────────────────
# 3. MoEP-MSIT ARCHITECTURE BLOCK (Expert Choice routing)
# ─────────────────────────────────────────────────────────────
class MoEPMSITBlock(nn.Module):
def __init__(self, d_model: int = 512, d_thin: int = 192, num_blocks: int = 14,
capacity_factor: float = 2.0):
super().__init__()
self.d_model = d_model
self.d_thin = d_thin
self.num_blocks = num_blocks
self.capacity_factor = capacity_factor
# 1. Global Block (Dense, d_model)
num_heads_global = max(1, d_model // 64)
self.global_block = MSITBranchBlock(d_model, num_heads_global, window_size=None)
# 2. Router
self.router_ln = nn.LayerNorm(d_model)
self.w_router = nn.Linear(d_model, num_blocks, bias=False)
# 3. Shrink Projection
self.w_down = nn.Linear(d_model, d_thin, bias=False)
# 4. Thin Parallel Blocks (d_thin)
self.windows = [None, 16, 8, 4, 2] + [None] * (num_blocks - 5)
self.heads = [max(1, d_thin // 64)] * num_blocks
self.thin_blocks = nn.ModuleList([
MSITBranchBlock(d_thin, self.heads[i], self.windows[i])
for i in range(num_blocks)
])
# 6. Grow Projection
self.w_up = nn.Linear(d_thin, d_model, bias=False)
self.last_topk_indices = None
def forward(self, x_0: torch.Tensor, past_kvs=None, use_cache: bool = False, bidirectional: bool = False):
B, T, D = x_0.size()
n_tokens = B * T
# Step 1: Global Block
pkv_g = past_kvs[0] if past_kvs else None
x_1, nkv_g = self.global_block(x_0, pkv_g, use_cache, bidirectional)
# Step 2: Gated input stream
x_2 = x_1 + x_0
# Router scores
r_logits = self.w_router(self.router_ln(x_2)).view(n_tokens, self.num_blocks)
r_probs = F.softmax(r_logits, dim=-1)
# Per-expert capacity: k = (n * c) / e
k_capacity = max(1, int(round(n_tokens * self.capacity_factor / self.num_blocks)))
k_capacity = min(k_capacity, n_tokens)
# Expert Choice routing: topk over the token axis for each expert
expert_token_scores = r_probs.transpose(0, 1) # (num_blocks, n_tokens)
topk_scores, topk_token_idx = torch.topk(expert_token_scores, k_capacity, dim=-1)
self.last_topk_indices = topk_token_idx
# Load balancing is guaranteed by construction in Expert Choice
layer_aux_loss = x_2.new_zeros(())
# Shrink projection
x_2_thin = self.w_down(x_2) # (B, T, d_thin)
x_2_thin_flat = x_2_thin.view(n_tokens, self.d_thin)
# Expert computations
new_kvs = [nkv_g]
expert_outputs_flat = torch.zeros(n_tokens, self.d_model, device=x_2.device, dtype=x_2.dtype)
for i, block in enumerate(self.thin_blocks):
sel_idx = topk_token_idx[i]
bucket_in = x_2_thin_flat[sel_idx].unsqueeze(0) # (1, k_capacity, d_thin)
pkv_i = past_kvs[i + 1] if past_kvs else None
bucket_out, nkv_i = block(bucket_in, pkv_i, use_cache, bidirectional)
if use_cache:
new_kvs.append(nkv_i)
bucket_out = bucket_out.squeeze(0) # (k_capacity, d_thin)
bucket_out_full = self.w_up(bucket_out) # (k_capacity, d_model)
gate = topk_scores[i].unsqueeze(-1) # (k_capacity, 1)
expert_outputs_flat.index_add_(0, sel_idx, bucket_out_full * gate)
x_3_full = expert_outputs_flat.view(B, T, self.d_model)
out = x_2 + x_3_full
present_kvs = tuple(new_kvs) if use_cache else None
return out, present_kvs, layer_aux_loss
# ─────────────────────────────────────────────────────────────
# 4. RAW MSIT-GPT-BERT MODEL
# ─────────────────────────────────────────────────────────────
class MSITGPTBERTModel(nn.Module):
def __init__(self, cfg):
super().__init__()
self.cfg = cfg
self.wte = nn.Embedding(cfg.vocab_size, cfg.d_model)
self.drop_emb = nn.Dropout(cfg.dropout)
self.blocks = nn.ModuleList([
MoEPMSITBlock(
d_model=cfg.d_model,
d_thin=cfg.d_thin,
num_blocks=cfg.num_blocks,
capacity_factor=cfg.capacity_factor
)
for _ in range(cfg.num_layers)
])
self.ln_f = nn.LayerNorm(cfg.d_model)
self.lm_head = nn.Linear(cfg.d_model, cfg.vocab_size, bias=False)
self.wte.weight = self.lm_head.weight
def forward(self, input_ids: torch.Tensor, targets: torch.Tensor = None, bidirectional: bool = False):
x = self.drop_emb(self.wte(input_ids))
total_aux_loss = 0.0
for block in self.blocks:
x, _, layer_aux = block(x, past_kvs=None, use_cache=False, bidirectional=bidirectional)
total_aux_loss += layer_aux
x = self.ln_f(x)
logits = self.lm_head(x)
loss = None
if targets is not None:
ce_loss = F.cross_entropy(logits.view(-1, self.cfg.vocab_size), targets.view(-1), ignore_index=-100)
avg_aux_loss = total_aux_loss / self.cfg.num_layers
loss = ce_loss + (0.01 * avg_aux_loss)
return logits, loss
# ─────────────────────────────────────────────────────────────
# 5. HUGGING FACE MODEL WRAPPERS
# ─────────────────────────────────────────────────────────────
class MSITGPTBERTModelWrapper(PreTrainedModel):
config_class = MSITGPTBERTHFConfig
base_model_prefix = "transformer"
def __init__(self, config: MSITGPTBERTHFConfig):
super().__init__(config)
self.wte = nn.Embedding(config.vocab_size, config.d_model)
self.drop_emb = nn.Dropout(config.dropout)
self.blocks = nn.ModuleList([
MoEPMSITBlock(config.d_model, config.d_thin, config.num_blocks, config.capacity_factor)
for _ in range(config.num_layers)
])
self.ln_f = nn.LayerNorm(config.d_model)
self.post_init()
def forward(self, input_ids, **kwargs):
x = self.drop_emb(self.wte(input_ids))
for block in self.blocks:
x, _, _ = block(x, past_kvs=None, use_cache=False, bidirectional=False)
x = self.ln_f(x)
return BaseModelOutputWithPast(last_hidden_state=x)
class MSITGPTBERTForCausalLM(PreTrainedModel, GenerationMixin):
config_class = MSITGPTBERTHFConfig
base_model_prefix = "transformer"
_no_split_modules = ["MoEPMSITBlock"]
_tied_weights_keys = {"transformer.lm_head.weight": "transformer.wte.weight"}
def __init__(self, config: MSITGPTBERTHFConfig):
super().__init__(config)
self.transformer = MSITGPTBERTModelWrapper(config)
self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False)
self.post_init()
# State-dict pre-hook for backwards compatibility with checkpoint key naming
def _prefix_cleaner(state_dict, prefix, local_metadata, Moore, missing_keys, unexpected_keys, error_msgs):
keys = list(state_dict.keys())
for k in keys:
if k.startswith("transformer."):
state_dict[k.replace("transformer.", "", 1)] = state_dict.pop(k)
elif f"{prefix}transformer." in k:
state_dict[k.replace("transformer.", "", 1)] = state_dict.pop(k)
self._register_load_state_dict_pre_hook(_prefix_cleaner)
def tie_weights(self, **kwargs):
if hasattr(self, "transformer") and hasattr(self.transformer, "wte") and hasattr(self.transformer, "lm_head"):
self.transformer.wte.weight = self.lm_head.weight
def get_input_embeddings(self):
return self.transformer.wte
def set_input_embeddings(self, new_embeddings):
self.transformer.wte = new_embeddings
def get_output_embeddings(self):
return self.lm_head
def set_output_embeddings(self, new_embeddings):
self.lm_head = new_embeddings
def forward(self,
input_ids: Optional[torch.LongTensor] = None,
attention_mask: Optional[torch.FloatTensor] = None,
labels: Optional[torch.LongTensor] = None,
**kwargs) -> CausalLMOutputWithPast:
outputs = self.transformer(input_ids)
hidden_states = outputs.last_hidden_state
logits = self.lm_head(hidden_states)
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),
ignore_index=-100
)
return CausalLMOutputWithPast(
loss=loss,
logits=logits,
past_key_values=None,
hidden_states=None,
attentions=None,
)
def prepare_inputs_for_generation(self, input_ids, **kwargs):
return {"input_ids": input_ids}
# Register with auto-mapping
AutoConfig.register("msit_gptbert", MSITGPTBERTHFConfig)
AutoModel.register(MSITGPTBERTHFConfig, MSITGPTBERTModelWrapper)
AutoModelForCausalLM.register(MSITGPTBERTHFConfig, MSITGPTBERTForCausalLM)