Ruqiya commited on
Commit
bc0b6a4
·
verified ·
1 Parent(s): 4181292

PyTorch weights as safetensors, with the architecture module

Browse files
configs.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ruq-0.7m": {
3
+ "vocab_size": 8192,
4
+ "d_model": 64,
5
+ "n_layers": 4,
6
+ "n_heads": 4,
7
+ "n_kv_heads": 4,
8
+ "ffn_hidden": 192,
9
+ "max_seq_len": 512,
10
+ "rope_theta": 10000.0,
11
+ "norm_eps": 1e-05,
12
+ "dropout": 0.0,
13
+ "tie_embeddings": true
14
+ },
15
+ "ruq-2m": {
16
+ "vocab_size": 8192,
17
+ "d_model": 128,
18
+ "n_layers": 4,
19
+ "n_heads": 4,
20
+ "n_kv_heads": 4,
21
+ "ffn_hidden": 384,
22
+ "max_seq_len": 512,
23
+ "rope_theta": 10000.0,
24
+ "norm_eps": 1e-05,
25
+ "dropout": 0.0,
26
+ "tie_embeddings": true
27
+ },
28
+ "ruq-5m": {
29
+ "vocab_size": 8192,
30
+ "d_model": 256,
31
+ "n_layers": 4,
32
+ "n_heads": 4,
33
+ "n_kv_heads": 4,
34
+ "ffn_hidden": 704,
35
+ "max_seq_len": 512,
36
+ "rope_theta": 10000.0,
37
+ "norm_eps": 1e-05,
38
+ "dropout": 0.0,
39
+ "tie_embeddings": true
40
+ },
41
+ "ruq-15m": {
42
+ "vocab_size": 8192,
43
+ "d_model": 384,
44
+ "n_layers": 6,
45
+ "n_heads": 6,
46
+ "n_kv_heads": 6,
47
+ "ffn_hidden": 1024,
48
+ "max_seq_len": 512,
49
+ "rope_theta": 10000.0,
50
+ "norm_eps": 1e-05,
51
+ "dropout": 0.0,
52
+ "tie_embeddings": true
53
+ },
54
+ "ruq-30m": {
55
+ "vocab_size": 8192,
56
+ "d_model": 512,
57
+ "n_layers": 8,
58
+ "n_heads": 8,
59
+ "n_kv_heads": 8,
60
+ "ffn_hidden": 1408,
61
+ "max_seq_len": 512,
62
+ "rope_theta": 10000.0,
63
+ "norm_eps": 1e-05,
64
+ "dropout": 0.0,
65
+ "tie_embeddings": true
66
+ }
67
+ }
modeling_ruqlm.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ معمارية Ruq-LM — محوّل صغير مُدرَّب من الصفر.
3
+
4
+ هذا هو النموذج نفسه: أوزان مهيّأة عشوائياً، لا اشتقاق من أي نموذج جاهز.
5
+ الوصفة حديثة وقياسية: pre-norm + RMSNorm + RoPE + SwiGLU + تضمينات مربوطة.
6
+
7
+ لماذا هذه الخيارات عند 30M بارامتر تحديداً:
8
+ - RMSNorm بدل LayerNorm: أقل عمليات، ولا فرق يُذكر في الجودة.
9
+ - RoPE بدل تضمينات موضعية مُتعلَّمة: لا بارامترات إضافية، وتعميم أفضل
10
+ على أطوال لم تُرَ أثناء التدريب.
11
+ - SwiGLU: أفضل من ReLU/GELU عند ثبات عدد البارامترات.
12
+ - ربط تضمينات الدخل بالخرج: يوفّر 4.2M بارامتر — أي 14% من النموذج
13
+ عند مفردات 8192. عند هذا الحجم الصغير هذا فرق جوهري لا تحسين هامشي.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import math
19
+ from dataclasses import dataclass, asdict
20
+
21
+ import torch
22
+ import torch.nn as nn
23
+ import torch.nn.functional as F
24
+
25
+
26
+ @dataclass
27
+ class ModelArgs:
28
+ vocab_size: int = 8192
29
+ d_model: int = 512
30
+ n_layers: int = 8
31
+ n_heads: int = 8
32
+ n_kv_heads: int | None = None # None = انتباه متعدد الرؤوس عادي؛ أقل = GQA
33
+ ffn_hidden: int | None = None # None = يُحسب تلقائياً (~8/3 × d مقرّباً لمضاعف 64)
34
+ max_seq_len: int = 512
35
+ rope_theta: float = 10000.0
36
+ norm_eps: float = 1e-5
37
+ dropout: float = 0.0
38
+ tie_embeddings: bool = True
39
+
40
+ def __post_init__(self) -> None:
41
+ if self.d_model % self.n_heads:
42
+ raise ValueError("d_model يجب أن يقبل القسمة على n_heads")
43
+ if self.n_kv_heads is None:
44
+ self.n_kv_heads = self.n_heads
45
+ if self.n_heads % self.n_kv_heads:
46
+ raise ValueError("n_heads يجب أن يقبل القسمة على n_kv_heads")
47
+ if self.ffn_hidden is None:
48
+ # 8/3 × d بدل 4 × d: SwiGLU يستخدم ثلاث مصفوفات لا اثنتين،
49
+ # فنقلّص العرض للحفاظ على نفس ميزانية البارامترات.
50
+ self.ffn_hidden = 64 * math.ceil((8 * self.d_model / 3) / 64)
51
+
52
+ @property
53
+ def head_dim(self) -> int:
54
+ return self.d_model // self.n_heads
55
+
56
+ def to_dict(self) -> dict:
57
+ return asdict(self)
58
+
59
+
60
+ # --------------------------------------------------------------------- الطبقات
61
+ class RMSNorm(nn.Module):
62
+ def __init__(self, dim: int, eps: float = 1e-5):
63
+ super().__init__()
64
+ self.eps = eps
65
+ self.weight = nn.Parameter(torch.ones(dim))
66
+
67
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
68
+ # يُحسب في float32 دائماً: التطبيع في bf16 يفقد دقة تُهم عند العمق
69
+ dtype = x.dtype
70
+ x = x.float()
71
+ x = x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
72
+ return (x * self.weight.float()).to(dtype)
73
+
74
+
75
+ def build_rope_cache(seq_len: int, head_dim: int, theta: float, device, dtype):
76
+ """يعيد (cos, sin) بشكل (seq_len, head_dim/2)."""
77
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
78
+ pos = torch.arange(seq_len, device=device).float()
79
+ freqs = torch.outer(pos, inv_freq)
80
+ return freqs.cos().to(dtype), freqs.sin().to(dtype)
81
+
82
+
83
+ def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
84
+ """x بشكل (B, H, S, D) — يدوّر كل زوج إحداثيات بزاوية تتناسب مع الموضع."""
85
+ x1, x2 = x.chunk(2, dim=-1)
86
+ cos = cos[None, None, : x.size(-2), :]
87
+ sin = sin[None, None, : x.size(-2), :]
88
+ return torch.cat([x1 * cos - x2 * sin, x2 * cos + x1 * sin], dim=-1)
89
+
90
+
91
+ class Attention(nn.Module):
92
+ def __init__(self, args: ModelArgs):
93
+ super().__init__()
94
+ self.n_heads, self.n_kv_heads = args.n_heads, args.n_kv_heads
95
+ self.head_dim = args.head_dim
96
+ self.repeat = self.n_heads // self.n_kv_heads
97
+ self.dropout = args.dropout
98
+
99
+ self.wq = nn.Linear(args.d_model, self.n_heads * self.head_dim, bias=False)
100
+ self.wk = nn.Linear(args.d_model, self.n_kv_heads * self.head_dim, bias=False)
101
+ self.wv = nn.Linear(args.d_model, self.n_kv_heads * self.head_dim, bias=False)
102
+ self.wo = nn.Linear(self.n_heads * self.head_dim, args.d_model, bias=False)
103
+
104
+ def forward(self, x, cos, sin):
105
+ B, S, _ = x.shape
106
+ q = self.wq(x).view(B, S, self.n_heads, self.head_dim).transpose(1, 2)
107
+ k = self.wk(x).view(B, S, self.n_kv_heads, self.head_dim).transpose(1, 2)
108
+ v = self.wv(x).view(B, S, self.n_kv_heads, self.head_dim).transpose(1, 2)
109
+
110
+ q, k = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
111
+
112
+ if self.repeat > 1: # GQA
113
+ k = k.repeat_interleave(self.repeat, dim=1)
114
+ v = v.repeat_interleave(self.repeat, dim=1)
115
+
116
+ out = F.scaled_dot_product_attention(
117
+ q, k, v, is_causal=True,
118
+ dropout_p=self.dropout if self.training else 0.0,
119
+ )
120
+ return self.wo(out.transpose(1, 2).contiguous().view(B, S, -1))
121
+
122
+
123
+ class SwiGLU(nn.Module):
124
+ def __init__(self, args: ModelArgs):
125
+ super().__init__()
126
+ h = args.ffn_hidden
127
+ self.w_gate = nn.Linear(args.d_model, h, bias=False)
128
+ self.w_up = nn.Linear(args.d_model, h, bias=False)
129
+ self.w_down = nn.Linear(h, args.d_model, bias=False)
130
+
131
+ def forward(self, x):
132
+ return self.w_down(F.silu(self.w_gate(x)) * self.w_up(x))
133
+
134
+
135
+ class Block(nn.Module):
136
+ def __init__(self, args: ModelArgs):
137
+ super().__init__()
138
+ self.attn_norm = RMSNorm(args.d_model, args.norm_eps)
139
+ self.attn = Attention(args)
140
+ self.ffn_norm = RMSNorm(args.d_model, args.norm_eps)
141
+ self.ffn = SwiGLU(args)
142
+ self.drop = nn.Dropout(args.dropout)
143
+
144
+ def forward(self, x, cos, sin):
145
+ x = x + self.drop(self.attn(self.attn_norm(x), cos, sin))
146
+ return x + self.drop(self.ffn(self.ffn_norm(x)))
147
+
148
+
149
+ # --------------------------------------------------------------------- النموذج
150
+ class RuqLM(nn.Module):
151
+ def __init__(self, args: ModelArgs):
152
+ super().__init__()
153
+ self.args = args
154
+ self.tok_emb = nn.Embedding(args.vocab_size, args.d_model)
155
+ self.drop = nn.Dropout(args.dropout)
156
+ self.blocks = nn.ModuleList(Block(args) for _ in range(args.n_layers))
157
+ self.norm = RMSNorm(args.d_model, args.norm_eps)
158
+ self.lm_head = nn.Linear(args.d_model, args.vocab_size, bias=False)
159
+
160
+ if args.tie_embeddings:
161
+ self.lm_head.weight = self.tok_emb.weight
162
+
163
+ self.apply(self._init)
164
+ # تدرّج المسارات المتبقية ينمو مع العمق؛ نقلّص أوزان الإسقاط الأخير
165
+ # في كل كتلة بـ 1/sqrt(2L) للحفاظ على تباين ثابت عبر الطبقات (GPT-2).
166
+ std = 0.02 / math.sqrt(2 * args.n_layers)
167
+ for block in self.blocks:
168
+ nn.init.normal_(block.attn.wo.weight, mean=0.0, std=std)
169
+ nn.init.normal_(block.ffn.w_down.weight, mean=0.0, std=std)
170
+
171
+ self._cache_key = None
172
+
173
+ @staticmethod
174
+ def _init(module):
175
+ if isinstance(module, nn.Linear):
176
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
177
+ if module.bias is not None:
178
+ nn.init.zeros_(module.bias)
179
+ elif isinstance(module, nn.Embedding):
180
+ nn.init.normal_(module.weight, mean=0.0, std=0.02)
181
+
182
+ def _rope(self, seq_len: int, device, dtype):
183
+ key = (seq_len, device, dtype)
184
+ if self._cache_key != key:
185
+ self._cos, self._sin = build_rope_cache(
186
+ max(seq_len, self.args.max_seq_len), self.args.head_dim,
187
+ self.args.rope_theta, device, dtype,
188
+ )
189
+ self._cache_key = key
190
+ return self._cos[:seq_len], self._sin[:seq_len]
191
+
192
+ def forward(self, input_ids: torch.Tensor, labels: torch.Tensor | None = None):
193
+ x = self.drop(self.tok_emb(input_ids))
194
+ cos, sin = self._rope(input_ids.size(1), x.device, x.dtype)
195
+ for block in self.blocks:
196
+ x = block(x, cos, sin)
197
+ logits = self.lm_head(self.norm(x))
198
+
199
+ loss = None
200
+ if labels is not None:
201
+ # الإزاحة: الموضع i يتنبأ بالتوكن i+1
202
+ loss = F.cross_entropy(
203
+ logits[:, :-1].reshape(-1, logits.size(-1)).float(),
204
+ labels[:, 1:].reshape(-1),
205
+ ignore_index=-100,
206
+ )
207
+ return logits, loss
208
+
209
+ # ------------------------------------------------------------- الإحصاءات
210
+ def num_params(self, embeddings: bool = True) -> int:
211
+ """التضمينات المربوطة تُحسب مرة واحدة (lm_head.weight هو نفسه tok_emb.weight)."""
212
+ seen, total = set(), 0
213
+ for name, p in self.named_parameters():
214
+ if id(p) in seen:
215
+ continue
216
+ seen.add(id(p))
217
+ if not embeddings and "tok_emb" in name:
218
+ continue
219
+ total += p.numel()
220
+ return total
221
+
222
+ @torch.no_grad()
223
+ def generate(self, input_ids, max_new_tokens=128, temperature=0.8,
224
+ top_k=50, eos_id=None):
225
+ """توليد بسيط بلا كاش KV — كافٍ للتقييم على تسلسلات قصيرة."""
226
+ self.eval()
227
+ for _ in range(max_new_tokens):
228
+ window = input_ids[:, -self.args.max_seq_len:]
229
+ logits, _ = self(window)
230
+ logits = logits[:, -1, :].float() / max(temperature, 1e-6)
231
+ if top_k:
232
+ kth = logits.topk(min(top_k, logits.size(-1)), dim=-1).values[:, -1:]
233
+ logits = logits.masked_fill(logits < kth, float("-inf"))
234
+ nxt = torch.multinomial(logits.softmax(-1), num_samples=1)
235
+ input_ids = torch.cat([input_ids, nxt], dim=1)
236
+ if eos_id is not None and (nxt == eos_id).all():
237
+ break
238
+ return input_ids
ruq-0.7m.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:27016d2f1bf1e55a76906f1be007806ee39d1199eeb26dc9f547959fd6db40a6
3
+ size 2954888
ruq-15m.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a71400bfadc737d6826816e8eea2f714975f3976e52021935b05d5dcf67c8282
3
+ size 55075584
ruq-2m.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6d8329724a88ed97ad2c3979e7fb0eb388c04b757c6d99636e4776fe7d26a780
3
+ size 7610360
ruq-30m.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e1e242c504b3ababa75154e6933d41d0f50c51635dd38322d9fa07dac8f0c90d
3
+ size 119579600
ruq-5m.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9334bcca423b254b641892be0095680d2b7f5752d90baa5e3ced296f1f2550e8
3
+ size 21246488