mirror of
https://github.com/wassname/moral-maps.git
synced 2026-09-26 14:00:29 +08:00
batch the eval: guided_rollout_batch + 4× speedup
Sequential eval was the bottleneck (~12s/vignette × 131 = 27 min/pass; with bidirectional ±C × 14 methods, that projected to ~17 h). Three model calls per row (phase1 generate, scoring forward, cosmetic continuation) became one phase1 + one scoring per *batch*; continuation generate dropped (callers only use p_true + pmass_format). Parity smoke (scripts/smoke_batch_parity.py): float32 is bit-exact (max Δp_true=0.0000 over 16 prompts, 4.3× speedup at limit=4). bf16 drifts on individual rows — greedy argmax flips at near-tie tokens then phase1 diverges — but pmass agrees within 0.03 (scoring forward correct) and aggregates over 131 vignettes will average out the per-row noise. Other changes shipped in this commit: - core.py: analyse() now returns raw_pmass dict alongside raw p_true (callers needed per-(vid,cond,frame) pmass for diagnostic warnings). - guided.py guided_rollout: warn + log top-5 when pmass<0.9 (catches OOD steering / format-broken vignettes without a separate audit pass). - eval.py: pre-tokenize a sample to log expected prompt+cache budget so OOM is predictable from the SHOULD line; group items by frame so each batch shares schema_hint + prefill.
This commit is contained in:
+48
-21
@@ -9,7 +9,7 @@ from tqdm.auto import tqdm
|
||||
|
||||
from .core import format_prompts, next_token_logits, score_prompts, analyse, CONDITIONS, FRAMES
|
||||
from .data import load_vignettes
|
||||
from .guided import guided_rollout, choice_token_ids_tf
|
||||
from .guided import guided_rollout_batch, choice_token_ids_tf
|
||||
|
||||
|
||||
def evaluate(
|
||||
@@ -39,36 +39,63 @@ def evaluate(
|
||||
t0 = time.time()
|
||||
|
||||
if max_think_tokens > 0:
|
||||
logger.info(f"Using guided_rollout with {max_think_tokens} max_think_tokens (sequential)")
|
||||
p_true_list = []
|
||||
meta = []
|
||||
bool_mass_list = []
|
||||
logger.info(f"Using guided_rollout_batch with {max_think_tokens} max_think_tokens, batch_size={batch_size}")
|
||||
choice_ids = choice_token_ids_tf(tokenizer)
|
||||
|
||||
for r in tqdm(vignettes, desc="Evaluating"):
|
||||
|
||||
# Build all (vid, cond, frame) items, grouped by frame so each batch
|
||||
# shares schema_hint + prefill (collapses the per-row branching).
|
||||
items_per_frame: dict[str, list[tuple]] = {f: [] for f in FRAMES}
|
||||
for r in vignettes:
|
||||
for cond in CONDITIONS:
|
||||
for frame, fr in FRAMES.items():
|
||||
user_prompt = f"{r[cond]}"
|
||||
schema_hint = fr["q"]
|
||||
prefill = fr["prefill"]
|
||||
|
||||
res = guided_rollout(
|
||||
for frame in FRAMES:
|
||||
items_per_frame[frame].append(
|
||||
(r["id"], r["foundation_coarse"], cond, frame, r.get("wrong"), r[cond])
|
||||
)
|
||||
|
||||
# Pretokenize a sample to log expected prompt length / cache budget.
|
||||
sample_user = items_per_frame[next(iter(FRAMES))][0][5]
|
||||
sample_q = FRAMES[next(iter(FRAMES))]["q"]
|
||||
sample_full = f"{sample_user}\n\n{sample_q}"
|
||||
sample_msgs = [{"role": "user", "content": sample_full}]
|
||||
try:
|
||||
sample_p = tokenizer.apply_chat_template(sample_msgs, tokenize=False, add_generation_prompt=True)
|
||||
except TypeError:
|
||||
sample_p = tokenizer.apply_chat_template(sample_msgs, tokenize=False)
|
||||
sample_p = sample_p + "<think>\n"
|
||||
sample_len = len(tokenizer(sample_p).input_ids)
|
||||
logger.info(
|
||||
f"SHOULD: prompt_len≈{sample_len} tok; max cache ≈ {sample_len + max_think_tokens} per row × "
|
||||
f"batch_size={batch_size}. If OOM, lower batch_size."
|
||||
)
|
||||
|
||||
p_true_list, meta, bool_mass_list = [], [], []
|
||||
total = sum(len(v) for v in items_per_frame.values())
|
||||
with tqdm(total=total, desc="Evaluating") as pbar:
|
||||
for frame, items in items_per_frame.items():
|
||||
fr = FRAMES[frame]
|
||||
schema_hint = fr["q"]
|
||||
prefill = fr["prefill"]
|
||||
for i in range(0, len(items), batch_size):
|
||||
chunk = items[i:i + batch_size]
|
||||
user_prompts = [it[5] for it in chunk]
|
||||
results = guided_rollout_batch(
|
||||
model, tokenizer,
|
||||
user_prompt=user_prompt,
|
||||
user_prompts=user_prompts,
|
||||
choice_token_ids=choice_ids,
|
||||
max_think_tokens=max_think_tokens,
|
||||
schema_hint=schema_hint,
|
||||
prefill=prefill,
|
||||
verbose=False
|
||||
)
|
||||
|
||||
p_true_list.append(res.p_true)
|
||||
meta.append((r["id"], r["foundation_coarse"], cond, frame, r.get("wrong")))
|
||||
bool_mass_list.append(res.pmass_format)
|
||||
|
||||
for it, res in zip(chunk, results):
|
||||
vid, found, cond, fr_name, wrong, _ = it
|
||||
p_true_list.append(res.p_true)
|
||||
meta.append((vid, found, cond, fr_name, wrong))
|
||||
bool_mass_list.append(res.pmass_format)
|
||||
pbar.update(len(chunk))
|
||||
|
||||
elapsed = time.time() - t0
|
||||
logger.info(f"guided eval: {elapsed:.1f}s ({len(p_true_list)/elapsed:.1f} prompts/s)")
|
||||
|
||||
|
||||
report = analyse(p_true_list, meta, bool_mass=bool_mass_list)
|
||||
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user