usrnotfound101 commited on
Commit
48a7823
·
verified ·
1 Parent(s): 04373fa

decoding results

Browse files
Files changed (4) hide show
  1. common.py +837 -0
  2. decoding.py +558 -0
  3. decoding_results.json +214 -217
  4. generations.json +0 -0
common.py ADDED
@@ -0,0 +1,837 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =============================================================================
2
+ # common.py - shared utilities for ANLP Assignment 2
3
+ # * device / AMP / seeding helpers
4
+ # * Hugging Face Hub helpers (token from Kaggle secrets, push, download)
5
+ # * decoder-only Transformer with pluggable FFN (dense MLP or MoE)
6
+ # * KV-cache + batched greedy/sampled generation (uses model.forward only)
7
+ # =============================================================================
8
+ import os, math, json, time, random, contextlib, shutil, re, sys, glob, hashlib
9
+ from dataclasses import dataclass, asdict, field, fields
10
+ from typing import Optional, List, Dict
11
+
12
+ import numpy as np
13
+ import torch
14
+ import torch.nn as nn
15
+ from torch.nn import functional as F
16
+
17
+ # -----------------------------------------------------------------------------
18
+ # basic helpers
19
+ # -----------------------------------------------------------------------------
20
+ def seed_everything(seed: int):
21
+ random.seed(seed); np.random.seed(seed); torch.manual_seed(seed)
22
+ if torch.cuda.is_available():
23
+ torch.cuda.manual_seed_all(seed)
24
+
25
+
26
+ def get_device():
27
+ return torch.device("cuda" if torch.cuda.is_available() else "cpu")
28
+
29
+
30
+ def amp_dtype(device):
31
+ """bf16 on Ampere+ (A100/H100/L4), fp16 on Kaggle's T4/P100, None on CPU."""
32
+ if device.type != "cuda":
33
+ return None
34
+ major, _ = torch.cuda.get_device_capability(device)
35
+ return torch.bfloat16 if major >= 8 else torch.float16
36
+
37
+
38
+ def autocast_ctx(device, dtype):
39
+ if dtype is None:
40
+ return contextlib.nullcontext()
41
+ return torch.autocast(device_type="cuda", dtype=dtype)
42
+
43
+
44
+ def make_grad_scaler(device, dtype):
45
+ enabled = (device.type == "cuda" and dtype == torch.float16)
46
+ try:
47
+ return torch.amp.GradScaler("cuda", enabled=enabled)
48
+ except Exception: # older torch
49
+ return torch.cuda.amp.GradScaler(enabled=enabled)
50
+
51
+
52
+ def fmt_num(n):
53
+ for unit, div in (("B", 1e9), ("M", 1e6), ("K", 1e3)):
54
+ if abs(n) >= div:
55
+ return f"{n/div:.2f}{unit}"
56
+ return str(n)
57
+
58
+
59
+ def save_json(obj, path):
60
+ os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
61
+ with open(path, "w", encoding="utf-8") as f:
62
+ json.dump(obj, f, indent=2, ensure_ascii=False, default=_json_default)
63
+
64
+
65
+ def load_json(path):
66
+ with open(path, encoding="utf-8") as f:
67
+ return json.load(f)
68
+
69
+
70
+ def _json_default(o):
71
+ if isinstance(o, (np.integer,)): return int(o)
72
+ if isinstance(o, (np.floating,)): return float(o)
73
+ if isinstance(o, np.ndarray): return o.tolist()
74
+ if isinstance(o, torch.Tensor): return o.tolist()
75
+ if isinstance(o, torch.dtype): return str(o)
76
+ return str(o)
77
+
78
+
79
+ def gpu_name():
80
+ return torch.cuda.get_device_name(0) if torch.cuda.is_available() else "cpu"
81
+
82
+
83
+ # -----------------------------------------------------------------------------
84
+ # Hugging Face Hub helpers
85
+ # -----------------------------------------------------------------------------
86
+ TOKENIZER_REPO = "anlp-a2-tokenizers" # holds mt_spm.model and lm_spm.model
87
+
88
+
89
+ def get_hf_token():
90
+ """HF token from env var HF_TOKEN or Kaggle secret named HF_TOKEN."""
91
+ tok = os.environ.get("HF_TOKEN")
92
+ if tok:
93
+ return tok
94
+ try:
95
+ from kaggle_secrets import UserSecretsClient
96
+ tok = UserSecretsClient().get_secret("HF_TOKEN")
97
+ if tok:
98
+ os.environ["HF_TOKEN"] = tok
99
+ return tok
100
+ except Exception:
101
+ pass
102
+ return None
103
+
104
+
105
+ def hf_login(token):
106
+ if not token:
107
+ print("[hf] no HF token found - add a Kaggle secret named HF_TOKEN (write access).")
108
+ return
109
+ try:
110
+ from huggingface_hub import login
111
+ login(token=token, add_to_git_credential=False)
112
+ except Exception as e:
113
+ print("[hf] login failed:", e)
114
+
115
+
116
+ def hf_whoami(token):
117
+ from huggingface_hub import HfApi
118
+ return HfApi(token=token).whoami()["name"]
119
+
120
+
121
+ def hf_resolve_user(cfg_user, token):
122
+ if cfg_user:
123
+ return cfg_user
124
+ if token:
125
+ try:
126
+ return hf_whoami(token)
127
+ except Exception as e:
128
+ print("[hf] whoami failed:", e)
129
+ return None
130
+
131
+
132
+ def _retry(fn, tries=4, wait=10):
133
+ last = None
134
+ for i in range(tries):
135
+ try:
136
+ return fn()
137
+ except Exception as e:
138
+ last = e
139
+ print(f"[hf] attempt {i+1}/{tries} failed: {e}")
140
+ time.sleep(wait * (i + 1))
141
+ raise last
142
+
143
+
144
+ def hf_create_repo(repo_id, token, private=False, repo_type="model"):
145
+ from huggingface_hub import HfApi
146
+ api = HfApi(token=token)
147
+ _retry(lambda: api.create_repo(repo_id, private=private, exist_ok=True, repo_type=repo_type))
148
+ return api
149
+
150
+
151
+ def hf_push_folder(folder, repo_id, token, private=False, message="upload",
152
+ path_in_repo=None, run_as_future=False, repo_type="model"):
153
+ api = hf_create_repo(repo_id, token, private, repo_type)
154
+ kw = dict(folder_path=folder, repo_id=repo_id, repo_type=repo_type,
155
+ commit_message=message)
156
+ if path_in_repo:
157
+ kw["path_in_repo"] = path_in_repo
158
+ if run_as_future:
159
+ return api.upload_folder(run_as_future=True, **kw)
160
+ return _retry(lambda: api.upload_folder(**kw))
161
+
162
+
163
+ def hf_push_file(local_path, path_in_repo, repo_id, token, private=False,
164
+ message="upload", run_as_future=False, repo_type="model"):
165
+ api = hf_create_repo(repo_id, token, private, repo_type)
166
+ kw = dict(path_or_fileobj=local_path, path_in_repo=path_in_repo, repo_id=repo_id,
167
+ repo_type=repo_type, commit_message=message)
168
+ if run_as_future:
169
+ return api.upload_file(run_as_future=True, **kw)
170
+ return _retry(lambda: api.upload_file(**kw))
171
+
172
+
173
+ def hf_try_download(repo_id, filename, token=None, repo_type="model", force=False):
174
+ """Returns a local path, or None if the repo/file does not exist."""
175
+ try:
176
+ from huggingface_hub import hf_hub_download
177
+ return hf_hub_download(repo_id=repo_id, filename=filename, token=token,
178
+ repo_type=repo_type, force_download=force)
179
+ except Exception as e:
180
+ print(f"[hf] could not download {repo_id}/{filename}: {type(e).__name__}")
181
+ return None
182
+
183
+
184
+ def hf_list_files(repo_id, token=None, repo_type="model"):
185
+ try:
186
+ from huggingface_hub import HfApi
187
+ return HfApi(token=token).list_repo_files(repo_id, repo_type=repo_type)
188
+ except Exception:
189
+ return []
190
+
191
+
192
+ # -----------------------------------------------------------------------------
193
+ # Weights & Biases (assignment: upload training logs to WandB, public links in README/report)
194
+ # Fail-soft: if wandb is missing / not logged in, training continues and logs only to JSON.
195
+ # Auth: `wandb login` or WANDB_API_KEY (Kaggle secret of that name is picked up too).
196
+ # WANDB_MODE=offline logs locally (sync later with `wandb sync`); cfg["wandb"]=False disables.
197
+ # -----------------------------------------------------------------------------
198
+ WANDB_PROJECT = "anlp-a2"
199
+
200
+
201
+ def _wandb_key():
202
+ key = os.environ.get("WANDB_API_KEY")
203
+ if key:
204
+ return key
205
+ try:
206
+ from kaggle_secrets import UserSecretsClient
207
+ key = UserSecretsClient().get_secret("WANDB_API_KEY")
208
+ if key:
209
+ os.environ["WANDB_API_KEY"] = key
210
+ return key
211
+ except Exception:
212
+ return None
213
+
214
+
215
+ def wandb_init(cfg, name, config, tags=(), group=None, notes=None, stable_id=False):
216
+ """Start (or resume, with the same stable id -> requeued jobs continue one run) a W&B run.
217
+ Returns the run, or None when disabled / unavailable."""
218
+ if not cfg.get("wandb", True):
219
+ return None
220
+ try:
221
+ import wandb
222
+ _wandb_key()
223
+ run = wandb.init(project=cfg.get("wandb_project") or WANDB_PROJECT, entity=cfg.get("wandb_entity"),
224
+ name=name, config=config,
225
+ **(dict(id=re.sub(r"[^A-Za-z0-9_-]", "-", name), resume="allow") if stable_id else {}),
226
+ tags=list(tags), group=group, notes=notes,
227
+ settings=wandb.Settings(init_timeout=120, console="off"))
228
+ print("[wandb] run:", getattr(run, "url", None) or "(offline - `wandb sync <dir>` later)")
229
+ return run
230
+ except Exception as e:
231
+ print(f"[wandb] disabled ({type(e).__name__}: {str(e)[:120]}) - logging to JSON only")
232
+ return None
233
+
234
+
235
+ def wandb_log(run, data, step=None):
236
+ if run is None:
237
+ return
238
+ try:
239
+ run.log({k: v for k, v in data.items() if v is not None}, step=step)
240
+ except Exception as e:
241
+ print("[wandb] log failed:", e)
242
+
243
+
244
+ def wandb_images(run, paths, step=None):
245
+ """paths: {key: png_path}."""
246
+ if run is None:
247
+ return
248
+ try:
249
+ import wandb
250
+ wandb_log(run, {k: wandb.Image(v) for k, v in paths.items() if os.path.exists(v)}, step)
251
+ except Exception as e:
252
+ print("[wandb] image log failed:", e)
253
+
254
+
255
+ def wandb_finish(run, summary=None):
256
+ if run is None:
257
+ return
258
+ try:
259
+ for k, v in (summary or {}).items():
260
+ run.summary[k] = v
261
+ run.finish()
262
+ except Exception as e:
263
+ print("[wandb] finish failed:", e)
264
+
265
+
266
+ def expert_load_scalars(prefix, loads):
267
+ """loads: [layer][expert] fractions -> flat {prefix/L{l}_E{e}: x} for W&B."""
268
+ return {f"{prefix}/L{l}_E{e}": float(x) for l, row in enumerate(loads) for e, x in enumerate(row)}
269
+
270
+
271
+ # -----------------------------------------------------------------------------
272
+ # Model
273
+ # -----------------------------------------------------------------------------
274
+ @dataclass
275
+ class ModelConfig:
276
+ vocab_size: int = 32000
277
+ block_size: int = 256
278
+ n_layer: int = 6
279
+ n_head: int = 8
280
+ n_embd: int = 512
281
+ dropout: float = 0.0
282
+ bias: bool = False
283
+ # ---- feed-forward ----
284
+ ffn_type: str = "dense" # "dense" | "moe"
285
+ mlp_hidden: int = 0 # dense hidden size (0 -> 4 * n_embd)
286
+ n_experts: int = 4 # number of *routed* experts
287
+ n_shared: int = 0 # number of always-on shared experts
288
+ top_k: int = 1 # routed experts active per token
289
+ expert_hidden: int = 0 # hidden size of each expert (routed & shared)
290
+ aux_loss_coef: float = 0.01 # Switch-style load-balancing loss
291
+ z_loss_coef: float = 1e-3 # router z-loss (ST-MoE)
292
+
293
+ def to_dict(self):
294
+ return asdict(self)
295
+
296
+ @classmethod
297
+ def from_dict(cls, d):
298
+ names = {f.name for f in fields(cls)}
299
+ return cls(**{k: v for k, v in d.items() if k in names})
300
+
301
+
302
+ # The five FFN variants of Part 1. H = 4 * d is the dense hidden size.
303
+ # dense : 1 MLP (d -> H -> d) total = active = 2dH
304
+ # moe_4e_top1 : 4 experts of hidden H/4, top-1 total = 2dH, active = 2dH/4
305
+ # moe_4e_top2 : 4 experts of hidden H/4, top-2 total = 2dH, active = 2dH/2
306
+ # moe_1s3r_top1 : 1 shared + 3 routed (hidden H/4), top-1 total = 2dH, active = 2dH/2
307
+ # moe_4e_top2_active: 4 experts of hidden H/2, top-2 total = 4dH, active = 2dH (= dense)
308
+ FFN_VARIANTS = ["dense", "moe_4e_top1", "moe_4e_top2", "moe_1s3r_top1", "moe_4e_top2_active"]
309
+
310
+ FFN_VARIANT_DESCRIPTIONS = {
311
+ "dense": "Standard 2-layer MLP (d -> 4d -> d)",
312
+ "moe_4e_top1": "MoE: 4 experts (hidden d), top-1 routing - same total params as dense",
313
+ "moe_4e_top2": "MoE: 4 experts (hidden d), top-2 routing - same total params as dense",
314
+ "moe_1s3r_top1": "MoE: 1 shared + 3 routed experts (hidden d), top-1 routed - same total params as dense",
315
+ "moe_4e_top2_active": "MoE: 4 experts (hidden 2d), top-2 routing - same ACTIVE params as dense",
316
+ }
317
+
318
+
319
+ def ffn_variant_kwargs(name, n_embd):
320
+ H = 4 * n_embd
321
+ if name == "dense":
322
+ return dict(ffn_type="dense", mlp_hidden=H)
323
+ if name == "moe_4e_top1":
324
+ return dict(ffn_type="moe", n_experts=4, n_shared=0, top_k=1, expert_hidden=H // 4)
325
+ if name == "moe_4e_top2":
326
+ return dict(ffn_type="moe", n_experts=4, n_shared=0, top_k=2, expert_hidden=H // 4)
327
+ if name == "moe_1s3r_top1":
328
+ return dict(ffn_type="moe", n_experts=3, n_shared=1, top_k=1, expert_hidden=H // 4)
329
+ if name == "moe_4e_top2_active":
330
+ return dict(ffn_type="moe", n_experts=4, n_shared=0, top_k=2, expert_hidden=H // 2)
331
+ raise ValueError(f"unknown FFN variant {name}")
332
+
333
+
334
+ class LayerNorm(nn.Module):
335
+ def __init__(self, ndim, bias):
336
+ super().__init__()
337
+ self.weight = nn.Parameter(torch.ones(ndim))
338
+ self.bias = nn.Parameter(torch.zeros(ndim)) if bias else None
339
+
340
+ def forward(self, x):
341
+ return F.layer_norm(x, self.weight.shape, self.weight, self.bias, 1e-5)
342
+
343
+
344
+ class KVCache:
345
+ """Simple per-layer key/value cache. Tensors are (B, n_head, T, head_dim)."""
346
+ def __init__(self, n_layer):
347
+ self.k = [None] * n_layer
348
+ self.v = [None] * n_layer
349
+
350
+ @property
351
+ def length(self):
352
+ return 0 if self.k[0] is None else self.k[0].shape[2]
353
+
354
+ def update(self, i, k, v):
355
+ if self.k[i] is None:
356
+ self.k[i], self.v[i] = k, v
357
+ else:
358
+ self.k[i] = torch.cat([self.k[i], k], dim=2)
359
+ self.v[i] = torch.cat([self.v[i], v], dim=2)
360
+ return self.k[i], self.v[i]
361
+
362
+ def reorder(self, idx):
363
+ for i in range(len(self.k)):
364
+ if self.k[i] is not None:
365
+ self.k[i] = self.k[i].index_select(0, idx)
366
+ self.v[i] = self.v[i].index_select(0, idx)
367
+
368
+
369
+ class CausalSelfAttention(nn.Module):
370
+ def __init__(self, cfg: ModelConfig):
371
+ super().__init__()
372
+ assert cfg.n_embd % cfg.n_head == 0
373
+ self.c_attn = nn.Linear(cfg.n_embd, 3 * cfg.n_embd, bias=cfg.bias)
374
+ self.c_proj = nn.Linear(cfg.n_embd, cfg.n_embd, bias=cfg.bias)
375
+ self.resid_dropout = nn.Dropout(cfg.dropout)
376
+ self.n_head, self.n_embd, self.dropout = cfg.n_head, cfg.n_embd, cfg.dropout
377
+
378
+ def forward(self, x, attn_mask=None, cache=None, layer_idx=0):
379
+ B, T, C = x.size()
380
+ q, k, v = self.c_attn(x).split(self.n_embd, dim=2)
381
+ hs = C // self.n_head
382
+ q = q.view(B, T, self.n_head, hs).transpose(1, 2)
383
+ k = k.view(B, T, self.n_head, hs).transpose(1, 2)
384
+ v = v.view(B, T, self.n_head, hs).transpose(1, 2)
385
+ if cache is not None:
386
+ k, v = cache.update(layer_idx, k, v)
387
+ dp = self.dropout if self.training else 0.0
388
+ if attn_mask is None:
389
+ # plain causal attention; valid only when queries and keys are aligned
390
+ assert T == 1 or k.shape[2] == T, "pass an explicit attn_mask when prefilling a non-empty cache"
391
+ y = F.scaled_dot_product_attention(q, k, v, dropout_p=dp, is_causal=(T > 1))
392
+ else:
393
+ y = F.scaled_dot_product_attention(q, k, v, attn_mask=attn_mask, dropout_p=dp)
394
+ y = y.transpose(1, 2).contiguous().view(B, T, C)
395
+ return self.resid_dropout(self.c_proj(y))
396
+
397
+
398
+ class MLP(nn.Module):
399
+ """Standard 2-layer GELU MLP: d -> hidden -> d."""
400
+ def __init__(self, n_embd, hidden, bias=False, dropout=0.0):
401
+ super().__init__()
402
+ self.c_fc = nn.Linear(n_embd, hidden, bias=bias)
403
+ self.gelu = nn.GELU()
404
+ self.c_proj = nn.Linear(hidden, n_embd, bias=bias)
405
+ self.dropout = nn.Dropout(dropout)
406
+
407
+ def forward(self, x):
408
+ return self.dropout(self.c_proj(self.gelu(self.c_fc(x))))
409
+
410
+
411
+ class MoE(nn.Module):
412
+ """Token-choice top-k Mixture-of-Experts, drop-in replacement for MLP.
413
+
414
+ * router: linear d -> n_experts, softmax over routed experts
415
+ * top-1: output = p_e * E_e(x) (Switch Transformer; keeps router gradient)
416
+ * top-k (k>1): gates renormalised over the chosen k (Mixtral-style)
417
+ * shared experts (DeepSeek-MoE style) are applied to every token and added
418
+ * aux loss = coef * E * sum_e f_e * P_e (+ router z-loss), computed on real tokens only
419
+ * optional tracking of which experts are chosen, per token group (language / segment)
420
+ """
421
+ def __init__(self, cfg: ModelConfig):
422
+ super().__init__()
423
+ h = cfg.expert_hidden or (4 * cfg.n_embd // max(cfg.n_experts + cfg.n_shared, 1))
424
+ self.n_experts, self.top_k, self.n_shared = cfg.n_experts, cfg.top_k, cfg.n_shared
425
+ self.aux_coef, self.z_coef = cfg.aux_loss_coef, cfg.z_loss_coef
426
+ self.experts = nn.ModuleList([MLP(cfg.n_embd, h, cfg.bias, cfg.dropout) for _ in range(cfg.n_experts)])
427
+ self.shared = nn.ModuleList([MLP(cfg.n_embd, h, cfg.bias, cfg.dropout) for _ in range(cfg.n_shared)])
428
+ self.router = nn.Linear(cfg.n_embd, cfg.n_experts, bias=False)
429
+ self.aux_loss = torch.zeros(())
430
+ # tracking state
431
+ self.track = False
432
+ self.n_groups = 0
433
+ self.counts = None # (n_groups, n_experts) long
434
+ self.token_groups = None # (N,) long, -1 = ignore
435
+ self.load_accum = None # (n_experts,) running fraction for training logs
436
+ self.load_steps = 0
437
+
438
+ def reset_tracking(self, n_groups):
439
+ self.n_groups = n_groups
440
+ self.counts = torch.zeros(n_groups, self.n_experts, dtype=torch.long, device=self.router.weight.device)
441
+
442
+ def forward(self, x, tok_mask=None):
443
+ B, T, C = x.shape
444
+ xf = x.reshape(-1, C)
445
+ N = xf.shape[0]
446
+ if tok_mask is not None:
447
+ sel = tok_mask.reshape(-1).nonzero(as_tuple=True)[0]
448
+ xs = xf.index_select(0, sel)
449
+ else:
450
+ sel, xs = None, xf
451
+
452
+ logits = self.router(xs).float() # (n, E)
453
+ probs = F.softmax(logits, dim=-1)
454
+ topv, topi = probs.topk(self.top_k, dim=-1) # (n, k)
455
+ if self.top_k > 1:
456
+ topv = topv / topv.sum(-1, keepdim=True)
457
+
458
+ out = torch.zeros(xs.shape, dtype=torch.float32, device=xs.device)
459
+ for e, expert in enumerate(self.experts):
460
+ tok, slot = (topi == e).nonzero(as_tuple=True)
461
+ if tok.numel() == 0:
462
+ continue
463
+ y = expert(xs.index_select(0, tok)).float() * topv[tok, slot].unsqueeze(-1)
464
+ out.index_add_(0, tok, y)
465
+ for s in self.shared:
466
+ out = out + s(xs).float()
467
+
468
+ if self.training and xs.shape[0] > 0:
469
+ E = self.n_experts
470
+ f = F.one_hot(topi, E).sum(1).float().mean(0) / self.top_k # fraction of assignments
471
+ P = probs.mean(0) # mean router prob
472
+ aux = E * (f * P).sum()
473
+ z = torch.logsumexp(logits, dim=-1).pow(2).mean()
474
+ self.aux_loss = self.aux_coef * aux + self.z_coef * z
475
+ with torch.no_grad():
476
+ self.load_accum = f.detach() if self.load_accum is None else self.load_accum + f.detach()
477
+ self.load_steps += 1
478
+ else:
479
+ self.aux_loss = torch.zeros((), device=x.device)
480
+
481
+ if self.track and self.token_groups is not None:
482
+ with torch.no_grad():
483
+ g = self.token_groups.reshape(-1)
484
+ if sel is not None:
485
+ g = g.index_select(0, sel)
486
+ g = g.unsqueeze(1).expand_as(topi)
487
+ ok = g >= 0
488
+ flat = (g[ok] * self.n_experts + topi[ok]).reshape(-1)
489
+ self.counts += torch.bincount(flat, minlength=self.n_groups * self.n_experts).view(
490
+ self.n_groups, self.n_experts)
491
+
492
+ out = out.to(x.dtype)
493
+ if sel is not None:
494
+ full = torch.zeros_like(xf)
495
+ full.index_copy_(0, sel, out)
496
+ out = full
497
+ return out.view(B, T, C)
498
+
499
+
500
+ class Block(nn.Module):
501
+ def __init__(self, cfg: ModelConfig):
502
+ super().__init__()
503
+ self.ln_1 = LayerNorm(cfg.n_embd, cfg.bias)
504
+ self.attn = CausalSelfAttention(cfg)
505
+ self.ln_2 = LayerNorm(cfg.n_embd, cfg.bias)
506
+ if cfg.ffn_type == "moe":
507
+ self.ffn = MoE(cfg)
508
+ else:
509
+ self.ffn = MLP(cfg.n_embd, cfg.mlp_hidden or 4 * cfg.n_embd, cfg.bias, cfg.dropout)
510
+ self.is_moe = cfg.ffn_type == "moe"
511
+
512
+ def forward(self, x, attn_mask=None, cache=None, layer_idx=0, tok_mask=None):
513
+ x = x + self.attn(self.ln_1(x), attn_mask, cache, layer_idx)
514
+ h = self.ln_2(x)
515
+ x = x + (self.ffn(h, tok_mask) if self.is_moe else self.ffn(h))
516
+ return x
517
+
518
+
519
+ class Transformer(nn.Module):
520
+ def __init__(self, cfg: ModelConfig):
521
+ super().__init__()
522
+ self.config = cfg
523
+ self.transformer = nn.ModuleDict(dict(
524
+ wte=nn.Embedding(cfg.vocab_size, cfg.n_embd),
525
+ wpe=nn.Embedding(cfg.block_size, cfg.n_embd),
526
+ drop=nn.Dropout(cfg.dropout),
527
+ h=nn.ModuleList([Block(cfg) for _ in range(cfg.n_layer)]),
528
+ ln_f=LayerNorm(cfg.n_embd, cfg.bias),
529
+ ))
530
+ self.lm_head = nn.Linear(cfg.n_embd, cfg.vocab_size, bias=False)
531
+ self.transformer.wte.weight = self.lm_head.weight # weight tying
532
+ self.apply(self._init_weights)
533
+ for pn, p in self.named_parameters():
534
+ if pn.endswith("c_proj.weight"):
535
+ torch.nn.init.normal_(p, mean=0.0, std=0.02 / math.sqrt(2 * cfg.n_layer))
536
+
537
+ def _init_weights(self, m):
538
+ if isinstance(m, nn.Linear):
539
+ torch.nn.init.normal_(m.weight, mean=0.0, std=0.02)
540
+ if m.bias is not None:
541
+ torch.nn.init.zeros_(m.bias)
542
+ elif isinstance(m, nn.Embedding):
543
+ torch.nn.init.normal_(m.weight, mean=0.0, std=0.02)
544
+
545
+ # ---- MoE helpers ----
546
+ def moe_layers(self):
547
+ return [b.ffn for b in self.transformer.h if isinstance(b.ffn, MoE)]
548
+
549
+ # ---- forward ----
550
+ def hidden_states(self, idx, pos=None, attn_mask=None, cache=None, tok_mask=None):
551
+ B, T = idx.shape
552
+ if pos is None:
553
+ start = cache.length if cache is not None else 0
554
+ assert start + T <= self.config.block_size, f"sequence length {start+T} > block_size"
555
+ pos = torch.arange(start, start + T, device=idx.device).unsqueeze(0)
556
+ x = self.transformer.drop(self.transformer.wte(idx) + self.transformer.wpe(pos))
557
+ for i, block in enumerate(self.transformer.h):
558
+ x = block(x, attn_mask, cache, i, tok_mask)
559
+ return self.transformer.ln_f(x)
560
+
561
+ def aux_loss(self):
562
+ layers = self.moe_layers()
563
+ if not layers:
564
+ return None
565
+ return sum(m.aux_loss for m in layers)
566
+
567
+ def forward(self, idx, targets=None, pos=None, attn_mask=None, cache=None,
568
+ tok_mask=None, last_only=False):
569
+ """targets: (B,T) with -1 = ignore. Returns (logits, loss, aux_loss).
570
+ When targets are given, logits are only computed on supervised positions
571
+ (saves the big vocab projection on source / padding tokens) and None is returned."""
572
+ x = self.hidden_states(idx, pos, attn_mask, cache, tok_mask)
573
+ aux = self.aux_loss()
574
+ if targets is not None:
575
+ valid = targets != -1
576
+ logits = self.lm_head(x[valid])
577
+ loss = F.cross_entropy(logits.float(), targets[valid])
578
+ return None, loss, aux
579
+ if last_only:
580
+ x = x[:, -1:, :]
581
+ return self.lm_head(x), None, aux
582
+
583
+ @torch.no_grad()
584
+ def token_nll(self, idx, targets, tok_mask=None):
585
+ """Per-token NLL (B,T), 0 where targets == -1."""
586
+ x = self.hidden_states(idx, tok_mask=tok_mask)
587
+ valid = targets != -1
588
+ out = torch.zeros(targets.shape, dtype=torch.float32, device=idx.device)
589
+ logits = self.lm_head(x[valid]).float()
590
+ out[valid] = F.cross_entropy(logits, targets[valid], reduction="none")
591
+ return out
592
+
593
+
594
+ # ---- parameter accounting ----
595
+ def _numel(m):
596
+ return sum(p.numel() for p in m.parameters())
597
+
598
+
599
+ def param_report(model: Transformer):
600
+ cfg = model.config
601
+ seen, total = set(), 0
602
+ for p in model.parameters():
603
+ if id(p) not in seen:
604
+ seen.add(id(p)); total += p.numel()
605
+ emb = model.transformer.wte.weight.numel() + model.transformer.wpe.weight.numel()
606
+ ffn_total = ffn_active = router = 0
607
+ for b in model.transformer.h:
608
+ f = b.ffn
609
+ if isinstance(f, MoE):
610
+ per_exp = _numel(f.experts[0])
611
+ r = _numel(f.router)
612
+ sh = sum(_numel(s) for s in f.shared)
613
+ ffn_total += per_exp * f.n_experts + sh + r
614
+ ffn_active += per_exp * f.top_k + sh + r
615
+ router += r
616
+ else:
617
+ ffn_total += _numel(f); ffn_active += _numel(f)
618
+ non_ffn = total - ffn_total
619
+ return {
620
+ "total_params": total,
621
+ "active_params_per_token": non_ffn + ffn_active,
622
+ "embedding_params": emb,
623
+ "total_non_embedding": total - emb,
624
+ "active_non_embedding": non_ffn + ffn_active - emb,
625
+ "ffn_total_params": ffn_total,
626
+ "ffn_active_params": ffn_active,
627
+ "router_params": router,
628
+ }
629
+
630
+
631
+ def print_param_report(rep, title=""):
632
+ print(f"--- parameters {title} ---")
633
+ for k, v in rep.items():
634
+ print(f" {k:26s} {v:>12,d} ({fmt_num(v)})")
635
+
636
+
637
+ # ---- save / load (safetensors handles the tied embedding) ----
638
+ def save_model_dir(model, out_dir, extra_config=None):
639
+ os.makedirs(out_dir, exist_ok=True)
640
+ try:
641
+ from safetensors.torch import save_model
642
+ save_model(model, os.path.join(out_dir, "model.safetensors"))
643
+ except Exception as e:
644
+ print("[save] safetensors failed, falling back to torch.save:", e)
645
+ torch.save(model.state_dict(), os.path.join(out_dir, "pytorch_model.bin"))
646
+ cfg = model.config.to_dict()
647
+ if extra_config:
648
+ cfg.update(extra_config)
649
+ save_json(cfg, os.path.join(out_dir, "config.json"))
650
+
651
+
652
+ def load_model_dir(path_or_repo, token=None, device="cpu"):
653
+ """Load a model saved by save_model_dir from a local dir or a HF repo id."""
654
+ if os.path.isdir(path_or_repo):
655
+ d = path_or_repo
656
+ cfg_path = os.path.join(d, "config.json")
657
+ st = os.path.join(d, "model.safetensors")
658
+ else:
659
+ cfg_path = hf_try_download(path_or_repo, "config.json", token)
660
+ st = hf_try_download(path_or_repo, "model.safetensors", token)
661
+ cfg = ModelConfig.from_dict(load_json(cfg_path))
662
+ model = Transformer(cfg)
663
+ from safetensors.torch import load_model
664
+ load_model(model, st, device="cpu")
665
+ return model.to(device)
666
+
667
+
668
+ # -----------------------------------------------------------------------------
669
+ # Generation (KV cache, left padding, batched). Uses model.forward only.
670
+ # -----------------------------------------------------------------------------
671
+ @torch.no_grad()
672
+ def generate(model, prompts: List[List[int]], max_new_tokens: int, eos_id: Optional[int],
673
+ pad_id: int = 0, temperature: float = 0.0, top_k: Optional[int] = None,
674
+ device=None, amp=None, generator=None):
675
+ """Batched autoregressive generation. temperature=0 -> greedy.
676
+ Returns list of generated token lists (EOS stripped)."""
677
+ device = device or next(model.parameters()).device
678
+ model.eval()
679
+ B = len(prompts)
680
+ L = max(len(p) for p in prompts)
681
+ max_new_tokens = min(max_new_tokens, model.config.block_size - L)
682
+ idx = torch.full((B, L), pad_id, dtype=torch.long)
683
+ valid = torch.zeros((B, L), dtype=torch.bool)
684
+ for i, p in enumerate(prompts):
685
+ idx[i, L - len(p):] = torch.tensor(p, dtype=torch.long)
686
+ valid[i, L - len(p):] = True
687
+ idx, valid = idx.to(device), valid.to(device)
688
+ pos = (valid.long().cumsum(1) - 1).clamp(min=0)
689
+ causal = torch.tril(torch.ones(L, L, dtype=torch.bool, device=device))
690
+ eye = torch.eye(L, dtype=torch.bool, device=device)
691
+ # key must be valid (not left padding); always allow self-attention so pad rows never go NaN
692
+ mask = (causal[None, None] & valid[:, None, None, :]) | eye[None, None]
693
+ cache = KVCache(model.config.n_layer)
694
+ with autocast_ctx(device, amp):
695
+ logits, _, _ = model(idx, pos=pos, attn_mask=mask, cache=cache, last_only=True)
696
+ next_pos = pos[:, -1] + 1
697
+ finished = torch.zeros(B, dtype=torch.bool, device=device)
698
+ outs = []
699
+ for _ in range(max_new_tokens):
700
+ lg = logits[:, -1, :].float()
701
+ if temperature and temperature > 0:
702
+ lg = lg / temperature
703
+ if top_k:
704
+ v, _ = torch.topk(lg, min(top_k, lg.size(-1)))
705
+ lg[lg < v[:, [-1]]] = -float("inf")
706
+ nxt = torch.multinomial(F.softmax(lg, -1), 1, generator=generator).squeeze(1)
707
+ else:
708
+ nxt = lg.argmax(-1)
709
+ nxt = torch.where(finished, torch.full_like(nxt, pad_id), nxt)
710
+ outs.append(nxt)
711
+ if eos_id is not None:
712
+ finished |= nxt == eos_id
713
+ if bool(finished.all()):
714
+ break
715
+ valid = torch.cat([valid, torch.ones(B, 1, dtype=torch.bool, device=device)], 1)
716
+ with autocast_ctx(device, amp):
717
+ logits, _, _ = model(nxt[:, None], pos=next_pos[:, None], attn_mask=valid[:, None, None, :],
718
+ cache=cache, last_only=True)
719
+ next_pos = next_pos + 1
720
+ if not outs:
721
+ return [[] for _ in range(B)]
722
+ gen = torch.stack(outs, 1).tolist()
723
+ res = []
724
+ for row in gen:
725
+ if eos_id is not None and eos_id in row:
726
+ row = row[:row.index(eos_id)]
727
+ res.append(row)
728
+ return res
729
+
730
+
731
+ @torch.no_grad()
732
+ def generate_sorted(model, prompts, max_new_tokens, eos_id, pad_id=0, batch_size=64,
733
+ device=None, amp=None, progress=False, **kw):
734
+ """Sort prompts by length for efficient batching, restore the original order."""
735
+ order = sorted(range(len(prompts)), key=lambda i: len(prompts[i]))
736
+ out = [None] * len(prompts)
737
+ t0 = time.time()
738
+ for bi, s in enumerate(range(0, len(order), batch_size)):
739
+ ids = order[s:s + batch_size]
740
+ res = generate(model, [prompts[i] for i in ids], max_new_tokens, eos_id, pad_id,
741
+ device=device, amp=amp, **kw)
742
+ for i, r in zip(ids, res):
743
+ out[i] = r
744
+ if progress and bi % 20 == 0:
745
+ print(f" generated {min(s+batch_size, len(order))}/{len(order)} ({time.time()-t0:.0f}s)")
746
+ return out
747
+
748
+
749
+ # -----------------------------------------------------------------------------
750
+ # HAP-E split (Part 2) - shared by the tokenizer notebook and the LM runs
751
+ # -----------------------------------------------------------------------------
752
+ def hape_split(doc_ids, seed=0, val_frac=0.05, test_frac=0.05):
753
+ """Deterministic split by base document id. Returns dict base -> 'train'|'val'|'test'."""
754
+ bases = sorted({d.split("@")[0] for d in doc_ids})
755
+ rng = np.random.default_rng(seed)
756
+ perm = rng.permutation(len(bases))
757
+ n_val, n_test = int(len(bases) * val_frac), int(len(bases) * test_frac)
758
+ split = {}
759
+ for rank, i in enumerate(perm):
760
+ split[bases[i]] = "val" if rank < n_val else ("test" if rank < n_val + n_test else "train")
761
+ return split
762
+
763
+
764
+ # -----------------------------------------------------------------------------
765
+ # LR schedule
766
+ # -----------------------------------------------------------------------------
767
+ def lr_factor(step, total_steps, warmup_steps, min_ratio=0.1):
768
+ """Linear warmup then cosine decay to min_ratio."""
769
+ if step < warmup_steps:
770
+ return (step + 1) / max(1, warmup_steps)
771
+ prog = (step - warmup_steps) / max(1, total_steps - warmup_steps)
772
+ prog = min(max(prog, 0.0), 1.0)
773
+ return min_ratio + (1 - min_ratio) * 0.5 * (1 + math.cos(math.pi * prog))
774
+
775
+
776
+ def set_lr(optimizer, factor):
777
+ for g in optimizer.param_groups:
778
+ if "base_lr" not in g:
779
+ g["base_lr"] = g["lr"]
780
+ g["lr"] = g["base_lr"] * factor
781
+
782
+
783
+ # -----------------------------------------------------------------------------
784
+ # run several jobs at once, one per GPU (e.g. Kaggle "GPU T4 x2")
785
+ # -----------------------------------------------------------------------------
786
+ def launch_parallel(jobs, poll_s=180, tail=3):
787
+ """jobs: list of (name, python_code). Job i runs in its own process on GPU i.
788
+ Output goes to <name>.log; the last lines of every log are printed every poll_s seconds."""
789
+ import subprocess
790
+ get_hf_token() # puts HF_TOKEN into os.environ for the children
791
+ n_gpu = max(torch.cuda.device_count(), 1)
792
+ assert len(jobs) <= n_gpu, f"{len(jobs)} jobs but only {n_gpu} GPU(s)"
793
+ procs = []
794
+ for gpu, (name, code) in enumerate(jobs):
795
+ env = dict(os.environ, CUDA_VISIBLE_DEVICES=str(gpu), PYTHONUNBUFFERED="1")
796
+ logf = open(f"{name}.log", "w")
797
+ procs.append((name, subprocess.Popen([sys.executable, "-u", "-c", code], env=env,
798
+ stdout=logf, stderr=subprocess.STDOUT), logf))
799
+ print(f"started {name} on GPU {gpu} (log: {name}.log)")
800
+ while True:
801
+ time.sleep(poll_s)
802
+ alive = False
803
+ for name, p, _ in procs:
804
+ alive |= p.poll() is None
805
+ try:
806
+ lines = open(f"{name}.log").read().splitlines()[-tail:]
807
+ except Exception:
808
+ lines = []
809
+ print(f"--- {name} ({'running' if p.poll() is None else 'exit ' + str(p.returncode)}) ---")
810
+ for l in lines:
811
+ print(" ", l[:200])
812
+ if not alive:
813
+ break
814
+ for _, _, f in procs:
815
+ f.close()
816
+ codes = {name: p.returncode for name, p, _ in procs}
817
+ print("exit codes:", codes)
818
+ return codes
819
+
820
+
821
+ # -----------------------------------------------------------------------------
822
+ # Plot style (validated categorical palette, fixed order)
823
+ # -----------------------------------------------------------------------------
824
+ PALETTE = ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#008300", "#4a3aa7", "#e34948"]
825
+
826
+
827
+ def setup_plot_style():
828
+ import matplotlib as mpl
829
+ mpl.rcParams.update({
830
+ "figure.dpi": 110, "savefig.dpi": 150, "savefig.bbox": "tight",
831
+ "axes.spines.top": False, "axes.spines.right": False,
832
+ "axes.grid": True, "grid.color": "#e4e3df", "grid.linewidth": 0.8,
833
+ "axes.edgecolor": "#8a8984", "axes.labelcolor": "#2b2a27",
834
+ "xtick.color": "#52514e", "ytick.color": "#52514e",
835
+ "axes.prop_cycle": mpl.cycler(color=PALETTE),
836
+ "lines.linewidth": 2.0, "legend.frameon": False, "font.size": 10,
837
+ })
decoding.py ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =============================================================================
2
+ # decoding.py - Part 3: decoding strategies implemented from scratch on top of
3
+ # EleutherAI/pythia-160m. Only model.forward() (i.e. model(...)) is used -
4
+ # never model.generate().
5
+ #
6
+ # greedy, beam search (width 1/2/4/...), top-k sampling, top-p (nucleus) sampling
7
+ #
8
+ # Evaluated on hamishivi/ROCStories (test split): prompt = first sentence,
9
+ # reference = the remaining four sentences.
10
+ # =============================================================================
11
+ import os, re, math, time, json, string
12
+ from collections import Counter
13
+ import numpy as np
14
+ import torch
15
+ import torch.nn.functional as F
16
+
17
+ DECODING_DEFAULTS = dict(
18
+ model_name="EleutherAI/pythia-160m",
19
+ dataset="hamishivi/ROCStories", split="test", n_samples=1000, seed=0,
20
+ max_new_tokens=70, batch_size=32, beam_batch_size=8, dtype="float32",
21
+ length_penalty=1.0,
22
+ configs=[
23
+ {"name": "greedy", "strategy": "greedy"},
24
+ {"name": "beam_w1", "strategy": "beam", "width": 1},
25
+ {"name": "beam_w2", "strategy": "beam", "width": 2},
26
+ {"name": "beam_w4", "strategy": "beam", "width": 4},
27
+ {"name": "topk_k5", "strategy": "top_k", "k": 5},
28
+ {"name": "topk_k20", "strategy": "top_k", "k": 20},
29
+ {"name": "topk_k50", "strategy": "top_k", "k": 50},
30
+ {"name": "topk_k50_t0.7", "strategy": "top_k", "k": 50, "temperature": 0.7},
31
+ {"name": "topp_p0.5", "strategy": "top_p", "p": 0.5},
32
+ {"name": "topp_p0.8", "strategy": "top_p", "p": 0.8},
33
+ {"name": "topp_p0.95", "strategy": "top_p", "p": 0.95},
34
+ {"name": "topp_p0.9_t0.7", "strategy": "top_p", "p": 0.9, "temperature": 0.7},
35
+ ],
36
+ timing_widths=[1, 2, 4, 8], timing_samples=100,
37
+ judge_model="EleutherAI/pythia-410m", # external LM for an extra perplexity column (None to skip)
38
+ use_bertscore=True,
39
+ out_dir="/kaggle/working/outputs/decoding" if os.path.isdir("/kaggle/working") else "outputs/decoding",
40
+ hf_user=None, repo_name="anlp-a2-decoding", push_to_hub=True, private=False,
41
+ )
42
+
43
+
44
+ # -----------------------------------------------------------------------------
45
+ # model wrapper: one forward step with KV cache
46
+ # -----------------------------------------------------------------------------
47
+ def load_lm(name, device, dtype="float32"):
48
+ from transformers import AutoModelForCausalLM, AutoTokenizer
49
+ tok = AutoTokenizer.from_pretrained(name)
50
+ dt = {"float32": torch.float32, "float16": torch.float16, "bfloat16": torch.bfloat16}[dtype]
51
+ try:
52
+ model = AutoModelForCausalLM.from_pretrained(name, dtype=dt)
53
+ except TypeError:
54
+ model = AutoModelForCausalLM.from_pretrained(name, torch_dtype=dt)
55
+ model.to(device).eval()
56
+ if tok.pad_token_id is None:
57
+ tok.pad_token = tok.eos_token
58
+ return model, tok
59
+
60
+
61
+ def reorder_cache(past, idx):
62
+ """Select rows (beams) of a KV cache - works for legacy tuples and Cache objects."""
63
+ if past is None:
64
+ return None
65
+ if isinstance(past, (tuple, list)):
66
+ return tuple(tuple(t.index_select(0, idx) for t in layer) for layer in past)
67
+ if hasattr(past, "reorder_cache"):
68
+ past.reorder_cache(idx)
69
+ return past
70
+ if hasattr(past, "layers"): # transformers >= 4.56 DynamicCache
71
+ for layer in past.layers:
72
+ layer.keys = layer.keys.index_select(0, idx)
73
+ layer.values = layer.values.index_select(0, idx)
74
+ return past
75
+ if hasattr(past, "key_cache"):
76
+ for i in range(len(past.key_cache)):
77
+ past.key_cache[i] = past.key_cache[i].index_select(0, idx)
78
+ past.value_cache[i] = past.value_cache[i].index_select(0, idx)
79
+ return past
80
+ raise TypeError(f"cannot reorder cache of type {type(past)}")
81
+
82
+
83
+ @torch.no_grad()
84
+ def forward_step(model, input_ids, attention_mask, position_ids, past):
85
+ out = model(input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids,
86
+ past_key_values=past, use_cache=True)
87
+ return out.logits[:, -1, :].float(), out.past_key_values
88
+
89
+
90
+ def left_pad(prompts, pad_id, device):
91
+ L = max(len(p) for p in prompts)
92
+ ids = torch.full((len(prompts), L), pad_id, dtype=torch.long)
93
+ mask = torch.zeros((len(prompts), L), dtype=torch.long)
94
+ for i, p in enumerate(prompts):
95
+ ids[i, L - len(p):] = torch.tensor(p)
96
+ mask[i, L - len(p):] = 1
97
+ pos = (mask.cumsum(1) - 1).clamp(min=0)
98
+ return ids.to(device), mask.to(device), pos.to(device)
99
+
100
+
101
+ # -----------------------------------------------------------------------------
102
+ # logit processors
103
+ # -----------------------------------------------------------------------------
104
+ def top_k_filter(logits, k):
105
+ k = min(k, logits.size(-1))
106
+ kth = torch.topk(logits, k, dim=-1).values[:, -1:]
107
+ return logits.masked_fill(logits < kth, float("-inf"))
108
+
109
+
110
+ def top_p_filter(logits, p):
111
+ """Keep the smallest set of tokens whose cumulative probability >= p (always >= 1 token)."""
112
+ sorted_logits, sorted_idx = torch.sort(logits, descending=True, dim=-1)
113
+ probs = F.softmax(sorted_logits, dim=-1)
114
+ cum = probs.cumsum(-1)
115
+ remove = (cum - probs) >= p # mass *before* this token already reaches p
116
+ remove[:, 0] = False
117
+ sorted_logits = sorted_logits.masked_fill(remove, float("-inf"))
118
+ return torch.full_like(logits, float("-inf")).scatter(-1, sorted_idx, sorted_logits)
119
+
120
+
121
+ # -----------------------------------------------------------------------------
122
+ # greedy / top-k / top-p (batched)
123
+ # -----------------------------------------------------------------------------
124
+ @torch.no_grad()
125
+ def sample_decode(model, prompts, max_new_tokens, eos_id, pad_id, strategy="greedy", k=None, p=None,
126
+ temperature=1.0, generator=None):
127
+ """Returns (list of generated id lists, list of sum log-prob under the *unmodified*
128
+ model distribution, list of lengths)."""
129
+ device = next(model.parameters()).device
130
+ B = len(prompts)
131
+ ids, mask, pos = left_pad(prompts, pad_id, device)
132
+ logits, past = forward_step(model, ids, mask, pos, None)
133
+ next_pos = pos[:, -1:] + 1
134
+ finished = torch.zeros(B, dtype=torch.bool, device=device)
135
+ out, lp_sum, lengths = [], torch.zeros(B, device=device), torch.zeros(B, dtype=torch.long, device=device)
136
+ for _ in range(max_new_tokens):
137
+ logp = F.log_softmax(logits, -1)
138
+ if strategy == "greedy":
139
+ nxt = logits.argmax(-1)
140
+ else:
141
+ lg = logits / max(temperature, 1e-6)
142
+ if strategy == "top_k":
143
+ lg = top_k_filter(lg, k)
144
+ elif strategy == "top_p":
145
+ lg = top_p_filter(lg, p)
146
+ nxt = torch.multinomial(F.softmax(lg, -1), 1, generator=generator).squeeze(1)
147
+ nxt = torch.where(finished, torch.full_like(nxt, pad_id), nxt)
148
+ lp_sum += torch.where(finished, torch.zeros_like(lp_sum), logp.gather(1, nxt[:, None]).squeeze(1))
149
+ lengths += (~finished).long()
150
+ out.append(nxt)
151
+ finished |= nxt == eos_id
152
+ if bool(finished.all()):
153
+ break
154
+ mask = torch.cat([mask, torch.ones(B, 1, dtype=mask.dtype, device=device)], 1)
155
+ logits, past = forward_step(model, nxt[:, None], mask, next_pos, past)
156
+ next_pos = next_pos + 1
157
+ gen = torch.stack(out, 1).tolist() if out else [[] for _ in range(B)]
158
+ res = []
159
+ for row in gen:
160
+ if eos_id in row:
161
+ row = row[:row.index(eos_id)]
162
+ res.append(row)
163
+ return res, lp_sum.tolist(), lengths.tolist()
164
+
165
+
166
+ # -----------------------------------------------------------------------------
167
+ # beam search (batched over prompts, width W per prompt)
168
+ # -----------------------------------------------------------------------------
169
+ @torch.no_grad()
170
+ def beam_search(model, prompts, width, max_new_tokens, eos_id, pad_id, length_penalty=1.0):
171
+ """Standard beam search with a KV cache.
172
+ * each step: scores(B,W) + log p(B,W,V) -> top 2W candidates per prompt
173
+ * candidates ending in EOS become finished hypotheses (score / len^alpha)
174
+ * a prompt is done once it has W finished hypotheses (early stopping)
175
+ * at max length, open beams are added to the finished pool.
176
+ Returns (ids, sum log-prob, length) of the best hypothesis per prompt."""
177
+ device = next(model.parameters()).device
178
+ B, W = len(prompts), width
179
+ rep = [p for p in prompts for _ in range(W)]
180
+ ids, mask, pos = left_pad(rep, pad_id, device)
181
+ logits, past = forward_step(model, ids, mask, pos, None)
182
+ V = logits.size(-1)
183
+ next_pos = pos[:, -1:] + 1
184
+ beam_scores = torch.zeros(B, W, device=device)
185
+ beam_scores[:, 1:] = float("-inf") # all beams identical at t=0 -> only expand beam 0
186
+ seqs = torch.zeros(B * W, 0, dtype=torch.long, device=device)
187
+ finished = [[] for _ in range(B)] # (normalised score, raw score, token list)
188
+ done = [False] * B
189
+ for t in range(max_new_tokens):
190
+ logp = F.log_softmax(logits, -1).view(B, W, V)
191
+ cand = (beam_scores.unsqueeze(-1) + logp).view(B, W * V)
192
+ top_s, top_i = cand.topk(2 * W, dim=-1)
193
+ top_s_l, top_i_l = top_s.tolist(), top_i.tolist()
194
+ new_scores = torch.zeros(B, W, device=device)
195
+ new_beam = torch.zeros(B, W, dtype=torch.long)
196
+ new_tok = torch.full((B, W), pad_id, dtype=torch.long)
197
+ seqs_l = None
198
+ for b in range(B):
199
+ if done[b]:
200
+ new_scores[b] = float("-inf"); new_scores[b, 0] = 0.0
201
+ continue
202
+ j = 0
203
+ for r in range(2 * W):
204
+ s, ci = top_s_l[b][r], top_i_l[b][r]
205
+ bi, ti = ci // V, ci % V
206
+ if s == float("-inf"):
207
+ break
208
+ if ti == eos_id:
209
+ if r < W:
210
+ if seqs_l is None:
211
+ seqs_l = seqs.tolist()
212
+ toks = seqs_l[b * W + bi]
213
+ n = len(toks) + 1
214
+ finished[b].append((s / (n ** length_penalty), s, toks, n))
215
+ continue
216
+ new_scores[b, j] = s; new_beam[b, j] = bi; new_tok[b, j] = ti
217
+ j += 1
218
+ if j == W:
219
+ break
220
+ while j < W: # not enough live candidates
221
+ new_scores[b, j] = float("-inf"); j += 1
222
+ if len(finished[b]) >= W:
223
+ done[b] = True
224
+ if all(done):
225
+ break
226
+ gidx = (torch.arange(B).unsqueeze(1) * W + new_beam).view(-1).to(device)
227
+ seqs = torch.cat([seqs.index_select(0, gidx), new_tok.view(-1, 1).to(device)], 1)
228
+ beam_scores = new_scores
229
+ past = reorder_cache(past, gidx)
230
+ mask = torch.cat([mask.index_select(0, gidx), torch.ones(B * W, 1, dtype=mask.dtype, device=device)], 1)
231
+ next_pos = next_pos.index_select(0, gidx)
232
+ logits, past = forward_step(model, new_tok.view(-1, 1).to(device), mask, next_pos, past)
233
+ next_pos = next_pos + 1
234
+ seqs_l = seqs.tolist(); sc = beam_scores.tolist()
235
+ res, lps, lens = [], [], []
236
+ for b in range(B):
237
+ if not done[b]:
238
+ for w in range(W):
239
+ if sc[b][w] > float("-inf"):
240
+ toks = seqs_l[b * W + w]
241
+ n = max(len(toks), 1)
242
+ finished[b].append((sc[b][w] / (n ** length_penalty), sc[b][w], toks, n))
243
+ best = max(finished[b], key=lambda z: z[0])
244
+ res.append(best[2]); lps.append(best[1]); lens.append(best[3])
245
+ return res, lps, lens
246
+
247
+
248
+ # -----------------------------------------------------------------------------
249
+ # metrics
250
+ # -----------------------------------------------------------------------------
251
+ _PUNCT = re.compile(f"[{re.escape(string.punctuation)}]")
252
+
253
+
254
+ def norm_words(s):
255
+ return _PUNCT.sub(" ", s.lower()).split()
256
+
257
+
258
+ def clean_generation(text, max_sentences=4):
259
+ """Post-processing applied before scoring: cut at the first newline (Pythia tends to
260
+ drift into unrelated text after a line break) and keep at most 4 sentences
261
+ (ROCStories continuations are exactly 4 sentences)."""
262
+ text = text.lstrip()
263
+ text = text.split("\n")[0]
264
+ ends = [m.end() for m in re.finditer(r"[.!?](?=\s|$)", text)]
265
+ if len(ends) >= max_sentences:
266
+ text = text[:ends[max_sentences - 1]]
267
+ return " " + text.strip()
268
+
269
+
270
+ def token_prf_acc(gen, ref):
271
+ g, r = norm_words(gen), norm_words(ref)
272
+ common = Counter(g) & Counter(r)
273
+ ov = sum(common.values())
274
+ P = ov / len(g) if g else 0.0
275
+ R = ov / len(r) if r else 0.0
276
+ F1 = 2 * P * R / (P + R) if P + R > 0 else 0.0
277
+ acc = sum(1 for i in range(len(r)) if i < len(g) and g[i] == r[i]) / len(r) if r else 0.0
278
+ return P, R, F1, acc
279
+
280
+
281
+ def distinct_n(texts, n):
282
+ grams, total = set(), 0
283
+ for t in texts:
284
+ w = norm_words(t)
285
+ ng = [tuple(w[i:i + n]) for i in range(len(w) - n + 1)]
286
+ grams.update(ng); total += len(ng)
287
+ return len(grams) / max(total, 1)
288
+
289
+
290
+ def repetition_rate(text, n=4):
291
+ w = norm_words(text)
292
+ ng = [tuple(w[i:i + n]) for i in range(len(w) - n + 1)]
293
+ return 1 - len(set(ng)) / len(ng) if ng else 0.0
294
+
295
+
296
+ @torch.no_grad()
297
+ def continuation_ppl(model, prompt_ids, cont_ids, pad_id, batch_size=32):
298
+ """Per-sample perplexity of cont given prompt (teacher forced)."""
299
+ device = next(model.parameters()).device
300
+ out = []
301
+ for s in range(0, len(prompt_ids), batch_size):
302
+ P = prompt_ids[s:s + batch_size]; C = cont_ids[s:s + batch_size]
303
+ seqs = [p + c for p, c in zip(P, C)]
304
+ L = max(len(x) for x in seqs)
305
+ ids = torch.full((len(seqs), L), pad_id, dtype=torch.long)
306
+ att = torch.zeros((len(seqs), L), dtype=torch.long)
307
+ lab = torch.full((len(seqs), L), -100, dtype=torch.long)
308
+ for i, (p, c) in enumerate(zip(P, C)):
309
+ x = p + c
310
+ ids[i, :len(x)] = torch.tensor(x); att[i, :len(x)] = 1
311
+ lab[i, len(p):len(x)] = torch.tensor(c) if c else lab[i, len(p):len(x)]
312
+ ids, att, lab = ids.to(device), att.to(device), lab.to(device)
313
+ logits = model(input_ids=ids, attention_mask=att).logits.float()
314
+ lp = F.cross_entropy(logits[:, :-1].reshape(-1, logits.size(-1)), lab[:, 1:].reshape(-1),
315
+ ignore_index=-100, reduction="none").view(len(seqs), -1)
316
+ n = (lab[:, 1:] != -100).sum(1).clamp(min=1)
317
+ out += torch.exp(lp.sum(1) / n).tolist()
318
+ return out
319
+
320
+
321
+ def score_config(gens, refs, self_lp, self_len):
322
+ import sacrebleu
323
+ prf = np.array([token_prf_acc(g, r) for g, r in zip(gens, refs)])
324
+ ppl_self = [math.exp(-lp / max(n, 1)) for lp, n in zip(self_lp, self_len)]
325
+ return {
326
+ "precision": float(prf[:, 0].mean()), "recall": float(prf[:, 1].mean()),
327
+ "f1": float(prf[:, 2].mean()), "accuracy": float(prf[:, 3].mean()),
328
+ "bleu": sacrebleu.corpus_bleu(gens, [refs]).score,
329
+ "self_ppl_mean": float(np.mean(ppl_self)), "self_ppl_median": float(np.median(ppl_self)),
330
+ "distinct_1": distinct_n(gens, 1), "distinct_2": distinct_n(gens, 2),
331
+ "rep_4gram": float(np.mean([repetition_rate(g) for g in gens])),
332
+ "avg_words": float(np.mean([len(norm_words(g)) for g in gens])),
333
+ }
334
+
335
+
336
+ # -----------------------------------------------------------------------------
337
+ # driver
338
+ # -----------------------------------------------------------------------------
339
+ def run_config(model, tok, prompts, conf, cfg, seed=0):
340
+ device = next(model.parameters()).device
341
+ eos, pad = tok.eos_token_id, tok.pad_token_id
342
+ gen_ids, lps, lens = [None] * len(prompts), [None] * len(prompts), [None] * len(prompts)
343
+ order = sorted(range(len(prompts)), key=lambda i: len(prompts[i]))
344
+ g = torch.Generator(device=device); g.manual_seed(seed)
345
+ bs = cfg["beam_batch_size"] if conf["strategy"] == "beam" else cfg["batch_size"]
346
+ if device.type == "cuda": torch.cuda.synchronize()
347
+ t0 = time.time()
348
+ for s in range(0, len(order), bs):
349
+ idx = order[s:s + bs]
350
+ P = [prompts[i] for i in idx]
351
+ if conf["strategy"] == "beam":
352
+ r, lp, ln = beam_search(model, P, conf["width"], cfg["max_new_tokens"], eos, pad, cfg["length_penalty"])
353
+ else:
354
+ r, lp, ln = sample_decode(model, P, cfg["max_new_tokens"], eos, pad, conf["strategy"],
355
+ conf.get("k"), conf.get("p"), conf.get("temperature", 1.0), g)
356
+ for i, a, b, c in zip(idx, r, lp, ln):
357
+ gen_ids[i], lps[i], lens[i] = a, b, c
358
+ if device.type == "cuda": torch.cuda.synchronize()
359
+ return gen_ids, lps, lens, time.time() - t0
360
+
361
+
362
+ def beam_timing(model, tok, prompts, widths, max_new_tokens):
363
+ """Wall time of beam search with batch size 1 (one prompt at a time) for each width.
364
+ Two modes: 'natural' (stops early once W hypotheses hit EOS) and 'fixed' (EOS disabled,
365
+ always max_new_tokens steps) - the fixed mode isolates the per-step cost as a function of W."""
366
+ device = next(model.parameters()).device
367
+ res = []
368
+ beam_search(model, prompts[:2], 2, 5, tok.eos_token_id, tok.pad_token_id) # warm-up
369
+ for mode, eos in (("natural", tok.eos_token_id), ("fixed", -1)):
370
+ for w in widths:
371
+ times, steps = [], []
372
+ for p in prompts:
373
+ if device.type == "cuda": torch.cuda.synchronize()
374
+ t0 = time.time()
375
+ r, _, ln = beam_search(model, [p], w, max_new_tokens, eos, tok.pad_token_id)
376
+ if device.type == "cuda": torch.cuda.synchronize()
377
+ times.append(time.time() - t0); steps.append(max(len(r[0]), 1))
378
+ rec = {"mode": mode, "width": w, "mean_s_per_prompt": float(np.mean(times)), "std_s": float(np.std(times)),
379
+ "mean_ms_per_token": float(np.mean([t / s for t, s in zip(times, steps)]) * 1e3),
380
+ "mean_output_tokens": float(np.mean(steps)), "total_s": float(np.sum(times)),
381
+ "n_prompts": len(prompts)}
382
+ res.append(rec)
383
+ print(f" [{mode}] width {w}: {rec['mean_s_per_prompt']*1e3:.1f} ms/prompt, "
384
+ f"{rec['mean_ms_per_token']:.2f} ms/token, {rec['mean_output_tokens']:.1f} tokens")
385
+ return res
386
+
387
+
388
+ def run_decoding_eval(user_cfg=None):
389
+ import sys
390
+ sys.path.insert(0, ".")
391
+ from common import get_hf_token, hf_login, hf_resolve_user, hf_push_folder, save_json
392
+ cfg = dict(DECODING_DEFAULTS); cfg.update(user_cfg or {})
393
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
394
+ os.makedirs(cfg["out_dir"], exist_ok=True)
395
+ torch.manual_seed(cfg["seed"])
396
+
397
+ token = get_hf_token(); hf_login(token)
398
+ model, tok = load_lm(cfg["model_name"], device, cfg["dtype"])
399
+ print(f"loaded {cfg['model_name']} on {device}; eos={tok.eos_token_id} pad={tok.pad_token_id}")
400
+
401
+ if cfg.get("dataset_override") is not None:
402
+ rows = cfg["dataset_override"]
403
+ else:
404
+ from datasets import load_dataset
405
+ rows = load_dataset(cfg["dataset"], split=cfg["split"])
406
+ prompts_txt = list(rows["prompt"]); refs_txt = list(rows["continuation"])
407
+ keep = [i for i, r in enumerate(refs_txt) if r and r.strip()]
408
+ rng = np.random.default_rng(cfg["seed"])
409
+ keep = sorted(rng.choice(keep, min(cfg["n_samples"], len(keep)), replace=False).tolist())
410
+ prompts_txt = [prompts_txt[i] for i in keep]; refs_txt = [refs_txt[i] for i in keep]
411
+ refs_txt = [" " + r.strip() for r in refs_txt]
412
+ prompts = [tok(p)["input_ids"] for p in prompts_txt]
413
+ print(f"{len(prompts)} test prompts")
414
+
415
+ # reference perplexity (independent of the decoding strategy)
416
+ ref_ids = [tok(r)["input_ids"] for r in refs_txt]
417
+ ref_ppl = continuation_ppl(model, prompts, ref_ids, tok.pad_token_id)
418
+ summary = {"reference_ppl_mean": float(np.mean(ref_ppl)), "reference_ppl_median": float(np.median(ref_ppl)),
419
+ "n_samples": len(prompts), "model": cfg["model_name"], "max_new_tokens": cfg["max_new_tokens"]}
420
+ print(f"reference continuation ppl: mean {summary['reference_ppl_mean']:.2f}, "
421
+ f"median {summary['reference_ppl_median']:.2f}")
422
+
423
+ results, generations = [], {"prompt": prompts_txt, "reference": refs_txt}
424
+ for conf in cfg["configs"]:
425
+ print(f"== {conf['name']} ==")
426
+ ids, lps, lens, dt = run_config(model, tok, prompts, conf, cfg, cfg["seed"])
427
+ raw = [tok.decode(x, skip_special_tokens=True) for x in ids]
428
+ gens = [clean_generation(t) for t in raw]
429
+ m = score_config(gens, refs_txt, lps, lens)
430
+ m.update({"name": conf["name"], "strategy": conf["strategy"], "params": conf,
431
+ "time_s": dt, "ms_per_prompt": dt / len(prompts) * 1e3,
432
+ "gen_tokens_mean": float(np.mean([len(x) for x in ids]))})
433
+ results.append(m)
434
+ generations[conf["name"]] = gens
435
+ generations[conf["name"] + "__raw"] = raw
436
+ print(f" P {m['precision']:.3f} R {m['recall']:.3f} F1 {m['f1']:.3f} Acc {m['accuracy']:.3f} "
437
+ f"BLEU {m['bleu']:.2f} self-ppl {m['self_ppl_median']:.2f} dist-2 {m['distinct_2']:.3f} "
438
+ f"rep4 {m['rep_4gram']:.3f} time {dt:.1f}s")
439
+
440
+ # sanity: beam width 1 must equal greedy
441
+ if "greedy" in generations and "beam_w1" in generations:
442
+ same = np.mean([a == b for a, b in zip(generations["greedy__raw"], generations["beam_w1__raw"])])
443
+ summary["beam_w1_equals_greedy_frac"] = float(same)
444
+ print(f"beam(w=1) identical to greedy on {same*100:.1f}% of prompts")
445
+
446
+ # external judge perplexity
447
+ if cfg["judge_model"]:
448
+ try:
449
+ judge, _ = load_lm(cfg["judge_model"], device, cfg["dtype"])
450
+ for m in results:
451
+ gen_ids = [tok(g)["input_ids"] for g in generations[m["name"]]]
452
+ pp = continuation_ppl(judge, prompts, gen_ids, tok.pad_token_id)
453
+ m["judge_ppl_median"] = float(np.median(pp)); m["judge_ppl_mean"] = float(np.mean(pp))
454
+ jr = continuation_ppl(judge, prompts, ref_ids, tok.pad_token_id)
455
+ summary["reference_judge_ppl_median"] = float(np.median(jr))
456
+ del judge
457
+ except Exception as e:
458
+ print("[judge] skipped:", e)
459
+
460
+ if cfg["use_bertscore"]:
461
+ try:
462
+ from bert_score import score as bscore
463
+ for m in results:
464
+ P, R, F1 = bscore(generations[m["name"]], refs_txt, lang="en", verbose=False,
465
+ device=str(device), batch_size=64)
466
+ m["bertscore_p"], m["bertscore_r"], m["bertscore_f1"] = (float(P.mean()), float(R.mean()),
467
+ float(F1.mean()))
468
+ except Exception as e:
469
+ print("[bertscore] skipped:", e)
470
+
471
+ print("beam-search timing (batch size 1):")
472
+ timing = beam_timing(model, tok, prompts[:cfg["timing_samples"]], cfg["timing_widths"], cfg["max_new_tokens"])
473
+
474
+ save_json({"summary": summary, "results": results, "beam_timing": timing, "config":
475
+ {k: v for k, v in cfg.items() if k != "dataset_override"}},
476
+ os.path.join(cfg["out_dir"], "decoding_results.json"))
477
+ save_json(generations, os.path.join(cfg["out_dir"], "generations.json"))
478
+ shutil_copy_code(cfg["out_dir"])
479
+ user = hf_resolve_user(cfg["hf_user"], token)
480
+ if cfg["push_to_hub"] and user:
481
+ hf_push_folder(cfg["out_dir"], f"{user}/{cfg['repo_name']}", token, cfg["private"], "decoding results")
482
+ print(f"pushed to https://huggingface.co/{user}/{cfg['repo_name']}")
483
+ return summary, results, timing, generations
484
+
485
+
486
+ def shutil_copy_code(out_dir):
487
+ import shutil
488
+ for f in ("decoding.py", "common.py"):
489
+ if os.path.exists(f):
490
+ shutil.copy(f, os.path.join(out_dir, f))
491
+
492
+
493
+ # -----------------------------------------------------------------------------
494
+ # plots / tables for the write-up
495
+ # -----------------------------------------------------------------------------
496
+ PALETTE = ["#2a78d6", "#eb6834", "#1baf7a", "#eda100", "#e87ba4", "#008300", "#4a3aa7", "#e34948"]
497
+ STRATEGY_COLOR = {"greedy": PALETTE[0], "beam": PALETTE[6], "top_k": PALETTE[1], "top_p": PALETTE[2]}
498
+
499
+
500
+ def decoding_report(res_json_path, out_dir):
501
+ import pandas as pd
502
+ import matplotlib.pyplot as plt
503
+ from common import setup_plot_style
504
+ setup_plot_style()
505
+ R = json.load(open(res_json_path))
506
+ df = pd.DataFrame(R["results"])
507
+ cols = ["name", "accuracy", "precision", "recall", "f1", "bleu", "self_ppl_median", "judge_ppl_median",
508
+ "bertscore_f1", "distinct_2", "rep_4gram", "avg_words", "ms_per_prompt"]
509
+ table = df[[c for c in cols if c in df.columns]].copy()
510
+ table.to_csv(os.path.join(out_dir, "decoding_table.csv"), index=False)
511
+ colors = [STRATEGY_COLOR[s] for s in df["strategy"]]
512
+
513
+ metrics = [("f1", "token F1"), ("accuracy", "positional accuracy"), ("bleu", "BLEU"),
514
+ ("self_ppl_median", "self perplexity (median)"), ("distinct_2", "distinct-2"),
515
+ ("rep_4gram", "repeated 4-gram rate")]
516
+ fig, axes = plt.subplots(2, 3, figsize=(15, 7.5), constrained_layout=True)
517
+ for ax, (k, lab) in zip(axes.flat, metrics):
518
+ ax.bar(range(len(df)), df[k], color=colors, width=0.7)
519
+ ax.set_xticks(range(len(df))); ax.set_xticklabels(df["name"], rotation=60, ha="right", fontsize=8)
520
+ ax.set_title(lab); ax.grid(axis="x", visible=False)
521
+ handles = [plt.Rectangle((0, 0), 1, 1, color=c) for c in STRATEGY_COLOR.values()]
522
+ fig.legend(handles, STRATEGY_COLOR.keys(), loc="upper right", ncol=4)
523
+ fig.savefig(os.path.join(out_dir, "decoding_metrics.png"))
524
+
525
+ # quality vs diversity
526
+ fig2, ax = plt.subplots(figsize=(6.5, 4.5), constrained_layout=True)
527
+ for _, r in df.iterrows():
528
+ ax.scatter(r["distinct_2"], r["f1"], color=STRATEGY_COLOR[r["strategy"]], s=60, zorder=3,
529
+ edgecolor="white", linewidth=1.5)
530
+ ax.annotate(r["name"], (r["distinct_2"], r["f1"]), fontsize=7, xytext=(4, 3), textcoords="offset points")
531
+ ax.set_xlabel("distinct-2 (diversity)"); ax.set_ylabel("token F1 vs reference")
532
+ ax.set_title("quality / diversity trade-off")
533
+ fig2.savefig(os.path.join(out_dir, "quality_vs_diversity.png"))
534
+
535
+ # beam timing
536
+ tm = pd.DataFrame(R["beam_timing"])
537
+ fig3, axes3 = plt.subplots(1, 2, figsize=(11, 3.8), constrained_layout=True)
538
+ for i, (mode, g) in enumerate(tm.groupby("mode", sort=False)):
539
+ axes3[0].plot(g["width"], g["mean_s_per_prompt"] * 1e3, marker="o", label=mode, color=PALETTE[i])
540
+ axes3[1].plot(g["width"], g["mean_ms_per_token"], marker="o", label=mode, color=PALETTE[i])
541
+ for ax, lab in zip(axes3, ["ms per prompt", "ms per generated token"]):
542
+ ax.set_xlabel("beam width"); ax.set_ylabel(lab); ax.set_xticks(sorted(tm["width"].unique())); ax.legend()
543
+ axes3[0].set_title("beam search wall time (batch size 1)")
544
+ axes3[1].set_title("per-step cost")
545
+ fig3.savefig(os.path.join(out_dir, "beam_timing.png"))
546
+ return table, tm
547
+
548
+
549
+ def show_examples(gen_json_path, n=5, names=None, seed=0):
550
+ G = json.load(open(gen_json_path))
551
+ names = names or [k for k in G if k not in ("prompt", "reference") and not k.endswith("__raw")]
552
+ rng = np.random.default_rng(seed)
553
+ for i in rng.choice(len(G["prompt"]), n, replace=False):
554
+ print("=" * 100)
555
+ print("PROMPT :", G["prompt"][i])
556
+ print("REFERENCE:", G["reference"][i].strip())
557
+ for k in names:
558
+ print(f"{k:>16s}: {G[k][i].strip()}")
decoding_results.json CHANGED
@@ -1,40 +1,40 @@
1
  {
2
  "summary": {
3
- "reference_ppl_mean": 27.426975273132324,
4
- "reference_ppl_median": 24.039149284362793,
5
  "n_samples": 1000,
6
  "model": "EleutherAI/pythia-160m",
7
  "max_new_tokens": 70,
8
- "beam_w1_equals_greedy_frac": 0.998,
9
- "reference_judge_ppl_median": 16.60109519958496
10
  },
11
  "results": [
12
  {
13
- "precision": 0.15680400502615022,
14
  "recall": 0.10761634910100995,
15
- "f1": 0.11683061986311886,
16
- "accuracy": 0.013858261701571426,
17
- "bleu": 0.45979362180213906,
18
- "self_ppl_mean": 2.129511192101869,
19
- "self_ppl_median": 1.977763465028044,
20
- "distinct_1": 0.03781326236416057,
21
- "distinct_2": 0.11821601289629231,
22
  "rep_4gram": 0.3296551592928797,
23
- "avg_words": 27.054,
24
  "name": "greedy",
25
  "strategy": "greedy",
26
  "params": {
27
  "name": "greedy",
28
  "strategy": "greedy"
29
  },
30
- "time_s": 24.53210139274597,
31
- "ms_per_prompt": 24.53210139274597,
32
  "gen_tokens_mean": 69.995,
33
- "judge_ppl_median": 3.7136964797973633,
34
- "judge_ppl_mean": 3.8388092192411425,
35
- "bertscore_p": 0.8487208485603333,
36
- "bertscore_r": 0.8463250994682312,
37
- "bertscore_f1": 0.8473963141441345
38
  },
39
  {
40
  "precision": 0.1568385962211188,
@@ -42,8 +42,8 @@
42
  "f1": 0.11688205526981743,
43
  "accuracy": 0.013886833130142854,
44
  "bleu": 0.4603869452185167,
45
- "self_ppl_mean": 2.1297556273252223,
46
- "self_ppl_median": 1.9777561918932842,
47
  "distinct_1": 0.03780348102435239,
48
  "distinct_2": 0.11810751697939449,
49
  "rep_4gram": 0.3296463873630552,
@@ -55,11 +55,11 @@
55
  "strategy": "beam",
56
  "width": 1
57
  },
58
- "time_s": 89.72963857650757,
59
- "ms_per_prompt": 89.72963857650757,
60
  "gen_tokens_mean": 69.995,
61
- "judge_ppl_median": 3.7136964797973633,
62
- "judge_ppl_mean": 3.839159709095955,
63
  "bertscore_p": 0.8486897945404053,
64
  "bertscore_r": 0.8463218808174133,
65
  "bertscore_f1": 0.8473787903785706
@@ -70,8 +70,8 @@
70
  "f1": 0.11012341441029258,
71
  "accuracy": 0.010045733414015445,
72
  "bleu": 0.29556628142570907,
73
- "self_ppl_mean": 1.8974248541153338,
74
- "self_ppl_median": 1.8301979892743205,
75
  "distinct_1": 0.045782342657342656,
76
  "distinct_2": 0.13661581137309292,
77
  "rep_4gram": 0.20882754144393487,
@@ -83,27 +83,27 @@
83
  "strategy": "beam",
84
  "width": 2
85
  },
86
- "time_s": 93.67243909835815,
87
- "ms_per_prompt": 93.67243909835815,
88
  "gen_tokens_mean": 69.827,
89
- "judge_ppl_median": 4.0844643115997314,
90
- "judge_ppl_mean": 5.812758823871612,
91
  "bertscore_p": 0.8609820604324341,
92
- "bertscore_r": 0.846573531627655,
93
  "bertscore_f1": 0.8535703420639038
94
  },
95
  {
96
- "precision": 0.197717653773812,
97
  "recall": 0.07404912467040367,
98
- "f1": 0.09570392741330655,
99
  "accuracy": 0.008065531697039307,
100
- "bleu": 0.3401904436011399,
101
- "self_ppl_mean": 1.7576851325086995,
102
- "self_ppl_median": 1.739890192921894,
103
- "distinct_1": 0.04027860266724259,
104
- "distinct_2": 0.11694537982991839,
105
- "rep_4gram": 0.23772850694366054,
106
- "avg_words": 18.521,
107
  "name": "beam_w4",
108
  "strategy": "beam",
109
  "params": {
@@ -111,27 +111,27 @@
111
  "strategy": "beam",
112
  "width": 4
113
  },
114
- "time_s": 106.44298911094666,
115
- "ms_per_prompt": 106.44298911094666,
116
  "gen_tokens_mean": 69.253,
117
- "judge_ppl_median": 3.722070574760437,
118
- "judge_ppl_mean": 549.2117587163449,
119
- "bertscore_p": 0.8594975471496582,
120
- "bertscore_r": 0.8445566892623901,
121
- "bertscore_f1": 0.8517829775810242
122
  },
123
  {
124
- "precision": 0.20247790936126098,
125
- "recall": 0.16240848409656986,
126
- "f1": 0.16326782514510493,
127
- "accuracy": 0.015387661973703906,
128
- "bleu": 0.6321177804113657,
129
- "self_ppl_mean": 5.788990184998834,
130
- "self_ppl_median": 5.7832007131546685,
131
- "distinct_1": 0.07138813761871816,
132
- "distinct_2": 0.35044483408013505,
133
- "rep_4gram": 0.03276803420190723,
134
- "avg_words": 31.798,
135
  "name": "topk_k5",
136
  "strategy": "top_k",
137
  "params": {
@@ -139,27 +139,27 @@
139
  "strategy": "top_k",
140
  "k": 5
141
  },
142
- "time_s": 24.93057608604431,
143
- "ms_per_prompt": 24.93057608604431,
144
- "gen_tokens_mean": 69.711,
145
- "judge_ppl_median": 8.327152252197266,
146
- "judge_ppl_mean": 10.00695155286789,
147
- "bertscore_p": 0.8572027683258057,
148
- "bertscore_r": 0.8528223037719727,
149
- "bertscore_f1": 0.8548986911773682
150
  },
151
  {
152
- "precision": 0.18981835217131812,
153
- "recall": 0.17505740033246367,
154
- "f1": 0.16608450826242258,
155
- "accuracy": 0.013520378544886719,
156
- "bleu": 0.5262730257755575,
157
- "self_ppl_mean": 10.425129562862075,
158
- "self_ppl_median": 10.068886870395898,
159
- "distinct_1": 0.08757376684110901,
160
- "distinct_2": 0.4628797205600252,
161
- "rep_4gram": 0.005379857536448407,
162
- "avg_words": 35.924,
163
  "name": "topk_k20",
164
  "strategy": "top_k",
165
  "params": {
@@ -167,24 +167,24 @@
167
  "strategy": "top_k",
168
  "k": 20
169
  },
170
- "time_s": 24.941646337509155,
171
- "ms_per_prompt": 24.941646337509155,
172
- "gen_tokens_mean": 69.067,
173
- "judge_ppl_median": 14.630431175231934,
174
- "judge_ppl_mean": 24.414003211975096
175
  },
176
  {
177
- "precision": 0.18100045337501575,
178
- "recall": 0.16393725762403472,
179
- "f1": 0.15556260822373996,
180
- "accuracy": 0.010887085233937758,
181
- "bleu": 0.40375922428576166,
182
- "self_ppl_mean": 14.773549448869355,
183
- "self_ppl_median": 13.841562918287725,
184
- "distinct_1": 0.10454026270702456,
185
- "distinct_2": 0.5344305657604702,
186
- "rep_4gram": 0.0027486902217292155,
187
- "avg_words": 35.02,
188
  "name": "topk_k50",
189
  "strategy": "top_k",
190
  "params": {
@@ -192,24 +192,24 @@
192
  "strategy": "top_k",
193
  "k": 50
194
  },
195
- "time_s": 24.886651039123535,
196
- "ms_per_prompt": 24.886651039123535,
197
- "gen_tokens_mean": 68.922,
198
- "judge_ppl_median": 21.331963539123535,
199
- "judge_ppl_mean": 501.72633791828156
200
  },
201
  {
202
- "precision": 0.20526678420165223,
203
- "recall": 0.15309069901051064,
204
- "f1": 0.15840480805376173,
205
- "accuracy": 0.014216155649643127,
206
- "bleu": 0.50328331736766,
207
- "self_ppl_mean": 7.068113830843848,
208
- "self_ppl_median": 6.912323629738417,
209
- "distinct_1": 0.09306409130816505,
210
- "distinct_2": 0.43222086318364494,
211
- "rep_4gram": 0.02030865920372129,
212
- "avg_words": 29.614,
213
  "name": "topk_k50_t0.7",
214
  "strategy": "top_k",
215
  "params": {
@@ -218,24 +218,24 @@
218
  "k": 50,
219
  "temperature": 0.7
220
  },
221
- "time_s": 24.90457844734192,
222
- "ms_per_prompt": 24.90457844734192,
223
- "gen_tokens_mean": 69.718,
224
- "judge_ppl_median": 10.698809146881104,
225
- "judge_ppl_mean": 15.45937124478817
226
  },
227
  {
228
- "precision": 0.20739706440864433,
229
- "recall": 0.1499013118788447,
230
- "f1": 0.15918566183599664,
231
- "accuracy": 0.014822320849886555,
232
- "bleu": 0.4478536424658865,
233
- "self_ppl_mean": 6.073593265666012,
234
- "self_ppl_median": 5.982498789525279,
235
- "distinct_1": 0.09500420993544766,
236
- "distinct_2": 0.41255817335660266,
237
- "rep_4gram": 0.03136431125812165,
238
- "avg_words": 28.504,
239
  "name": "topp_p0.5",
240
  "strategy": "top_p",
241
  "params": {
@@ -243,24 +243,24 @@
243
  "strategy": "top_p",
244
  "p": 0.5
245
  },
246
- "time_s": 27.650500059127808,
247
- "ms_per_prompt": 27.650500059127808,
248
- "gen_tokens_mean": 69.866,
249
- "judge_ppl_median": 9.648499011993408,
250
- "judge_ppl_mean": 13.219340204000472
251
  },
252
  {
253
- "precision": 0.1786455293948896,
254
- "recall": 0.15898556161207772,
255
- "f1": 0.15182183169853083,
256
- "accuracy": 0.01125190065874159,
257
- "bleu": 0.22743045693599412,
258
- "self_ppl_mean": 16.296575027955598,
259
- "self_ppl_median": 14.50375505361637,
260
- "distinct_1": 0.13724910579680155,
261
- "distinct_2": 0.5839322694603484,
262
- "rep_4gram": 0.003038437207161451,
263
- "avg_words": 33.829,
264
  "name": "topp_p0.8",
265
  "strategy": "top_p",
266
  "params": {
@@ -268,24 +268,24 @@
268
  "strategy": "top_p",
269
  "p": 0.8
270
  },
271
- "time_s": 27.59784722328186,
272
- "ms_per_prompt": 27.59784722328186,
273
- "gen_tokens_mean": 69.023,
274
- "judge_ppl_median": 23.389395713806152,
275
- "judge_ppl_mean": 4002.7205463142395
276
  },
277
  {
278
- "precision": 0.15578726318266997,
279
- "recall": 0.14156256433661007,
280
- "f1": 0.13438127406575798,
281
- "accuracy": 0.008718096877912114,
282
- "bleu": 0.16851247100645653,
283
- "self_ppl_mean": 40.52525353706895,
284
- "self_ppl_median": 34.312852488083806,
285
- "distinct_1": 0.18865485737786786,
286
- "distinct_2": 0.7045688350983359,
287
- "rep_4gram": 0.0005719175842843666,
288
- "avg_words": 34.041,
289
  "name": "topp_p0.95",
290
  "strategy": "top_p",
291
  "params": {
@@ -293,24 +293,24 @@
293
  "strategy": "top_p",
294
  "p": 0.95
295
  },
296
- "time_s": 27.591065883636475,
297
- "ms_per_prompt": 27.591065883636475,
298
- "gen_tokens_mean": 68.753,
299
- "judge_ppl_median": 58.844425201416016,
300
- "judge_ppl_mean": 4085.3288768868447
301
  },
302
  {
303
- "precision": 0.20651589086843022,
304
- "recall": 0.1484358064877369,
305
- "f1": 0.15685438845860386,
306
- "accuracy": 0.014285365431144705,
307
- "bleu": 0.6053996783877227,
308
- "self_ppl_mean": 5.729693244652217,
309
- "self_ppl_median": 5.577883722137798,
310
- "distinct_1": 0.09334971047552203,
311
- "distinct_2": 0.4059283506092017,
312
- "rep_4gram": 0.03475423802329605,
313
- "avg_words": 28.495,
314
  "name": "topp_p0.9_t0.7",
315
  "strategy": "top_p",
316
  "params": {
@@ -319,92 +319,92 @@
319
  "p": 0.9,
320
  "temperature": 0.7
321
  },
322
- "time_s": 27.645728826522827,
323
- "ms_per_prompt": 27.645728826522827,
324
- "gen_tokens_mean": 69.939,
325
- "judge_ppl_median": 9.250245571136475,
326
- "judge_ppl_mean": 11.254222588062287
327
  }
328
  ],
329
  "beam_timing": [
330
  {
331
  "mode": "natural",
332
  "width": 1,
333
- "mean_s_per_prompt": 0.6848450946807861,
334
- "std_s": 0.03387934526472246,
335
- "mean_ms_per_token": 9.78350135258266,
336
  "mean_output_tokens": 70.0,
337
- "total_s": 68.48450946807861,
338
  "n_prompts": 100
339
  },
340
  {
341
  "mode": "natural",
342
  "width": 2,
343
- "mean_s_per_prompt": 0.6798255443572998,
344
- "std_s": 0.009905260321141293,
345
- "mean_ms_per_token": 9.7313968216702,
346
  "mean_output_tokens": 69.86,
347
- "total_s": 67.98255443572998,
348
  "n_prompts": 100
349
  },
350
  {
351
  "mode": "natural",
352
  "width": 4,
353
- "mean_s_per_prompt": 0.6822133040428162,
354
- "std_s": 0.027737611702376378,
355
- "mean_ms_per_token": 9.803958435890708,
356
  "mean_output_tokens": 69.59,
357
- "total_s": 68.22133040428162,
358
  "n_prompts": 100
359
  },
360
  {
361
  "mode": "natural",
362
  "width": 8,
363
- "mean_s_per_prompt": 0.7037396359443665,
364
- "std_s": 0.03137733007915173,
365
- "mean_ms_per_token": 10.1683868747084,
366
  "mean_output_tokens": 69.34,
367
- "total_s": 70.37396359443665,
368
  "n_prompts": 100
369
  },
370
  {
371
  "mode": "fixed",
372
  "width": 1,
373
- "mean_s_per_prompt": 0.6784237551689148,
374
- "std_s": 0.0016587773214447714,
375
- "mean_ms_per_token": 9.691767930984499,
376
  "mean_output_tokens": 70.0,
377
- "total_s": 67.84237551689148,
378
  "n_prompts": 100
379
  },
380
  {
381
  "mode": "fixed",
382
  "width": 2,
383
- "mean_s_per_prompt": 0.6811458969116211,
384
- "std_s": 0.001066027939126381,
385
- "mean_ms_per_token": 9.730655670166016,
386
  "mean_output_tokens": 70.0,
387
- "total_s": 68.11458969116211,
388
  "n_prompts": 100
389
  },
390
  {
391
  "mode": "fixed",
392
  "width": 4,
393
- "mean_s_per_prompt": 0.6865559673309326,
394
- "std_s": 0.0012468601240148754,
395
- "mean_ms_per_token": 9.807942390441896,
396
  "mean_output_tokens": 70.0,
397
- "total_s": 68.65559673309326,
398
  "n_prompts": 100
399
  },
400
  {
401
  "mode": "fixed",
402
  "width": 8,
403
- "mean_s_per_prompt": 0.7092588877677918,
404
- "std_s": 0.0009664558877848735,
405
- "mean_ms_per_token": 10.132269825254168,
406
  "mean_output_tokens": 70.0,
407
- "total_s": 70.92588877677917,
408
  "n_prompts": 100
409
  }
410
  ],
@@ -491,13 +491,10 @@
491
  "timing_samples": 100,
492
  "judge_model": "EleutherAI/pythia-410m",
493
  "use_bertscore": true,
494
- "out_dir": "/home2/ojas.k/a2/outputs/decoding",
495
  "hf_user": null,
496
  "repo_name": "anlp-a2-decoding",
497
  "push_to_hub": true,
498
- "private": false,
499
- "wandb": true,
500
- "wandb_project": "anlp-a2",
501
- "wandb_entity": null
502
  }
503
  }
 
1
  {
2
  "summary": {
3
+ "reference_ppl_mean": 27.426984312057495,
4
+ "reference_ppl_median": 24.0395565032959,
5
  "n_samples": 1000,
6
  "model": "EleutherAI/pythia-160m",
7
  "max_new_tokens": 70,
8
+ "beam_w1_equals_greedy_frac": 0.997,
9
+ "reference_judge_ppl_median": 16.601248741149902
10
  },
11
  "results": [
12
  {
13
+ "precision": 0.156788215552466,
14
  "recall": 0.10761634910100995,
15
+ "f1": 0.11682790615891263,
16
+ "accuracy": 0.013836985105826744,
17
+ "bleu": 0.45975591061692944,
18
+ "self_ppl_mean": 2.129623557711418,
19
+ "self_ppl_median": 1.9777633712182454,
20
+ "distinct_1": 0.037811864720014784,
21
+ "distinct_2": 0.11828823642295144,
22
  "rep_4gram": 0.3296551592928797,
23
+ "avg_words": 27.055,
24
  "name": "greedy",
25
  "strategy": "greedy",
26
  "params": {
27
  "name": "greedy",
28
  "strategy": "greedy"
29
  },
30
+ "time_s": 26.33022141456604,
31
+ "ms_per_prompt": 26.33022141456604,
32
  "gen_tokens_mean": 69.995,
33
+ "judge_ppl_median": 3.7136934995651245,
34
+ "judge_ppl_mean": 3.8431873861551287,
35
+ "bertscore_p": 0.8487190008163452,
36
+ "bertscore_r": 0.8463276028633118,
37
+ "bertscore_f1": 0.847396731376648
38
  },
39
  {
40
  "precision": 0.1568385962211188,
 
42
  "f1": 0.11688205526981743,
43
  "accuracy": 0.013886833130142854,
44
  "bleu": 0.4603869452185167,
45
+ "self_ppl_mean": 2.1293727944369447,
46
+ "self_ppl_median": 1.977726615504968,
47
  "distinct_1": 0.03780348102435239,
48
  "distinct_2": 0.11810751697939449,
49
  "rep_4gram": 0.3296463873630552,
 
55
  "strategy": "beam",
56
  "width": 1
57
  },
58
+ "time_s": 110.271555185318,
59
+ "ms_per_prompt": 110.271555185318,
60
  "gen_tokens_mean": 69.995,
61
+ "judge_ppl_median": 3.7136934995651245,
62
+ "judge_ppl_mean": 3.839161422610283,
63
  "bertscore_p": 0.8486897945404053,
64
  "bertscore_r": 0.8463218808174133,
65
  "bertscore_f1": 0.8473787903785706
 
70
  "f1": 0.11012341441029258,
71
  "accuracy": 0.010045733414015445,
72
  "bleu": 0.29556628142570907,
73
+ "self_ppl_mean": 1.8974243870210339,
74
+ "self_ppl_median": 1.8302103567597376,
75
  "distinct_1": 0.045782342657342656,
76
  "distinct_2": 0.13661581137309292,
77
  "rep_4gram": 0.20882754144393487,
 
83
  "strategy": "beam",
84
  "width": 2
85
  },
86
+ "time_s": 107.95216870307922,
87
+ "ms_per_prompt": 107.95216870307922,
88
  "gen_tokens_mean": 69.827,
89
+ "judge_ppl_median": 4.08447003364563,
90
+ "judge_ppl_mean": 5.812772500276566,
91
  "bertscore_p": 0.8609820604324341,
92
+ "bertscore_r": 0.8465735912322998,
93
  "bertscore_f1": 0.8535703420639038
94
  },
95
  {
96
+ "precision": 0.1976862812247924,
97
  "recall": 0.07404912467040367,
98
+ "f1": 0.09568651928951545,
99
  "accuracy": 0.008065531697039307,
100
+ "bleu": 0.340259807839598,
101
+ "self_ppl_mean": 1.7576852720858818,
102
+ "self_ppl_median": 1.739890666494589,
103
+ "distinct_1": 0.040265558374264585,
104
+ "distinct_2": 0.11690534603754207,
105
+ "rep_4gram": 0.23782374503889867,
106
+ "avg_words": 18.527,
107
  "name": "beam_w4",
108
  "strategy": "beam",
109
  "params": {
 
111
  "strategy": "beam",
112
  "width": 4
113
  },
114
+ "time_s": 118.95717024803162,
115
+ "ms_per_prompt": 118.95717024803162,
116
  "gen_tokens_mean": 69.253,
117
+ "judge_ppl_median": 3.7220958471298218,
118
+ "judge_ppl_mean": 549.211596192956,
119
+ "bertscore_p": 0.8594744801521301,
120
+ "bertscore_r": 0.8445523977279663,
121
+ "bertscore_f1": 0.851769208908081
122
  },
123
  {
124
+ "precision": 0.20828856855773425,
125
+ "recall": 0.16455016427602215,
126
+ "f1": 0.16573516044843375,
127
+ "accuracy": 0.015756478767379786,
128
+ "bleu": 0.5589188575413241,
129
+ "self_ppl_mean": 5.8339501863641905,
130
+ "self_ppl_median": 5.838575948334771,
131
+ "distinct_1": 0.07212438521339044,
132
+ "distinct_2": 0.35802064558413893,
133
+ "rep_4gram": 0.028893427447938293,
134
+ "avg_words": 31.515,
135
  "name": "topk_k5",
136
  "strategy": "top_k",
137
  "params": {
 
139
  "strategy": "top_k",
140
  "k": 5
141
  },
142
+ "time_s": 28.06767702102661,
143
+ "ms_per_prompt": 28.06767702102661,
144
+ "gen_tokens_mean": 69.697,
145
+ "judge_ppl_median": 8.458185195922852,
146
+ "judge_ppl_mean": 9.329122072458267,
147
+ "bertscore_p": 0.8579514026641846,
148
+ "bertscore_r": 0.8530383706092834,
149
+ "bertscore_f1": 0.8553735613822937
150
  },
151
  {
152
+ "precision": 0.1919000986912784,
153
+ "recall": 0.1684283187083047,
154
+ "f1": 0.1622514196784391,
155
+ "accuracy": 0.013794441209097598,
156
+ "bleu": 0.44804585179427503,
157
+ "self_ppl_mean": 10.859201539564154,
158
+ "self_ppl_median": 9.966973243847942,
159
+ "distinct_1": 0.09244218671549662,
160
+ "distinct_2": 0.4732998630762209,
161
+ "rep_4gram": 0.004260111395545372,
162
+ "avg_words": 33.859,
163
  "name": "topk_k20",
164
  "strategy": "top_k",
165
  "params": {
 
167
  "strategy": "top_k",
168
  "k": 20
169
  },
170
+ "time_s": 27.953922510147095,
171
+ "ms_per_prompt": 27.953922510147095,
172
+ "gen_tokens_mean": 69.165,
173
+ "judge_ppl_median": 14.761709213256836,
174
+ "judge_ppl_mean": 50.361770061254504
175
  },
176
  {
177
+ "precision": 0.17549255996014995,
178
+ "recall": 0.16199041954465634,
179
+ "f1": 0.15203213911919755,
180
+ "accuracy": 0.011857919480724241,
181
+ "bleu": 0.44096547944277925,
182
+ "self_ppl_mean": 15.653030294977679,
183
+ "self_ppl_median": 13.604209219791796,
184
+ "distinct_1": 0.106938636169668,
185
+ "distinct_2": 0.5367388758782201,
186
+ "rep_4gram": 0.002577424448111978,
187
+ "avg_words": 35.151,
188
  "name": "topk_k50",
189
  "strategy": "top_k",
190
  "params": {
 
192
  "strategy": "top_k",
193
  "k": 50
194
  },
195
+ "time_s": 27.92880129814148,
196
+ "ms_per_prompt": 27.92880129814148,
197
+ "gen_tokens_mean": 68.635,
198
+ "judge_ppl_median": 20.48440647125244,
199
+ "judge_ppl_mean": 72.74511349225044
200
  },
201
  {
202
+ "precision": 0.20069336987864045,
203
+ "recall": 0.14639978279849664,
204
+ "f1": 0.1519355828806502,
205
+ "accuracy": 0.01273002667646736,
206
+ "bleu": 0.5404882348932311,
207
+ "self_ppl_mean": 7.576500820452772,
208
+ "self_ppl_median": 6.75727633465444,
209
+ "distinct_1": 0.09244264507422402,
210
+ "distinct_2": 0.43183742591024554,
211
+ "rep_4gram": 0.021025283581782708,
212
+ "avg_words": 28.158,
213
  "name": "topk_k50_t0.7",
214
  "strategy": "top_k",
215
  "params": {
 
218
  "k": 50,
219
  "temperature": 0.7
220
  },
221
+ "time_s": 27.997068405151367,
222
+ "ms_per_prompt": 27.997068405151367,
223
+ "gen_tokens_mean": 69.563,
224
+ "judge_ppl_median": 10.548301219940186,
225
+ "judge_ppl_mean": 1104.2365723810196
226
  },
227
  {
228
+ "precision": 0.21224675405441254,
229
+ "recall": 0.14685590502611967,
230
+ "f1": 0.15687408460192412,
231
+ "accuracy": 0.013971389777601836,
232
+ "bleu": 0.5397175501288309,
233
+ "self_ppl_mean": 6.038022332522205,
234
+ "self_ppl_median": 5.858416877967823,
235
+ "distinct_1": 0.1009201217053411,
236
+ "distinct_2": 0.4334855403348554,
237
+ "rep_4gram": 0.02484618782785406,
238
+ "avg_words": 27.279,
239
  "name": "topp_p0.5",
240
  "strategy": "top_p",
241
  "params": {
 
243
  "strategy": "top_p",
244
  "p": 0.5
245
  },
246
+ "time_s": 31.63517665863037,
247
+ "ms_per_prompt": 31.635176658630375,
248
+ "gen_tokens_mean": 69.823,
249
+ "judge_ppl_median": 9.4617280960083,
250
+ "judge_ppl_mean": 1095.229723884821
251
  },
252
  {
253
+ "precision": 0.1754558860783798,
254
+ "recall": 0.15128935460540213,
255
+ "f1": 0.14627172467982152,
256
+ "accuracy": 0.010996779233892341,
257
+ "bleu": 0.3233499868741911,
258
+ "self_ppl_mean": 17.14533789994608,
259
+ "self_ppl_median": 14.304176726370176,
260
+ "distinct_1": 0.13880592419250545,
261
+ "distinct_2": 0.5860382392174299,
262
+ "rep_4gram": 0.003378257483535917,
263
+ "avg_words": 32.477,
264
  "name": "topp_p0.8",
265
  "strategy": "top_p",
266
  "params": {
 
268
  "strategy": "top_p",
269
  "p": 0.8
270
  },
271
+ "time_s": 31.48725962638855,
272
+ "ms_per_prompt": 31.48725962638855,
273
+ "gen_tokens_mean": 69.009,
274
+ "judge_ppl_median": 23.211604118347168,
275
+ "judge_ppl_mean": 74.37143414640427
276
  },
277
  {
278
+ "precision": 0.15602106748659775,
279
+ "recall": 0.14042348676499086,
280
+ "f1": 0.13287360175699992,
281
+ "accuracy": 0.00936425787467936,
282
+ "bleu": 0.16536685767069867,
283
+ "self_ppl_mean": 39.594243798258624,
284
+ "self_ppl_median": 33.30959383748847,
285
+ "distinct_1": 0.18891279361056262,
286
+ "distinct_2": 0.701587687076895,
287
+ "rep_4gram": 0.0007256760411977545,
288
+ "avg_words": 33.931,
289
  "name": "topp_p0.95",
290
  "strategy": "top_p",
291
  "params": {
 
293
  "strategy": "top_p",
294
  "p": 0.95
295
  },
296
+ "time_s": 31.551427125930786,
297
+ "ms_per_prompt": 31.551427125930786,
298
+ "gen_tokens_mean": 68.636,
299
+ "judge_ppl_median": 54.274309158325195,
300
+ "judge_ppl_mean": 297.4285540444851
301
  },
302
  {
303
+ "precision": 0.19970724391003492,
304
+ "recall": 0.14364941747564147,
305
+ "f1": 0.15108641777377704,
306
+ "accuracy": 0.01395932104782926,
307
+ "bleu": 0.5577671411688147,
308
+ "self_ppl_mean": 5.73871449144444,
309
+ "self_ppl_median": 5.589571006013263,
310
+ "distinct_1": 0.09601266733841946,
311
+ "distinct_2": 0.408413901228116,
312
+ "rep_4gram": 0.034714608915333796,
313
+ "avg_words": 27.788,
314
  "name": "topp_p0.9_t0.7",
315
  "strategy": "top_p",
316
  "params": {
 
319
  "p": 0.9,
320
  "temperature": 0.7
321
  },
322
+ "time_s": 31.56927514076233,
323
+ "ms_per_prompt": 31.569275140762326,
324
+ "gen_tokens_mean": 69.865,
325
+ "judge_ppl_median": 9.213471412658691,
326
+ "judge_ppl_mean": 1094.454825058937
327
  }
328
  ],
329
  "beam_timing": [
330
  {
331
  "mode": "natural",
332
  "width": 1,
333
+ "mean_s_per_prompt": 0.781981463432312,
334
+ "std_s": 0.01558768772931337,
335
+ "mean_ms_per_token": 11.171163763318747,
336
  "mean_output_tokens": 70.0,
337
+ "total_s": 78.1981463432312,
338
  "n_prompts": 100
339
  },
340
  {
341
  "mode": "natural",
342
  "width": 2,
343
+ "mean_s_per_prompt": 0.793917977809906,
344
+ "std_s": 0.022438927991426688,
345
+ "mean_ms_per_token": 11.364886324126998,
346
  "mean_output_tokens": 69.86,
347
+ "total_s": 79.3917977809906,
348
  "n_prompts": 100
349
  },
350
  {
351
  "mode": "natural",
352
  "width": 4,
353
+ "mean_s_per_prompt": 0.8327497005462646,
354
+ "std_s": 0.033384079158313755,
355
+ "mean_ms_per_token": 11.970075382877122,
356
  "mean_output_tokens": 69.59,
357
+ "total_s": 83.27497005462646,
358
  "n_prompts": 100
359
  },
360
  {
361
  "mode": "natural",
362
  "width": 8,
363
+ "mean_s_per_prompt": 0.8716441249847412,
364
+ "std_s": 0.043158742726592765,
365
+ "mean_ms_per_token": 12.592297802702353,
366
  "mean_output_tokens": 69.34,
367
+ "total_s": 87.16441249847412,
368
  "n_prompts": 100
369
  },
370
  {
371
  "mode": "fixed",
372
  "width": 1,
373
+ "mean_s_per_prompt": 0.7738243556022644,
374
+ "std_s": 0.01421080819544866,
375
+ "mean_ms_per_token": 11.054633651460922,
376
  "mean_output_tokens": 70.0,
377
+ "total_s": 77.38243556022644,
378
  "n_prompts": 100
379
  },
380
  {
381
  "mode": "fixed",
382
  "width": 2,
383
+ "mean_s_per_prompt": 0.7971447992324829,
384
+ "std_s": 0.02233234077874458,
385
+ "mean_ms_per_token": 11.387782846178329,
386
  "mean_output_tokens": 70.0,
387
+ "total_s": 79.71447992324829,
388
  "n_prompts": 100
389
  },
390
  {
391
  "mode": "fixed",
392
  "width": 4,
393
+ "mean_s_per_prompt": 0.8367985081672669,
394
+ "std_s": 0.013029678554817858,
395
+ "mean_ms_per_token": 11.954264402389526,
396
  "mean_output_tokens": 70.0,
397
+ "total_s": 83.67985081672668,
398
  "n_prompts": 100
399
  },
400
  {
401
  "mode": "fixed",
402
  "width": 8,
403
+ "mean_s_per_prompt": 0.8789880871772766,
404
+ "std_s": 0.012277801042479799,
405
+ "mean_ms_per_token": 12.556972673961095,
406
  "mean_output_tokens": 70.0,
407
+ "total_s": 87.89880871772766,
408
  "n_prompts": 100
409
  }
410
  ],
 
491
  "timing_samples": 100,
492
  "judge_model": "EleutherAI/pythia-410m",
493
  "use_bertscore": true,
494
+ "out_dir": "/kaggle/working/outputs/decoding",
495
  "hf_user": null,
496
  "repo_name": "anlp-a2-decoding",
497
  "push_to_hub": true,
498
+ "private": false
 
 
 
499
  }
500
  }
generations.json CHANGED
The diff for this file is too large to render. See raw diff