Upload code/wavecurriculum_run/train/evaluater.py with huggingface_hub
Browse files
code/wavecurriculum_run/train/evaluater.py
CHANGED
|
@@ -32,6 +32,78 @@ def _lcs_len(a, b):
|
|
| 32 |
return prev[-1]
|
| 33 |
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
def _cand_value_stats(logp_digits, bits):
|
| 36 |
"""Value-head statistics against one stage's candidate sets.
|
| 37 |
|
|
@@ -301,6 +373,14 @@ def get_eval_metrics(state, eval_data_iter, p_eval_step, config):
|
|
| 301 |
vhgap_sum = np.zeros(max(K, 1), dtype=np.float64)
|
| 302 |
vhall_sum = np.zeros(max(K, 1), dtype=np.int64)
|
| 303 |
vhgrp_sum = np.zeros(max(K, 1), dtype=np.int64)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
# Per-round-bin cell accuracy, for the round-count DATA curriculum: bin b is
|
| 306 |
# unlocked at stage b, so bin_ok[b]/bin_tot[b] measures competence on the
|
|
@@ -326,6 +406,10 @@ def get_eval_metrics(state, eval_data_iter, p_eval_step, config):
|
|
| 326 |
levels = np.array(batch_tuple[3]).reshape(-1)
|
| 327 |
rbins = (np.array(batch_tuple[5]).reshape(-1)
|
| 328 |
if len(batch_tuple) > 5 else np.zeros_like(levels))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
total_pred, sucess_pred = 0, 0
|
| 330 |
# Location = did the model emit the ground-truth next (r,c) cell.
|
| 331 |
loc_tot, loc_ok, val_given_loc_ok = 0, 0, 0
|
|
@@ -476,6 +560,17 @@ def get_eval_metrics(state, eval_data_iter, p_eval_step, config):
|
|
| 476 |
vhgap_sum[s] += st["hgap"]
|
| 477 |
vhall_sum[s] += st["hbound_all"]
|
| 478 |
vhgrp_sum[s] += st["hbound_groups"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 479 |
|
| 480 |
min_start_index = int(np.min(start_index))
|
| 481 |
cur_input_seq = input_seq[:, :(min_start_index*3)]
|
|
@@ -743,6 +838,20 @@ def get_eval_metrics(state, eval_data_iter, p_eval_step, config):
|
|
| 743 |
eval_metrics["per_stage_val_hbound"] = _per_stage(vhok_sum, vspread_cnt)
|
| 744 |
eval_metrics["per_stage_val_hgap"] = _per_stage(vhgap_sum, vspread_cnt)
|
| 745 |
eval_metrics["per_stage_val_hbound_puz"] = _per_stage(vhall_sum, vhgrp_sum)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 746 |
if _verbose_eval() and K > 0 and any(v >= 0 for v in per_stage_vce.values()):
|
| 747 |
print("PER-STAGE val_excess (0 = uniform over candidate set):",
|
| 748 |
{s: (f"{v:.3f}" if per_stage_vce[s] >= 0 else "n/a")
|
|
|
|
| 32 |
return prev[-1]
|
| 33 |
|
| 34 |
|
| 35 |
+
def _inst_ce_stats(logp_digits, bits, qcounts):
|
| 36 |
+
"""CE(q || p) against the ceiling log|S|, per cell.
|
| 37 |
+
|
| 38 |
+
q is the empirical per-cell digit distribution of this puzzle's stored
|
| 39 |
+
instances at this stage: the POST-constraint distribution the data actually
|
| 40 |
+
teaches. That is the whole point of using it instead of Uniform(S). By
|
| 41 |
+
Gibbs, CE(Uniform(S) || p) >= log|S| for every p, so comparing that to
|
| 42 |
+
log|S| is a tautology (it is exactly `excess >= 0`). CE(q || p) instead has
|
| 43 |
+
floor H(q), and H(q) < log|S| strictly whenever the constraints prune
|
| 44 |
+
anything, so the comparison
|
| 45 |
+
|
| 46 |
+
CE(q || p) <= log|S| equivalently KL(q || p) <= log|S| - H(q)
|
| 47 |
+
|
| 48 |
+
is both satisfiable and meaningful: the model's divergence from the truth
|
| 49 |
+
must be smaller than the information the constraints carry. It is also a
|
| 50 |
+
complete test on its own, unlike mass/spread, because it punishes every
|
| 51 |
+
failure mode at once -- dropping a candidate that instances do use sends
|
| 52 |
+
-log p(d) up, and leaking mass outside S lowers p on the digits that are
|
| 53 |
+
actually targets.
|
| 54 |
+
|
| 55 |
+
A cell is only counted when the bar is above the floor, i.e. when
|
| 56 |
+
H(q) < log|S| strictly. Two kinds of cell are dropped:
|
| 57 |
+
|
| 58 |
+
|S| = 1 the ceiling is log 1 = 0 and no finite CE can meet it.
|
| 59 |
+
H(q) = log|S| q is already uniform on S, so the bar EQUALS the floor and
|
| 60 |
+
by Gibbs only p == q exactly passes. Asking for that is asking
|
| 61 |
+
for zero error, not for learning.
|
| 62 |
+
|
| 63 |
+
On the real pool this second case is 8-27% of cells depending on stage, so
|
| 64 |
+
leaving them in would cap ceq_ok well below 1 and make any fixed threshold
|
| 65 |
+
mean something different at every stage. The exclusion depends only on q and
|
| 66 |
+
S, never on p, so it cannot be gamed by the model.
|
| 67 |
+
|
| 68 |
+
Args:
|
| 69 |
+
logp_digits: (n, 9) log-probabilities of digits 1..9 under the model.
|
| 70 |
+
bits: (n,) int bitmask; bit d-1 set iff digit d is a candidate.
|
| 71 |
+
qcounts: (n, 9) instance-digit counts; column d-1 counts digit d.
|
| 72 |
+
|
| 73 |
+
Returns:
|
| 74 |
+
Sums over the countable cells plus the count they divide by.
|
| 75 |
+
"""
|
| 76 |
+
out = {"ceq": 0.0, "hq": 0.0, "ceq_ok": 0, "ceq_gap": 0.0, "ceq_count": 0,
|
| 77 |
+
"ceq_all": 0, "ceq_groups": 0, "ceq_skip": 0}
|
| 78 |
+
in_s = ((bits[:, None] >> np.arange(9)) & 1).astype(np.float64)
|
| 79 |
+
size = in_s.sum(axis=1)
|
| 80 |
+
tot = qcounts.sum(axis=1)
|
| 81 |
+
cand = (size >= 2) & (tot > 0)
|
| 82 |
+
if not cand.any():
|
| 83 |
+
return out
|
| 84 |
+
q_all = qcounts[cand].astype(np.float64) / tot[cand][:, None]
|
| 85 |
+
h_all = -(q_all * np.log(np.maximum(q_all, 1e-12))).sum(axis=1)
|
| 86 |
+
l_all = np.log(size[cand])
|
| 87 |
+
# Keep only cells where the constraints left headroom under the ceiling.
|
| 88 |
+
room = (l_all - h_all) > 1e-9
|
| 89 |
+
out["ceq_skip"] = int((~room).sum())
|
| 90 |
+
if not room.any():
|
| 91 |
+
return out
|
| 92 |
+
q = q_all[room]
|
| 93 |
+
lp = logp_digits[cand][room]
|
| 94 |
+
ce_q = -(q * lp).sum(axis=1)
|
| 95 |
+
h_q = h_all[room]
|
| 96 |
+
gap = l_all[room] - ce_q
|
| 97 |
+
out["ceq"] = float(ce_q.sum())
|
| 98 |
+
out["hq"] = float(h_q.sum())
|
| 99 |
+
out["ceq_ok"] = int((gap >= 0).sum())
|
| 100 |
+
out["ceq_gap"] = float(gap.sum())
|
| 101 |
+
out["ceq_count"] = int(room.sum())
|
| 102 |
+
out["ceq_all"] = int(bool((gap >= 0).all()))
|
| 103 |
+
out["ceq_groups"] = 1
|
| 104 |
+
return out
|
| 105 |
+
|
| 106 |
+
|
| 107 |
def _cand_value_stats(logp_digits, bits):
|
| 108 |
"""Value-head statistics against one stage's candidate sets.
|
| 109 |
|
|
|
|
| 373 |
vhgap_sum = np.zeros(max(K, 1), dtype=np.float64)
|
| 374 |
vhall_sum = np.zeros(max(K, 1), dtype=np.int64)
|
| 375 |
vhgrp_sum = np.zeros(max(K, 1), dtype=np.int64)
|
| 376 |
+
# CE(q||p) vs the ceiling log|S|, q = the instances' own digit frequencies.
|
| 377 |
+
vceq_sum = np.zeros(max(K, 1), dtype=np.float64)
|
| 378 |
+
vhq_sum = np.zeros(max(K, 1), dtype=np.float64)
|
| 379 |
+
vceq_ok = np.zeros(max(K, 1), dtype=np.int64)
|
| 380 |
+
vceq_gap = np.zeros(max(K, 1), dtype=np.float64)
|
| 381 |
+
vceq_cnt = np.zeros(max(K, 1), dtype=np.int64)
|
| 382 |
+
vceq_all = np.zeros(max(K, 1), dtype=np.int64)
|
| 383 |
+
vceq_grp = np.zeros(max(K, 1), dtype=np.int64)
|
| 384 |
|
| 385 |
# Per-round-bin cell accuracy, for the round-count DATA curriculum: bin b is
|
| 386 |
# unlocked at stage b, so bin_ok[b]/bin_tot[b] measures competence on the
|
|
|
|
| 406 |
levels = np.array(batch_tuple[3]).reshape(-1)
|
| 407 |
rbins = (np.array(batch_tuple[5]).reshape(-1)
|
| 408 |
if len(batch_tuple) > 5 else np.zeros_like(levels))
|
| 409 |
+
# (bs, K, 81, 9) instance-digit counts; zeros when the run has no
|
| 410 |
+
# instance pool, in which case CE(q||p) is simply not reported.
|
| 411 |
+
q_counts = (np.array(batch_tuple[6]).astype(np.int64)
|
| 412 |
+
if len(batch_tuple) > 6 else None)
|
| 413 |
total_pred, sucess_pred = 0, 0
|
| 414 |
# Location = did the model emit the ground-truth next (r,c) cell.
|
| 415 |
loc_tot, loc_ok, val_given_loc_ok = 0, 0, 0
|
|
|
|
| 560 |
vhgap_sum[s] += st["hgap"]
|
| 561 |
vhall_sum[s] += st["hbound_all"]
|
| 562 |
vhgrp_sum[s] += st["hbound_groups"]
|
| 563 |
+
if q_counts is not None:
|
| 564 |
+
qs = _inst_ce_stats(
|
| 565 |
+
logp_d[sel], bits[sel],
|
| 566 |
+
q_counts[j, s, np.where(ok_cell, cells, 0)][sel])
|
| 567 |
+
vceq_sum[s] += qs["ceq"]
|
| 568 |
+
vhq_sum[s] += qs["hq"]
|
| 569 |
+
vceq_ok[s] += qs["ceq_ok"]
|
| 570 |
+
vceq_gap[s] += qs["ceq_gap"]
|
| 571 |
+
vceq_cnt[s] += qs["ceq_count"]
|
| 572 |
+
vceq_all[s] += qs["ceq_all"]
|
| 573 |
+
vceq_grp[s] += qs["ceq_groups"]
|
| 574 |
|
| 575 |
min_start_index = int(np.min(start_index))
|
| 576 |
cur_input_seq = input_seq[:, :(min_start_index*3)]
|
|
|
|
| 838 |
eval_metrics["per_stage_val_hbound"] = _per_stage(vhok_sum, vspread_cnt)
|
| 839 |
eval_metrics["per_stage_val_hgap"] = _per_stage(vhgap_sum, vspread_cnt)
|
| 840 |
eval_metrics["per_stage_val_hbound_puz"] = _per_stage(vhall_sum, vhgrp_sum)
|
| 841 |
+
# The single promotion criterion: CE(q||p) <= log|S| per cell, counted.
|
| 842 |
+
# ceq mean CE(q||p), to compare against floor H(q) and ceiling log|S|
|
| 843 |
+
# hq mean H(q), the best CE any model could achieve on this data
|
| 844 |
+
# ceq_ok fraction of |S|>=2 cells clearing the ceiling <- gate on this
|
| 845 |
+
# ceq_gap mean slack log|S| - CE(q||p); negative means worse than uniform
|
| 846 |
+
# ceq_puz fraction of puzzles where every such cell clears it
|
| 847 |
+
# This subsumes mass and spread: leak lowers p on real targets, and dropping
|
| 848 |
+
# a candidate the instances use sends -log p(d) up, so both are penalized by
|
| 849 |
+
# the one number. log|S| needs no tuning and recalibrates per stage.
|
| 850 |
+
eval_metrics["per_stage_val_ceq"] = _per_stage(vceq_sum, vceq_cnt)
|
| 851 |
+
eval_metrics["per_stage_val_hq"] = _per_stage(vhq_sum, vceq_cnt)
|
| 852 |
+
eval_metrics["per_stage_val_ceq_ok"] = _per_stage(vceq_ok, vceq_cnt)
|
| 853 |
+
eval_metrics["per_stage_val_ceq_gap"] = _per_stage(vceq_gap, vceq_cnt)
|
| 854 |
+
eval_metrics["per_stage_val_ceq_puz"] = _per_stage(vceq_all, vceq_grp)
|
| 855 |
if _verbose_eval() and K > 0 and any(v >= 0 for v in per_stage_vce.values()):
|
| 856 |
print("PER-STAGE val_excess (0 = uniform over candidate set):",
|
| 857 |
{s: (f"{v:.3f}" if per_stage_vce[s] >= 0 else "n/a")
|