VertexAIco commited on
Commit
e0ca615
·
verified ·
1 Parent(s): e3e33cf

Upload model.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. model.py +262 -0
model.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ import torch.utils.checkpoint as ckpt
7
+
8
+ from config import MoEConfig
9
+
10
+
11
+ class RMSNorm(nn.Module):
12
+ def __init__(self, dim: int, eps: float):
13
+ super().__init__()
14
+ self.eps = eps
15
+ self.weight = nn.Parameter(torch.ones(dim))
16
+
17
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
18
+ var = x.pow(2).mean(-1, keepdim=True)
19
+ x = x * torch.rsqrt(var + self.eps)
20
+ return x * self.weight
21
+
22
+
23
+ def precompute_rope(head_dim: int, max_seq_len: int, theta: float, device, dtype):
24
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
25
+ t = torch.arange(max_seq_len, device=device).float()
26
+ freqs = torch.outer(t, inv_freq)
27
+ cos = freqs.cos().to(dtype)
28
+ sin = freqs.sin().to(dtype)
29
+ return cos, sin
30
+
31
+
32
+ def rotate_half(x: torch.Tensor) -> torch.Tensor:
33
+ x1, x2 = x.chunk(2, dim=-1)
34
+ return torch.cat((-x2, x1), dim=-1)
35
+
36
+
37
+ def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
38
+ # x: (batch, heads, seq, head_dim); cos/sin: (seq, head_dim/2)
39
+ cos = torch.cat([cos, cos], dim=-1)[None, None, :, :]
40
+ sin = torch.cat([sin, sin], dim=-1)[None, None, :, :]
41
+ return x * cos + rotate_half(x) * sin
42
+
43
+
44
+ class GQAAttention(nn.Module):
45
+ def __init__(self, cfg: MoEConfig):
46
+ super().__init__()
47
+ self.n_heads = cfg.num_attn_heads
48
+ self.n_kv_heads = cfg.num_kv_heads
49
+ self.head_dim = cfg.head_dim
50
+ self.q_proj = nn.Linear(cfg.hidden_size, self.n_heads * self.head_dim, bias=False)
51
+ self.k_proj = nn.Linear(cfg.hidden_size, self.n_kv_heads * self.head_dim, bias=False)
52
+ self.v_proj = nn.Linear(cfg.hidden_size, self.n_kv_heads * self.head_dim, bias=False)
53
+ self.o_proj = nn.Linear(self.n_heads * self.head_dim, cfg.hidden_size, bias=False)
54
+
55
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
56
+ b, t, _ = x.shape
57
+ q = self.q_proj(x).view(b, t, self.n_heads, self.head_dim).transpose(1, 2)
58
+ k = self.k_proj(x).view(b, t, self.n_kv_heads, self.head_dim).transpose(1, 2)
59
+ v = self.v_proj(x).view(b, t, self.n_kv_heads, self.head_dim).transpose(1, 2)
60
+
61
+ q = apply_rope(q, cos, sin)
62
+ k = apply_rope(k, cos, sin)
63
+
64
+ rep = self.n_heads // self.n_kv_heads
65
+ k = k.repeat_interleave(rep, dim=1)
66
+ v = v.repeat_interleave(rep, dim=1)
67
+
68
+ out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
69
+ out = out.transpose(1, 2).contiguous().view(b, t, -1)
70
+ return self.o_proj(out)
71
+
72
+
73
+ class SwiGLU(nn.Module):
74
+ def __init__(self, hidden_size: int, intermediate_size: int):
75
+ super().__init__()
76
+ self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
77
+ self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False)
78
+ self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False)
79
+
80
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
81
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
82
+
83
+
84
+ class DeepSeekMoE(nn.Module):
85
+ """Fine-grained MoE: many small routed experts + always-on shared experts.
86
+
87
+ Standard emergent top-k routing (no domain seeding). Load-balancing is
88
+ encouraged via an auxiliary loss on router probabilities, not by
89
+ constraining which tokens go where.
90
+ """
91
+
92
+ def __init__(self, cfg: MoEConfig):
93
+ super().__init__()
94
+ self.num_routed = cfg.num_routed_experts
95
+ self.top_k = cfg.num_experts_per_tok
96
+ self.aux_loss_coef = cfg.router_aux_loss_coef
97
+
98
+ self.shared_experts = nn.ModuleList(
99
+ [SwiGLU(cfg.hidden_size, cfg.moe_intermediate_size) for _ in range(cfg.num_shared_experts)]
100
+ )
101
+ self.routed_experts = nn.ModuleList(
102
+ [SwiGLU(cfg.hidden_size, cfg.moe_intermediate_size) for _ in range(cfg.num_routed_experts)]
103
+ )
104
+ self.gate = nn.Linear(cfg.hidden_size, cfg.num_routed_experts, bias=False)
105
+
106
+ self.last_aux_loss: torch.Tensor | None = None
107
+
108
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
109
+ b, t, h = x.shape
110
+ x_flat = x.view(-1, h) # (N, h)
111
+
112
+ out = torch.zeros_like(x_flat)
113
+ for expert in self.shared_experts:
114
+ out = out + expert(x_flat)
115
+
116
+ router_logits = self.gate(x_flat) # (N, num_routed)
117
+ router_probs = F.softmax(router_logits, dim=-1, dtype=torch.float32)
118
+
119
+ top_w, top_idx = torch.topk(router_probs, self.top_k, dim=-1)
120
+ top_w = (top_w / top_w.sum(dim=-1, keepdim=True)).to(x.dtype) # renormalize
121
+
122
+ # Vectorized dispatch: sort the N*top_k (token, expert) assignments by expert
123
+ # id so each expert's tokens are contiguous, then run each expert once on its
124
+ # whole contiguous slice. This forces exactly one GPU->CPU sync per layer
125
+ # (for `counts.tolist()`) instead of one per expert -- 24x fewer syncs than a
126
+ # per-expert masked-boolean loop, and no wasted matmuls on empty selections.
127
+ flat_expert_ids = top_idx.reshape(-1) # (N*top_k,)
128
+ flat_weights = top_w.reshape(-1) # (N*top_k,)
129
+ flat_token_ids = (
130
+ torch.arange(x_flat.shape[0], device=x.device).unsqueeze(1).expand(-1, self.top_k).reshape(-1)
131
+ )
132
+
133
+ sort_order = torch.argsort(flat_expert_ids)
134
+ sorted_expert_ids = flat_expert_ids[sort_order]
135
+ sorted_token_ids = flat_token_ids[sort_order]
136
+ sorted_weights = flat_weights[sort_order]
137
+ sorted_x = x_flat[sorted_token_ids]
138
+
139
+ counts = torch.bincount(sorted_expert_ids, minlength=self.num_routed).tolist() # single sync
140
+ x_chunks = torch.split(sorted_x, counts)
141
+ weight_chunks = torch.split(sorted_weights, counts)
142
+ token_id_chunks = torch.split(sorted_token_ids, counts)
143
+
144
+ for expert_id, (x_chunk, w_chunk, tok_chunk) in enumerate(zip(x_chunks, weight_chunks, token_id_chunks)):
145
+ if x_chunk.shape[0] == 0: # cheap Python-int check, no GPU sync
146
+ continue
147
+ expert_out = self.routed_experts[expert_id](x_chunk) * w_chunk.unsqueeze(-1)
148
+ out.index_add_(0, tok_chunk, expert_out)
149
+
150
+ # load-balancing aux loss: encourage uniform expert usage (Switch-style)
151
+ tokens_per_expert = F.one_hot(top_idx, self.num_routed).sum(dim=1).float().mean(dim=0) # (num_routed,)
152
+ prob_per_expert = router_probs.mean(dim=0) # (num_routed,)
153
+ self.last_aux_loss = self.aux_loss_coef * self.num_routed * (tokens_per_expert * prob_per_expert).sum()
154
+
155
+ return out.view(b, t, h)
156
+
157
+
158
+ class Block(nn.Module):
159
+ def __init__(self, cfg: MoEConfig, layer_idx: int):
160
+ super().__init__()
161
+ self.attn_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
162
+ self.attn = GQAAttention(cfg)
163
+ self.ffn_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
164
+
165
+ self.is_moe = layer_idx >= cfg.first_k_dense_layers
166
+ if self.is_moe:
167
+ self.ffn = DeepSeekMoE(cfg)
168
+ else:
169
+ self.ffn = SwiGLU(cfg.hidden_size, cfg.dense_intermediate_size)
170
+
171
+ def forward(self, x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
172
+ x = x + self.attn(self.attn_norm(x), cos, sin)
173
+ x = x + self.ffn(self.ffn_norm(x))
174
+ return x
175
+
176
+
177
+ class MoELanguageModel(nn.Module):
178
+ def __init__(self, cfg: MoEConfig):
179
+ super().__init__()
180
+ self.cfg = cfg
181
+ self.embed = nn.Embedding(cfg.vocab_size, cfg.hidden_size)
182
+ self.layers = nn.ModuleList([Block(cfg, i) for i in range(cfg.num_layers)])
183
+ self.final_norm = RMSNorm(cfg.hidden_size, cfg.rms_norm_eps)
184
+ self.lm_head = nn.Linear(cfg.hidden_size, cfg.vocab_size, bias=False)
185
+
186
+ if cfg.tie_word_embeddings:
187
+ self.lm_head.weight = self.embed.weight
188
+
189
+ self._rope_cache = None
190
+ self.gradient_checkpointing = False
191
+ self.apply(self._init_weights)
192
+
193
+ def _init_weights(self, module: nn.Module):
194
+ if isinstance(module, nn.Linear):
195
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
196
+ elif isinstance(module, nn.Embedding):
197
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
198
+
199
+ def _rope(self, seq_len: int, device, dtype):
200
+ if self._rope_cache is None or self._rope_cache[0].shape[0] < seq_len:
201
+ self._rope_cache = precompute_rope(
202
+ self.cfg.head_dim, max(seq_len, self.cfg.max_seq_len), self.cfg.rope_theta, device, dtype
203
+ )
204
+ cos, sin = self._rope_cache
205
+ return cos[:seq_len], sin[:seq_len]
206
+
207
+ def forward(self, input_ids: torch.Tensor, labels: torch.Tensor | None = None):
208
+ x = self.embed(input_ids)
209
+ cos, sin = self._rope(input_ids.shape[1], x.device, x.dtype)
210
+
211
+ aux_losses = []
212
+ for layer in self.layers:
213
+ if self.gradient_checkpointing and self.training:
214
+ x = ckpt.checkpoint(layer, x, cos, sin, use_reentrant=False)
215
+ else:
216
+ x = layer(x, cos, sin)
217
+ if layer.is_moe and layer.ffn.last_aux_loss is not None:
218
+ aux_losses.append(layer.ffn.last_aux_loss)
219
+
220
+ x = self.final_norm(x)
221
+ logits = self.lm_head(x)
222
+
223
+ if labels is None:
224
+ return logits, None
225
+
226
+ ce = F.cross_entropy(
227
+ logits[:, :-1, :].reshape(-1, logits.size(-1)),
228
+ labels[:, 1:].reshape(-1),
229
+ ignore_index=-100,
230
+ )
231
+ aux = torch.stack(aux_losses).sum() if aux_losses else torch.zeros((), device=x.device)
232
+ loss = ce + aux
233
+ return logits, loss
234
+
235
+ def num_params(self, active_only: bool = False) -> int:
236
+ if not active_only:
237
+ return sum(p.numel() for p in self.parameters())
238
+
239
+ total = 0
240
+ for name, p in self.named_parameters():
241
+ if ".routed_experts." in name:
242
+ continue # only a top_k/num_routed fraction is active per token
243
+ total += p.numel()
244
+
245
+ cfg = self.cfg
246
+ per_expert_params = 3 * cfg.hidden_size * cfg.moe_intermediate_size
247
+ num_moe_layers = cfg.num_layers - cfg.first_k_dense_layers
248
+ active_routed_params = num_moe_layers * cfg.num_experts_per_tok * per_expert_params
249
+ return total + active_routed_params
250
+
251
+
252
+ if __name__ == "__main__":
253
+ cfg = MoEConfig()
254
+ model = MoELanguageModel(cfg)
255
+ total = model.num_params()
256
+ active = model.num_params(active_only=True)
257
+ print(f"total params: {total / 1e6:.1f}M")
258
+ print(f"active params: {active / 1e6:.1f}M")
259
+
260
+ x = torch.randint(0, cfg.vocab_size, (2, 128))
261
+ logits, loss = model(x, labels=x)
262
+ print("logits:", logits.shape, "loss:", loss.item())