Avra98 commited on
Commit
2ae9016
·
verified ·
1 Parent(s): ecd557d

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")