remove __pycache__ and a decoder backup file that were uploaded by mistake
Browse files- maram_inference/maram/__pycache__/__init__.cpython-312.pyc +0 -0
- maram_inference/maram/__pycache__/load.cpython-312.pyc +0 -0
- maram_inference/maram/data/__pycache__/__init__.cpython-312.pyc +0 -0
- maram_inference/maram/data/__pycache__/label.cpython-312.pyc +0 -0
- maram_inference/maram/decode/__pycache__/__init__.cpython-312.pyc +0 -0
- maram_inference/maram/decode/__pycache__/decode.cpython-312.pyc +0 -0
- maram_inference/maram/decode/decode.py.pre_events_20261031T0701 +0 -472
- maram_inference/maram/model/__pycache__/__init__.cpython-312.pyc +0 -0
- maram_inference/maram/model/__pycache__/judge.cpython-312.pyc +0 -0
- maram_inference/maram/model/__pycache__/mask.cpython-312.pyc +0 -0
- maram_inference/maram/model/__pycache__/model.cpython-312.pyc +0 -0
- maram_inference/maram/tokenizer/__pycache__/__init__.cpython-312.pyc +0 -0
- maram_inference/maram/tokenizer/__pycache__/maram_tokenizer.cpython-312.pyc +0 -0
maram_inference/maram/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (151 Bytes)
|
|
|
maram_inference/maram/__pycache__/load.cpython-312.pyc
DELETED
|
Binary file (1.27 kB)
|
|
|
maram_inference/maram/data/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (156 Bytes)
|
|
|
maram_inference/maram/data/__pycache__/label.cpython-312.pyc
DELETED
|
Binary file (11.6 kB)
|
|
|
maram_inference/maram/decode/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (158 Bytes)
|
|
|
maram_inference/maram/decode/__pycache__/decode.cpython-312.pyc
DELETED
|
Binary file (44.6 kB)
|
|
|
maram_inference/maram/decode/decode.py.pre_events_20261031T0701
DELETED
|
@@ -1,472 +0,0 @@
|
|
| 1 |
-
"""MARAM decoding: greedy1 | bestofK | native, sharing the prompt KV cache.
|
| 2 |
-
|
| 3 |
-
The physical sequence grows "as generated": each step appends one token per stream slot (or <pad> for a
|
| 4 |
-
finished/dead slot), block_idx = current block, and a <chk> query after every N content tokens. Attention
|
| 5 |
-
for the new queries is computed over the whole physical cache with the SAME mask_mod as training
|
| 6 |
-
(`maram.model.mask.build_mask`) evaluated on the concatenated (cache + new) metadata, materialised densely
|
| 7 |
-
(queries are few, so this is exact and cheap).
|
| 8 |
-
|
| 9 |
-
native: every N tokens a <chk> forward; score_k = sigmoid(correct) - 0.1*sigmoid(remaining);
|
| 10 |
-
kill the lowest-scored unfinished stream; fork the highest-scored unfinished stream's KV entries into the
|
| 11 |
-
killed slot (copies of its K/V with the new slot's seg id) with header <s_k><strat:z>, z = a strategy the
|
| 12 |
-
killed stream did not have (preferring one the parent does not have either). --no_respawn = prune only.
|
| 13 |
-
"""
|
| 14 |
-
from __future__ import annotations
|
| 15 |
-
import math, re
|
| 16 |
-
from dataclasses import dataclass, field
|
| 17 |
-
from typing import List, Optional
|
| 18 |
-
import numpy as np, torch, torch.nn.functional as F
|
| 19 |
-
|
| 20 |
-
from maram.model.mask import build_mask, PAD_SEG
|
| 21 |
-
from maram.model.model import MaramModel, ModelConfig
|
| 22 |
-
from maram.tokenizer.maram_tokenizer import (PAD, PROMPT, CHK, END, STREAM_IDS, STRAT_IDS, STRAT_NAMES, FIRST_MERGE_ID)
|
| 23 |
-
|
| 24 |
-
ANSWER_RE = re.compile(r"Answer\s*:\s*(.+)", re.I)
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
class LayerKV:
|
| 28 |
-
def __init__(self, B, H, S, hd, device, dtype):
|
| 29 |
-
self.k = torch.zeros(B, H, S, hd, device=device, dtype=dtype)
|
| 30 |
-
self.v = torch.zeros(B, H, S, hd, device=device, dtype=dtype)
|
| 31 |
-
self.len = 0
|
| 32 |
-
|
| 33 |
-
def update(self, k_new, v_new):
|
| 34 |
-
n = k_new.shape[2]
|
| 35 |
-
self.k[:, :, self.len:self.len + n] = k_new
|
| 36 |
-
self.v[:, :, self.len:self.len + n] = v_new
|
| 37 |
-
return self.k[:, :, :self.len + n], self.v[:, :, :self.len + n]
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
class KVCache:
|
| 41 |
-
def __init__(self, model: MaramModel, B: int, S: int, device, dtype):
|
| 42 |
-
cfg = model.cfg
|
| 43 |
-
self.layers = [LayerKV(B, cfg.n_heads, S, cfg.head_dim, device, dtype) for _ in range(cfg.n_layers)]
|
| 44 |
-
self.seg = torch.full((B, S), PAD_SEG, dtype=torch.long, device=device)
|
| 45 |
-
self.pos = torch.zeros((B, S), dtype=torch.long, device=device)
|
| 46 |
-
self.block = torch.zeros((B, S), dtype=torch.long, device=device)
|
| 47 |
-
self.B, self.S_alloc, self.len, self.device = B, S, 0, device
|
| 48 |
-
|
| 49 |
-
def commit(self, n, seg, pos, block):
|
| 50 |
-
self.seg[:, self.len:self.len + n] = seg
|
| 51 |
-
self.pos[:, self.len:self.len + n] = pos
|
| 52 |
-
self.block[:, self.len:self.len + n] = block
|
| 53 |
-
self.len += n
|
| 54 |
-
for l in self.layers:
|
| 55 |
-
l.len = self.len
|
| 56 |
-
|
| 57 |
-
def expand_rows(self, reps: int):
|
| 58 |
-
"""Repeat every batch row `reps` times (share a prefilled prompt across samples)."""
|
| 59 |
-
for l in self.layers:
|
| 60 |
-
l.k = l.k.repeat_interleave(reps, dim=0); l.v = l.v.repeat_interleave(reps, dim=0)
|
| 61 |
-
self.seg = self.seg.repeat_interleave(reps, dim=0); self.pos = self.pos.repeat_interleave(reps, dim=0)
|
| 62 |
-
self.block = self.block.repeat_interleave(reps, dim=0); self.B *= reps
|
| 63 |
-
|
| 64 |
-
def invalidate(self, row, seg_id):
|
| 65 |
-
"""Mask every cache entry of (row, seg): a killed stream disappears for all later queries."""
|
| 66 |
-
m = self.seg[row, :self.len] == seg_id
|
| 67 |
-
self.seg[row, :self.len][m] = PAD_SEG
|
| 68 |
-
return int(m.sum())
|
| 69 |
-
|
| 70 |
-
def fork(self, rows, src_seg, dst_seg):
|
| 71 |
-
"""For each (row, src, dst): append copies of all cache entries with seg==src relabelled seg=dst."""
|
| 72 |
-
if not rows:
|
| 73 |
-
return 0
|
| 74 |
-
counts = []
|
| 75 |
-
idxs = []
|
| 76 |
-
for r, s, d in zip(rows, src_seg, dst_seg):
|
| 77 |
-
ix = torch.nonzero(self.seg[r, :self.len] == s).flatten()
|
| 78 |
-
idxs.append(ix); counts.append(len(ix))
|
| 79 |
-
m = max(counts)
|
| 80 |
-
if self.len + m > self.S_alloc:
|
| 81 |
-
raise RuntimeError("KV cache too small for fork")
|
| 82 |
-
for r, s, d, ix in zip(rows, src_seg, dst_seg, idxs):
|
| 83 |
-
n = len(ix)
|
| 84 |
-
if n == 0:
|
| 85 |
-
continue
|
| 86 |
-
for l in self.layers:
|
| 87 |
-
l.k[r, :, self.len:self.len + n] = l.k[r, :, ix]
|
| 88 |
-
l.v[r, :, self.len:self.len + n] = l.v[r, :, ix]
|
| 89 |
-
self.seg[r, self.len:self.len + n] = d
|
| 90 |
-
self.pos[r, self.len:self.len + n] = self.pos[r, ix]
|
| 91 |
-
self.block[r, self.len:self.len + n] = self.block[r, ix]
|
| 92 |
-
self.len += m
|
| 93 |
-
for l in self.layers:
|
| 94 |
-
l.len = self.len
|
| 95 |
-
return m
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
@dataclass
|
| 99 |
-
class Stream:
|
| 100 |
-
slot: int # 0-based -> seg = slot+1, header STREAM_IDS[slot]
|
| 101 |
-
strat: int
|
| 102 |
-
tokens: List[int] = field(default_factory=list) # content tokens incl <end>
|
| 103 |
-
n_fed: int = 0 # tokens fed so far (headers + content) -> next pos = P + n_fed
|
| 104 |
-
finished: bool = False
|
| 105 |
-
dead: bool = False
|
| 106 |
-
score: float = float("nan")
|
| 107 |
-
p_correct: float = float("nan")
|
| 108 |
-
p_remain: float = float("nan")
|
| 109 |
-
last_h: Optional[torch.Tensor] = None
|
| 110 |
-
pending: Optional[int] = None # token to feed next (sampled last step)
|
| 111 |
-
parent: Optional[int] = None
|
| 112 |
-
born_block: int = 0
|
| 113 |
-
generated: int = 0 # sampled tokens (counted for cost), incl <end>
|
| 114 |
-
gen: int = 0 # occupant index of this slot (0 = original, +1 per respawn)
|
| 115 |
-
temp: Optional[float] = None # sampling temperature of this stream (None = run-level `temperature`)
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
class Decoder:
|
| 119 |
-
def __init__(self, model: MaramModel, tokenizer, device="cuda", dtype=torch.bfloat16, judge_mode="full"):
|
| 120 |
-
self.model = model.eval().to(device)
|
| 121 |
-
self.tk = tokenizer
|
| 122 |
-
self.device = device; self.dtype = dtype
|
| 123 |
-
self.cfg = model.cfg
|
| 124 |
-
self.judge_mode = judge_mode
|
| 125 |
-
|
| 126 |
-
# ------------------------------------------------------------------ core forward over the cache
|
| 127 |
-
@torch.no_grad()
|
| 128 |
-
def _forward(self, cache: KVCache, tokens, seg, pos, block):
|
| 129 |
-
"""tokens/seg/pos/block: [B, n] new queries. Appends them to the cache. Returns hidden [B,n,d]."""
|
| 130 |
-
B, n = tokens.shape
|
| 131 |
-
S = cache.len
|
| 132 |
-
seg_all = torch.cat([cache.seg[:, :S], seg], 1); pos_all = torch.cat([cache.pos[:, :S], pos], 1)
|
| 133 |
-
blk_all = torch.cat([cache.block[:, :S], block], 1)
|
| 134 |
-
mm = build_mask(seg_all, pos_all, blk_all, self.cfg.K, self.cfg.N)
|
| 135 |
-
b = torch.arange(B, device=self.device)[:, None, None]
|
| 136 |
-
q = (S + torch.arange(n, device=self.device))[None, :, None]
|
| 137 |
-
kv = torch.arange(S + n, device=self.device)[None, None, :]
|
| 138 |
-
dense = mm(b, 0, q, kv) # [B, n, S+n]
|
| 139 |
-
with torch.autocast(self.device if self.device == "cuda" else "cpu", dtype=self.dtype, enabled=self.dtype != torch.float32):
|
| 140 |
-
h = self.model.hidden_states(tokens, pos, None, dense, cache.layers)
|
| 141 |
-
cache.commit(n, seg, pos, block)
|
| 142 |
-
return h
|
| 143 |
-
|
| 144 |
-
def _logits(self, h):
|
| 145 |
-
return self.model.lm_head(h).float()
|
| 146 |
-
|
| 147 |
-
def _judge(self, h_chk, h_last):
|
| 148 |
-
with torch.autocast(self.device if self.device == "cuda" else "cpu", dtype=self.dtype, enabled=self.dtype != torch.float32):
|
| 149 |
-
return self.model.judge(h_chk, h_last, self.judge_mode).float()
|
| 150 |
-
|
| 151 |
-
def _sample(self, logits, temperature):
|
| 152 |
-
if temperature <= 0:
|
| 153 |
-
return logits.argmax(-1)
|
| 154 |
-
return torch.multinomial(torch.softmax(logits / temperature, -1), 1).squeeze(-1)
|
| 155 |
-
|
| 156 |
-
# ------------------------------------------------------------------ prompt prefill (shared)
|
| 157 |
-
def prefill(self, prompts: List[List[int]], K_slots: int, max_new: int, extra: int = 0):
|
| 158 |
-
"""prompts: list of token id lists (<=256). Builds a cache sized for the run and prefills the prompt
|
| 159 |
-
region (left-aligned, pad masked). Returns cache and P per row."""
|
| 160 |
-
B = len(prompts)
|
| 161 |
-
Pm = max(len(p) for p in prompts) + 1
|
| 162 |
-
S = Pm + K_slots * (max_new + 2 + 2 * 8) + 2 * 8 + extra
|
| 163 |
-
cache = KVCache(self.model, B, S, self.device, self.dtype)
|
| 164 |
-
toks = torch.full((B, Pm), PAD, dtype=torch.long, device=self.device)
|
| 165 |
-
seg = torch.full((B, Pm), PAD_SEG, dtype=torch.long, device=self.device)
|
| 166 |
-
pos = torch.zeros((B, Pm), dtype=torch.long, device=self.device)
|
| 167 |
-
P = torch.zeros(B, dtype=torch.long, device=self.device)
|
| 168 |
-
for i, p in enumerate(prompts):
|
| 169 |
-
toks[i, 0] = PROMPT; toks[i, 1:1 + len(p)] = torch.tensor(p, device=self.device)
|
| 170 |
-
seg[i, :1 + len(p)] = 0; pos[i, :1 + len(p)] = torch.arange(1 + len(p), device=self.device)
|
| 171 |
-
P[i] = 1 + len(p)
|
| 172 |
-
self._forward(cache, toks, seg, pos, torch.zeros_like(pos))
|
| 173 |
-
return cache, P
|
| 174 |
-
|
| 175 |
-
# ------------------------------------------------------------------ generic multi-stream loop
|
| 176 |
-
@torch.no_grad()
|
| 177 |
-
def run_streams(self, prompts, strats: List[List[int]], temperature: float, max_new: int = 512,
|
| 178 |
-
use_judge: bool = False, respawn: bool = True, N: Optional[int] = None, sample_seed: int = 1337,
|
| 179 |
-
slot_temps: Optional[List[float]] = None, strat_pool: Optional[List[int]] = None,
|
| 180 |
-
fork_temp: Optional[float] = None, row_temps: Optional[List[List[float]]] = None,
|
| 181 |
-
prune: str = "always", tau: float = 0.0, delta: float = 0.0, immune_slots: Optional[List[int]] = None,
|
| 182 |
-
dump_tokens: bool = False):
|
| 183 |
-
"""strats[i] = strategy token per slot for problem i (len K_slots). Returns per-problem dict.
|
| 184 |
-
prune (use_judge only): "always" = at every <chk> with >=2 unfinished streams kill the lowest-scored one (the
|
| 185 |
-
original rule); "gated" = kill it only if its p_correct < tau and the best unfinished stream's p_correct exceeds
|
| 186 |
-
it by >= delta, otherwise do nothing at that checkpoint; "none" = never kill (every stream runs to completion and
|
| 187 |
-
the judge only picks at the final <chk>). immune_slots: slots that are never killed (pruning happens among the
|
| 188 |
-
other slots only; the fork source may still be an immune slot). dump_tokens: keep every stream's token ids
|
| 189 |
-
(live and killed) in the records.
|
| 190 |
-
slot_temps: optional per-slot sampling temperature (overrides `temperature`; a forked child inherits its
|
| 191 |
-
slot's temperature unless fork_temp is given). row_temps[i][k]: per-problem, per-slot temperature (overrides
|
| 192 |
-
slot_temps). fork_temp: temperature of every respawned child. strat_pool: strategies a forked child may take
|
| 193 |
-
(default all three)."""
|
| 194 |
-
K = len(strats[0]); N = N or self.cfg.N
|
| 195 |
-
pool = list(strat_pool) if strat_pool else list(STRAT_IDS)
|
| 196 |
-
assert prune in ("always", "gated", "none"), prune
|
| 197 |
-
immune = set(immune_slots or [])
|
| 198 |
-
gate_skips = [0] * len(prompts) # checkpoints where the gate blocked a kill
|
| 199 |
-
if slot_temps is not None:
|
| 200 |
-
assert len(slot_temps) == K, (slot_temps, K)
|
| 201 |
-
per_stream_temps = slot_temps is not None or row_temps is not None or fork_temp is not None
|
| 202 |
-
def init_temp(i, k):
|
| 203 |
-
if row_temps is not None: return row_temps[i][k]
|
| 204 |
-
if slot_temps is not None: return slot_temps[k]
|
| 205 |
-
return temperature
|
| 206 |
-
assert K <= len(STREAM_IDS)
|
| 207 |
-
B = len(prompts)
|
| 208 |
-
g = torch.Generator(device=self.device); g.manual_seed(sample_seed)
|
| 209 |
-
nb = math.ceil(max_new / N)
|
| 210 |
-
cache, P = self.prefill(prompts, K, max_new, extra=((nb - 1) * (max_new + nb + 2) if use_judge and respawn else 0))
|
| 211 |
-
Pl = P.tolist() # per-step token/seg/pos tensors are built on the CPU and moved once (avoids per-element CUDA launches)
|
| 212 |
-
streams = [[Stream(slot=k, strat=strats[i][k], temp=init_temp(i, k)) for k in range(K)] for i in range(B)]
|
| 213 |
-
chk_count = [0] * B; forks = [0] * B; kills = [0] * B; dead_generated = [0] * B
|
| 214 |
-
judge_log = [[] for _ in range(B)]; dead_recs = [[] for _ in range(B)]
|
| 215 |
-
dev = self.device
|
| 216 |
-
n_blocks = nb
|
| 217 |
-
for b in range(n_blocks):
|
| 218 |
-
# ---- headers for this block
|
| 219 |
-
for hdr_step in range(2 if b == 0 else 1):
|
| 220 |
-
toks = torch.full((B, K), PAD, dtype=torch.long)
|
| 221 |
-
seg = torch.full((B, K), PAD_SEG, dtype=torch.long)
|
| 222 |
-
pos = torch.zeros((B, K), dtype=torch.long)
|
| 223 |
-
for i in range(B):
|
| 224 |
-
for k, s in enumerate(streams[i]):
|
| 225 |
-
if s.finished or s.dead:
|
| 226 |
-
continue
|
| 227 |
-
if b == 0 or s.born_block == b:
|
| 228 |
-
tid = STREAM_IDS[k] if hdr_step == 0 else s.strat
|
| 229 |
-
else:
|
| 230 |
-
if hdr_step == 1: continue
|
| 231 |
-
tid = STREAM_IDS[k]
|
| 232 |
-
toks[i, k] = tid; seg[i, k] = k + 1; pos[i, k] = Pl[i] + s.n_fed; s.n_fed += 1
|
| 233 |
-
if (seg != PAD_SEG).any():
|
| 234 |
-
toks, seg, pos = toks.to(dev), seg.to(dev), pos.to(dev)
|
| 235 |
-
h = self._forward(cache, toks, seg, pos, torch.full_like(pos, b))
|
| 236 |
-
for i in range(B):
|
| 237 |
-
for k, s in enumerate(streams[i]):
|
| 238 |
-
if seg[i, k] != PAD_SEG:
|
| 239 |
-
s.last_h = h[i, k]
|
| 240 |
-
# for streams born this block (forks) the two headers were fed above when hdr_step covered them
|
| 241 |
-
# ---- N content tokens
|
| 242 |
-
for t in range(N):
|
| 243 |
-
if b * N + t >= max_new:
|
| 244 |
-
break
|
| 245 |
-
# sample next token for every live stream from the logits at its last fed position
|
| 246 |
-
toks = torch.full((B, K), PAD, dtype=torch.long)
|
| 247 |
-
seg = torch.full((B, K), PAD_SEG, dtype=torch.long)
|
| 248 |
-
pos = torch.zeros((B, K), dtype=torch.long)
|
| 249 |
-
live = [(i, k) for i in range(B) for k, s in enumerate(streams[i]) if not (s.finished or s.dead)]
|
| 250 |
-
if not live:
|
| 251 |
-
break
|
| 252 |
-
hs = torch.stack([streams[i][k].last_h for i, k in live])
|
| 253 |
-
logits = self._logits(hs)
|
| 254 |
-
# never sample control tokens: <pad> <prompt> <s1..s4> <strat:*> <chk> (256..265) and <right>/<wrong>
|
| 255 |
-
# (267, 268). Byte tokens 0..255 (every single character incl. digits) and <end> (266) stay allowed.
|
| 256 |
-
logits[:, PAD:END] = -float("inf")
|
| 257 |
-
logits[:, END + 1:FIRST_MERGE_ID] = -float("inf")
|
| 258 |
-
if per_stream_temps:
|
| 259 |
-
tt = torch.tensor([streams[i][k].temp for i, k in live], device=logits.device, dtype=logits.dtype)[:, None]
|
| 260 |
-
samp = torch.multinomial(torch.softmax(logits / tt.clamp_min(1e-6), -1), 1, generator=g).squeeze(-1)
|
| 261 |
-
nxt = torch.where(tt[:, 0] <= 0, logits.argmax(-1), samp)
|
| 262 |
-
elif temperature <= 0:
|
| 263 |
-
nxt = logits.argmax(-1)
|
| 264 |
-
else:
|
| 265 |
-
nxt = torch.multinomial(torch.softmax(logits / temperature, -1), 1, generator=g).squeeze(-1)
|
| 266 |
-
for (i, k), tid in zip(live, nxt.tolist()):
|
| 267 |
-
s = streams[i][k]
|
| 268 |
-
if len(s.tokens) + 1 >= max_new and tid != END:
|
| 269 |
-
tid = END # forced finish at the budget
|
| 270 |
-
s.tokens.append(tid); s.generated += 1
|
| 271 |
-
toks[i, k] = tid; seg[i, k] = k + 1; pos[i, k] = Pl[i] + s.n_fed; s.n_fed += 1
|
| 272 |
-
toks, seg, pos = toks.to(dev), seg.to(dev), pos.to(dev)
|
| 273 |
-
h = self._forward(cache, toks, seg, pos, torch.full_like(pos, b))
|
| 274 |
-
for (i, k) in live:
|
| 275 |
-
s = streams[i][k]
|
| 276 |
-
s.last_h = h[i, k]
|
| 277 |
-
if s.tokens[-1] == END:
|
| 278 |
-
s.finished = True
|
| 279 |
-
# ---- checkpoint
|
| 280 |
-
if use_judge:
|
| 281 |
-
toks = torch.full((B, 1), CHK, dtype=torch.long, device=dev)
|
| 282 |
-
seg = torch.full((B, 1), K + 1, dtype=torch.long, device=dev)
|
| 283 |
-
pos = (P + (b + 1) * N)[:, None]
|
| 284 |
-
h = self._forward(cache, toks, seg, pos, torch.full_like(pos, b))
|
| 285 |
-
fork_rows, fork_src, fork_dst = [], [], [] # one batched fork per checkpoint (shared cache length)
|
| 286 |
-
for i in range(B):
|
| 287 |
-
chk_count[i] += 1
|
| 288 |
-
cand = [s for s in streams[i] if not s.dead and s.last_h is not None]
|
| 289 |
-
if not cand:
|
| 290 |
-
continue
|
| 291 |
-
hl = torch.stack([s.last_h for s in cand])
|
| 292 |
-
hc = h[i, 0][None].expand(len(cand), -1)
|
| 293 |
-
j = self._judge(hc, hl)
|
| 294 |
-
pc = torch.sigmoid(j[:, 0]).tolist(); pr = torch.sigmoid(j[:, 1]).tolist()
|
| 295 |
-
for s, a, r in zip(cand, pc, pr):
|
| 296 |
-
s.p_correct, s.p_remain = a, r
|
| 297 |
-
s.score = a - 0.1 * r
|
| 298 |
-
judge_log[i].append({"block": b, "slot": s.slot, "gen": s.gen, "strat": STRAT_NAMES[s.strat], "p_correct": a,
|
| 299 |
-
"p_remain": r, "finished": s.finished, "len": len(s.tokens)})
|
| 300 |
-
last_block = (b + 1) * N >= max_new
|
| 301 |
-
unfinished = [s for s in cand if not s.finished]
|
| 302 |
-
killable = [s for s in unfinished if s.slot not in immune]
|
| 303 |
-
if len(unfinished) >= 2 and killable and not last_block and prune != "none":
|
| 304 |
-
worst = min(killable, key=lambda s: s.score)
|
| 305 |
-
best = max(unfinished, key=lambda s: s.score)
|
| 306 |
-
allowed = worst is not best
|
| 307 |
-
if allowed and prune == "gated":
|
| 308 |
-
allowed = worst.p_correct < tau and (best.p_correct - worst.p_correct) >= delta
|
| 309 |
-
if not allowed:
|
| 310 |
-
gate_skips[i] += 1
|
| 311 |
-
if allowed:
|
| 312 |
-
worst.dead = True; kills[i] += 1; dead_generated[i] += worst.generated
|
| 313 |
-
cache.invalidate(i, worst.slot + 1) # killed stream vanishes from the cache
|
| 314 |
-
dead_recs[i].append({"slot": worst.slot, "gen": worst.gen, "strat": STRAT_NAMES[worst.strat],
|
| 315 |
-
"text": self.tk.decode(worst.tokens), "len": len(worst.tokens),
|
| 316 |
-
"generated_own": worst.generated, "killed_block": b, "parent": worst.parent,
|
| 317 |
-
"p_correct": worst.p_correct, "p_remain": worst.p_remain,
|
| 318 |
-
**({"tokens": list(worst.tokens)} if dump_tokens else {})})
|
| 319 |
-
if respawn:
|
| 320 |
-
fork_rows.append(i); fork_src.append(best.slot + 1); fork_dst.append(worst.slot + 1)
|
| 321 |
-
forks[i] += 1
|
| 322 |
-
taken = {s.strat for s in streams[i] if not s.dead}
|
| 323 |
-
bi_ = pool.index(best.strat) if best.strat in pool else -1
|
| 324 |
-
order = pool[bi_ + 1:] + pool[:bi_ + 1]
|
| 325 |
-
choices = ([z for z in order if z != worst.strat and z != best.strat]
|
| 326 |
-
or [z for z in order if z != worst.strat] or order)
|
| 327 |
-
child = Stream(slot=worst.slot, strat=choices[0], tokens=list(best.tokens), n_fed=best.n_fed,
|
| 328 |
-
last_h=best.last_h, parent=best.slot, born_block=b + 1, gen=worst.gen + 1,
|
| 329 |
-
temp=fork_temp if fork_temp is not None else init_temp(i, worst.slot))
|
| 330 |
-
streams[i][worst.slot] = child
|
| 331 |
-
if fork_rows:
|
| 332 |
-
cache.fork(fork_rows, fork_src, fork_dst)
|
| 333 |
-
if all(s.finished or s.dead for i in range(B) for s in streams[i]):
|
| 334 |
-
break
|
| 335 |
-
# ---- collect
|
| 336 |
-
out = []
|
| 337 |
-
for i in range(B):
|
| 338 |
-
recs = []
|
| 339 |
-
for s in streams[i]:
|
| 340 |
-
recs.append({"slot": s.slot, "gen": s.gen, "strat": STRAT_NAMES[s.strat], "text": self.tk.decode(s.tokens), "finished": s.finished,
|
| 341 |
-
"dead": s.dead, "score": s.score, "p_correct": s.p_correct, "p_remain": s.p_remain, "len": len(s.tokens),
|
| 342 |
-
"parent": s.parent, "generated_own": s.generated, "temp": s.temp,
|
| 343 |
-
**({"tokens": list(s.tokens)} if dump_tokens else {})})
|
| 344 |
-
out.append({"streams": recs, "dead_streams": dead_recs[i], "chk_forwards": chk_count[i], "kills": kills[i], "forks": forks[i],
|
| 345 |
-
"judge_log": judge_log[i], "cache_len": cache.len, "_dead_generated": dead_generated[i],
|
| 346 |
-
"gate_skips": gate_skips[i]})
|
| 347 |
-
return out, streams
|
| 348 |
-
|
| 349 |
-
# ------------------------------------------------------------------ public modes
|
| 350 |
-
def _account(self, result, live_streams):
|
| 351 |
-
gen = sum(s.generated for s in live_streams) + result.get("_dead_generated", 0)
|
| 352 |
-
result["tokens_generated"] = gen + result["chk_forwards"]
|
| 353 |
-
result["tokens_sampled"] = gen
|
| 354 |
-
return result
|
| 355 |
-
|
| 356 |
-
@torch.no_grad()
|
| 357 |
-
def greedy1(self, prompts, strat=STRAT_IDS[0], max_new=512):
|
| 358 |
-
res, streams = self.run_streams(prompts, [[strat]] * len(prompts), temperature=0.0, max_new=max_new, use_judge=False)
|
| 359 |
-
out = []
|
| 360 |
-
for i, r in enumerate(res):
|
| 361 |
-
r = self._account(r, streams[i]); r["answer"] = extract_answer(r["streams"][0]["text"]); out.append(r)
|
| 362 |
-
return out
|
| 363 |
-
|
| 364 |
-
@torch.no_grad()
|
| 365 |
-
def bestofK(self, prompts, K=3, temperature=0.7, max_new=512, seed=1337,
|
| 366 |
-
sample_strats: Optional[List[int]] = None, sample_temps: Optional[List[float]] = None):
|
| 367 |
-
"""K independent sampled streams (strategies round-robin, or sample_strats[j] per sample; temperature, or
|
| 368 |
-
sample_temps[j] per sample, 0 = greedy), majority vote on the final answer.
|
| 369 |
-
Implemented as K single-stream rows sharing nothing but the code path (prompt prefill is cheap)."""
|
| 370 |
-
rows = [p for p in prompts for _ in range(K)]
|
| 371 |
-
strats = [[sample_strats[j] if sample_strats else STRAT_IDS[j % 3]] for _ in prompts for j in range(K)]
|
| 372 |
-
row_temps = [[sample_temps[j]] for _ in prompts for j in range(K)] if sample_temps else None
|
| 373 |
-
res, streams = self.run_streams(rows, strats, temperature=temperature, max_new=max_new, use_judge=False, sample_seed=seed,
|
| 374 |
-
row_temps=row_temps)
|
| 375 |
-
out = []
|
| 376 |
-
for i in range(len(prompts)):
|
| 377 |
-
grp = res[i * K:(i + 1) * K]; gs = streams[i * K:(i + 1) * K]
|
| 378 |
-
answers = [extract_answer(g["streams"][0]["text"]) for g in grp]
|
| 379 |
-
ans, ans_str = majority_vote(answers) # value vote (expressions evaluated); string vote kept for reference
|
| 380 |
-
tg = sum(g["streams"][0]["generated_own"] for g in grp)
|
| 381 |
-
out.append({"answer": ans, "answer_stringvote": ans_str, "answers": answers, "tokens_generated": tg, "tokens_sampled": tg,
|
| 382 |
-
"chk_forwards": 0, "streams": [g["streams"][0] for g in grp], "kills": 0, "forks": 0})
|
| 383 |
-
return out
|
| 384 |
-
|
| 385 |
-
@torch.no_grad()
|
| 386 |
-
def native(self, prompts, K=3, N=None, temperature=0.7, max_new=512, respawn=True, seed=1337,
|
| 387 |
-
slot_strats: Optional[List[int]] = None, slot_temps: Optional[List[float]] = None, strat_pool: Optional[List[int]] = None,
|
| 388 |
-
fork_temp: Optional[float] = None, prune: str = "always", tau: float = 0.0, delta: float = 0.0,
|
| 389 |
-
immune_slots: Optional[List[int]] = None, dump_tokens: bool = False):
|
| 390 |
-
"""slot_strats: strategy token per slot (default round-robin over all three); slot_temps / strat_pool / fork_temp /
|
| 391 |
-
prune / tau / delta / immune_slots / dump_tokens as in run_streams."""
|
| 392 |
-
base = list(slot_strats) if slot_strats else [STRAT_IDS[j % 3] for j in range(K)]
|
| 393 |
-
strats = [list(base) for _ in prompts]
|
| 394 |
-
self._all_streams = []
|
| 395 |
-
res, streams = self._run_native(prompts, strats, temperature, max_new, respawn, N, seed, slot_temps, strat_pool, fork_temp,
|
| 396 |
-
prune, tau, delta, immune_slots, dump_tokens)
|
| 397 |
-
return res
|
| 398 |
-
|
| 399 |
-
def _run_native(self, prompts, strats, temperature, max_new, respawn, N, seed, slot_temps=None, strat_pool=None, fork_temp=None,
|
| 400 |
-
prune="always", tau=0.0, delta=0.0, immune_slots=None, dump_tokens=False):
|
| 401 |
-
# run_streams replaces killed streams in place; we need the killed ones' generated counts, so wrap Stream creation
|
| 402 |
-
res, streams = self.run_streams(prompts, strats, temperature=temperature, max_new=max_new, use_judge=True,
|
| 403 |
-
respawn=respawn, N=N, sample_seed=seed, slot_temps=slot_temps, strat_pool=strat_pool,
|
| 404 |
-
fork_temp=fork_temp, prune=prune, tau=tau, delta=delta, immune_slots=immune_slots,
|
| 405 |
-
dump_tokens=dump_tokens)
|
| 406 |
-
out = []
|
| 407 |
-
for i, r in enumerate(res):
|
| 408 |
-
# generated by killed streams is recorded in judge_log lens at kill time; recompute from stream history
|
| 409 |
-
gen_live = sum(s.generated for s in streams[i])
|
| 410 |
-
gen_dead = r.get("_dead_generated", 0)
|
| 411 |
-
r["tokens_sampled"] = gen_live + gen_dead
|
| 412 |
-
r["tokens_generated"] = r["tokens_sampled"] + r["chk_forwards"]
|
| 413 |
-
fin = [s for s in r["streams"] if s["finished"] and not s["dead"]]
|
| 414 |
-
pick = max(fin, key=lambda s: (s["score"] if s["score"] == s["score"] else -1e9)) if fin else \
|
| 415 |
-
max([s for s in r["streams"] if not s["dead"]], key=lambda s: (s["score"] if s["score"] == s["score"] else -1e9))
|
| 416 |
-
r["answer"] = extract_answer(pick["text"]); r["picked_slot"] = pick["slot"]
|
| 417 |
-
# value vote over the surviving streams' final answers (same samples as the judge pick): isolates the judge's
|
| 418 |
-
# selection value from the pruning; identical to best-of-K's vote when prune == "none"
|
| 419 |
-
live_answers = [extract_answer(s["text"]) for s in r["streams"] if not s["dead"]]
|
| 420 |
-
r["answer_vote"], r["answer_stringvote"] = majority_vote(live_answers); r["answers"] = live_answers
|
| 421 |
-
r["anchor_killed"] = any(d["slot"] == 0 and d.get("gen", 0) == 0 for d in r["dead_streams"])
|
| 422 |
-
out.append(r)
|
| 423 |
-
return out, streams
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
def extract_answer(text: str):
|
| 427 |
-
ms = ANSWER_RE.findall(text or "")
|
| 428 |
-
return ms[-1].strip() if ms else None
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
def answer_key(ans):
|
| 432 |
-
"""Canonical vote key for majority voting. An arithmetic expression (Countdown-style answer) is keyed by its
|
| 433 |
-
evaluated value — computed from the expression alone, without the gold target and without the uses-each-number-
|
| 434 |
-
once check — so distinct valid expressions for the same target agree. A number is keyed by its normalised
|
| 435 |
-
value; anything else by its stripped string. None stays None."""
|
| 436 |
-
from fractions import Fraction
|
| 437 |
-
from maram.data.label import normalize_expr, _fractionize, norm_number
|
| 438 |
-
if ans is None:
|
| 439 |
-
return None
|
| 440 |
-
e = normalize_expr(ans)
|
| 441 |
-
if e and re.fullmatch(r"[\d\s\+\-\*/\(\)]+", e) and re.search(r"[\+\-\*/]", e) and re.search(r"\d", e):
|
| 442 |
-
try:
|
| 443 |
-
v = eval(_fractionize(e), {"Fraction": Fraction, "__builtins__": {}})
|
| 444 |
-
return f"v:{v}"
|
| 445 |
-
except Exception:
|
| 446 |
-
pass
|
| 447 |
-
n = norm_number(ans)
|
| 448 |
-
return f"n:{n}" if n is not None else f"s:{ans.strip()}"
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
def majority_vote(answers):
|
| 452 |
-
"""Returns (winning raw answer, string-vote answer). Ties: first seen."""
|
| 453 |
-
keys = [answer_key(a) for a in answers]
|
| 454 |
-
counts = {}
|
| 455 |
-
for k in keys:
|
| 456 |
-
if k is not None:
|
| 457 |
-
counts[k] = counts.get(k, 0) + 1
|
| 458 |
-
win = max(counts, key=counts.get) if counts else None
|
| 459 |
-
ans = next((a for a, k in zip(answers, keys) if k == win), None) if win is not None else None
|
| 460 |
-
sc = {}
|
| 461 |
-
for a in answers:
|
| 462 |
-
if a is not None:
|
| 463 |
-
sc[a.strip()] = sc.get(a.strip(), 0) + 1
|
| 464 |
-
return ans, (max(sc, key=sc.get) if sc else None)
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
def load_model(ckpt_path, device="cuda", judge_mode="full"):
|
| 468 |
-
sd = torch.load(ckpt_path, map_location="cpu", weights_only=False)
|
| 469 |
-
cfg = ModelConfig.from_dict(sd["model_cfg"])
|
| 470 |
-
cfg.judge_mode = judge_mode
|
| 471 |
-
m = MaramModel(cfg); m.load_state_dict(sd["model"])
|
| 472 |
-
return m.to(device), sd
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
maram_inference/maram/model/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (157 Bytes)
|
|
|
maram_inference/maram/model/__pycache__/judge.cpython-312.pyc
DELETED
|
Binary file (2.64 kB)
|
|
|
maram_inference/maram/model/__pycache__/mask.cpython-312.pyc
DELETED
|
Binary file (3.84 kB)
|
|
|
maram_inference/maram/model/__pycache__/model.cpython-312.pyc
DELETED
|
Binary file (15.4 kB)
|
|
|
maram_inference/maram/tokenizer/__pycache__/__init__.cpython-312.pyc
DELETED
|
Binary file (161 Bytes)
|
|
|
maram_inference/maram/tokenizer/__pycache__/maram_tokenizer.cpython-312.pyc
DELETED
|
Binary file (4.29 kB)
|
|
|