harims95 commited on
Commit
12f0a98
·
verified ·
1 Parent(s): dd6bcaf

Initial release: LoopLM-135M-naive trained on FineWeb 4.6B tokens

Browse files
README.md CHANGED
@@ -1,3 +1,96 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ tags:
4
+ - looped-transformer
5
+ - language-model
6
+ - research
7
+ - from-scratch
8
+ language:
9
+ - en
10
+ library_name: transformers
11
+ datasets:
12
+ - HuggingFaceFW/fineweb
13
  ---
14
+
15
+ # LoopLM-135M-naive
16
+
17
+ A 135M parameter **dense looped transformer** trained from scratch on FineWeb. Built as part of an exploration of looped LLM architectures inspired by the Parcae paper ([arXiv:2604.12946](https://arxiv.org/abs/2604.12946)).
18
+
19
+ Trained on Modal H100s as a hobby research project. This is the **naive looped** variant — a clean baseline without Parcae's LTI stability mechanisms, which were found to underperform at this scale.
20
+
21
+ ## Architecture
22
+
23
+ - **Type:** Dense looped transformer (recurrent reuse of one block)
24
+ - **Total params:** 135M (134.1M unique trainable)
25
+ - **d_model:** 1024
26
+ - **Prelude:** 4 transformer blocks
27
+ - **Loop:** 1 transformer block, called T ~ Poisson(μ=6) times per sequence
28
+ - **Coda:** 2 transformer blocks
29
+ - **Attention:** GQA (16 query heads / 8 KV heads), head_dim=64, RoPE θ=10000, QK-norm
30
+ - **FFN:** SwiGLU, dense_ffn=2816
31
+ - **Vocab:** 50304 (GPT-2 BPE + padding), tied embeddings
32
+
33
+ ## Training
34
+
35
+ - **Dataset:** FineWeb (raw, ~4.6B tokens consumed)
36
+ - **Hardware:** 2× H100 on Modal
37
+ - **Steps:** 17,500 (stopped early at checkpoint to fit budget)
38
+ - **Batch:** 262,144 tokens/step
39
+ - **Optimizer:** Muon (matrices) + AdamW (norms, embeddings)
40
+ - **LR:** Muon 0.02, AdamW 3e-4, cosine decay (60% constant + 40% decay to 0.1×)
41
+ - **Precision:** bf16 with fp32 logits
42
+
43
+ ## Results
44
+
45
+ | Model | Architecture | Tokens | Val Loss |
46
+ |---|---|---|---|
47
+ | LoopLM-30M (prior) | Dense (8 layers) | 1B | 3.91 |
48
+ | **LoopLM-135M-naive (this)** | **Dense looped** | **4.6B** | **3.95** |
49
+ | HobbyLM-130M-MoE (sibling) | MoE (140M total / 62M active) | 10B | 3.30 |
50
+
51
+ ## Honest Findings
52
+
53
+ This project explored several Parcae stability mechanisms (A matrix constraints, prelude input normalization, identity-init B matrix, custom optimizer routing). **None outperformed naive looping at this scale.** The paper's improvements appear to be most relevant at 1B+ parameters and 100B+ tokens, while at 135M / 4.6B tokens, simple recurrent reuse is competitive enough.
54
+
55
+ Compared to a sibling 130M MoE trained on more data, the dense looped model loses by ~0.65 in val loss — MoE wins on token-efficiency at this scale.
56
+
57
+ ## Usage
58
+
59
+ ```python
60
+ from transformers import AutoTokenizer, AutoModelForCausalLM
61
+ import torch
62
+
63
+ tokenizer = AutoTokenizer.from_pretrained("harims95/LoopLM-135M-naive")
64
+ model = AutoModelForCausalLM.from_pretrained(
65
+ "harims95/LoopLM-135M-naive",
66
+ trust_remote_code=True,
67
+ torch_dtype=torch.float32,
68
+ )
69
+ model.eval()
70
+
71
+ # Forward pass for next-token logits
72
+ inputs = tokenizer("The capital of France is", return_tensors="pt")
73
+ with torch.no_grad():
74
+ out = model(**inputs)
75
+ next_token_logits = out.logits[0, -1]
76
+ next_token = tokenizer.decode(next_token_logits.argmax())
77
+ print(next_token)
78
+ ```
79
+
80
+ Note: a HuggingFace-style `.generate()` is not wired in this release. The model returns logits and you'd write a simple sampling loop manually. Training was focused on the architecture exploration, not deployment.
81
+
82
+ ## Reproducibility
83
+
84
+ Training code: [github.com/harims95/LoopLM](https://github.com/harims95/LoopLM)
85
+
86
+ The `spec.json` in this repo captures the exact training config used for this checkpoint.
87
+
88
+ ## Acknowledgments
89
+
90
+ - Built on top of [harishsg993010/HobbyLM](https://github.com/harishsg993010/HobbyLM) (training infrastructure, optimizer setup, data pipeline)
91
+ - Architecture inspired by [Parcae](https://arxiv.org/abs/2604.12946) — Scaling Laws For Stable Looped Language Models
92
+ - Trained on [FineWeb](https://huggingface.co/datasets/HuggingFaceFW/fineweb)
93
+
94
+ ## License
95
+
96
+ Apache 2.0 — feel free to use, study, or build on. Acknowledgment appreciated but not required.
config.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "looplm",
3
+ "architectures": ["LoopLMForCausalLM"],
4
+ "auto_map": {
5
+ "AutoConfig": "configuration_looplm.LoopLMConfig",
6
+ "AutoModelForCausalLM": "modeling_looplm.LoopLMForCausalLM"
7
+ },
8
+ "vocab_size": 50304,
9
+ "d_model": 1024,
10
+ "n_prelude": 4,
11
+ "n_coda": 2,
12
+ "mu_rec": 6,
13
+ "n_q_heads": 16,
14
+ "n_kv_heads": 8,
15
+ "head_dim": 64,
16
+ "qk_norm": true,
17
+ "rope_theta": 10000.0,
18
+ "dense_ffn": 2816,
19
+ "tie_embeddings": true,
20
+ "final_z_loss_coef": 0.0001,
21
+ "use_a_matrix": false,
22
+ "use_input_norm": false,
23
+ "init_std": 0.02,
24
+ "torch_dtype": "float32",
25
+ "transformers_version": "4.40.0"
26
+ }
configuration_looplm.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """LoopLM configuration class for HuggingFace integration."""
2
+ from transformers import PretrainedConfig
3
+
4
+
5
+ class LoopLMConfig(PretrainedConfig):
6
+ model_type = "looplm"
7
+
8
+ def __init__(
9
+ self,
10
+ vocab_size: int = 50304,
11
+ d_model: int = 1024,
12
+ n_prelude: int = 4,
13
+ n_coda: int = 2,
14
+ mu_rec: int = 6,
15
+ n_q_heads: int = 16,
16
+ n_kv_heads: int = 8,
17
+ head_dim: int = 64,
18
+ qk_norm: bool = True,
19
+ rope_theta: float = 10000.0,
20
+ dense_ffn: int = 2816,
21
+ tie_embeddings: bool = True,
22
+ final_z_loss_coef: float = 1e-4,
23
+ use_a_matrix: bool = False,
24
+ use_input_norm: bool = False,
25
+ init_std: float = 0.02,
26
+ **kwargs,
27
+ ):
28
+ self.vocab_size = vocab_size
29
+ self.d_model = d_model
30
+ self.n_prelude = n_prelude
31
+ self.n_coda = n_coda
32
+ self.mu_rec = mu_rec
33
+ self.n_q_heads = n_q_heads
34
+ self.n_kv_heads = n_kv_heads
35
+ self.head_dim = head_dim
36
+ self.qk_norm = qk_norm
37
+ self.rope_theta = rope_theta
38
+ self.dense_ffn = dense_ffn
39
+ self.tie_embeddings = tie_embeddings
40
+ self.final_z_loss_coef = final_z_loss_coef
41
+ self.use_a_matrix = use_a_matrix
42
+ self.use_input_norm = use_input_norm
43
+ self.init_std = init_std
44
+ super().__init__(**kwargs)
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3a57a0942fd460fc31260a2cdccac52c5d31d80c7c32fdaf48394673184a6bb
3
+ size 742462848
modeling_looplm.py ADDED
@@ -0,0 +1,315 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Self-contained LoopLM model definition for HuggingFace integration."""
2
+ from __future__ import annotations
3
+
4
+ import math
5
+ from dataclasses import dataclass
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+ from torch import Tensor
11
+
12
+
13
+ def rms_norm(x: Tensor, weight: Tensor | None = None, eps: float = 1e-6) -> Tensor:
14
+ out = F.rms_norm(x, (x.size(-1),), eps=eps)
15
+ return out * weight if weight is not None else out
16
+
17
+
18
+ class RMSNorm(nn.Module):
19
+ def __init__(self, dim: int, eps: float = 1e-6):
20
+ super().__init__()
21
+ self.weight = nn.Parameter(torch.ones(dim))
22
+ self.eps = eps
23
+
24
+ def forward(self, x: Tensor) -> Tensor:
25
+ return rms_norm(x, self.weight, self.eps)
26
+
27
+
28
+ def precompute_rope(head_dim: int, max_seq: int, theta: float, device) -> tuple[Tensor, Tensor]:
29
+ inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2, device=device).float() / head_dim))
30
+ t = torch.arange(max_seq, device=device).float()
31
+ freqs = torch.outer(t, inv_freq)
32
+ return freqs.cos(), freqs.sin()
33
+
34
+
35
+ def apply_rope(x: Tensor, cos: Tensor, sin: Tensor) -> Tensor:
36
+ seq_len, dim = x.shape[-2], x.shape[-1]
37
+ cos = cos[:seq_len].view(1, 1, seq_len, dim // 2)
38
+ sin = sin[:seq_len].view(1, 1, seq_len, dim // 2)
39
+ x1, x2 = x[..., : dim // 2], x[..., dim // 2 :]
40
+ return torch.cat([x1 * cos - x2 * sin, x2 * cos + x1 * sin], dim=-1)
41
+
42
+
43
+ class Attention(nn.Module):
44
+ """GQA attention with per-head QK-norm before RoPE."""
45
+
46
+ def __init__(self, cfg):
47
+ super().__init__()
48
+ self.cfg = cfg
49
+ self.nq, self.nkv, self.hd = cfg.n_q_heads, cfg.n_kv_heads, cfg.head_dim
50
+ assert self.nq % self.nkv == 0, "n_q_heads must be divisible by n_kv_heads"
51
+ self.rep = self.nq // self.nkv
52
+ qkv_out = (self.nq + 2 * self.nkv) * self.hd
53
+ self.qkv = nn.Linear(cfg.d_model, qkv_out, bias=False)
54
+ self.proj = nn.Linear(self.nq * self.hd, cfg.d_model, bias=False)
55
+ if cfg.qk_norm:
56
+ self.q_norm = RMSNorm(self.hd)
57
+ self.k_norm = RMSNorm(self.hd)
58
+
59
+ def forward(self, x: Tensor, cos: Tensor, sin: Tensor) -> Tensor:
60
+ batch, seq_len, _ = x.shape
61
+ qkv = self.qkv(x)
62
+ q, k, v = qkv.split(
63
+ [self.nq * self.hd, self.nkv * self.hd, self.nkv * self.hd], dim=-1
64
+ )
65
+ q = q.view(batch, seq_len, self.nq, self.hd).transpose(1, 2)
66
+ k = k.view(batch, seq_len, self.nkv, self.hd).transpose(1, 2)
67
+ v = v.view(batch, seq_len, self.nkv, self.hd).transpose(1, 2)
68
+ if self.cfg.qk_norm:
69
+ q, k = self.q_norm(q), self.k_norm(k)
70
+ q, k = apply_rope(q, cos, sin), apply_rope(k, cos, sin)
71
+ k = k.repeat_interleave(self.rep, dim=1)
72
+ v = v.repeat_interleave(self.rep, dim=1)
73
+ out = F.scaled_dot_product_attention(q, k, v, is_causal=True)
74
+ out = out.transpose(1, 2).reshape(batch, seq_len, self.nq * self.hd)
75
+ return self.proj(out)
76
+
77
+
78
+ class DenseFFN(nn.Module):
79
+ """SwiGLU feed-forward."""
80
+
81
+ def __init__(self, cfg):
82
+ super().__init__()
83
+ self.w13 = nn.Linear(cfg.d_model, 2 * cfg.dense_ffn, bias=False)
84
+ self.w2 = nn.Linear(cfg.dense_ffn, cfg.d_model, bias=False)
85
+
86
+ def forward(self, x: Tensor) -> Tensor:
87
+ gate, up = self.w13(x).chunk(2, dim=-1)
88
+ return self.w2(F.silu(gate) * up)
89
+
90
+
91
+ class TransformerBlock(nn.Module):
92
+ """Standard pre-norm block for Prelude / Coda (no loop, no LTI params)."""
93
+
94
+ def __init__(self, cfg):
95
+ super().__init__()
96
+ self.attn_norm = RMSNorm(cfg.d_model)
97
+ self.attn = Attention(cfg)
98
+ self.ffn_norm = RMSNorm(cfg.d_model)
99
+ self.ffn = DenseFFN(cfg)
100
+
101
+ def forward(self, x: Tensor, cos: Tensor, sin: Tensor) -> Tensor:
102
+ x = x + self.attn(self.attn_norm(x), cos, sin)
103
+ x = x + self.ffn(self.ffn_norm(x))
104
+ return x
105
+
106
+
107
+ class ParcaeLoopBlock(nn.Module):
108
+ """One iteration of the recurrent unit. Called T times by ParcaeTransformer."""
109
+
110
+ def __init__(self, cfg):
111
+ super().__init__()
112
+ self.cfg = cfg
113
+ self.attn_norm = RMSNorm(cfg.d_model)
114
+ self.attn = Attention(cfg)
115
+ self.ffn_norm = RMSNorm(cfg.d_model)
116
+ self.ffn = DenseFFN(cfg)
117
+ d_model = cfg.d_model
118
+ if cfg.use_a_matrix:
119
+ self.A_log = nn.Parameter(torch.zeros(d_model))
120
+ target_decay = math.sqrt(1.0 / 5.0)
121
+ target_dt = -math.log(target_decay)
122
+ dt_bias_init = math.log(math.expm1(target_dt))
123
+ self.dt_bias = nn.Parameter(torch.full((d_model,), dt_bias_init))
124
+ self.B = nn.Parameter(torch.eye(d_model))
125
+
126
+ def _lti_step(self, h: Tensor, e: Tensor) -> Tensor:
127
+ dt = F.softplus(self.dt_bias)
128
+ A = torch.exp(self.A_log)
129
+ decay = torch.exp(-dt * A)
130
+ input_gain = dt
131
+ input_write = F.linear(e, self.B)
132
+ return decay * h + input_gain * input_write
133
+
134
+ def forward(self, h: Tensor, e: Tensor, cos: Tensor, sin: Tensor) -> Tensor:
135
+ if self.cfg.use_a_matrix:
136
+ y = self._lti_step(h, e)
137
+ else:
138
+ y = h + e
139
+ y = y + self.attn(self.attn_norm(y), cos, sin)
140
+ y = y + self.ffn(self.ffn_norm(y))
141
+ return y
142
+
143
+
144
+ class ParcaeTransformer(nn.Module):
145
+ """Prelude + recurrent loop + coda."""
146
+
147
+ def __init__(self, cfg):
148
+ super().__init__()
149
+ self.cfg = cfg
150
+ d_model = cfg.d_model
151
+ self.embed = nn.Embedding(cfg.vocab_size, d_model)
152
+ self.prelude = nn.ModuleList([TransformerBlock(cfg) for _ in range(cfg.n_prelude)])
153
+ self.prelude_norm = RMSNorm(d_model) if cfg.use_input_norm else nn.Identity()
154
+ self.loop = ParcaeLoopBlock(cfg)
155
+ self.coda = nn.ModuleList([TransformerBlock(cfg) for _ in range(cfg.n_coda)])
156
+ self.final_norm = RMSNorm(d_model)
157
+ self.lm_head = nn.Linear(d_model, cfg.vocab_size, bias=False)
158
+ if cfg.tie_embeddings:
159
+ self.lm_head.weight = self.embed.weight
160
+ self.h0_std = 0.02
161
+ self._rope_cache: dict = {}
162
+ self.apply(self._init)
163
+
164
+ def _init(self, module: nn.Module):
165
+ std = self.cfg.init_std
166
+ if isinstance(module, nn.Linear):
167
+ nn.init.normal_(module.weight, mean=0.0, std=std)
168
+ if module.bias is not None:
169
+ nn.init.zeros_(module.bias)
170
+ elif isinstance(module, nn.Embedding):
171
+ nn.init.normal_(module.weight, mean=0.0, std=std)
172
+
173
+ def rope(self, seq_len: int, device, dtype):
174
+ key = (seq_len, device, dtype)
175
+ if key not in self._rope_cache:
176
+ cos, sin = precompute_rope(self.cfg.head_dim, seq_len, self.cfg.rope_theta, device)
177
+ self._rope_cache[key] = (cos.to(dtype), sin.to(dtype))
178
+ return self._rope_cache[key]
179
+
180
+ def _run_prelude(self, idx: Tensor):
181
+ x = self.embed(idx)
182
+ cos, sin = self.rope(x.size(1), x.device, x.dtype)
183
+ for blk in self.prelude:
184
+ x = blk(x, cos, sin)
185
+ return self.prelude_norm(x), cos, sin
186
+
187
+ def _run_coda(self, h: Tensor, cos: Tensor, sin: Tensor) -> Tensor:
188
+ for blk in self.coda:
189
+ h = blk(h, cos, sin)
190
+ return self.final_norm(h)
191
+
192
+ def _h0(self, batch: int, seq_len: int, device, dtype) -> Tensor:
193
+ return torch.randn(batch, seq_len, self.cfg.d_model, device=device, dtype=dtype) * self.h0_std
194
+
195
+ def forward(
196
+ self,
197
+ idx: Tensor,
198
+ targets: Tensor | None = None,
199
+ T_per_seq: Tensor | None = None,
200
+ n_no_grad: int = 0,
201
+ ):
202
+ e, cos, sin = self._run_prelude(idx)
203
+ batch, seq_len = idx.shape
204
+
205
+ if T_per_seq is None:
206
+ T_per_seq = torch.full((batch,), self.cfg.mu_rec, device=idx.device, dtype=torch.long)
207
+ t_max = int(T_per_seq.max().item())
208
+ n_no_grad = min(n_no_grad, t_max)
209
+
210
+ h = self._h0(batch, seq_len, idx.device, e.dtype)
211
+
212
+ with torch.no_grad():
213
+ for t in range(n_no_grad):
214
+ active = (t < T_per_seq).view(batch, 1, 1).to(h.dtype)
215
+ h_new = self.loop(h, e, cos, sin)
216
+ h = active * h_new + (1.0 - active) * h
217
+
218
+ for t in range(n_no_grad, t_max):
219
+ active = (t < T_per_seq).view(batch, 1, 1).to(h.dtype)
220
+ h_new = self.loop(h, e, cos, sin)
221
+ h = active * h_new + (1.0 - active) * h
222
+
223
+ h = self._run_coda(h, cos, sin)
224
+
225
+ if targets is None:
226
+ return self.lm_head(h), h.new_zeros(())
227
+
228
+ logits = self.lm_head(h).float()
229
+ ce = F.cross_entropy(logits.view(-1, logits.size(-1)), targets.view(-1), ignore_index=-1)
230
+ z = (torch.logsumexp(logits, dim=-1) ** 2).mean()
231
+ loss = ce + self.cfg.final_z_loss_coef * z
232
+ return loss, {"ce": ce.detach(), "z": z.detach()}
233
+
234
+
235
+ def count_params(model: ParcaeTransformer) -> dict:
236
+ cfg = model.cfg
237
+ total = sum(p.numel() for p in model.parameters())
238
+ embed = cfg.vocab_size * cfg.d_model
239
+ return {"total": total, "non_embed": total - embed, "embed": embed}
240
+
241
+
242
+ # =============================================================================
243
+ # HuggingFace wrapper
244
+ # =============================================================================
245
+ from transformers import PreTrainedModel
246
+ from transformers.modeling_outputs import CausalLMOutput
247
+ try:
248
+ from .configuration_looplm import LoopLMConfig
249
+ except ImportError:
250
+ from configuration_looplm import LoopLMConfig
251
+
252
+
253
+ @dataclass
254
+ class _MC:
255
+ """Internal ModelConfig adapter so ParcaeTransformer can be constructed from HF config."""
256
+
257
+ vocab_size: int
258
+ d_model: int
259
+ n_prelude: int
260
+ n_coda: int
261
+ mu_rec: int
262
+ n_q_heads: int
263
+ n_kv_heads: int
264
+ head_dim: int
265
+ qk_norm: bool
266
+ rope_theta: float
267
+ dense_ffn: int
268
+ tie_embeddings: bool
269
+ final_z_loss_coef: float
270
+ use_a_matrix: bool
271
+ use_input_norm: bool
272
+ init_std: float
273
+
274
+
275
+ class LoopLMForCausalLM(PreTrainedModel):
276
+ config_class = LoopLMConfig
277
+ base_model_prefix = "model"
278
+ supports_gradient_checkpointing = False
279
+
280
+ def __init__(self, config: LoopLMConfig):
281
+ super().__init__(config)
282
+ mc = _MC(
283
+ vocab_size=config.vocab_size,
284
+ d_model=config.d_model,
285
+ n_prelude=config.n_prelude,
286
+ n_coda=config.n_coda,
287
+ mu_rec=config.mu_rec,
288
+ n_q_heads=config.n_q_heads,
289
+ n_kv_heads=config.n_kv_heads,
290
+ head_dim=config.head_dim,
291
+ qk_norm=config.qk_norm,
292
+ rope_theta=config.rope_theta,
293
+ dense_ffn=config.dense_ffn,
294
+ tie_embeddings=config.tie_embeddings,
295
+ final_z_loss_coef=config.final_z_loss_coef,
296
+ use_a_matrix=config.use_a_matrix,
297
+ use_input_norm=config.use_input_norm,
298
+ init_std=config.init_std,
299
+ )
300
+ self.model = ParcaeTransformer(mc)
301
+
302
+ def forward(self, input_ids, labels=None, **kwargs):
303
+ if labels is not None:
304
+ loss, _ = self.model(input_ids, labels)
305
+ logits = None
306
+ else:
307
+ logits, _ = self.model(input_ids, None)
308
+ loss = None
309
+ return CausalLMOutput(loss=loss, logits=logits)
310
+
311
+ def get_input_embeddings(self):
312
+ return self.model.embed
313
+
314
+ def set_input_embeddings(self, new_embeddings):
315
+ self.model.embed = new_embeddings
spec.json ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "timestamp": "2026-06-28T11:27:05.869434+00:00",
3
+ "run_name": "real_naive_fineweb_5B_2gpu",
4
+ "git_commit": "unknown",
5
+ "cli_args": {
6
+ "preset": "135M",
7
+ "run_name": "real_naive_fineweb_5B_2gpu",
8
+ "data_dir": "/data/fineweb",
9
+ "train_pattern": "fineweb_train_*.bin",
10
+ "val_pattern": "fineweb_train_*.bin",
11
+ "max_steps": 20000,
12
+ "seq_len": 1024,
13
+ "batch_tokens": 262144,
14
+ "micro_batch_seqs": 32,
15
+ "val_every": 250,
16
+ "out_dir": "/data/runs",
17
+ "save_every": 2500,
18
+ "no_compile": false,
19
+ "holdout_last_for_val": true,
20
+ "set": [
21
+ "use_a_matrix=false",
22
+ "use_input_norm=false"
23
+ ]
24
+ },
25
+ "train_config": {
26
+ "data_dir": "/data/fineweb_edu",
27
+ "train_pattern": "edu_fineweb_train_*.bin",
28
+ "val_pattern": "edu_fineweb_val_*.bin",
29
+ "seq_len": 1024,
30
+ "batch_tokens": 262144,
31
+ "micro_batch_seqs": 32,
32
+ "max_steps": 20000,
33
+ "warmup_steps": 100,
34
+ "cooldown_frac": 0.4,
35
+ "final_lr_frac": 0.1,
36
+ "muon_lr": 0.02,
37
+ "muon_momentum": 0.95,
38
+ "muon_wd": 0.1,
39
+ "muon_ns_steps": 5,
40
+ "adam_lr": 0.0003,
41
+ "adam_betas": [
42
+ 0.9,
43
+ 0.95
44
+ ],
45
+ "adam_wd": 0.1,
46
+ "grad_clip": 1.0,
47
+ "val_every": 250,
48
+ "val_tokens": 10485760,
49
+ "log_every": 10,
50
+ "seed": 1337,
51
+ "compile": true,
52
+ "bf16": true,
53
+ "out_dir": "/data/runs",
54
+ "run_name": "real_naive_fineweb_5B_2gpu"
55
+ },
56
+ "model_config": {
57
+ "vocab_size": 50304,
58
+ "d_model": 1024,
59
+ "n_prelude": 4,
60
+ "n_coda": 2,
61
+ "mu_rec": 6,
62
+ "n_q_heads": 16,
63
+ "n_kv_heads": 8,
64
+ "head_dim": 64,
65
+ "qk_norm": true,
66
+ "rope_theta": 10000.0,
67
+ "dense_ffn": 2816,
68
+ "tie_embeddings": true,
69
+ "final_z_loss_coef": 0.0001,
70
+ "use_a_matrix": false,
71
+ "use_input_norm": false,
72
+ "init_std": 0.02
73
+ },
74
+ "hostname": "modal",
75
+ "gpu_count": 2,
76
+ "gpu_type": "NVIDIA H100 80GB HBM3",
77
+ "pytorch_version": "2.12.0+cu130"
78
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": "<|endoftext|>",
5
+ "eos_token": "<|endoftext|>",
6
+ "errors": "replace",
7
+ "is_local": false,
8
+ "local_files_only": false,
9
+ "model_max_length": 1024,
10
+ "pad_token": null,
11
+ "tokenizer_class": "GPT2Tokenizer",
12
+ "unk_token": "<|endoftext|>"
13
+ }