rajpdus commited on
Commit
5573ff6
·
verified ·
1 Parent(s): 1d33d98

JugnuLM-110M-R2: value residuals + Muon (ablation rung R2)

Browse files
README.md ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ library_name: transformers
6
+ pipeline_tag: text-generation
7
+ tags:
8
+ - tiny-lm
9
+ - qwen3
10
+ - value-residual
11
+ - resformer
12
+ - muon
13
+ - ablation
14
+ datasets:
15
+ - HuggingFaceFW/fineweb-edu
16
+ ---
17
+
18
+ # JugnuLM-110M-R2 (value residuals + Muon)
19
+
20
+ **What "R2" means.** This is **rung 2 of the JugnuLM phase-2 ablation ladder.** We take
21
+ rung **R1** — [JugnuLM-110M-R1](https://huggingface.co/altslate/JugnuLM-110M-R1) (value
22
+ residuals) — and add **one** change: we swap the optimizer to **Muon** for the 2D hidden
23
+ matrices, holding *everything else fixed* (same value-residual architecture, data, tokens,
24
+ schedule shape, batch, seed). The ladder keeps a lever only if it beats the previous rung.
25
+ **R2 does, so it's kept.**
26
+
27
+ ## What changed: the Muon optimizer
28
+
29
+ R0 and R1 trained with AdamW on every parameter. R2 uses **Muon** — Newton-Schulz
30
+ **orthogonalized momentum** updates — on the 2D hidden weight matrices (attention + MLP,
31
+ 81.4M params), and **keeps AdamW** for the rest (tied embedding/head, RMSNorm gains, and
32
+ the 22 value-residual `vr_lambda` scalars, 28.3M params). Muon's advantage is known to be
33
+ **largest at exactly this scale** (sub-150M) and to shrink as models grow.
34
+
35
+ Both optimizers ride one shared cosine LR multiplier: Muon peak `2e-2`, AdamW peak `1.5e-3`,
36
+ each annealed to 10% of peak.
37
+
38
+ ## Result vs the R1 and R0 rungs
39
+
40
+ | 110M rung | BLiMP ↑ | ARC-Easy ↑ | WikiText-2 byte-ppl ↓ |
41
+ |---|---|---|---|
42
+ | R0 — baseline | 81.25 | 52.48 | 1.95 |
43
+ | R1 — + value residuals | 81.10 | 54.67 | 1.940 |
44
+ | **R2 — + Muon** | 80.78 | **56.10** | **1.932** |
45
+
46
+ **Muon lifts ARC-Easy by +1.43 over R1 (54.67 → 56.10) and lowers perplexity, at a small
47
+ BLiMP dip (−0.32, ~2× SE).** Across the two ladder rungs ARC-Easy has climbed **+3.6**
48
+ (52.48 → 56.10) while BLiMP stayed ~flat — steadily closing the reasoning gap to the
49
+ 125–143M leaderboard models. Evaluated with EleutherAI `lm-evaluation-harness`.
50
+
51
+ **Training-efficiency note.** Muon's convergence lead was largest early — val-perplexity
52
+ was **−26% vs R1 at step 1000** — and compressed to ~−1% by the end of the fixed 8.4B-token
53
+ budget (the classic "Muon wins the sprint, converges toward AdamW" pattern). Notably, the
54
+ **downstream ARC gain persisted** even as the final perplexity gap narrowed.
55
+
56
+ ## ⚠️ Loading — requires `trust_remote_code`
57
+
58
+ Value residuals are a **custom attention pathway**, so this model must be loaded with the
59
+ included modeling file. Loading it as a stock Qwen3 **silently drops the residual** and
60
+ understates quality.
61
+
62
+ ```python
63
+ from transformers import AutoModelForCausalLM, AutoTokenizer
64
+ tok = AutoTokenizer.from_pretrained("altslate/JugnuLM-110M-R2")
65
+ model = AutoModelForCausalLM.from_pretrained("altslate/JugnuLM-110M-R2", trust_remote_code=True)
66
+ ids = tok("The theory of relativity was developed by", return_tensors="pt").input_ids
67
+ print(tok.decode(model.generate(ids, max_new_tokens=30, repetition_penalty=1.3)[0], skip_special_tokens=True))
68
+ ```
69
+
70
+ ## Architecture & training
71
+
72
+ Identical architecture to R1: Qwen3-arch + value-residual pathway, **23 layers × 576**,
73
+ GQA 9/3, RoPE, SwiGLU, RMSNorm, QK-Norm, tied embeddings, SmolLM2 tokenizer (49,152),
74
+ z-loss 1e-4. Trained from scratch on ~8.4B tokens of FineWeb-Edu (sample-10BT), shared
75
+ cosine schedule (Muon 2e-2, AdamW 1.5e-3, → 10% floor), ~0.5M-token global batch, bf16,
76
+ DDP on 4× RTX PRO 4500 Blackwell. Only the optimizer differs from R1.
77
+
78
+ ## Intended use & limitations
79
+
80
+ Research **base** model (not instruction-tuned). Fluent English continuation and
81
+ reasonable recall for its size; limited multi-step reasoning, occasional repetition,
82
+ English-only. Not for production.
83
+
84
+ ## Links
85
+
86
+ - Previous rung: [altslate/JugnuLM-110M-R1](https://huggingface.co/altslate/JugnuLM-110M-R1)
87
+ - Baseline: [altslate/JugnuLM-110M](https://huggingface.co/altslate/JugnuLM-110M)
88
+ - Training code & ablation ladder: https://github.com/AltSlate-Labs/jugnu
config.json ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "JugnuVRForCausalLM"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "auto_map": {
8
+ "AutoConfig": "modeling_jugnu_vr.JugnuVRConfig",
9
+ "AutoModelForCausalLM": "modeling_jugnu_vr.JugnuVRForCausalLM"
10
+ },
11
+ "bos_token_id": 0,
12
+ "dtype": "bfloat16",
13
+ "eos_token_id": 0,
14
+ "head_dim": 64,
15
+ "hidden_act": "silu",
16
+ "hidden_size": 576,
17
+ "initializer_range": 0.02,
18
+ "intermediate_size": 1536,
19
+ "layer_types": [
20
+ "full_attention",
21
+ "full_attention",
22
+ "full_attention",
23
+ "full_attention",
24
+ "full_attention",
25
+ "full_attention",
26
+ "full_attention",
27
+ "full_attention",
28
+ "full_attention",
29
+ "full_attention",
30
+ "full_attention",
31
+ "full_attention",
32
+ "full_attention",
33
+ "full_attention",
34
+ "full_attention",
35
+ "full_attention",
36
+ "full_attention",
37
+ "full_attention",
38
+ "full_attention",
39
+ "full_attention",
40
+ "full_attention",
41
+ "full_attention",
42
+ "full_attention"
43
+ ],
44
+ "max_position_embeddings": 2048,
45
+ "max_window_layers": 28,
46
+ "model_type": "jugnu_vr",
47
+ "num_attention_heads": 9,
48
+ "num_hidden_layers": 23,
49
+ "num_key_value_heads": 3,
50
+ "pad_token_id": 0,
51
+ "rms_norm_eps": 1e-06,
52
+ "rope_parameters": {
53
+ "rope_theta": 10000.0,
54
+ "rope_type": "default"
55
+ },
56
+ "sliding_window": null,
57
+ "tie_word_embeddings": true,
58
+ "transformers_version": "5.2.0",
59
+ "use_cache": true,
60
+ "use_sliding_window": false,
61
+ "vocab_size": 49152
62
+ }
generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 0,
4
+ "eos_token_id": 0,
5
+ "output_attentions": false,
6
+ "output_hidden_states": false,
7
+ "pad_token_id": 0,
8
+ "transformers_version": "5.2.0",
9
+ "use_cache": true
10
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ba6f6d2cf935d4bcba4f50c12e0a1af027c2be8bf737dd6a870c8eca8b0c66ac
3
+ size 219505412
modeling_jugnu_vr.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """JugnuLM-110M-R1 — Qwen3 decoder + value residuals (ResFormer).
2
+
3
+ Each layer's value projection gains a learned-gated residual from the FIRST layer's
4
+ value: v_i = v_proj_i(x) + lambda_i * v_0 (v_0 = layer-0 value; lambda_i learned).
5
+ Implemented by replacing each layer's v_proj with a residual Linear that shares a
6
+ per-forward context; layer 0 writes v_0, later layers add lambda_i * v_0.
7
+
8
+ Loadable via: AutoModelForCausalLM.from_pretrained("altslate/JugnuLM-110M-R1", trust_remote_code=True)
9
+ (Loading as a stock Qwen3 silently drops the residual pathway.)
10
+ """
11
+ import torch
12
+ import torch.nn as nn
13
+ from transformers import Qwen3Config, Qwen3ForCausalLM
14
+
15
+
16
+ class JugnuVRConfig(Qwen3Config):
17
+ model_type = "jugnu_vr"
18
+
19
+
20
+ class _VResidualLinear(nn.Linear):
21
+ def __init__(self, in_features, out_features, is_first, bias=False):
22
+ super().__init__(in_features, out_features, bias=bias)
23
+ self._vr_is_first = is_first
24
+ self._vr_ctx = None
25
+ if not is_first:
26
+ self.vr_lambda = nn.Parameter(torch.zeros(1))
27
+
28
+ def forward(self, x):
29
+ v = super().forward(x)
30
+ ctx = self._vr_ctx
31
+ if ctx is None:
32
+ return v
33
+ if self._vr_is_first:
34
+ ctx["v0"] = v
35
+ else:
36
+ v0 = ctx.get("v0")
37
+ if v0 is not None:
38
+ v = v + self.vr_lambda * v0
39
+ return v
40
+
41
+
42
+ class JugnuVRForCausalLM(Qwen3ForCausalLM):
43
+ config_class = JugnuVRConfig
44
+
45
+ def __init__(self, config):
46
+ super().__init__(config)
47
+ ctx = {}
48
+ for i, layer in enumerate(self.model.layers):
49
+ old = layer.self_attn.v_proj
50
+ new = _VResidualLinear(old.in_features, old.out_features,
51
+ is_first=(i == 0), bias=(old.bias is not None))
52
+ new._vr_ctx = ctx
53
+ layer.self_attn.v_proj = new
54
+ self.post_init()
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": "<|endoftext|>",
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|endoftext|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|endoftext|>",
10
+ "<|im_start|>",
11
+ "<|im_end|>",
12
+ "<repo_name>",
13
+ "<reponame>",
14
+ "<file_sep>",
15
+ "<filename>",
16
+ "<gh_stars>",
17
+ "<issue_start>",
18
+ "<issue_comment>",
19
+ "<issue_closed>",
20
+ "<jupyter_start>",
21
+ "<jupyter_text>",
22
+ "<jupyter_code>",
23
+ "<jupyter_output>",
24
+ "<jupyter_script>",
25
+ "<empty_output>"
26
+ ],
27
+ "is_local": true,
28
+ "local_files_only": false,
29
+ "model_max_length": 8192,
30
+ "pad_token": "<|endoftext|>",
31
+ "tokenizer_class": "TokenizersBackend",
32
+ "unk_token": "<|endoftext|>",
33
+ "vocab_size": 49152
34
+ }