ViuAI commited on
Commit
d29f5f8
·
verified ·
1 Parent(s): 0ce4742

Upload model.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. model.py +279 -0
model.py ADDED
@@ -0,0 +1,279 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from torch.utils.checkpoint import checkpoint
6
+ from config import ViuAIConfig
7
+
8
+ class RMSNorm(nn.Module):
9
+ def __init__(self, dim, eps=1e-5):
10
+ super().__init__()
11
+ self.eps = eps
12
+ self.weight = nn.Parameter(torch.ones(dim))
13
+
14
+ def forward(self, x):
15
+ x_fp32 = x.float()
16
+ norm = x_fp32 * torch.rsqrt(x_fp32.pow(2).mean(-1, keepdim=True) + self.eps)
17
+ # Optimized: removed redundant self.weight.float() cast for bfloat16/float16 throughput
18
+ return (norm * self.weight).type_as(x)
19
+
20
+ def precompute_rope(head_dim, max_len, theta=10000.0, device="cpu"):
21
+ freqs = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
22
+ t = torch.arange(max_len, device=device).float()
23
+ freqs = torch.outer(t, freqs)
24
+ return torch.cos(freqs), torch.sin(freqs)
25
+
26
+ def apply_rope(x, cos, sin):
27
+ cos = cos.to(x.dtype)
28
+ sin = sin.to(x.dtype)
29
+ x1, x2 = x[..., ::2], x[..., 1::2]
30
+ # x is [B, n_heads, T, head_dim]
31
+ # cos, sin are [max_len, head_dim//2]
32
+ T = x.size(2)
33
+ cos = cos[:T][None, None, :, :]
34
+ sin = sin[:T][None, None, :, :]
35
+ rotated = torch.stack([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)
36
+ return rotated.flatten(-2)
37
+
38
+ def repeat_kv(x, n_rep):
39
+ if n_rep == 1:
40
+ return x
41
+ return x.repeat_interleave(n_rep, dim=1)
42
+
43
+ class Attention(nn.Module):
44
+ def __init__(self, cfg: ViuAIConfig):
45
+ super().__init__()
46
+ self.n_heads = cfg.n_heads
47
+ self.n_kv_heads = cfg.n_kv_heads
48
+ self.head_dim = cfg.d_model // cfg.n_heads
49
+ self.n_rep = self.n_heads // self.n_kv_heads
50
+
51
+ self.q_proj = nn.Linear(cfg.d_model, cfg.n_heads * self.head_dim, bias=False)
52
+ self.k_proj = nn.Linear(cfg.d_model, cfg.n_kv_heads * self.head_dim, bias=False)
53
+ self.v_proj = nn.Linear(cfg.d_model, cfg.n_kv_heads * self.head_dim, bias=False)
54
+ self.o_proj = nn.Linear(cfg.n_heads * self.head_dim, cfg.d_model, bias=False)
55
+ self.o_proj._is_residual = True # flag for depth-scaled init
56
+
57
+ self.q_norm = RMSNorm(self.head_dim, cfg.norm_eps)
58
+ self.k_norm = RMSNorm(self.head_dim, cfg.norm_eps)
59
+ self.attn_dropout = cfg.attn_dropout
60
+
61
+ def forward(self, x, cos, sin, attn_mask=None):
62
+ B, T, C = x.shape
63
+ q = self.q_proj(x).view(B, T, self.n_heads, self.head_dim).transpose(1, 2)
64
+ k = self.k_proj(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
65
+ v = self.v_proj(x).view(B, T, self.n_kv_heads, self.head_dim).transpose(1, 2)
66
+
67
+ q, k = self.q_norm(q), self.k_norm(k)
68
+ q, k = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
69
+
70
+ dropout_p = self.attn_dropout if self.training else 0.0
71
+ try:
72
+ if attn_mask is not None:
73
+ out = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, dropout_p=dropout_p, enable_gqa=True)
74
+ else:
75
+ out = F.scaled_dot_product_attention(q, k, v, is_causal=True, dropout_p=dropout_p, enable_gqa=True)
76
+ except TypeError:
77
+ if self.n_rep > 1:
78
+ k = repeat_kv(k, self.n_rep)
79
+ v = repeat_kv(v, self.n_rep)
80
+ if attn_mask is not None:
81
+ out = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, dropout_p=dropout_p)
82
+ else:
83
+ out = F.scaled_dot_product_attention(q, k, v, is_causal=True, dropout_p=dropout_p)
84
+
85
+ out = out.transpose(1, 2).contiguous().view(B, T, -1)
86
+ return self.o_proj(out)
87
+
88
+ class SwiGLU(nn.Module):
89
+ def __init__(self, cfg: ViuAIConfig):
90
+ super().__init__()
91
+ self.gate_proj = nn.Linear(cfg.d_model, cfg.ffn_hidden, bias=False)
92
+ self.up_proj = nn.Linear(cfg.d_model, cfg.ffn_hidden, bias=False)
93
+ self.down_proj = nn.Linear(cfg.ffn_hidden, cfg.d_model, bias=False)
94
+ self.down_proj._is_residual = True # flag for depth-scaled init
95
+
96
+ def forward(self, x):
97
+ return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
98
+
99
+ class Block(nn.Module):
100
+ def __init__(self, cfg: ViuAIConfig):
101
+ super().__init__()
102
+ self.attn_norm = RMSNorm(cfg.d_model, cfg.norm_eps)
103
+ self.attn = Attention(cfg)
104
+ self.ffn_norm = RMSNorm(cfg.d_model, cfg.norm_eps)
105
+ self.ffn = SwiGLU(cfg)
106
+ self.resid_dropout = nn.Dropout(cfg.resid_dropout) if cfg.resid_dropout > 0 else nn.Identity()
107
+
108
+ def forward(self, x, cos, sin, attn_mask=None):
109
+ x = x + self.resid_dropout(self.attn(self.attn_norm(x), cos, sin, attn_mask))
110
+ x = x + self.resid_dropout(self.ffn(self.ffn_norm(x)))
111
+ return x
112
+
113
+ class ViuAI(nn.Module):
114
+ def __init__(self, cfg: ViuAIConfig):
115
+ super().__init__()
116
+ assert cfg.d_model % cfg.n_heads == 0, "d_model must be divisible by n_heads"
117
+ assert cfg.n_heads % cfg.n_kv_heads == 0, "n_heads must be divisible by n_kv_heads"
118
+ assert (cfg.d_model // cfg.n_heads) % 2 == 0, "head_dim must be even for RoPE"
119
+
120
+ self.cfg = cfg
121
+ self.tok_emb = nn.Embedding(cfg.vocab_size, cfg.d_model)
122
+ self.blocks = nn.ModuleList([Block(cfg) for _ in range(cfg.n_layers)])
123
+ self.final_norm = RMSNorm(cfg.d_model, cfg.norm_eps)
124
+ self.head = nn.Linear(cfg.d_model, cfg.vocab_size, bias=False)
125
+ self.head.weight = self.tok_emb.weight
126
+
127
+ head_dim = cfg.d_model // cfg.n_heads
128
+ cos, sin = precompute_rope(head_dim, cfg.context_length, cfg.rope_theta)
129
+ self.register_buffer("rope_cos", cos, persistent=False)
130
+ self.register_buffer("rope_sin", sin, persistent=False)
131
+
132
+ self.apply(self._init_weights)
133
+
134
+ def _init_weights(self, module):
135
+ if isinstance(module, nn.Linear):
136
+ if hasattr(self, 'head') and module is self.head:
137
+ return
138
+ std = 0.02
139
+ if getattr(module, '_is_residual', False):
140
+ std *= (2 * self.cfg.n_layers) ** -0.5
141
+ nn.init.normal_(module.weight, mean=0.0, std=std)
142
+ elif isinstance(module, nn.Embedding):
143
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
144
+
145
+ def forward(self, idx, targets=None, pad_id=None, shift_labels=False):
146
+ B, T = idx.shape
147
+ assert T <= self.cfg.context_length, f"Sequence length {T} exceeds context_length {self.cfg.context_length}"
148
+ x = self.tok_emb(idx)
149
+
150
+ # NEFTune: Noisy Embedding Fine-Tuning for conversational quality boost
151
+ if self.training and getattr(self.cfg, "neftune_alpha", 0.0) > 0.0:
152
+ dims = x.size(1) * x.size(2)
153
+ mag = self.cfg.neftune_alpha / math.sqrt(max(1, dims))
154
+ noise = torch.zeros_like(x).uniform_(-1.0, 1.0) * mag
155
+ x = x + noise
156
+
157
+ cos = self.rope_cos.to(x.device)
158
+ sin = self.rope_sin.to(x.device)
159
+
160
+ # BUG 2 FIX: Additive Float Attention Mask (0.0 = attend, -inf = mask out)
161
+ attn_mask = None
162
+ if pad_id is not None:
163
+ causal = torch.tril(torch.ones(T, T, dtype=torch.bool, device=x.device))
164
+ key_mask = (idx != pad_id).unsqueeze(1).unsqueeze(2) # [B, 1, 1, T]
165
+ bool_mask = causal.unsqueeze(0).unsqueeze(0) & key_mask # [B, 1, T, T] True = allowed
166
+ attn_mask = torch.zeros(B, 1, T, T, dtype=x.dtype, device=x.device)
167
+ attn_mask = attn_mask.masked_fill(~bool_mask, float("-inf"))
168
+
169
+ for block in self.blocks:
170
+ if self.training and self.cfg.use_checkpoint:
171
+ if attn_mask is not None:
172
+ x = checkpoint(block, x, cos, sin, attn_mask, use_reentrant=False)
173
+ else:
174
+ x = checkpoint(block, x, cos, sin, use_reentrant=False)
175
+ else:
176
+ x = block(x, cos, sin, attn_mask)
177
+ x = self.final_norm(x)
178
+ logits = self.head(x)
179
+
180
+ loss = None
181
+ if targets is not None:
182
+ # BUG 1 FIX: Explicit shift_labels flag support
183
+ if shift_labels:
184
+ shift_logits = logits[..., :-1, :].contiguous()
185
+ shift_targets = targets[..., 1:].contiguous()
186
+ loss = F.cross_entropy(shift_logits.view(-1, shift_logits.size(-1)), shift_targets.view(-1), ignore_index=-100)
187
+ else:
188
+ # Targets are already pre-shifted (e.g. pretraining ShardPool y = tokens[i+1:])
189
+ loss = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-100)
190
+
191
+ # z-loss: stabilizes logit magnitudes during pretraining; disable for SFT (z_loss_weight=0)
192
+ if self.cfg.z_loss_weight > 0:
193
+ z_loss = self.cfg.z_loss_weight * (torch.logsumexp(logits, dim=-1) ** 2).mean()
194
+ loss = loss + z_loss
195
+
196
+ return logits, loss
197
+
198
+ def num_params(self):
199
+ """Count parameters, deduplicating tied weights."""
200
+ seen = set()
201
+ total = 0
202
+ for p in self.parameters():
203
+ if p.data_ptr() not in seen:
204
+ seen.add(p.data_ptr())
205
+ total += p.numel()
206
+ return total
207
+
208
+ @torch.no_grad()
209
+ def generate(self, idx, max_new_tokens, temperature=1.0, top_k=50, top_p=0.9,
210
+ eos_token_id=None, repetition_penalty=1.0):
211
+ self.eval()
212
+ idx = idx.to(next(self.parameters()).device)
213
+ def _is_all_eos(next_tokens):
214
+ if eos_token_id is None:
215
+ return False
216
+ if isinstance(eos_token_id, (list, tuple, set)):
217
+ is_eos = torch.zeros(next_tokens.shape[0], dtype=torch.bool, device=next_tokens.device)
218
+ for eid in eos_token_id:
219
+ is_eos |= (next_tokens.squeeze(-1) == eid)
220
+ return is_eos.all().item()
221
+ return (next_tokens == eos_token_id).all().item()
222
+
223
+ for _ in range(max_new_tokens):
224
+ # Crop to context window
225
+ idx_cond = idx if idx.size(1) <= self.cfg.context_length else idx[:, -self.cfg.context_length:]
226
+ logits, _ = self(idx_cond)
227
+ logits = logits[:, -1, :] # only last position
228
+
229
+ # Repetition penalty (Standard Hugging Face formulation)
230
+ if repetition_penalty != 1.0:
231
+ for b in range(idx.size(0)):
232
+ prev_tokens = idx[b].unique()
233
+ score = logits[b, prev_tokens]
234
+ logits[b, prev_tokens] = torch.where(
235
+ score < 0, score * repetition_penalty, score / repetition_penalty
236
+ )
237
+
238
+ if temperature <= 0:
239
+ idx_next = logits.argmax(dim=-1, keepdim=True)
240
+ idx = torch.cat([idx, idx_next], dim=1)
241
+ if _is_all_eos(idx_next):
242
+ break
243
+ continue
244
+
245
+ # Temperature scaling
246
+ logits = logits / max(temperature, 1e-8)
247
+ if not torch.isfinite(logits).any():
248
+ raise ValueError("Model logits became non-finite (NaN/Inf) during generation. Ensure stable precision or checkpoint validity.")
249
+
250
+ # Top-k filtering
251
+ if top_k > 0:
252
+ top_k_val = min(top_k, logits.size(-1))
253
+ kth_vals, _ = torch.topk(logits, top_k_val)
254
+ logits[logits < kth_vals[:, [-1]]] = float('-inf')
255
+
256
+ # Top-p (nucleus) filtering
257
+ if top_p < 1.0:
258
+ sorted_logits, sorted_indices = torch.sort(logits, descending=True)
259
+ cumulative_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
260
+ sorted_indices_to_remove = cumulative_probs > top_p
261
+ sorted_indices_to_remove[..., 1:] = sorted_indices_to_remove[..., :-1].clone()
262
+ sorted_indices_to_remove[..., 0] = False
263
+ indices_to_remove = torch.zeros_like(logits, dtype=torch.bool).scatter_(
264
+ 1, sorted_indices, sorted_indices_to_remove
265
+ )
266
+ logits[indices_to_remove] = float('-inf')
267
+
268
+ probs = F.softmax(logits, dim=-1)
269
+ idx_next = torch.multinomial(probs, num_samples=1)
270
+ idx = torch.cat([idx, idx_next], dim=1)
271
+
272
+ if _is_all_eos(idx_next):
273
+ break
274
+
275
+ return idx
276
+
277
+ # Aliases for universal compatibility
278
+ Transformer = ViuAI
279
+ ModelArgs = ViuAIConfig