harims95 commited on
Commit
35722b4
·
verified ·
1 Parent(s): 9e26710

SFT on Alpaca 52k, 3 epochs, bf16 on H200

Browse files
README.md CHANGED
@@ -1,3 +1,56 @@
1
  ---
 
2
  license: apache-2.0
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ library_name: transformers
3
  license: apache-2.0
4
+ base_model: harims95/LoopLM-135M-naive
5
+ tags:
6
+ - generated_from_trainer
7
+ model-index:
8
+ - name: LoopLM-135M-naive-sft
9
+ results: []
10
  ---
11
+
12
+ <!-- This model card has been generated automatically according to the information the Trainer had access to. You
13
+ should probably proofread and complete it, then remove this comment. -->
14
+
15
+ # LoopLM-135M-naive-sft
16
+
17
+ This model is a fine-tuned version of [harims95/LoopLM-135M-naive](https://huggingface.co/harims95/LoopLM-135M-naive) on an unknown dataset.
18
+
19
+ ## Model description
20
+
21
+ More information needed
22
+
23
+ ## Intended uses & limitations
24
+
25
+ More information needed
26
+
27
+ ## Training and evaluation data
28
+
29
+ More information needed
30
+
31
+ ## Training procedure
32
+
33
+ ### Training hyperparameters
34
+
35
+ The following hyperparameters were used during training:
36
+ - learning_rate: 2e-05
37
+ - train_batch_size: 16
38
+ - eval_batch_size: 8
39
+ - seed: 42
40
+ - gradient_accumulation_steps: 2
41
+ - total_train_batch_size: 32
42
+ - optimizer: Use OptimizerNames.ADAMW_TORCH_FUSED with betas=(0.9,0.999) and epsilon=1e-08 and optimizer_args=No additional optimizer arguments
43
+ - lr_scheduler_type: cosine
44
+ - lr_scheduler_warmup_steps: 100
45
+ - num_epochs: 3
46
+
47
+ ### Training results
48
+
49
+
50
+
51
+ ### Framework versions
52
+
53
+ - Transformers 5.12.1
54
+ - Pytorch 2.12.1+cu130
55
+ - Datasets 5.0.0
56
+ - Tokenizers 0.22.2
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)
modeling_looplm.py ADDED
@@ -0,0 +1,342 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ _tied_weights_keys = {"model.lm_head.weight": "model.embed.weight"}
280
+
281
+ @property
282
+ def all_tied_weights_keys(self):
283
+ return self._tied_weights_keys
284
+
285
+ def __init__(self, config: LoopLMConfig):
286
+ super().__init__(config)
287
+ mc = _MC(
288
+ vocab_size=config.vocab_size,
289
+ d_model=config.d_model,
290
+ n_prelude=config.n_prelude,
291
+ n_coda=config.n_coda,
292
+ mu_rec=config.mu_rec,
293
+ n_q_heads=config.n_q_heads,
294
+ n_kv_heads=config.n_kv_heads,
295
+ head_dim=config.head_dim,
296
+ qk_norm=config.qk_norm,
297
+ rope_theta=config.rope_theta,
298
+ dense_ffn=config.dense_ffn,
299
+ tie_embeddings=config.tie_embeddings,
300
+ final_z_loss_coef=config.final_z_loss_coef,
301
+ use_a_matrix=config.use_a_matrix,
302
+ use_input_norm=config.use_input_norm,
303
+ init_std=config.init_std,
304
+ )
305
+ self.model = ParcaeTransformer(mc)
306
+
307
+ def tie_weights(self, *args, **kwargs):
308
+ """Tie input and output embeddings (called by HF on load).
309
+
310
+ Accepts *args, **kwargs to be compatible with various transformers versions
311
+ that may pass missing_keys, recompute_mapping, etc.
312
+ """
313
+ if hasattr(self.model, "embed") and hasattr(self.model, "lm_head"):
314
+ self.model.lm_head.weight = self.model.embed.weight
315
+
316
+ def forward(self, input_ids, labels=None, attention_mask=None, **kwargs):
317
+ # Always compute logits so HF Trainer can use them in train and eval.
318
+ logits, _ = self.model(input_ids, None)
319
+
320
+ loss = None
321
+ if labels is not None:
322
+ shift_logits = logits[..., :-1, :].contiguous()
323
+ shift_labels = labels[..., 1:].contiguous()
324
+ loss = F.cross_entropy(
325
+ shift_logits.view(-1, shift_logits.size(-1)).float(),
326
+ shift_labels.view(-1),
327
+ ignore_index=-100,
328
+ )
329
+
330
+ return CausalLMOutput(loss=loss, logits=logits)
331
+
332
+ def get_input_embeddings(self):
333
+ return self.model.embed
334
+
335
+ def set_input_embeddings(self, new_embeddings):
336
+ self.model.embed = new_embeddings
337
+
338
+ def get_output_embeddings(self):
339
+ return self.model.lm_head
340
+
341
+ def set_output_embeddings(self, new_embeddings):
342
+ self.model.lm_head = new_embeddings
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": "<|endoftext|>",
11
+ "tokenizer_class": "GPT2Tokenizer",
12
+ "unk_token": "<|endoftext|>"
13
+ }