SofiTesfay2010 commited on
Commit
af6e981
·
verified ·
1 Parent(s): 248268a

Alpha: System-One-style typed decision model

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ alpha_report.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,65 +1,62 @@
1
  ---
2
  license: apache-2.0
3
- base_model: convaiinnovations/laya
4
- tags:
5
- - system-one
6
- - calibrated-decisions
7
- - rlcd
8
- - jev-alternative
9
- - laya
10
- - routing
11
- - guardrails
12
  ---
13
-
14
- # Alpha (System-One Decision Model)
15
-
16
- Alpha is an open, calibrated **System-One decision model** continuing [convaiinnovations/laya](https://huggingface.co/convaiinnovations/laya) (Apache-2.0).
17
-
18
- Given text/JSON states and typed questions (`choice`, `score`, `noul`), Alpha returns typed answers with mathematically calibrated probabilities in a single forward pass without autoregressive token generation.
19
-
20
- ## Benchmark Results (Measured vs. TypeSafe Jev)
21
-
22
- Evaluated on held-out test splits:
23
-
24
- | Benchmark | Question Type | Chance | TypeSafe Jev (Ref) | Alpha (Ours @ Step 2500) | Delta vs. Jev |
25
- | :--- | :--- | :--- | :--- | :--- | :--- |
26
- | **AG News** | choice ($k=4$) | 25.0% | 91.0% | **94.8%** | **+3.8%** |
27
- | **Emotion** | choice ($k=6$) | 16.7% | 48.0% | **88.5%** | **+40.5%** |
28
- | **SST-2** | noul ($k=2$) | 50.0% | 89.8% | **93.2%** | **+3.4%** |
29
- | **Banking77** | choice ($k=77$) | 1.3% | **87.0%** | **86.5%** | -0.5% |
30
- | **SMS Spam** | noul ($k=2$) | 50.0% | N/A | **99.0%** | — |
31
- | **MNLI** | choice ($k=3$) | 33.3% | N/A | **87.5%** | — |
32
- | **SST-5** | score ($k=5$) | 20.0% | N/A | **54.7%** | — |
33
-
34
- ## Quickstart
35
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  ```python
37
- from laya_mm import LayaMM
38
- from huggingface_hub import snapshot_download
39
-
40
- agent = LayaMM.from_dir(snapshot_download("SofiTesfay2010/Alpha"))
41
-
42
- # 1. Binary Decision (noul)
43
- res = agent.system_one(
44
- "Your package was delivered to the front porch.",
45
- {"delivered": {"type": "noul", "instructions": "Was the delivery completed?"}}
46
- )
47
- print(res["answers"]["delivered"])
48
-
49
- # 2. Multi-Class Choice
50
- res = agent.system_one(
51
- "I received a bill with unauthorized roaming charges.",
52
- {"intent": {
53
- "type": "choice",
54
- "instructions": "Identify the customer support ticket intent.",
55
- "criteria": ["billing_dispute", "technical_issue", "account_closure"]
56
- }}
57
- )
58
- print(res["answers"]["intent"])
59
  ```
60
-
61
- ## Architecture
62
- - **Text Backbone:** Bidirectional ModernBERT encoder (423M parameters).
63
- - **Vision Pathway:** Frozen SigLIP vision tower (`google/siglip-base-patch16-224`) with patch projection.
64
- - **Adaptive Head Budget:** Dynamic expansion (10k + 48 tokens, capped at 1024) preventing label truncation.
65
- - **License:** Apache-2.0.
 
1
  ---
2
  license: apache-2.0
3
+ base_model: distilroberta-base
4
+ tags: [system-one, structured-output, calibrated, classification, parallel-decoding]
 
 
 
 
 
 
 
5
  ---
6
+ # Alpha (System-One-style typed decision model)
7
+
8
+ Alpha takes unstructured / structured state plus **typed questions with declared options** and returns calibrated probabilities for
9
+ **all questions in one parallel forward pass**. It cannot generate strings and cannot answer outside the declared options
10
+ (masked softmax -> schema violations are impossible by construction). Option embeddings are independent of the state, so a schema can be
11
+ compiled once and cached; cardinality is limited only by memory.
12
+
13
+ Architecture: `distilroberta-base` encoder (state + question slots with `<mask>` readout tokens) -> 2-layer **Slot Reasoner** (set transformer, squared-ReLU,
14
+ zero-init residuals) -> biaffine/cosine scorer against option embeddings with per-question learned temperature + post-hoc temperature (T=1.04).
15
+
16
+ Training: 6000 steps x 32 workflow-style examples (1-4 questions over named fields, mixed from 10 public datasets with paraphrased
17
+ questions/option wordings), proper-scoring-rule loss (NLL + Brier + ordinal term), EMA weights, functional-memory anchor, discovery controller.
18
+ Held-out families (never trained on): cola, dbpedia, offensive.
19
+
20
+ ## Results (test splits; see results.json, alpha_report.png)
21
+ | family | acc (seen wording) | acc (unseen wording) | ECE |
22
+ |---|---|---|---|
23
+ | ag_news | 0.926 | 0.924 | 0.032 |
24
+ | banking77 | 0.902 | 0.890 | 0.036 |
25
+ | emotion | 0.922 | 0.894 | 0.030 |
26
+ | imdb | 0.886 | 0.892 | 0.027 |
27
+ | mrpc | 0.822 | 0.822 | 0.042 |
28
+ | news20 | 0.674 | 0.678 | 0.068 |
29
+ | sms | 0.993 | 0.346 | 0.004 |
30
+ | snli | 0.772 | 0.538 | 0.046 |
31
+ | sst2 | 0.912 | 0.910 | 0.034 |
32
+ | yelp | 0.572 | 0.522 | 0.072 |
33
+
34
+ Workflow eval (4 questions per query): per-question acc 0.825, all-correct 0.437, joint-confidence ECE 0.101.
35
+
36
+ Held-out task families (unseen schema wording): zero-shot / after 10 / after 40 gradient steps
37
+ | family | zero-shot | +10 steps | +40 steps |
38
+ |---|---|---|---|
39
+ | cola | 0.374 | 0.658 | 0.686 |
40
+ | dbpedia | 0.312 | 0.586 | 0.952 |
41
+ | offensive | 0.528 | 0.632 | 0.694 |
42
+
43
+ Speed on Tesla T4: p50 11.1 ms, p95 13.5 ms for a 3-question query (local, no network); 354 workflows/s at batch 64.
44
+ Qwen2.5-0.5B-Instruct (generative, same 120 questions): acc 0.583, p50 latency 82 ms vs Alpha acc 0.875, p50 11.1 ms.
45
+
46
+ Framework diagnostics: DMR=1.08; FRR by perturbation: sigma=0.5: FRR 4250.0 sigma=1: FRR 4250.0 sigma=2: FRR 4250.0 sigma=4: FRR 4250.0 sigma=8: FRR 14.2* (* = not re-reached within budget); MGR by checkpoint in results.json.
47
+
48
+ ## Honest limits
49
+ * This is a small research model trained on public classification datasets. **It is not a replacement for, and has not been compared against, TypeSafe's Jev**
50
+ (a closed API; its claims were not verified here). Do not read the latency comparison as a Jev comparison.
51
+ * Narrow task coverage (sentiment/topic/intent/NLI/paraphrase/spam/rating); free-form reasoning is out of scope by design.
52
+ * Calibration is measured in-distribution; shift will degrade it.
53
+
54
+ ## Usage
55
  ```python
56
+ from huggingface_hub import snapshot_download; import sys
57
+ p = snapshot_download("SofiTesfay2010/Alpha"); sys.path.insert(0, p)
58
+ from alpha_s1 import Alpha
59
+ m = Alpha.from_pretrained(p, device="cuda").eval()
60
+ print(m.predict({"ticket": "I was charged twice, refund me now."},
61
+ [{"id": "refund", "question": "Does the customer want a refund?", "options": ["no", "yes"]}]))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  ```
 
 
 
 
 
 
alpha_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backbone": "distilroberta-base",
3
+ "d_head": 256,
4
+ "slot_layers": 2,
5
+ "slot_heads": 8,
6
+ "act": "relu2",
7
+ "max_len": 512,
8
+ "field_max": 96,
9
+ "opt_max": 16
10
+ }
alpha_report.png ADDED

Git LFS Details

  • SHA256: 44afc9e8c33b63b552264c2841db35f5ae0d7b02bc752456b7102e23831d28ef
  • Pointer size: 131 Bytes
  • Size of remote file: 259 kB
alpha_s1.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Alpha-S1: a System-One-style model. Unstructured / structured state in, typed probabilistic decisions out.
3
+
4
+ * No text generation. Every question carries its own option list; the model can only score those options
5
+ (masked softmax) -> a type error / out-of-schema answer is impossible by construction.
6
+ * All questions of a query are answered in ONE parallel forward pass (no sequential decoding).
7
+ * Options are embedded independently of the state (bi-encoder side) so a schema can be "compiled" once and cached;
8
+ cardinality is limited only by memory (hundreds of options are cheap).
9
+ * Slot Reasoner: a small set-transformer (squared-ReLU FFN) lets the questions of one query condition on each other.
10
+ * Calibration: per-question learned temperature + post-hoc global temperature; outputs probs, confidence, certainty.
11
+ """
12
+ import math, json, os
13
+ from dataclasses import dataclass, asdict
14
+ import torch
15
+ import torch.nn as nn
16
+ import torch.nn.functional as F
17
+ from transformers import AutoModel, AutoConfig, AutoTokenizer
18
+
19
+
20
+ @dataclass
21
+ class AlphaConfig:
22
+ backbone: str = "distilroberta-base"
23
+ d_head: int = 256
24
+ slot_layers: int = 2
25
+ slot_heads: int = 8
26
+ act: str = "relu2" # "relu2" (squared ReLU, high-accessibility prior) | "gelu"
27
+ max_len: int = 512
28
+ field_max: int = 96
29
+ opt_max: int = 16
30
+
31
+
32
+ class Tk:
33
+ """Tokenizer wrapper: no truncation args (thread-safe), caches short strings."""
34
+ def __init__(self, tok): self.tok, self.c = tok, {}
35
+ def __call__(self, text, maxlen=None):
36
+ ids = self.c.get(text)
37
+ if ids is None:
38
+ ids = self.tok(text, add_special_tokens=False)["input_ids"]
39
+ if len(text) < 200: self.c[text] = ids
40
+ return ids[:maxlen] if maxlen else ids
41
+
42
+
43
+ def opt_batch(texts, tk, tokz, cfg):
44
+ cls, sep, pad = tokz.cls_token_id, tokz.sep_token_id, tokz.pad_token_id
45
+ seqs = [[cls] + tk(" " + t)[:cfg.opt_max] + [sep] for t in texts]
46
+ L = max(len(s) for s in seqs)
47
+ ids = torch.full((len(seqs), L), pad, dtype=torch.long); att = torch.zeros((len(seqs), L), dtype=torch.long)
48
+ for i, s in enumerate(seqs):
49
+ ids[i, :len(s)] = torch.tensor(s); att[i, :len(s)] = 1
50
+ return ids, att
51
+
52
+
53
+ def build_batch(exs, tk, tokz, cfg):
54
+ """exs: [{"fields":[(name,text),..], "questions":[{"q":str,"options":[str],"gold":int,"ordinal":bool,"fam":str}]}]"""
55
+ cls, sep, msk, pad = tokz.cls_token_id, tokz.sep_token_id, tokz.mask_token_id, tokz.pad_token_id
56
+ seqs, qposs, rows = [], [], []
57
+ opt_index, opt_texts = {}, []
58
+ for bi, ex in enumerate(exs):
59
+ qids = [tk(" " + q["q"]) for q in ex["questions"]]
60
+ qtot = sum(len(x) + 1 for x in qids) + 3
61
+ nf = max(len(ex["fields"]), 1)
62
+ per = max(8, min(cfg.field_max, (cfg.max_len - qtot) // nf - 8))
63
+ while True:
64
+ ids = [cls]
65
+ for name, text in ex["fields"]:
66
+ ids += tk(f" {name}:") + tk(" " + text, per)
67
+ ids.append(sep)
68
+ pos = []
69
+ for x in qids:
70
+ ids += x; ids.append(msk); pos.append(len(ids) - 1)
71
+ ids.append(sep)
72
+ if len(ids) <= cfg.max_len or per <= 8: break
73
+ per -= 8
74
+ ids = ids[:cfg.max_len]; pos = [min(p, cfg.max_len - 1) for p in pos]
75
+ seqs.append(ids); qposs.append(pos)
76
+ for q in ex["questions"]:
77
+ idxs = []
78
+ for o in q["options"]:
79
+ if o not in opt_index:
80
+ opt_index[o] = len(opt_texts); opt_texts.append(o)
81
+ idxs.append(opt_index[o])
82
+ rows.append((idxs, q.get("gold", 0), bool(q.get("ordinal", False)), q.get("fam", ""), bi))
83
+ B, T = len(seqs), max(len(s) for s in seqs)
84
+ ids = torch.full((B, T), pad, dtype=torch.long); att = torch.zeros((B, T), dtype=torch.long)
85
+ kmax = max(len(p) for p in qposs)
86
+ qpos = torch.zeros((B, kmax), dtype=torch.long); qvalid = torch.zeros((B, kmax), dtype=torch.bool)
87
+ for i, (s, p) in enumerate(zip(seqs, qposs)):
88
+ ids[i, :len(s)] = torch.tensor(s); att[i, :len(s)] = 1
89
+ qpos[i, :len(p)] = torch.tensor(p); qvalid[i, :len(p)] = True
90
+ nQ, nO = len(rows), len(opt_texts)
91
+ allowed = torch.zeros((nQ, nO), dtype=torch.bool); rank = torch.zeros((nQ, nO))
92
+ gold = torch.zeros(nQ, dtype=torch.long); ordn = torch.zeros(nQ, dtype=torch.bool)
93
+ for r, (idxs, g, od, fam, bi) in enumerate(rows):
94
+ allowed[r, idxs] = True
95
+ rank[r, idxs] = torch.arange(len(idxs)).float() / max(len(idxs) - 1, 1)
96
+ gold[r] = idxs[g]; ordn[r] = od
97
+ oi, oa = opt_batch(opt_texts, tk, tokz, cfg)
98
+ return dict(ids=ids, att=att, qpos=qpos, qvalid=qvalid, opt_ids=oi, opt_att=oa, allowed=allowed, rank=rank,
99
+ gold=gold, ordinal=ordn, opt_texts=opt_texts, opt_lists=[r[0] for r in rows],
100
+ gold_pos=[r[1] for r in rows], fams=[r[3] for r in rows], q_ex=[r[4] for r in rows])
101
+
102
+
103
+ def to_device(b, dev):
104
+ return {k: (v.to(dev, non_blocking=True) if torch.is_tensor(v) else v) for k, v in b.items()}
105
+
106
+
107
+ class EncWrap(nn.Module):
108
+ """Backbone call with autocast INSIDE forward (required for nn.DataParallel replicas)."""
109
+ def __init__(self, m): super().__init__(); self.m = m
110
+ def forward(self, ids, att):
111
+ with torch.autocast("cuda", dtype=torch.float16, enabled=ids.is_cuda):
112
+ return self.m(input_ids=ids, attention_mask=att).last_hidden_state
113
+
114
+
115
+ class SlotLayer(nn.Module):
116
+ """Pre-LN transformer layer over the query slots of one example; squared-ReLU FFN; zero-init residual branches
117
+ (starts as identity -> the generalizing 'read straight from the encoder' solution is the easiest to reach)."""
118
+ def __init__(self, d, heads, act):
119
+ super().__init__()
120
+ self.n1, self.n2 = nn.LayerNorm(d), nn.LayerNorm(d)
121
+ self.att = nn.MultiheadAttention(d, heads, batch_first=True)
122
+ self.f1, self.f2, self.act = nn.Linear(d, 4 * d), nn.Linear(4 * d, d), act
123
+ nn.init.zeros_(self.att.out_proj.weight); nn.init.zeros_(self.att.out_proj.bias)
124
+ nn.init.zeros_(self.f2.weight); nn.init.zeros_(self.f2.bias)
125
+ def forward(self, x, pad):
126
+ y = self.n1(x)
127
+ a, _ = self.att(y, y, y, key_padding_mask=pad, need_weights=False)
128
+ x = x + a
129
+ h = self.f1(self.n2(x))
130
+ h = F.relu(h) ** 2 if self.act == "relu2" else F.gelu(h)
131
+ return x + self.f2(h)
132
+
133
+
134
+ class Alpha(nn.Module):
135
+ def __init__(self, cfg=None, tokenizer=None, pretrained=True, backbone_module=None):
136
+ super().__init__()
137
+ self.cfg = cfg or AlphaConfig()
138
+ self.tokz = tokenizer or AutoTokenizer.from_pretrained(self.cfg.backbone)
139
+ self.tk = Tk(self.tokz)
140
+ if backbone_module is not None: self.backbone = backbone_module
141
+ elif pretrained:
142
+ try: self.backbone = AutoModel.from_pretrained(self.cfg.backbone, add_pooling_layer=False)
143
+ except TypeError: self.backbone = AutoModel.from_pretrained(self.cfg.backbone)
144
+ else:
145
+ c = AutoConfig.from_pretrained(self.cfg.backbone)
146
+ try: self.backbone = AutoModel.from_config(c, add_pooling_layer=False)
147
+ except TypeError: self.backbone = AutoModel.from_config(c)
148
+ d = self.backbone.config.hidden_size
149
+ self.ln = nn.LayerNorm(d)
150
+ self.slots = nn.ModuleList([SlotLayer(d, self.cfg.slot_heads, self.cfg.act) for _ in range(self.cfg.slot_layers)])
151
+ self.q_proj, self.o_proj = nn.Linear(d, self.cfg.d_head), nn.Linear(d, self.cfg.d_head)
152
+ self.temp = nn.Linear(d, 1) # per-question (heteroscedastic) temperature
153
+ nn.init.zeros_(self.temp.weight); nn.init.constant_(self.temp.bias, 0.11)
154
+ self.log_scale = nn.Parameter(torch.tensor(math.log(10.0)))
155
+ self.register_buffer("post_T", torch.tensor(1.0)) # post-hoc global temperature (fit on held-out calibration set)
156
+ object.__setattr__(self, "_enc", EncWrap(self.backbone)) # not registered: shares backbone params
157
+ object.__setattr__(self, "_dp", None)
158
+ self._opt_cache = {}
159
+
160
+ # ---- plumbing ----
161
+ def use_data_parallel(self):
162
+ if torch.cuda.device_count() > 1:
163
+ object.__setattr__(self, "_dp", nn.DataParallel(EncWrap(self.backbone)))
164
+ return self
165
+
166
+ def train(self, mode=True):
167
+ self._opt_cache = {}
168
+ return super().train(mode)
169
+
170
+ def new_params(self):
171
+ bb = {id(p) for p in self.backbone.parameters()}
172
+ return [p for p in self.parameters() if id(p) not in bb]
173
+
174
+ def encode(self, ids, att):
175
+ enc = self._dp if (self._dp is not None and ids.size(0) >= 8) else self._enc
176
+ return enc(ids, att)
177
+
178
+ def embed_options(self, opt_ids, opt_att):
179
+ H = self.encode(opt_ids, opt_att).float(); m = opt_att.unsqueeze(-1).float()
180
+ return (H * m).sum(1) / m.sum(1).clamp(min=1)
181
+
182
+ # ---- core: all questions, all options, one pass ----
183
+ def forward(self, b, opt_emb=None):
184
+ H = self.encode(b["ids"], b["att"])
185
+ B = H.size(0)
186
+ hq = H[torch.arange(B, device=H.device)[:, None], b["qpos"]].float()
187
+ hq = self.ln(hq)
188
+ pad = ~b["qvalid"]
189
+ for layer in self.slots: hq = layer(hq, pad)
190
+ hq = hq[b["qvalid"]] # [nQ, d] row-major == question order in batch
191
+ if opt_emb is None: opt_emb = self.embed_options(b["opt_ids"], b["opt_att"])
192
+ q = F.normalize(self.q_proj(hq).float(), dim=-1); o = F.normalize(self.o_proj(opt_emb).float(), dim=-1)
193
+ T = F.softplus(self.temp(hq).float()) + 0.25
194
+ logits = (q @ o.T) * self.log_scale.exp() / T
195
+ return logits.masked_fill(~b["allowed"], -1e4) # type safety: only declared options exist
196
+
197
+ # ---- public API ----
198
+ @torch.no_grad()
199
+ def predict(self, state, questions):
200
+ """state: {field: text}; questions: [{"id","question","options":[...],"ordinal":bool}] -> {id: {...}}"""
201
+ dev = next(self.parameters()).device
202
+ if self.training: self.eval() # (train() clears the option cache)
203
+ ex = dict(fields=list(state.items()),
204
+ questions=[dict(q=q["question"], options=list(q["options"]), gold=0, ordinal=q.get("ordinal", False)) for q in questions])
205
+ b = to_device(build_batch([ex], self.tk, self.tokz, self.cfg), dev)
206
+ miss = [t for t in b["opt_texts"] if t not in self._opt_cache]
207
+ if miss: # "schema compilation": option embeddings cached
208
+ oi, oa = opt_batch(miss, self.tk, self.tokz, self.cfg)
209
+ emb = self.embed_options(oi.to(dev), oa.to(dev))
210
+ for t, e in zip(miss, emb): self._opt_cache[t] = e
211
+ opt_emb = torch.stack([self._opt_cache[t] for t in b["opt_texts"]])
212
+ logits = self.forward(b, opt_emb) / self.post_T
213
+ out = {}
214
+ for r, q in enumerate(questions):
215
+ idx = b["opt_lists"][r]
216
+ p = F.softmax(logits[r, idx], -1).cpu()
217
+ K = len(idx); j = int(p.argmax())
218
+ ent = float(-(p * p.clamp_min(1e-9).log()).sum())
219
+ out[q["id"]] = dict(answer=q["options"][j], probs={o: float(x) for o, x in zip(q["options"], p)},
220
+ confidence=float(p[j]), certainty=(1 - ent / math.log(K)) if K > 1 else 1.0)
221
+ return out
222
+
223
+ def save_pretrained(self, path):
224
+ os.makedirs(path, exist_ok=True)
225
+ torch.save({k: v.detach().cpu() for k, v in self.state_dict().items()}, f"{path}/pytorch_model.bin")
226
+ json.dump(asdict(self.cfg), open(f"{path}/alpha_config.json", "w"), indent=1)
227
+ self.tokz.save_pretrained(path)
228
+
229
+ @classmethod
230
+ def from_pretrained(cls, path_or_repo, device=None):
231
+ if not os.path.isdir(path_or_repo):
232
+ from huggingface_hub import snapshot_download
233
+ path_or_repo = snapshot_download(path_or_repo)
234
+ cfg = AlphaConfig(**json.load(open(f"{path_or_repo}/alpha_config.json")))
235
+ m = cls(cfg, AutoTokenizer.from_pretrained(path_or_repo), pretrained=False)
236
+ missing, unexpected = m.load_state_dict(torch.load(f"{path_or_repo}/pytorch_model.bin", map_location="cpu"), strict=False)
237
+ missing = [k for k in missing if "pooler" not in k]
238
+ assert not missing, f"missing keys: {missing[:5]}"
239
+ return m.to(device) if device else m
alpha_train.py ADDED
@@ -0,0 +1,645 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Alpha training / benchmark / publish pipeline (Kaggle, 2x T4).
3
+
4
+ Framework rules from the spec are used as *design priors and instruments*, not as proven facts:
5
+ * Accessibility A(theta,F): squared-ReLU + zero-init Slot Reasoner, pretrained encoder, schema-compositional heads
6
+ (the general function is the easiest one to reach), many paraphrased schemas (high information rate).
7
+ * Regularisation shapes the manifold: very different weight decay for pretrained backbone vs new modules, adapted online
8
+ by the Discovery Controller from rho = d(val)/d(train) (proxy for P(B_G)/P(B_M)).
9
+ * Functional memory M_F: function-space signatures of past EMA checkpoints on diagnostic set E_F, used as a KL anchor
10
+ (self-distillation to earlier functions); L_FRR measured mid-training scales the anchor weight.
11
+ * FRR: perturb the discovered solution (basin test), retrain, time re-discovery -> FRR = T_first / T_re.
12
+ * Meta-grokking: held-out task families never trained on; adaptation speed measured at checkpoints (MGR).
13
+ * DMR: T_discover / T_memorize from train/val trajectories ( ~1 => no delayed generalisation, i.e. 'groks from the start').
14
+ """
15
+ import os, re, json, math, time, random, shutil
16
+ import numpy as np
17
+ import torch
18
+ import torch.nn.functional as F
19
+ from alpha_s1 import Alpha, AlphaConfig, build_batch, to_device
20
+
21
+ dev = "cuda:0" if torch.cuda.is_available() else "cpu"
22
+ ac = lambda: torch.autocast("cuda", dtype=torch.float16, enabled=torch.cuda.is_available())
23
+
24
+
25
+ class C:
26
+ backbone = "distilroberta-base" # try "roberta-base" for more accuracy (slower)
27
+ repo_id = "SofiTesfay2010/Alpha"
28
+ seed = 0
29
+ steps, bs = 6000, 32
30
+ lr_bb, lr_new = 5e-5, 5e-4
31
+ wd_bb, wd_new, wd_min, wd_max = 0.01, 0.5, 0.05, 2.0
32
+ warmup, clip = 300, 1.0
33
+ ls, brier_w, ord_w = 0.02, 0.3, 1.0
34
+ ema = 0.999
35
+ eval_every, mem_every, anchor_every, lam_mem = 250, 1000, 4, 0.3
36
+ probe_every, probe_steps, probe_bs, probe_test_n = 1000, 40, 16, 200
37
+ probe_at = (0, 5, 10, 20, 40)
38
+ fast_n = 48
39
+ n_cal, n_test = 150, 500
40
+ sigmas = [0.5, 1, 2, 4, 8]
41
+ noise_unit, frr_max_steps, frr_every = 0.02, 300, 25
42
+ llm_baseline = "Qwen/Qwen2.5-0.5B-Instruct"
43
+
44
+
45
+ # ----------------------------------------------------------------------------------------------- data
46
+ def S(paths, fields, fn, qs, names=None, alts=None, tr="train", va="validation", ordinal=False, held=False, ntr=30000, camel=False):
47
+ return dict(paths=paths, fields=fields, fn=fn, qs=qs, names=names, alts=alts, tr=tr, va=va, ordinal=ordinal, held=held, ntr=ntr, camel=camel)
48
+
49
+ SPECS = dict(
50
+ sst2=S([("nyu-mll/glue", "sst2")], ["sentence"], [["review"], ["comment"], ["snippet"]],
51
+ ["What is the sentiment of the {f}?", "Is the {f} positive or negative?", "Judge the attitude expressed in the {f}."],
52
+ names=["negative", "positive"], alts=[["negative", "positive"], ["bad", "good"], ["unfavorable", "favorable"]]),
53
+ imdb=S([("stanfordnlp/imdb",)], ["text"], [["movie_review"], ["critique"]],
54
+ ["Is the {f} favorable or unfavorable?", "What verdict does the {f} give?", "Does the author of the {f} like the film?"],
55
+ names=["negative", "positive"], alts=[["negative", "positive"], ["thumbs down", "thumbs up"], ["dislikes it", "likes it"]], va="test", ntr=20000),
56
+ ag_news=S([("fancyzhx/ag_news",)], ["text"], [["headline"], ["article"], ["news_item"]],
57
+ ["Which section would the {f} appear in?", "What topic is the {f} about?", "Categorize the {f}."],
58
+ names=["world news", "sports", "business", "science and technology"],
59
+ alts=[["world news", "sports", "business", "science and technology"], ["world", "sports", "finance", "tech"], ["international affairs", "athletics", "economy", "technology"]], va="test"),
60
+ emotion=S([("dair-ai/emotion",)], ["text"], [["message"], ["post"], ["tweet"]],
61
+ ["Which emotion does the {f} convey?", "What is the author of the {f} feeling?", "Name the dominant emotion in the {f}."],
62
+ names=["sadness", "joy", "love", "anger", "fear", "surprise"],
63
+ alts=[["sadness", "joy", "love", "anger", "fear", "surprise"], ["sad", "happy", "affection", "angry", "afraid", "astonished"], ["grief", "delight", "adoration", "rage", "dread", "shock"]]),
64
+ banking77=S([("PolyAI/banking77",), ("mteb/banking77",)], ["text"], [["customer_message"], ["support_query"]],
65
+ ["What is the customer asking about in the {f}?", "Which intent does the {f} express?", "Classify the banking request in the {f}."], va="test"),
66
+ yelp=S([("Yelp/yelp_review_full",)], ["text"], [["review"], ["feedback"]],
67
+ ["How many stars does the {f} deserve?", "Rate the {f}.", "What score would the author of the {f} give?"],
68
+ names=["1 star", "2 stars", "3 stars", "4 stars", "5 stars"],
69
+ alts=[["1 star", "2 stars", "3 stars", "4 stars", "5 stars"], ["terrible", "poor", "average", "good", "excellent"], ["very negative", "negative", "neutral", "positive", "very positive"]],
70
+ va="test", ordinal=True),
71
+ snli=S([("stanfordnlp/snli",)], ["premise", "hypothesis"], [["premise", "hypothesis"], ["context", "claim"]],
72
+ ["What is the logical relationship between {f}?", "Does the second follow from the first, contradict it, or neither? ({f})", "Relation between {f}:"],
73
+ names=["entailment", "neutral", "contradiction"],
74
+ alts=[["entailment", "neutral", "contradiction"], ["supported", "undetermined", "contradicted"], ["follows", "unrelated", "conflicts"]], ntr=60000),
75
+ sms=S([("ucirvine/sms_spam",)], ["sms"], [["message"], ["text_message"]],
76
+ ["Is the {f} spam?", "Classify the {f}.", "Is the {f} an unwanted advertisement or a normal message?"],
77
+ names=["legitimate", "spam"], alts=[["legitimate", "spam"], ["ham", "spam"], ["normal", "unwanted"]], va=None),
78
+ news20=S([("SetFit/20_newsgroups",)], ["text"], [["post"], ["usenet_message"]],
79
+ ["Which newsgroup does the {f} belong to?", "What topic does the {f} discuss?", "Categorize the {f} by topic."], va="test"),
80
+ mrpc=S([("nyu-mll/glue", "mrpc")], ["sentence1", "sentence2"], [["sentence_a", "sentence_b"], ["statement_1", "statement_2"]],
81
+ ["Do {f} mean the same thing?", "Are {f} paraphrases of each other?", "Compare the meaning of {f}."],
82
+ names=["different meaning", "same meaning"], alts=[["different meaning", "same meaning"], ["not paraphrase", "paraphrase"], ["unrelated", "equivalent"]]),
83
+ # ---- held-out families: NEVER trained on in the main run; used for zero-shot + meta-grokking probes ----
84
+ dbpedia=S([("fancyzhx/dbpedia_14",)], ["content"], [["entry"], ["wiki_summary"]],
85
+ ["What kind of entity does the {f} describe?", "Classify the subject of the {f}.", "Which category fits the {f}?"], va="test", held=True, ntr=20000, camel=True),
86
+ offensive=S([("cardiffnlp/tweet_eval", "offensive")], ["text"], [["tweet"], ["post"]],
87
+ ["Is the {f} offensive?", "Does the {f} contain abuse or insults?", "Moderate the {f}."],
88
+ names=["not offensive", "offensive"], alts=[["not offensive", "offensive"], ["civil", "abusive"], ["acceptable", "insulting"]], held=True),
89
+ cola=S([("nyu-mll/glue", "cola")], ["sentence"], [["sentence"], ["utterance"]],
90
+ ["Is the {f} grammatical?", "Is the {f} well-formed English?", "Judge the grammar of the {f}."],
91
+ names=["ungrammatical", "grammatical"], alts=[["ungrammatical", "grammatical"], ["incorrect", "correct"], ["unacceptable", "acceptable"]], held=True),
92
+ )
93
+
94
+ DATA, ST = {}, {}
95
+
96
+
97
+ def _clean(n, camel):
98
+ n = n.replace("_", " ").replace(".", " ")
99
+ if camel: n = re.sub(r"(?<!^)(?=[A-Z])", " ", n)
100
+ return n.lower().strip()
101
+
102
+
103
+ def load_all():
104
+ from datasets import load_dataset
105
+ for name, sp in SPECS.items():
106
+ for p in sp["paths"]:
107
+ try:
108
+ if sp["va"]:
109
+ dtr = load_dataset(*p, split=sp["tr"]).shuffle(seed=0); dva = load_dataset(*p, split=sp["va"]).shuffle(seed=0)
110
+ else:
111
+ full = load_dataset(*p, split="train").shuffle(seed=0); cut = int(0.9 * len(full))
112
+ dtr, dva = full.select(range(cut)), full.select(range(cut, len(full)))
113
+ dtr = dtr.select(range(min(len(dtr), sp["ntr"]))); dva = dva.select(range(min(len(dva), 1500)))
114
+ names = sp["names"]
115
+ if names is None:
116
+ feat = dtr.features["label"]
117
+ if hasattr(feat, "names"): names = list(feat.names)
118
+ else:
119
+ m = {int(l): t for l, t in zip(dtr["label"], dtr["label_text"])}; names = [m[i] for i in range(max(m) + 1)]
120
+ names = [_clean(n, sp["camel"]) for n in names]
121
+ alts = sp["alts"] or [names]
122
+
123
+ def rows(d):
124
+ cols = [d[f] for f in sp["fields"]]; lab = d["label"]
125
+ return [(tuple(str(c[i])[:1200] for c in cols), int(lab[i])) for i in range(len(lab)) if int(lab[i]) >= 0]
126
+ R = dict(train=rows(dtr), val=rows(dva))
127
+ assert len(R["val"]) >= 60 and len(R["train"]) >= 200
128
+ DATA[name] = dict(spec=sp, names=names, alts=alts, rows=R)
129
+ print(f"[data] {name:10s} classes={len(names):3d} train={len(R['train']):6d} val={len(R['val']):5d} {'(held-out)' if sp['held'] else ''}")
130
+ break
131
+ except Exception as e:
132
+ print(f"[skip] {name} via {p}: {type(e).__name__}: {str(e)[:100]}")
133
+ ST["train_fams"] = sorted(f for f in DATA if not DATA[f]["spec"]["held"])
134
+ ST["held_fams"] = sorted(f for f in DATA if DATA[f]["spec"]["held"])
135
+ assert len(ST["train_fams"]) >= 4, "too few training families loaded (is internet enabled?)"
136
+
137
+
138
+ def make_example(rng, fams, split="train", wording="train", idx=None, lo=0):
139
+ fields, qs, used = [], [], set()
140
+ for fam in fams:
141
+ D = DATA[fam]; sp = D["spec"]; rows = D["rows"][split]
142
+ if idx is not None: texts, label = rows[idx % len(rows)]
143
+ else: texts, label = rows[rng.randrange(lo if len(rows) > lo else 0, len(rows))]
144
+ nm = []
145
+ for n0 in rng.choice(sp["fn"]):
146
+ n1, c = n0, 2
147
+ while n1 in used: n1 = f"{n0}_{c}"; c += 1
148
+ used.add(n1); nm.append(n1)
149
+ fields += list(zip(nm, texts))
150
+ if wording == "unseen": q, alt = sp["qs"][2], D["alts"][-1]
151
+ elif wording == "any": q, alt = rng.choice(sp["qs"]), rng.choice(D["alts"])
152
+ else: q, alt = rng.choice(sp["qs"][:2]), rng.choice(D["alts"][:-1] or D["alts"])
153
+ qs.append(dict(q=q.format(f=" and ".join(nm)), options=list(alt), gold=label, ordinal=sp["ordinal"], fam=fam))
154
+ rng.shuffle(fields); rng.shuffle(qs)
155
+ return dict(fields=fields, questions=qs)
156
+
157
+
158
+ def sample_exs(rng, n):
159
+ fams, w = ST["train_fams"], ST["w"]; out = []
160
+ for _ in range(n):
161
+ k = min(rng.choices([1, 2, 3, 4], [.3, .3, .25, .15])[0], len(fams)); ch = []
162
+ while len(ch) < k:
163
+ f = rng.choices(fams, w)[0]
164
+ if f not in ch: ch.append(f)
165
+ out.append(make_example(rng, ch))
166
+ return out
167
+
168
+
169
+ def prep(exs, bs=48):
170
+ m = ST["model"]
171
+ return [to_device(build_batch(exs[i:i + bs], m.tk, m.tokz, m.cfg), dev) for i in range(0, len(exs), bs)]
172
+
173
+
174
+ def val_examples(fam, lo, hi, wording):
175
+ n = len(DATA[fam]["rows"]["val"])
176
+ return [make_example(random.Random(1000 + i), [fam], "val", wording, idx=i) for i in range(lo, min(hi, n))]
177
+
178
+
179
+ # ----------------------------------------------------------------------------------------------- metrics
180
+ def ece_fn(conf, corr, nb=15):
181
+ conf, corr = np.asarray(conf, float), np.asarray(corr, float); e = 0.0
182
+ edges = np.linspace(0, 1, nb + 1)
183
+ for lo, hi in zip(edges[:-1], edges[1:]):
184
+ m = (conf > lo) & (conf <= hi)
185
+ if m.any(): e += m.mean() * abs(corr[m].mean() - conf[m].mean())
186
+ return float(e)
187
+
188
+
189
+ @torch.no_grad()
190
+ def predict_res(model, batches):
191
+ model.eval(); out = []
192
+ for b in batches:
193
+ with ac(): lg = model(b)
194
+ lg = lg.float().cpu()
195
+ for r, idx in enumerate(b["opt_lists"]):
196
+ out.append((b["fams"][r], lg[r, idx].numpy(), b["gold_pos"][r]))
197
+ return out
198
+
199
+
200
+ def _calc(items):
201
+ acc = nll = br = 0.0; conf, corr = [], []
202
+ for lg, g in items:
203
+ z = lg - lg.max(); lp = z - np.log(np.exp(z).sum()); p = np.exp(lp)
204
+ oh = np.zeros_like(p); oh[g] = 1
205
+ acc += float(p.argmax() == g); nll += -lp[g]; br += ((p - oh) ** 2).sum()
206
+ conf.append(float(p.max())); corr.append(float(p.argmax() == g))
207
+ n = len(items)
208
+ return dict(acc=acc / n, nll=nll / n, brier=br / n, ece=ece_fn(conf, corr), n=n, conf=conf, corr=corr)
209
+
210
+
211
+ def stat(res, T=1.0):
212
+ per = {}
213
+ for fam, lg, g in res: per.setdefault(fam, []).append((lg / T, g))
214
+ P = {f: _calc(v) for f, v in per.items()}
215
+ allitems = [x for v in per.values() for x in v]; micro = _calc(allitems)
216
+ mac = lambda k: float(np.mean([P[f][k] for f in P]))
217
+ return dict(per=P, score=mac("acc"), nll=mac("nll"), brier=mac("brier"), ece=micro["ece"], micro=micro,
218
+ per_acc={f: P[f]["acc"] for f in P})
219
+
220
+
221
+ def fast_eval(model, which):
222
+ return stat(predict_res(model, ST["fast_" + which]), float(model.post_T))
223
+
224
+
225
+ # ----------------------------------------------------------------------------------------------- training parts
226
+ def loss_fn(model, b):
227
+ lg = model(b).float(); lp = F.log_softmax(lg, -1); al = b["allowed"]
228
+ ar = torch.arange(lg.size(0), device=lg.device)
229
+ nll = -lp[ar, b["gold"]]
230
+ uni = -(lp.masked_fill(~al, 0.0)).sum(1) / al.sum(1)
231
+ p = lp.exp(); oh = F.one_hot(b["gold"], lg.size(1)).float()
232
+ brier = ((p - oh) ** 2).sum(1)
233
+ rg = b["rank"][ar, b["gold"]]
234
+ ordl = (p * (b["rank"] - rg[:, None]) ** 2 * al).sum(1)
235
+ L = (1 - C.ls) * nll + C.ls * uni + C.brier_w * brier + C.ord_w * b["ordinal"].float() * ordl
236
+ return L.mean(), float(nll.mean())
237
+
238
+
239
+ def make_scaler():
240
+ try: return torch.amp.GradScaler("cuda", enabled=torch.cuda.is_available())
241
+ except Exception: return torch.cuda.amp.GradScaler(enabled=torch.cuda.is_available())
242
+
243
+
244
+ def make_opt(model, lr_bb, lr_new, wd_new):
245
+ def split(ps): return [p for p in ps if p.ndim >= 2], [p for p in ps if p.ndim < 2]
246
+ bd, bn = split(list(model.backbone.parameters())); nd, nn_ = split(model.new_params())
247
+ gs = [dict(params=bd, base_lr=lr_bb, lr=lr_bb, weight_decay=C.wd_bb, tag="bb"), dict(params=bn, base_lr=lr_bb, lr=lr_bb, weight_decay=0.0, tag="bb0"),
248
+ dict(params=nd, base_lr=lr_new, lr=lr_new, weight_decay=wd_new, tag="new"), dict(params=nn_, base_lr=lr_new, lr=lr_new, weight_decay=0.0, tag="new0")]
249
+ return torch.optim.AdamW([g for g in gs if g["params"]], betas=(0.9, 0.98), eps=1e-6)
250
+
251
+
252
+ def sgd_step(model, opt, scaler, b, mem=None, lam=0.0, anchor=False):
253
+ model.train()
254
+ with ac():
255
+ loss, nll = loss_fn(model, b)
256
+ if anchor and mem is not None and mem.entries: loss = loss + lam * mem.anchor(model)
257
+ opt.zero_grad(set_to_none=True); scaler.scale(loss).backward(); scaler.unscale_(opt)
258
+ gn = torch.nn.utils.clip_grad_norm_(model.parameters(), C.clip); scaler.step(opt); scaler.update()
259
+ return nll, float(gn)
260
+
261
+
262
+ def clone_state(m): return {k: v.detach().cpu().clone() for k, v in m.state_dict().items()}
263
+
264
+
265
+ class EMA:
266
+ def __init__(s, model, decay):
267
+ s.d, s.bk = decay, None
268
+ s.sh = {k: v.detach().clone().float() for k, v in model.state_dict().items() if v.dtype.is_floating_point}
269
+ @torch.no_grad()
270
+ def update(s, model, step):
271
+ d = min(s.d, (1 + step) / (10 + step))
272
+ for k, v in model.state_dict().items():
273
+ if k in s.sh: s.sh[k].mul_(d).add_(v.detach().float(), alpha=1 - d)
274
+ @torch.no_grad()
275
+ def apply(s, model):
276
+ sd = model.state_dict(); s.bk = {k: sd[k].detach().clone() for k in s.sh}
277
+ model.load_state_dict({k: s.sh[k].to(sd[k].dtype) for k in s.sh}, strict=False)
278
+ @torch.no_grad()
279
+ def restore(s, model): model.load_state_dict(s.bk, strict=False)
280
+
281
+
282
+ class FMem:
283
+ """Functional memory M_F: entries = {step, signature Sigma_F (dense logits on diagnostic set E_F), val score}."""
284
+ def __init__(s, diag_batch): s.b, s.entries = diag_batch, []
285
+ @torch.no_grad()
286
+ def add(s, model, step, score):
287
+ model.eval()
288
+ with ac(): lg = model(s.b).float()
289
+ s.entries.append(dict(step=step, sig=lg.cpu().half(), score=score))
290
+ def anchor(s, model):
291
+ e = random.choice(s.entries); old = e["sig"].to(s.b["allowed"].device).float()
292
+ lp = F.log_softmax(model(s.b).float(), -1); p = F.softmax(old, -1); lpo = F.log_softmax(old, -1)
293
+ return ((p * (lpo - lp)).masked_fill(~s.b["allowed"], 0.0)).sum(1).mean()
294
+
295
+
296
+ class Controller:
297
+ """Discovery controller: rho = d(val nll)/d(train nll) ~ share of fitting progress that generalises (proxy of P(B_G)/P(B_M))."""
298
+ def __init__(s): s.wd, s.rho, s.prev, s.LFRR = C.wd_new, None, None, 0.0
299
+ def update(s, rec):
300
+ if s.prev is not None:
301
+ dtr, dv = s.prev["tr_nll"] - rec["tr_nll"], s.prev["nll"] - rec["nll"]
302
+ if dtr > 1e-3:
303
+ r = float(np.clip(dv / dtr, -1, 2)); s.rho = r if s.rho is None else 0.5 * s.rho + 0.5 * r
304
+ if s.rho is not None:
305
+ if s.rho < 0.3: s.wd = min(C.wd_max, s.wd * 1.25) # memorising -> shape manifold harder
306
+ elif s.rho > 0.8: s.wd = max(C.wd_min, s.wd * 0.9) # generalising -> relax
307
+ s.prev = rec
308
+
309
+
310
+ def perturb(model, sigma):
311
+ with torch.no_grad():
312
+ for p in model.parameters():
313
+ if p.ndim >= 2: p.add_(torch.randn_like(p) * p.std() * sigma * C.noise_unit)
314
+
315
+
316
+ def frr_test(model, hist, final_score, sigmas):
317
+ """FRR = T_first / T_re. T_first: first logged step the main run reached tau. T_re: steps to re-reach tau after perturbing."""
318
+ snap = clone_state(model); tau = final_score - 0.01
319
+ T_first = next((h["step"] for h in hist if h["score"] >= tau), hist[-1]["step"]); res = {}
320
+ for sg in sigmas:
321
+ model.load_state_dict(snap); torch.manual_seed(int(sg * 100)); perturb(model, sg)
322
+ v0 = fast_eval(model, "val")["score"]; trivial = v0 >= tau; T_re, hit = 1, trivial
323
+ if not trivial:
324
+ opt, scaler, rng = make_opt(model, C.lr_bb, C.lr_new, C.wd_new), make_scaler(), random.Random(999 + int(sg * 10)); T_re = C.frr_max_steps
325
+ for s in range(1, C.frr_max_steps + 1):
326
+ for g in opt.param_groups: g["lr"] = g["base_lr"] * min(1.0, s / 5)
327
+ sgd_step(model, opt, scaler, to_device(build_batch(sample_exs(rng, C.bs), model.tk, model.tokz, model.cfg), dev))
328
+ if s % C.frr_every == 0 and fast_eval(model, "val")["score"] >= tau: T_re, hit = s, True; break
329
+ res[sg] = dict(v0=v0, tau=tau, T_first=T_first, T_re=T_re, FRR=T_first / max(T_re, 1), L_FRR=max(T_re, 1) / max(T_first, 1),
330
+ censored=(not hit), trivial=trivial)
331
+ print(f" [FRR] sigma={sg}: perturbed score {v0:.3f} (tau {tau:.3f}) T_first={T_first} T_re={T_re}{'+' if not hit else ''} FRR={res[sg]['FRR']:.1f}")
332
+ model.load_state_dict(snap); return res
333
+
334
+
335
+ def adapt_curve(model, fam, eval_batches, steps, at):
336
+ """Few-shot adaptation of a snapshot to a held-out family; returns {step: (acc, nll)}. Model is restored afterwards."""
337
+ snap = clone_state(model); opt, scaler, rng = make_opt(model, C.lr_bb, C.lr_new, 0.0), make_scaler(), random.Random(777); curve = {}
338
+ for s in range(steps + 1):
339
+ if s in at:
340
+ r = stat(predict_res(model, eval_batches), float(model.post_T)); curve[s] = (r["score"], r["nll"])
341
+ if s == steps: break
342
+ for g in opt.param_groups: g["lr"] = g["base_lr"] * min(1.0, (s + 1) / 3)
343
+ b = to_device(build_batch([make_example(rng, [fam], "train", "any") for _ in range(C.probe_bs)], model.tk, model.tokz, model.cfg), dev)
344
+ sgd_step(model, opt, scaler, b)
345
+ model.load_state_dict(snap); return curve
346
+
347
+
348
+ def meta_probe(model, step):
349
+ out = [dict(step=step, fam=f, curve=adapt_curve(model, f, ST["held_test"][f], C.probe_steps, C.probe_at)) for f in ST["held_fams"]]
350
+ msg = " | ".join(f"{o['fam']}: zs {o['curve'][0][0]:.2f} -> @10 {o['curve'][10][0]:.2f} -> @{C.probe_steps} {o['curve'][C.probe_steps][0]:.2f}" for o in out)
351
+ print(f" [meta-probe @ {step}] {msg}"); return out
352
+
353
+
354
+ # ----------------------------------------------------------------------------------------------- setup + train
355
+ def setup():
356
+ random.seed(C.seed); np.random.seed(C.seed); torch.manual_seed(C.seed)
357
+ load_all()
358
+ m = Alpha(AlphaConfig(backbone=C.backbone)).to(dev).use_data_parallel()
359
+ ST["model"] = m; ST["w"] = [math.sqrt(len(DATA[f]["rows"]["train"])) for f in ST["train_fams"]]
360
+ tf, hf = ST["train_fams"], ST["held_fams"]
361
+ fast = lambda split, lo, n: [make_example(random.Random(i * 31 + j), [f], split, "train", idx=i) for j, f in enumerate(tf) for i in range(lo, lo + n)]
362
+ ST["fast_val"], ST["fast_train"] = prep(fast("val", 0, C.fast_n)), prep(fast("train", 0, C.fast_n))
363
+ ST["cal"] = prep([e for f in tf for e in val_examples(f, 0, C.n_cal, "train")])
364
+ ST["held_test"] = {f: prep(val_examples(f, 0, C.probe_test_n, "unseen")) for f in hf}
365
+ rng = random.Random(4242); ST["diag"] = prep([make_example(rng, rng.sample(tf, min(3, len(tf)))) for _ in range(16)], bs=16)[0]
366
+ n_all = sum(p.numel() for p in m.parameters()); print(f"params: {n_all/1e6:.1f}M | GPUs: {torch.cuda.device_count()} | train fams {tf} | held-out {hf}")
367
+
368
+
369
+ def train():
370
+ m, hist = ST["model"], []; rng = random.Random(C.seed + 1)
371
+ ctl, mem, ema = Controller(), FMem(ST["diag"]), EMA(m, C.ema)
372
+ opt, scaler = make_opt(m, C.lr_bb, C.lr_new, ctl.wd), make_scaler()
373
+ ST.update(ctl=ctl, mem=mem, hist=hist, probes=meta_probe(m, 0)); best, lam, loss_ema, t0, mid_done = -1, C.lam_mem, None, time.time(), False
374
+ for step in range(1, C.steps + 1):
375
+ prog = max(0.0, (step - C.warmup) / max(1, C.steps - C.warmup))
376
+ mult = min(1.0, step / C.warmup) * (0.1 + 0.9 * 0.5 * (1 + math.cos(math.pi * prog)))
377
+ for g in opt.param_groups:
378
+ g["lr"] = g["base_lr"] * mult
379
+ if g["tag"] == "new": g["weight_decay"] = ctl.wd
380
+ b = to_device(build_batch(sample_exs(rng, C.bs), m.tk, m.tokz, m.cfg), dev)
381
+ nll, gn = sgd_step(m, opt, scaler, b, mem, lam, anchor=(step % C.anchor_every == 0))
382
+ ema.update(m, step); loss_ema = nll if loss_ema is None else 0.98 * loss_ema + 0.02 * nll
383
+ if step % C.eval_every == 0 or step == C.steps:
384
+ ema.apply(m); v, t = fast_eval(m, "val"), fast_eval(m, "train")
385
+ rec = dict(step=step, score=v["score"], nll=v["nll"], ece=v["ece"], tr_score=t["score"], tr_nll=t["nll"], fam=v["per_acc"],
386
+ wd=ctl.wd, lam=lam, gn=gn, loss=loss_ema, rho=ctl.rho, lr=opt.param_groups[0]["lr"])
387
+ hist.append(rec)
388
+ if v["score"] > best: best = v["score"]; ST["best_state"] = clone_state(m); ST["best_step"] = step
389
+ ctl.update(rec)
390
+ if step % C.mem_every == 0: mem.add(m, step, v["score"])
391
+ if step % C.probe_every == 0: ST["probes"] += meta_probe(m, step)
392
+ if not mid_done and step >= C.steps // 2:
393
+ mid_done = True; r = frr_test(m, hist, v["score"], [1])[1]; ctl.LFRR = r["L_FRR"]; lam = C.lam_mem * (1 + min(ctl.LFRR, 3.0))
394
+ print(f" [L_FRR={ctl.LFRR:.3f}] -> anchor weight {lam:.3f}")
395
+ ema.restore(m)
396
+ print(f"step {step:5d} | val acc {v['score']:.3f} nll {v['nll']:.3f} ece {v['ece']:.3f} | train acc {t['score']:.3f} nll {t['nll']:.3f} | "
397
+ f"rho {ctl.rho if ctl.rho is None else round(ctl.rho, 2)} wd {ctl.wd:.3f} lam {lam:.2f} gn {gn:.2f} | {time.time()-t0:.0f}s")
398
+ m.load_state_dict(ST["best_state"]); print(f"loaded best EMA checkpoint (step {ST['best_step']})")
399
+
400
+
401
+ # ----------------------------------------------------------------------------------------------- finalize + benchmark
402
+ def finalize():
403
+ m, hist = ST["model"], ST["hist"]; m.eval()
404
+ res = predict_res(m, ST["cal"]); grid = np.arange(0.5, 3.01, 0.02)
405
+ nlls = [np.mean([-(lg / T - np.logaddexp.reduce(lg / T))[g] for _, lg, g in res]) for T in grid]
406
+ T = float(grid[int(np.argmin(nlls))]); m.post_T.fill_(T); print(f"post-hoc temperature T={T:.2f}")
407
+ best = max(h["score"] for h in hist); print("FRR sweep (basin test):"); ST["frr"] = frr_test(m, hist, best, C.sigmas)
408
+ tau = min(h["nll"] for h in hist) + 0.05
409
+ Tv = next((h["step"] for h in hist if h["nll"] <= tau), hist[-1]["step"]); Tt = next((h["step"] for h in hist if h["tr_nll"] <= tau), hist[-1]["step"])
410
+ ST["DMR"] = dict(T_discover=Tv, T_memorize=Tt, DMR=Tv / max(Tt, 1)); print("DMR:", ST["DMR"])
411
+ # meta-grokking: T_D(n) = adaptation steps to reach 95% of the best adapted accuracy seen over all probes (per family)
412
+ P, fams, mg = ST["probes"], ST["held_fams"], {}
413
+ steps_ = sorted({p["step"] for p in P})
414
+ for f in fams:
415
+ tau_f = 0.95 * max(p["curve"][C.probe_steps][0] for p in P if p["fam"] == f)
416
+ for s in steps_:
417
+ c = next(p for p in P if p["fam"] == f and p["step"] == s)["curve"]
418
+ td = next((k for k in sorted(c) if c[k][0] >= tau_f), C.probe_steps * 2)
419
+ mg.setdefault(s, {})[f] = dict(T_D=max(td, 1), zero_shot=c[0][0], acc10=c[10][0], final=c[C.probe_steps][0])
420
+ base = steps_[0]
421
+ for s in steps_:
422
+ g = [mg[base][f]["T_D"] / mg[s][f]["T_D"] for f in fams]; mg[s]["MGR"] = float(np.exp(np.mean(np.log(g))))
423
+ ST["meta"] = mg; print("meta-grokking MGR by checkpoint:", {s: round(mg[s]["MGR"], 2) for s in steps_})
424
+
425
+
426
+ @torch.no_grad()
427
+ def workflow_eval(n=300, k=4):
428
+ m = ST["model"]; rng = random.Random(2024); exs = []
429
+ for _ in range(n): exs.append(make_example(rng, rng.sample(ST["train_fams"], min(k, len(ST["train_fams"]))), "val", "train", lo=C.n_cal))
430
+ m.eval(); qacc, allok, jc, jok = [], [], [], []
431
+ for i in range(0, n, 32):
432
+ b = to_device(build_batch(exs[i:i + 32], m.tk, m.tokz, m.cfg), dev)
433
+ with ac(): lg = m(b).float() / m.post_T
434
+ per = {}
435
+ for r, idx in enumerate(b["opt_lists"]):
436
+ p = F.softmax(lg[r, idx], -1); ok = int(p.argmax()) == b["gold_pos"][r]
437
+ qacc.append(ok); per.setdefault(b["q_ex"][r], []).append((ok, float(p.max())))
438
+ for v in per.values():
439
+ allok.append(all(o for o, _ in v)); jc.append(float(np.prod([c for _, c in v]))); jok.append(float(all(o for o, _ in v)))
440
+ return dict(q_acc=float(np.mean(qacc)), all_correct=float(np.mean(allok)), joint_ece=ece_fn(jc, jok), k=k, n=n)
441
+
442
+
443
+ def speed_bench():
444
+ m = ST["model"]; m.eval()
445
+ state = dict(customer_ticket="My card was charged twice for the same order and support has not replied in three days. I want my money back.",
446
+ account="plan: pro; tenure_months: 14; open_tickets: 2")
447
+ qs = [dict(id="refund", question="Does the customer ask for a refund?", options=["no", "yes"]),
448
+ dict(id="urgency", question="How urgent is the ticket?", options=["low", "medium", "high"], ordinal=True),
449
+ dict(id="team", question="Which team should handle the ticket?", options=["billing", "technical", "sales", "account", "abuse"])]
450
+ sync = (lambda: torch.cuda.synchronize()) if torch.cuda.is_available() else (lambda: None)
451
+ t = time.perf_counter(); m.predict(state, qs); sync(); cold = (time.perf_counter() - t) * 1e3
452
+ for _ in range(10): m.predict(state, qs)
453
+ ts = []
454
+ for _ in range(200):
455
+ sync(); t = time.perf_counter(); m.predict(state, qs); sync(); ts.append((time.perf_counter() - t) * 1e3)
456
+ rng = random.Random(1); exs = [make_example(rng, rng.sample(ST["train_fams"], 3)) for _ in range(64)]
457
+ with torch.no_grad():
458
+ for _ in range(3): m(to_device(build_batch(exs, m.tk, m.tokz, m.cfg), dev))
459
+ sync(); t = time.perf_counter()
460
+ for _ in range(10):
461
+ b = to_device(build_batch(exs, m.tk, m.tokz, m.cfg), dev)
462
+ with torch.no_grad(), ac(): m(b)
463
+ sync(); e2e = 10 * 64 / (time.perf_counter() - t)
464
+ return dict(latency_ms_p50=float(np.percentile(ts, 50)), latency_ms_p95=float(np.percentile(ts, 95)), cold_first_call_ms=cold,
465
+ throughput_workflows_per_s_bs64=e2e, questions_per_workflow=3, device=torch.cuda.get_device_name(0) if torch.cuda.is_available() else "cpu")
466
+
467
+
468
+ def llm_baseline(n_per=30, fams=("sst2", "ag_news", "emotion", "snli")):
469
+ """Generative small-LLM baseline on the SAME k=1 questions (answers parsed from free text) -> accuracy + latency."""
470
+ from transformers import AutoModelForCausalLM, AutoTokenizer
471
+ fams = [f for f in fams if f in DATA]; name = C.llm_baseline
472
+ t = AutoTokenizer.from_pretrained(name); lm = AutoModelForCausalLM.from_pretrained(name, torch_dtype=torch.float16).to(dev).eval()
473
+ m = ST["model"]; m.eval(); lat, ok_l, ok_a = [], [], []
474
+ for f in fams:
475
+ for i in range(n_per):
476
+ ex = make_example(random.Random(i), [f], "val", "train", idx=C.n_cal + i); q = ex["questions"][0]
477
+ state = "\n".join(f"{k}: {v}" for k, v in ex["fields"])
478
+ msg = [{"role": "user", "content": f"{state}\n\nQuestion: {q['q']}\nOptions: {', '.join(q['options'])}\nReply with exactly one option and nothing else."}]
479
+ ids = t.apply_chat_template(msg, add_generation_prompt=True, return_tensors="pt", return_dict=True).to(dev)
480
+ torch.cuda.synchronize(); t0 = time.perf_counter()
481
+ with torch.no_grad(): out = lm.generate(**ids, max_new_tokens=12, do_sample=False, pad_token_id=t.eos_token_id)
482
+ torch.cuda.synchronize(); lat.append((time.perf_counter() - t0) * 1e3)
483
+ txt = t.decode(out[0, ids["input_ids"].shape[1]:], skip_special_tokens=True).strip().lower()
484
+ cand = [o for o in q["options"] if o.lower() in txt]; pred = max(cand, key=len) if cand else None
485
+ ok_l.append(pred is not None and pred == q["options"][q["gold"]])
486
+ b = to_device(build_batch([ex], m.tk, m.tokz, m.cfg), dev)
487
+ with torch.no_grad(), ac(): lg = m(b)
488
+ ok_a.append(int(lg[0, b["opt_lists"][0]].argmax()) == b["gold_pos"][0])
489
+ del lm; torch.cuda.empty_cache()
490
+ return dict(model=name, n=len(ok_l), llm_acc=float(np.mean(ok_l)), alpha_acc=float(np.mean(ok_a)), llm_latency_ms_p50=float(np.percentile(lat, 50)),
491
+ llm_latency_ms_p95=float(np.percentile(lat, 95)), families=fams)
492
+
493
+
494
+ def benchmark(run_llm=True):
495
+ m = ST["model"]; m.eval(); T = float(m.post_T); R = dict(post_T=T, quality={}, calib={})
496
+ for wording in ("train", "unseen"):
497
+ allr = []
498
+ for f in ST["train_fams"]:
499
+ res = predict_res(m, prep(val_examples(f, C.n_cal, C.n_cal + C.n_test, wording))); allr += res
500
+ s = stat(res, T)["per"][f]; R["quality"].setdefault(wording, {})[f] = {k: s[k] for k in ("acc", "nll", "ece", "brier", "n")}
501
+ s0, s1 = stat(allr, 1.0), stat(allr, T)
502
+ R["quality"][wording]["_macro"] = dict(acc=s1["score"], nll=s1["nll"], ece=s1["ece"], brier=s1["brier"])
503
+ R["calib"][wording] = dict(ece_before_T=s0["ece"], ece_after_T=s1["ece"], conf=s1["micro"]["conf"], corr=s1["micro"]["corr"])
504
+ R["held_out"] = {}
505
+ for f in ST["held_fams"]:
506
+ eb = prep(val_examples(f, C.n_cal, C.n_cal + C.n_test, "unseen"))
507
+ c = adapt_curve(m, f, eb, C.probe_steps, C.probe_at); R["held_out"][f] = dict(zero_shot_acc=c[0][0], adapted_acc={k: v[0] for k, v in c.items()}, n=len(val_examples(f, C.n_cal, C.n_cal + C.n_test, "unseen")))
508
+ R["workflow"] = workflow_eval()
509
+ R["speed"] = speed_bench()
510
+ R["type_safety"] = dict(schema_violations=0, note="outputs are a masked softmax over the declared options; an out-of-schema answer cannot be produced")
511
+ if run_llm:
512
+ try: R["llm_baseline"] = llm_baseline()
513
+ except Exception as e: R["llm_baseline"] = dict(error=f"{type(e).__name__}: {str(e)[:200]}")
514
+ R["frr"] = ST["frr"]; R["dmr"] = ST["DMR"]; R["meta"] = {str(k): v for k, v in ST["meta"].items()}
515
+ ST["R"] = R
516
+ print(json.dumps({k: v for k, v in R.items() if k not in ("calib", "meta")}, indent=1, default=float)[:6000]); return R
517
+
518
+
519
+ def plots(path="alpha_report.png"):
520
+ import matplotlib; import matplotlib.pyplot as plt
521
+ H, R = ST["hist"], ST["R"]; st = [h["step"] for h in H]
522
+ fig, ax = plt.subplots(2, 4, figsize=(22, 9)); ax = ax.ravel()
523
+ ax[0].plot(st, [h["score"] for h in H], label="val acc"); ax[0].plot(st, [h["tr_score"] for h in H], label="train acc"); ax[0].set_title("Learning curve (EMA weights)"); ax[0].legend()
524
+ ax[1].plot(st, [h["nll"] for h in H], label="val nll"); ax[1].plot(st, [h["tr_nll"] for h in H], label="train nll"); ax[1].axvline(ST["DMR"]["T_discover"], color="g", ls=":"); ax[1].axvline(ST["DMR"]["T_memorize"], color="r", ls=":")
525
+ ax[1].set_title(f"DMR = {ST['DMR']['DMR']:.2f} (T_discover/T_memorize)"); ax[1].legend()
526
+ ax[2].plot(st, [h["wd"] for h in H], label="wd (new modules)"); ax[2].plot(st, [h["rho"] if h["rho"] is not None else 0 for h in H], label="rho"); ax[2].plot(st, [h["gn"] for h in H], label="grad norm"); ax[2].legend(); ax[2].set_title("Discovery controller")
527
+ sg = list(R["frr"]); ax[3].bar([str(s) for s in sg], [min(R["frr"][s]["FRR"], 100) for s in sg]); ax[3].axhline(1, color="k", ls="--"); ax[3].set_yscale("log"); ax[3].set_title("FRR vs perturbation sigma (basin test)")
528
+ mg = ST["meta"]; ss = sorted(k for k in mg); ax[4].plot(ss, [mg[s]["MGR"] for s in ss], "o-"); ax[4].axhline(1, color="k", ls="--"); ax[4].set_title("Meta-grokking rate MGR (held-out families)")
529
+ for f in ST["held_fams"]: ax[5].plot(ss, [mg[s][f]["zero_shot"] for s in ss], "o-", label=f"{f} zero-shot"); ax[5].plot(ss, [mg[s][f]["acc10"] for s in ss], "s--", label=f"{f} +10 steps")
530
+ ax[5].legend(fontsize=7); ax[5].set_title("Unseen families: zero-shot / after 10 steps")
531
+ conf, corr = np.array(R["calib"]["train"]["conf"]), np.array(R["calib"]["train"]["corr"]); e = np.linspace(0, 1, 11)
532
+ xs, ys = [], []
533
+ for lo, hi in zip(e[:-1], e[1:]):
534
+ mk = (conf > lo) & (conf <= hi)
535
+ if mk.sum() > 5: xs.append(conf[mk].mean()); ys.append(corr[mk].mean())
536
+ ax[6].plot([0, 1], [0, 1], "k--"); ax[6].plot(xs, ys, "o-"); ax[6].set_title(f"Reliability (ECE {R['calib']['train']['ece_after_T']:.3f})")
537
+ fams = ST["train_fams"]; x = np.arange(len(fams))
538
+ ax[7].bar(x - .2, [R["quality"]["train"][f]["acc"] for f in fams], .4, label="seen wording"); ax[7].bar(x + .2, [R["quality"]["unseen"][f]["acc"] for f in fams], .4, label="unseen wording")
539
+ ax[7].set_xticks(x); ax[7].set_xticklabels(fams, rotation=60); ax[7].legend(); ax[7].set_title("Test accuracy per family")
540
+ plt.tight_layout(); plt.savefig(path, dpi=110); plt.show()
541
+
542
+
543
+ # ----------------------------------------------------------------------------------------------- publish
544
+ def get_token():
545
+ t = os.environ.get("HF_TOKEN")
546
+ if not t:
547
+ try:
548
+ from kaggle_secrets import UserSecretsClient
549
+ t = UserSecretsClient().get_secret("HF_TOKEN")
550
+ except Exception: pass
551
+ return t
552
+
553
+
554
+ def model_card(R):
555
+ q, sp, wf, h = R["quality"], R["speed"], R["workflow"], R["held_out"]
556
+ rows = "\n".join(f"| {f} | {q['train'][f]['acc']:.3f} | {q['unseen'][f]['acc']:.3f} | {q['train'][f]['ece']:.3f} |" for f in ST["train_fams"])
557
+ hrows = "\n".join(f"| {f} | {v['zero_shot_acc']:.3f} | {v['adapted_acc'][10]:.3f} | {v['adapted_acc'][C.probe_steps]:.3f} |" for f, v in h.items())
558
+ llm = R.get("llm_baseline", {}); llm_s = (f"Qwen2.5-0.5B-Instruct (generative, same {llm['n']} questions): acc {llm['llm_acc']:.3f}, p50 latency {llm['llm_latency_ms_p50']:.0f} ms "
559
+ f"vs Alpha acc {llm['alpha_acc']:.3f}, p50 {sp['latency_ms_p50']:.1f} ms.") if "llm_acc" in llm else "LLM baseline not run."
560
+ fr = " ".join(f"sigma={s}: FRR {v['FRR']:.1f}{'*' if v['censored'] else ''}" for s, v in R["frr"].items())
561
+ return f"""---
562
+ license: apache-2.0
563
+ base_model: {C.backbone}
564
+ tags: [system-one, structured-output, calibrated, classification, parallel-decoding]
565
+ ---
566
+ # Alpha (System-One-style typed decision model)
567
+
568
+ Alpha takes unstructured / structured state plus **typed questions with declared options** and returns calibrated probabilities for
569
+ **all questions in one parallel forward pass**. It cannot generate strings and cannot answer outside the declared options
570
+ (masked softmax -> schema violations are impossible by construction). Option embeddings are independent of the state, so a schema can be
571
+ compiled once and cached; cardinality is limited only by memory.
572
+
573
+ Architecture: `{C.backbone}` encoder (state + question slots with `<mask>` readout tokens) -> 2-layer **Slot Reasoner** (set transformer, squared-ReLU,
574
+ zero-init residuals) -> biaffine/cosine scorer against option embeddings with per-question learned temperature + post-hoc temperature (T={R['post_T']:.2f}).
575
+
576
+ Training: {C.steps} steps x {C.bs} workflow-style examples (1-4 questions over named fields, mixed from {len(ST['train_fams'])} public datasets with paraphrased
577
+ questions/option wordings), proper-scoring-rule loss (NLL + Brier + ordinal term), EMA weights, functional-memory anchor, discovery controller.
578
+ Held-out families (never trained on): {', '.join(ST['held_fams'])}.
579
+
580
+ ## Results (test splits; see results.json, alpha_report.png)
581
+ | family | acc (seen wording) | acc (unseen wording) | ECE |
582
+ |---|---|---|---|
583
+ {rows}
584
+
585
+ Workflow eval ({wf['k']} questions per query): per-question acc {wf['q_acc']:.3f}, all-correct {wf['all_correct']:.3f}, joint-confidence ECE {wf['joint_ece']:.3f}.
586
+
587
+ Held-out task families (unseen schema wording): zero-shot / after 10 / after {C.probe_steps} gradient steps
588
+ | family | zero-shot | +10 steps | +{C.probe_steps} steps |
589
+ |---|---|---|---|
590
+ {hrows}
591
+
592
+ Speed on {sp['device']}: p50 {sp['latency_ms_p50']:.1f} ms, p95 {sp['latency_ms_p95']:.1f} ms for a 3-question query (local, no network); {sp['throughput_workflows_per_s_bs64']:.0f} workflows/s at batch 64.
593
+ {llm_s}
594
+
595
+ Framework diagnostics: DMR={R['dmr']['DMR']:.2f}; FRR by perturbation: {fr} (* = not re-reached within budget); MGR by checkpoint in results.json.
596
+
597
+ ## Honest limits
598
+ * This is a small research model trained on public classification datasets. **It is not a replacement for, and has not been compared against, TypeSafe's Jev**
599
+ (a closed API; its claims were not verified here). Do not read the latency comparison as a Jev comparison.
600
+ * Narrow task coverage (sentiment/topic/intent/NLI/paraphrase/spam/rating); free-form reasoning is out of scope by design.
601
+ * Calibration is measured in-distribution; shift will degrade it.
602
+
603
+ ## Usage
604
+ ```python
605
+ from huggingface_hub import snapshot_download; import sys
606
+ p = snapshot_download("{C.repo_id}"); sys.path.insert(0, p)
607
+ from alpha_s1 import Alpha
608
+ m = Alpha.from_pretrained(p, device="cuda").eval()
609
+ print(m.predict({{"ticket": "I was charged twice, refund me now."}},
610
+ [{{"id": "refund", "question": "Does the customer want a refund?", "options": ["no", "yes"]}}]))
611
+ ```
612
+ """
613
+
614
+
615
+ def publish(out_dir="alpha_out"):
616
+ from huggingface_hub import HfApi
617
+ tok = get_token(); assert tok, "Set a Kaggle secret named HF_TOKEN (Add-ons > Secrets) or env var HF_TOKEN."
618
+ R = ST["R"]; m = ST["model"]; m.save_pretrained(out_dir)
619
+ shutil.copy("alpha_s1.py", out_dir); shutil.copy("alpha_train.py", out_dir)
620
+ if os.path.exists("alpha_report.png"): shutil.copy("alpha_report.png", out_dir)
621
+ slim = {k: v for k, v in R.items() if k != "calib"}; json.dump(slim, open(f"{out_dir}/results.json", "w"), indent=1, default=float)
622
+ json.dump(dict(hist=ST["hist"], probes=ST["probes"]), open(f"{out_dir}/training_log.json", "w"), default=float)
623
+ open(f"{out_dir}/README.md", "w").write(model_card(R))
624
+ api = HfApi(token=tok); api.create_repo(C.repo_id, exist_ok=True, private=False)
625
+ api.upload_folder(folder_path=out_dir, repo_id=C.repo_id, commit_message="Alpha: System-One-style typed decision model")
626
+ print(f"published -> https://huggingface.co/{C.repo_id}")
627
+
628
+
629
+ def demo():
630
+ m = ST["model"]; m.eval()
631
+ state = dict(review="The battery died after two days and support never answered. Total waste of money.", account="plan: pro; tenure_months: 14")
632
+ qs = [dict(id="sentiment", question="Is the review positive or negative?", options=["negative", "positive"]),
633
+ dict(id="stars", question="How many stars does the review deserve?", options=["1 star", "2 stars", "3 stars", "4 stars", "5 stars"], ordinal=True),
634
+ dict(id="topic", question="What is the review about?", options=["hardware", "billing", "support", "shipping"])]
635
+ print(json.dumps(m.predict(state, qs), indent=1))
636
+
637
+
638
+ def chat():
639
+ m = ST["model"]; print("Alpha structured console. Empty text to quit.")
640
+ while True:
641
+ text = input("state text> ").strip()
642
+ if not text: break
643
+ q = input("question> ").strip(); opts = [o.strip() for o in input("options (comma separated)> ").split(",") if o.strip()]
644
+ r = m.predict(dict(input=text), [dict(id="q", question=q, options=opts)])["q"]
645
+ print(f" answer={r['answer']} confidence={r['confidence']:.3f} certainty={r['certainty']:.3f}\n probs={ {k: round(v, 3) for k, v in r['probs'].items()} }")
pytorch_model.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:689fdf21871e4e76390985ef67be0cd40913eec674ad1deffd626bae9cf37243
3
+ size 384442495
results.json ADDED
@@ -0,0 +1,435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "post_T": 1.0399999618530273,
3
+ "quality": {
4
+ "train": {
5
+ "ag_news": {
6
+ "acc": 0.926,
7
+ "nll": 0.23406508564949036,
8
+ "ece": 0.0323681704998016,
9
+ "brier": 0.11545059829950333,
10
+ "n": 500
11
+ },
12
+ "banking77": {
13
+ "acc": 0.902,
14
+ "nll": 0.3612114489078522,
15
+ "ece": 0.035957046896219286,
16
+ "brier": 0.15479955077171326,
17
+ "n": 500
18
+ },
19
+ "emotion": {
20
+ "acc": 0.922,
21
+ "nll": 0.15576711297035217,
22
+ "ece": 0.030244563996791818,
23
+ "brier": 0.08809012919664383,
24
+ "n": 500
25
+ },
26
+ "imdb": {
27
+ "acc": 0.886,
28
+ "nll": 0.2880212068557739,
29
+ "ece": 0.027435769557952863,
30
+ "brier": 0.1726732850074768,
31
+ "n": 500
32
+ },
33
+ "mrpc": {
34
+ "acc": 0.8217054263565892,
35
+ "nll": 0.3895823657512665,
36
+ "ece": 0.042235060486682595,
37
+ "brier": 0.25083833932876587,
38
+ "n": 258
39
+ },
40
+ "news20": {
41
+ "acc": 0.674,
42
+ "nll": 1.0884853601455688,
43
+ "ece": 0.06790356469154359,
44
+ "brier": 0.4469330608844757,
45
+ "n": 500
46
+ },
47
+ "sms": {
48
+ "acc": 0.9926470588235294,
49
+ "nll": 0.038313742727041245,
50
+ "ece": 0.0037884642096127656,
51
+ "brier": 0.015446269884705544,
52
+ "n": 408
53
+ },
54
+ "snli": {
55
+ "acc": 0.772,
56
+ "nll": 0.5716506838798523,
57
+ "ece": 0.045889752805233,
58
+ "brier": 0.3297711908817291,
59
+ "n": 500
60
+ },
61
+ "sst2": {
62
+ "acc": 0.912,
63
+ "nll": 0.24116064608097076,
64
+ "ece": 0.034141219377517684,
65
+ "brier": 0.14030593633651733,
66
+ "n": 500
67
+ },
68
+ "yelp": {
69
+ "acc": 0.572,
70
+ "nll": 0.9883294105529785,
71
+ "ece": 0.0719212611913681,
72
+ "brier": 0.5307314991950989,
73
+ "n": 500
74
+ },
75
+ "_macro": {
76
+ "acc": 0.8380352485180118,
77
+ "nll": 0.4356587529182434,
78
+ "ece": 0.01943733276175781,
79
+ "brier": 0.2245039939880371
80
+ }
81
+ },
82
+ "unseen": {
83
+ "ag_news": {
84
+ "acc": 0.924,
85
+ "nll": 0.25852325558662415,
86
+ "ece": 0.041514035522937774,
87
+ "brier": 0.12416581809520721,
88
+ "n": 500
89
+ },
90
+ "banking77": {
91
+ "acc": 0.89,
92
+ "nll": 0.4005417227745056,
93
+ "ece": 0.06322333976626397,
94
+ "brier": 0.16635079681873322,
95
+ "n": 500
96
+ },
97
+ "emotion": {
98
+ "acc": 0.894,
99
+ "nll": 0.38785520195961,
100
+ "ece": 0.12037511637806893,
101
+ "brier": 0.1763131022453308,
102
+ "n": 500
103
+ },
104
+ "imdb": {
105
+ "acc": 0.892,
106
+ "nll": 0.28929969668388367,
107
+ "ece": 0.023690259099006624,
108
+ "brier": 0.17336110770702362,
109
+ "n": 500
110
+ },
111
+ "mrpc": {
112
+ "acc": 0.8217054263565892,
113
+ "nll": 0.4847447872161865,
114
+ "ece": 0.16866437339967533,
115
+ "brier": 0.3047117590904236,
116
+ "n": 258
117
+ },
118
+ "news20": {
119
+ "acc": 0.678,
120
+ "nll": 1.0626460313796997,
121
+ "ece": 0.05303302364051344,
122
+ "brier": 0.44085004925727844,
123
+ "n": 500
124
+ },
125
+ "sms": {
126
+ "acc": 0.34558823529411764,
127
+ "nll": 1.1533381938934326,
128
+ "ece": 0.3848064363587137,
129
+ "brier": 0.8449604511260986,
130
+ "n": 408
131
+ },
132
+ "snli": {
133
+ "acc": 0.538,
134
+ "nll": 0.9769825339317322,
135
+ "ece": 0.11520019578933716,
136
+ "brier": 0.5887723565101624,
137
+ "n": 500
138
+ },
139
+ "sst2": {
140
+ "acc": 0.91,
141
+ "nll": 0.22101609408855438,
142
+ "ece": 0.02428796315193177,
143
+ "brier": 0.1290440559387207,
144
+ "n": 500
145
+ },
146
+ "yelp": {
147
+ "acc": 0.522,
148
+ "nll": 1.1097854375839233,
149
+ "ece": 0.09259637647867204,
150
+ "brier": 0.6057519912719727,
151
+ "n": 500
152
+ },
153
+ "_macro": {
154
+ "acc": 0.7415293661650708,
155
+ "nll": 0.6344732642173767,
156
+ "ece": 0.03709241313372004,
157
+ "brier": 0.35542815923690796
158
+ }
159
+ }
160
+ },
161
+ "held_out": {
162
+ "cola": {
163
+ "zero_shot_acc": 0.374,
164
+ "adapted_acc": {
165
+ "0": 0.374,
166
+ "5": 0.652,
167
+ "10": 0.658,
168
+ "20": 0.686,
169
+ "40": 0.686
170
+ },
171
+ "n": 500
172
+ },
173
+ "dbpedia": {
174
+ "zero_shot_acc": 0.312,
175
+ "adapted_acc": {
176
+ "0": 0.312,
177
+ "5": 0.488,
178
+ "10": 0.586,
179
+ "20": 0.876,
180
+ "40": 0.952
181
+ },
182
+ "n": 500
183
+ },
184
+ "offensive": {
185
+ "zero_shot_acc": 0.528,
186
+ "adapted_acc": {
187
+ "0": 0.528,
188
+ "5": 0.674,
189
+ "10": 0.632,
190
+ "20": 0.656,
191
+ "40": 0.694
192
+ },
193
+ "n": 500
194
+ }
195
+ },
196
+ "workflow": {
197
+ "q_acc": 0.825,
198
+ "all_correct": 0.43666666666666665,
199
+ "joint_ece": 0.10074469861705296,
200
+ "k": 4,
201
+ "n": 300
202
+ },
203
+ "speed": {
204
+ "latency_ms_p50": 11.066616000334761,
205
+ "latency_ms_p95": 13.510893649527132,
206
+ "cold_first_call_ms": 173.55092199977662,
207
+ "throughput_workflows_per_s_bs64": 354.2243768052664,
208
+ "questions_per_workflow": 3,
209
+ "device": "Tesla T4"
210
+ },
211
+ "type_safety": {
212
+ "schema_violations": 0,
213
+ "note": "outputs are a masked softmax over the declared options; an out-of-schema answer cannot be produced"
214
+ },
215
+ "llm_baseline": {
216
+ "model": "Qwen/Qwen2.5-0.5B-Instruct",
217
+ "n": 120,
218
+ "llm_acc": 0.5833333333333334,
219
+ "alpha_acc": 0.875,
220
+ "llm_latency_ms_p50": 82.17461200001708,
221
+ "llm_latency_ms_p95": 150.7793488504376,
222
+ "families": [
223
+ "sst2",
224
+ "ag_news",
225
+ "emotion",
226
+ "snli"
227
+ ]
228
+ },
229
+ "frr": {
230
+ "0.5": {
231
+ "v0": 0.8416666666666666,
232
+ "tau": 0.83375,
233
+ "T_first": 4250,
234
+ "T_re": 1,
235
+ "FRR": 4250.0,
236
+ "L_FRR": 0.00023529411764705883,
237
+ "censored": false,
238
+ "trivial": true
239
+ },
240
+ "1": {
241
+ "v0": 0.8479166666666667,
242
+ "tau": 0.83375,
243
+ "T_first": 4250,
244
+ "T_re": 1,
245
+ "FRR": 4250.0,
246
+ "L_FRR": 0.00023529411764705883,
247
+ "censored": false,
248
+ "trivial": true
249
+ },
250
+ "2": {
251
+ "v0": 0.8375,
252
+ "tau": 0.83375,
253
+ "T_first": 4250,
254
+ "T_re": 1,
255
+ "FRR": 4250.0,
256
+ "L_FRR": 0.00023529411764705883,
257
+ "censored": false,
258
+ "trivial": true
259
+ },
260
+ "4": {
261
+ "v0": 0.8416666666666668,
262
+ "tau": 0.83375,
263
+ "T_first": 4250,
264
+ "T_re": 1,
265
+ "FRR": 4250.0,
266
+ "L_FRR": 0.00023529411764705883,
267
+ "censored": false,
268
+ "trivial": true
269
+ },
270
+ "8": {
271
+ "v0": 0.7979166666666667,
272
+ "tau": 0.83375,
273
+ "T_first": 4250,
274
+ "T_re": 300,
275
+ "FRR": 14.166666666666666,
276
+ "L_FRR": 0.07058823529411765,
277
+ "censored": true,
278
+ "trivial": false
279
+ }
280
+ },
281
+ "dmr": {
282
+ "T_discover": 3250,
283
+ "T_memorize": 3000,
284
+ "DMR": 1.0833333333333333
285
+ },
286
+ "meta": {
287
+ "0": {
288
+ "cola": {
289
+ "T_D": 5,
290
+ "zero_shot": 0.46,
291
+ "acc10": 0.665,
292
+ "final": 0.665
293
+ },
294
+ "dbpedia": {
295
+ "T_D": 80,
296
+ "zero_shot": 0.115,
297
+ "acc10": 0.045,
298
+ "final": 0.045
299
+ },
300
+ "offensive": {
301
+ "T_D": 80,
302
+ "zero_shot": 0.645,
303
+ "acc10": 0.64,
304
+ "final": 0.64
305
+ },
306
+ "MGR": 1.0
307
+ },
308
+ "1000": {
309
+ "cola": {
310
+ "T_D": 5,
311
+ "zero_shot": 0.55,
312
+ "acc10": 0.635,
313
+ "final": 0.665
314
+ },
315
+ "dbpedia": {
316
+ "T_D": 40,
317
+ "zero_shot": 0.34,
318
+ "acc10": 0.525,
319
+ "final": 0.93
320
+ },
321
+ "offensive": {
322
+ "T_D": 40,
323
+ "zero_shot": 0.64,
324
+ "acc10": 0.64,
325
+ "final": 0.745
326
+ },
327
+ "MGR": 1.5874010519681994
328
+ },
329
+ "2000": {
330
+ "cola": {
331
+ "T_D": 20,
332
+ "zero_shot": 0.4,
333
+ "acc10": 0.61,
334
+ "final": 0.665
335
+ },
336
+ "dbpedia": {
337
+ "T_D": 40,
338
+ "zero_shot": 0.305,
339
+ "acc10": 0.7,
340
+ "final": 0.955
341
+ },
342
+ "offensive": {
343
+ "T_D": 40,
344
+ "zero_shot": 0.625,
345
+ "acc10": 0.69,
346
+ "final": 0.755
347
+ },
348
+ "MGR": 1.0
349
+ },
350
+ "3000": {
351
+ "cola": {
352
+ "T_D": 10,
353
+ "zero_shot": 0.4,
354
+ "acc10": 0.665,
355
+ "final": 0.665
356
+ },
357
+ "dbpedia": {
358
+ "T_D": 40,
359
+ "zero_shot": 0.245,
360
+ "acc10": 0.64,
361
+ "final": 0.975
362
+ },
363
+ "offensive": {
364
+ "T_D": 80,
365
+ "zero_shot": 0.59,
366
+ "acc10": 0.685,
367
+ "final": 0.65
368
+ },
369
+ "MGR": 1.0
370
+ },
371
+ "4000": {
372
+ "cola": {
373
+ "T_D": 5,
374
+ "zero_shot": 0.41,
375
+ "acc10": 0.665,
376
+ "final": 0.665
377
+ },
378
+ "dbpedia": {
379
+ "T_D": 40,
380
+ "zero_shot": 0.305,
381
+ "acc10": 0.71,
382
+ "final": 0.975
383
+ },
384
+ "offensive": {
385
+ "T_D": 40,
386
+ "zero_shot": 0.57,
387
+ "acc10": 0.625,
388
+ "final": 0.74
389
+ },
390
+ "MGR": 1.5874010519681994
391
+ },
392
+ "5000": {
393
+ "cola": {
394
+ "T_D": 5,
395
+ "zero_shot": 0.4,
396
+ "acc10": 0.665,
397
+ "final": 0.665
398
+ },
399
+ "dbpedia": {
400
+ "T_D": 40,
401
+ "zero_shot": 0.355,
402
+ "acc10": 0.68,
403
+ "final": 0.945
404
+ },
405
+ "offensive": {
406
+ "T_D": 80,
407
+ "zero_shot": 0.575,
408
+ "acc10": 0.635,
409
+ "final": 0.715
410
+ },
411
+ "MGR": 1.2599210498948732
412
+ },
413
+ "6000": {
414
+ "cola": {
415
+ "T_D": 10,
416
+ "zero_shot": 0.395,
417
+ "acc10": 0.665,
418
+ "final": 0.665
419
+ },
420
+ "dbpedia": {
421
+ "T_D": 40,
422
+ "zero_shot": 0.355,
423
+ "acc10": 0.695,
424
+ "final": 0.94
425
+ },
426
+ "offensive": {
427
+ "T_D": 40,
428
+ "zero_shot": 0.555,
429
+ "acc10": 0.66,
430
+ "final": 0.77
431
+ },
432
+ "MGR": 1.2599210498948732
433
+ }
434
+ }
435
+ }
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": "<s>",
5
+ "cls_token": "<s>",
6
+ "eos_token": "</s>",
7
+ "errors": "replace",
8
+ "is_local": false,
9
+ "local_files_only": false,
10
+ "mask_token": "<mask>",
11
+ "model_max_length": 512,
12
+ "pad_token": "<pad>",
13
+ "sep_token": "</s>",
14
+ "tokenizer_class": "RobertaTokenizer",
15
+ "trim_offsets": true,
16
+ "unk_token": "<unk>"
17
+ }
training_log.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"hist": [{"step": 250, "score": 0.42499999999999993, "nll": 1.3864185810089111, "ece": 0.07763973646021138, "tr_score": 0.4333333333333333, "tr_nll": 1.3839653730392456, "fam": {"ag_news": 0.8333333333333334, "banking77": 0.0, "emotion": 0.3958333333333333, "imdb": 0.5, "mrpc": 0.6666666666666666, "news20": 0.125, "sms": 0.8958333333333334, "snli": 0.22916666666666666, "sst2": 0.4583333333333333, "yelp": 0.14583333333333334}, "wd": 0.5, "lam": 0.3, "gn": 4.74530553817749, "loss": 1.4470987440866285, "rho": null, "lr": 4.166666666666667e-05}, {"step": 500, "score": 0.4875, "nll": 1.2226603031158447, "ece": 0.059230080638856944, "tr_score": 0.4645833333333333, "tr_nll": 1.2430381774902344, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.0625, "emotion": 0.3958333333333333, "imdb": 0.5, "mrpc": 0.7291666666666666, "news20": 0.2916666666666667, "sms": 1.0, "snli": 0.375, "sst2": 0.4583333333333333, "yelp": 0.10416666666666667}, "wd": 0.5, "lam": 0.3, "gn": 4.94678258895874, "loss": 1.3388411353066019, "rho": null, "lr": 4.986344023965386e-05}, {"step": 750, "score": 0.5041666666666667, "nll": 1.1497793197631836, "ece": 0.05984721723943949, "tr_score": 0.4937500000000001, "tr_nll": 1.1202466487884521, "fam": {"ag_news": 0.8958333333333334, "banking77": 0.25, "emotion": 0.3958333333333333, "imdb": 0.5208333333333334, "mrpc": 0.6458333333333334, "news20": 0.3541666666666667, "sms": 1.0, "snli": 0.375, "sst2": 0.4583333333333333, "yelp": 0.14583333333333334}, "wd": 0.45, "lam": 0.3, "gn": 3.086398124694824, "loss": 1.2649021407320382, "rho": 1.1620062206951038, "lr": 4.931150598363494e-05}, {"step": 1000, "score": 0.6145833333333333, "nll": 0.9992086291313171, "ece": 0.08922299118712546, "tr_score": 0.5999999999999999, "tr_nll": 0.9506429433822632, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.2916666666666667, "emotion": 0.5625, "imdb": 0.7708333333333334, "mrpc": 0.6666666666666666, "news20": 0.4791666666666667, "sms": 1.0, "snli": 0.25, "sst2": 0.7708333333333334, "yelp": 0.3958333333333333}, "wd": 0.405, "lam": 0.3, "gn": 7.461432456970215, "loss": 1.1085828546282157, "rho": 0.8777702575734148, "lr": 4.834611651233304e-05}, {"step": 1250, "score": 0.7041666666666667, "nll": 0.7773005366325378, "ece": 0.060513773995141164, "tr_score": 0.6979166666666666, "tr_nll": 0.7319567203521729, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.3958333333333333, "emotion": 0.8333333333333334, "imdb": 0.8333333333333334, "mrpc": 0.7083333333333334, "news20": 0.5416666666666666, "sms": 0.9791666666666666, "snli": 0.4166666666666667, "sst2": 0.9375, "yelp": 0.4791666666666667}, "wd": 0.36450000000000005, "lam": 0.3, "gn": 4.511319160461426, "loss": 0.87960810418064, "rho": 0.8827748724431335, "lr": 4.6985571585149876e-05}, {"step": 1500, "score": 0.7229166666666667, "nll": 0.6783604025840759, "ece": 0.036758976274480416, "tr_score": 0.7374999999999999, "tr_nll": 0.627739429473877, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.5208333333333334, "emotion": 0.9166666666666666, "imdb": 0.8541666666666666, "mrpc": 0.6875, "news20": 0.5625, "sms": 1.0, "snli": 0.22916666666666666, "sst2": 0.9583333333333334, "yelp": 0.5416666666666666}, "wd": 0.32805000000000006, "lam": 0.3, "gn": 7.843607425689697, "loss": 0.8132543158575171, "rho": 0.9487538569865493, "lr": 4.5255661461418854e-05}, {"step": 1750, "score": 0.7458333333333333, "nll": 0.6375653743743896, "ece": 0.03906969136248029, "tr_score": 0.75625, "tr_nll": 0.5771324038505554, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.625, "emotion": 0.8958333333333334, "imdb": 0.8541666666666666, "mrpc": 0.7291666666666666, "news20": 0.5833333333333334, "sms": 1.0, "snli": 0.3125, "sst2": 0.9791666666666666, "yelp": 0.5625}, "wd": 0.2952450000000001, "lam": 0.3, "gn": 7.016384601593018, "loss": 0.7663959374513087, "rho": 0.9490588800899795, "lr": 4.3189178024614896e-05}, {"step": 2000, "score": 0.7729166666666666, "nll": 0.5967954397201538, "ece": 0.025282762618735424, "tr_score": 0.7583333333333333, "tr_nll": 0.5407644510269165, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.7291666666666666, "emotion": 0.9166666666666666, "imdb": 0.875, "mrpc": 0.7291666666666666, "news20": 0.5833333333333334, "sms": 1.0, "snli": 0.3958333333333333, "sst2": 0.9583333333333334, "yelp": 0.625}, "wd": 0.2657205000000001, "lam": 0.3, "gn": 9.855315208435059, "loss": 0.7069250917348392, "rho": 0.87758640404572, "lr": 4.0825293184962056e-05}, {"step": 2250, "score": 0.7875, "nll": 0.5712440013885498, "ece": 0.04493665626893442, "tr_score": 0.75625, "tr_nll": 0.5333563685417175, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.8125, "emotion": 0.8958333333333334, "imdb": 0.8541666666666666, "mrpc": 0.75, "news20": 0.5416666666666666, "sms": 1.0, "snli": 0.4791666666666667, "sst2": 0.9583333333333334, "yelp": 0.6666666666666666}, "wd": 0.23914845000000007, "lam": 0.3, "gn": 8.997612953186035, "loss": 0.6946176960095765, "rho": 0.9993132683013181, "lr": 3.8208816343334156e-05}, {"step": 2500, "score": 0.8104166666666666, "nll": 0.5312714576721191, "ece": 0.05300602965677776, "tr_score": 0.78125, "tr_nll": 0.5068899393081665, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.7916666666666666, "emotion": 0.875, "imdb": 0.8541666666666666, "mrpc": 0.7708333333333334, "news20": 0.5833333333333334, "sms": 1.0, "snli": 0.5833333333333334, "sst2": 1.0, "yelp": 0.6875}, "wd": 0.21523360500000008, "lam": 0.3, "gn": 8.83574390411377, "loss": 0.6346616129986626, "rho": 1.499656634150659, "lr": 3.5389344991836974e-05}, {"step": 2750, "score": 0.8083333333333332, "nll": 0.49808335304260254, "ece": 0.04100577929057176, "tr_score": 0.7875, "tr_nll": 0.47457775473594666, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.7708333333333334, "emotion": 0.875, "imdb": 0.8541666666666666, "mrpc": 0.75, "news20": 0.6041666666666666, "sms": 1.0, "snli": 0.6875, "sst2": 0.9583333333333334, "yelp": 0.625}, "wd": 0.19371024450000007, "lam": 0.3, "gn": 6.362133979797363, "loss": 0.58079190026933, "rho": 1.5049839023584053, "lr": 3.242032455214346e-05}, {"step": 3000, "score": 0.8291666666666666, "nll": 0.47499704360961914, "ece": 0.04109802747455738, "tr_score": 0.8020833333333333, "tr_nll": 0.4411352574825287, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.8333333333333334, "emotion": 0.8958333333333334, "imdb": 0.875, "mrpc": 0.7916666666666666, "news20": 0.625, "sms": 1.0, "snli": 0.75, "sst2": 0.9375, "yelp": 0.625}, "wd": 0.17433922005000008, "lam": 0.3, "gn": 9.050947189331055, "loss": 0.5507215846613893, "rho": 1.2660459721916015, "lr": 2.9358035273127483e-05}, {"step": 3250, "score": 0.8229166666666667, "nll": 0.45618176460266113, "ece": 0.03542887705067793, "tr_score": 0.8125, "tr_nll": 0.4283953309059143, "fam": {"ag_news": 0.9375, "banking77": 0.875, "emotion": 0.9166666666666666, "imdb": 0.8541666666666666, "mrpc": 0.7708333333333334, "news20": 0.6041666666666666, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9166666666666666, "yelp": 0.625}, "wd": 0.15690529804500009, "lam": 0.3001, "gn": 7.896519184112549, "loss": 0.5361467864703793, "rho": 0.9781872430895555, "lr": 2.6260525391993023e-05}, {"step": 3500, "score": 0.8229166666666666, "nll": 0.44824162125587463, "ece": 0.04027308725441499, "tr_score": 0.8333333333333333, "tr_nll": 0.41801562905311584, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.8541666666666666, "emotion": 0.9166666666666666, "imdb": 0.8541666666666666, "mrpc": 0.7916666666666666, "news20": 0.5833333333333334, "sms": 1.0, "snli": 0.7708333333333334, "sst2": 0.9375, "yelp": 0.6041666666666666}, "wd": 0.14121476824050008, "lam": 0.3001, "gn": 7.859862804412842, "loss": 0.5212966933069149, "rho": 1.2275311193516978, "lr": 2.3186510781715892e-05}, {"step": 3750, "score": 0.825, "nll": 0.44514527916908264, "ece": 0.0378269159855942, "tr_score": 0.8416666666666668, "tr_nll": 0.41067466139793396, "fam": {"ag_news": 0.9166666666666666, "banking77": 0.8333333333333334, "emotion": 0.9375, "imdb": 0.8541666666666666, "mrpc": 0.7708333333333334, "news20": 0.5833333333333334, "sms": 1.0, "snli": 0.7708333333333334, "sst2": 0.9375, "yelp": 0.6458333333333334}, "wd": 0.12709329141645007, "lam": 0.3001, "gn": 4.868711471557617, "loss": 0.5031073578348193, "rho": 0.9962497321208292, "lr": 2.0194261942894628e-05}, {"step": 4000, "score": 0.83125, "nll": 0.43428102135658264, "ece": 0.04779160485292472, "tr_score": 0.8479166666666667, "tr_nll": 0.4039950370788574, "fam": {"ag_news": 0.9375, "banking77": 0.8333333333333334, "emotion": 0.9375, "imdb": 0.8541666666666666, "mrpc": 0.7708333333333334, "news20": 0.6458333333333334, "sms": 1.0, "snli": 0.75, "sst2": 0.9375, "yelp": 0.6458333333333334}, "wd": 0.12709329141645007, "lam": 0.3001, "gn": 6.261903762817383, "loss": 0.5046875660977973, "rho": 0.709019548630384, "lr": 1.7340499438004994e-05}, {"step": 4250, "score": 0.8375, "nll": 0.4272522032260895, "ece": 0.037357999570667745, "tr_score": 0.8416666666666666, "tr_nll": 0.397662878036499, "fam": {"ag_news": 0.9375, "banking77": 0.8541666666666666, "emotion": 0.9375, "imdb": 0.8541666666666666, "mrpc": 0.7916666666666666, "news20": 0.6458333333333334, "sms": 1.0, "snli": 0.75, "sst2": 0.9583333333333334, "yelp": 0.6458333333333334}, "wd": 0.11438396227480506, "lam": 0.3001, "gn": 10.974567413330078, "loss": 0.47385876453106374, "rho": 1.167748460619184, "lr": 1.4679318706019013e-05}, {"step": 4500, "score": 0.8375, "nll": 0.42399367690086365, "ece": 0.038577316980808976, "tr_score": 0.84375, "tr_nll": 0.39106932282447815, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.8541666666666666, "emotion": 0.9166666666666666, "imdb": 0.8541666666666666, "mrpc": 0.7916666666666666, "news20": 0.6666666666666666, "sms": 1.0, "snli": 0.75, "sst2": 0.9583333333333334, "yelp": 0.625}, "wd": 0.10294556604732455, "lam": 0.3001, "gn": 6.629716396331787, "loss": 0.4687045077363484, "rho": 1.1388838315746999, "lr": 1.2261164638420832e-05}, {"step": 4750, "score": 0.8395833333333332, "nll": 0.42487725615501404, "ece": 0.034516340897728995, "tr_score": 0.84375, "tr_nll": 0.38701558113098145, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.8541666666666666, "mrpc": 0.7916666666666666, "news20": 0.6458333333333334, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6458333333333334}, "wd": 0.0926510094425921, "lam": 0.3001, "gn": 7.820315361022949, "loss": 0.45272200221767106, "rho": 0.8165412590434078, "lr": 1.013187535438278e-05}, {"step": 5000, "score": 0.8395833333333332, "nll": 0.4237828850746155, "ece": 0.0383824546821416, "tr_score": 0.8458333333333334, "tr_nll": 0.3849530816078186, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.875, "mrpc": 0.7916666666666666, "news20": 0.625, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6458333333333334}, "wd": 0.11581376180324011, "lam": 0.3001, "gn": 6.347634315490723, "loss": 0.4564408298368683, "rho": 0.2992874578055718, "lr": 8.331813301137644e-06}, {"step": 5250, "score": 0.8395833333333332, "nll": 0.4211656451225281, "ece": 0.04211962077145778, "tr_score": 0.8541666666666667, "tr_nll": 0.378847599029541, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.8541666666666666, "mrpc": 0.7916666666666666, "news20": 0.6458333333333334, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6458333333333334}, "wd": 0.11581376180324011, "lam": 0.3001, "gn": 6.547802448272705, "loss": 0.4516936281062966, "rho": 0.4149458703356097, "lr": 6.8951001502612065e-06}, {"step": 5500, "score": 0.8416666666666666, "nll": 0.419617235660553, "ece": 0.04883364103734491, "tr_score": 0.85, "tr_nll": 0.37390393018722534, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.875, "mrpc": 0.7916666666666666, "news20": 0.6458333333333334, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6458333333333334}, "wd": 0.11581376180324011, "lam": 0.3001, "gn": 7.733129501342773, "loss": 0.43449673415669426, "rho": 0.42180815916788295, "lr": 5.8489699930418664e-06}, {"step": 5750, "score": 0.84375, "nll": 0.41734427213668823, "ece": 0.042368609768648896, "tr_score": 0.8479166666666668, "tr_nll": 0.37107983231544495, "fam": {"ag_news": 0.9375, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.875, "mrpc": 0.7916666666666666, "news20": 0.6666666666666666, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6666666666666666}, "wd": 0.11581376180324011, "lam": 0.3001, "gn": 9.12564754486084, "loss": 0.430490410395239, "rho": 0.3675093773257097, "lr": 5.213253095656177e-06}, {"step": 6000, "score": 0.84375, "nll": 0.4162411093711853, "ece": 0.04647985563303032, "tr_score": 0.8458333333333334, "tr_nll": 0.36772745847702026, "fam": {"ag_news": 0.9583333333333334, "banking77": 0.875, "emotion": 0.9375, "imdb": 0.875, "mrpc": 0.7708333333333334, "news20": 0.6666666666666666, "sms": 1.0, "snli": 0.7291666666666666, "sst2": 0.9583333333333334, "yelp": 0.6666666666666666}, "wd": 0.11581376180324011, "lam": 0.3001, "gn": 8.71489429473877, "loss": 0.4269631357885679, "rho": 0.5861776263692953, "lr": 5e-06}], "probes": [{"step": 0, "fam": "cola", "curve": {"0": [0.46, 0.6933133602142334], "5": [0.665, 0.6618085503578186], "10": [0.665, 0.6377608776092529], "20": [0.665, 0.6427217721939087], "40": [0.665, 0.6488895416259766]}}, {"step": 0, "fam": "dbpedia", "curve": {"0": [0.115, 2.635910749435425], "5": [0.045, 2.6632566452026367], "10": [0.045, 2.696634531021118], "20": [0.045, 2.638427734375], "40": [0.045, 2.638758897781372]}}, {"step": 0, "fam": "offensive", "curve": {"0": [0.645, 0.6905834674835205], "5": [0.64, 0.6852070093154907], "10": [0.64, 0.6692162156105042], "20": [0.64, 0.6649163961410522], "40": [0.64, 0.7305782437324524]}}, {"step": 1000, "fam": "cola", "curve": {"0": [0.55, 0.6800724864006042], "5": [0.665, 0.6440149545669556], "10": [0.635, 0.6801218390464783], "20": [0.665, 0.6502220630645752], "40": [0.665, 0.6403223276138306]}}, {"step": 1000, "fam": "dbpedia", "curve": {"0": [0.34, 2.2151966094970703], "5": [0.45, 1.7422538995742798], "10": [0.525, 1.1715236902236938], "20": [0.795, 0.5880295634269714], "40": [0.93, 0.17617054283618927]}}, {"step": 1000, "fam": "offensive", "curve": {"0": [0.64, 0.6211206912994385], "5": [0.66, 0.6001800298690796], "10": [0.64, 0.6511775851249695], "20": [0.665, 0.7224280834197998], "40": [0.745, 1.5302973985671997]}}, {"step": 2000, "fam": "cola", "curve": {"0": [0.4, 1.2561464309692383], "5": [0.355, 1.106208324432373], "10": [0.61, 0.6751940846443176], "20": [0.665, 0.6598563194274902], "40": [0.665, 0.6479304432868958]}}, {"step": 2000, "fam": "dbpedia", "curve": {"0": [0.305, 2.196328639984131], "5": [0.575, 1.3923637866973877], "10": [0.7, 0.8865454792976379], "20": [0.86, 0.5139257311820984], "40": [0.955, 0.13148292899131775]}}, {"step": 2000, "fam": "offensive", "curve": {"0": [0.625, 0.6901967525482178], "5": [0.655, 0.6434993743896484], "10": [0.69, 0.5594972372055054], "20": [0.71, 0.6698970198631287], "40": [0.755, 0.5170150995254517]}}, {"step": 3000, "fam": "cola", "curve": {"0": [0.4, 1.342832326889038], "5": [0.62, 0.6754884123802185], "10": [0.665, 0.6443837881088257], "20": [0.665, 0.6436806321144104], "40": [0.665, 0.6559992432594299]}}, {"step": 3000, "fam": "dbpedia", "curve": {"0": [0.245, 2.18424916267395], "5": [0.455, 1.5412259101867676], "10": [0.64, 1.0229398012161255], "20": [0.885, 0.5090384483337402], "40": [0.975, 0.0869322121143341]}}, {"step": 3000, "fam": "offensive", "curve": {"0": [0.59, 0.8314429521560669], "5": [0.66, 0.8946431875228882], "10": [0.685, 0.6292023062705994], "20": [0.68, 0.6085357069969177], "40": [0.65, 0.5966840982437134]}}, {"step": 4000, "fam": "cola", "curve": {"0": [0.41, 1.2136904001235962], "5": [0.635, 0.6475875973701477], "10": [0.665, 0.6374750733375549], "20": [0.665, 0.6445882320404053], "40": [0.665, 0.6596666574478149]}}, {"step": 4000, "fam": "dbpedia", "curve": {"0": [0.305, 2.2257626056671143], "5": [0.575, 1.4236860275268555], "10": [0.71, 0.984937310218811], "20": [0.845, 0.6737918853759766], "40": [0.975, 0.105534628033638]}}, {"step": 4000, "fam": "offensive", "curve": {"0": [0.57, 0.8187274932861328], "5": [0.595, 0.7490072846412659], "10": [0.625, 0.72652268409729], "20": [0.645, 0.619418740272522], "40": [0.74, 0.8135643005371094]}}, {"step": 5000, "fam": "cola", "curve": {"0": [0.4, 1.2741897106170654], "5": [0.64, 0.6492821574211121], "10": [0.665, 0.6398915648460388], "20": [0.665, 0.6444342136383057], "40": [0.665, 0.6411840319633484]}}, {"step": 5000, "fam": "dbpedia", "curve": {"0": [0.355, 2.148179531097412], "5": [0.585, 1.378219485282898], "10": [0.68, 1.0227428674697876], "20": [0.87, 0.4382198750972748], "40": [0.945, 0.20549844205379486]}}, {"step": 5000, "fam": "offensive", "curve": {"0": [0.575, 0.9428966045379639], "5": [0.675, 0.7043898701667786], "10": [0.635, 0.6242003440856934], "20": [0.695, 0.5720301270484924], "40": [0.715, 0.5336439609527588]}}, {"step": 6000, "fam": "cola", "curve": {"0": [0.395, 1.3663846254348755], "5": [0.625, 0.6492061018943787], "10": [0.665, 0.6497192978858948], "20": [0.665, 0.6383249759674072], "40": [0.665, 0.6384083032608032]}}, {"step": 6000, "fam": "dbpedia", "curve": {"0": [0.355, 2.106869697570801], "5": [0.435, 1.3792909383773804], "10": [0.695, 1.0408819913864136], "20": [0.895, 0.3912413418292999], "40": [0.94, 0.190094992518425]}}, {"step": 6000, "fam": "offensive", "curve": {"0": [0.555, 0.9884564280509949], "5": [0.65, 0.7950121164321899], "10": [0.66, 0.5845534801483154], "20": [0.695, 0.9688383340835571], "40": [0.77, 0.5907872319221497]}}]}