EgeEken commited on
Commit
03d40fb
·
1 Parent(s): c5e320e

sweep fix + reset

Browse files
Files changed (2) hide show
  1. pbc3_sweep.py +109 -88
  2. server.py +15 -10
pbc3_sweep.py CHANGED
@@ -30,68 +30,55 @@ JPEG_QUALITIES = (1, 3, 5, 10, 20, 40, 70, 95)
30
  AVIF_QUALITIES = (0, 1, 3, 5, 10, 20, 40, 70, 95)
31
  WEBP_QUALITIES = (0, 1, 3, 5, 10, 20, 40, 70, 95)
32
 
33
- # Optimizer search space. (name, kind, *args). Ranges are a starting guess — tweak freely.
34
  SEARCH_SPACE = [
35
- ("patch_count", "int", 1, 250, True), ("search_depth", "int", 20, 1000, True),
36
- ("proposal_depth", "int", 5, 200, False), ("exact_depth", "int", 1, 60, False),
37
- ("top_k", "cat", [4, 8, 16, 32, 64], False), ("anchor_block_size", "int", 2, 32, False),
38
- ("cell_sizes_per_candidate", "int", 1, 6, False),
39
- ("search_q_start", "float", 0.0, 1.0), ("search_q_end", "float", 0.0, 1.0),
40
- ("q_init", "float", 0.0, 1.0), ("q_start", "float", 0.0, 1.0), ("q_end", "float", 0.0, 1.0),
41
- ("patch_palette_bitcount", "int", 1, 5, False),
 
 
 
 
 
 
42
  ("mask_size", "cat", [4, 6, 8, 16], False),
43
  ]
44
 
45
  _DEFAULTS = PBC3Config().__dict__
46
-
47
  PRESETS = ("speed", "balanced", "quality", "compression", "high_quality")
48
 
49
- def ensure_pbc_baselines(study, images):
50
- have = {t.user_attrs.get("baseline") for t in study.trials}
51
- for name in PRESETS:
52
- tag = f"preset_{name}"
53
- if tag in have:
54
- continue
55
- _log(f"PBC preset baseline: {name} started")
56
- cfg = dict(vars(getattr(PBC3Config, name)()))
57
- try:
58
- _new_bar()
59
- rows = _eval_pbc3(cfg, images, on_image=_bar_tick)
60
- except Exception as e:
61
- _log(f"preset {name} failed: {e}")
62
- continue
63
- values = _avg(rows)
64
- _add_completed(study, values,
65
- {"kind": "pbc3", "baseline": tag, "preset": name,
66
- "config": cfg, "per_image": rows})
67
- _log_result(f"PBC preset baseline: {name} done", values, _is_pareto_candidate(study, values))
68
 
69
 
70
- def suggest_config(trial):
71
- cfg = {}
72
- for name, kind, *a in SEARCH_SPACE:
73
- if kind == "int":
74
- cfg[name] = trial.suggest_int(name, a[0], a[1], log=a[2])
75
- elif kind == "float":
76
- cfg[name] = trial.suggest_float(name, a[0], a[1])
77
- else:
78
- cfg[name] = trial.suggest_categorical(name, a[0])
79
- return cfg
80
 
81
 
82
- def _coerce(params):
83
- out = {}
84
- for k, v in params.items():
85
- d = _DEFAULTS.get(k)
86
- if isinstance(d, bool):
87
- out[k] = v if isinstance(v, bool) else str(v).lower() in ("true", "1")
88
- elif isinstance(d, int) and not isinstance(d, bool):
89
- out[k] = int(v)
90
- elif isinstance(d, float):
91
- out[k] = float(v)
92
- else:
93
- out[k] = v
94
- return out
 
 
 
95
 
96
 
97
  def dataset():
@@ -113,10 +100,16 @@ def _avg(rows):
113
  return [float(np.mean([r[k] for r in rows])) for k in ("seconds", "bpp", "mse")]
114
 
115
 
 
 
 
 
 
 
116
  def _is_pareto_candidate(study, values):
117
  vals = np.asarray(values, dtype=np.float64)
118
  for t in study.trials:
119
- if t.state != optuna.trial.TrialState.COMPLETE or not t.values or len(t.values) != 3:
120
  continue
121
  other = np.asarray(t.values, dtype=np.float64)
122
  if np.all(other <= vals) and np.any(other < vals):
@@ -124,9 +117,39 @@ def _is_pareto_candidate(study, values):
124
  return True
125
 
126
 
127
- def _log_result(prefix, values, pareto):
128
  seconds, bpp, mse = values
129
- _log(f"{prefix} | MSE {mse:.3f} | bpp {bpp:.5f} | speed {seconds:.3f}s | pareto {'yes' if pareto else 'no'}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
 
132
  def _eval_pbc3(cfg, images, on_image=None):
@@ -146,6 +169,8 @@ def _eval_pbc3(cfg, images, on_image=None):
146
  def _eval_codec(fmt, q, images, on_image=None):
147
  rows = []
148
  for im in images:
 
 
149
  arr = im["arr"]
150
  pixels = arr.shape[0] * arr.shape[1]
151
  buf = io.BytesIO()
@@ -180,6 +205,8 @@ def ensure_baselines(study, images):
180
  have = {t.user_attrs.get("baseline") for t in study.trials}
181
  for fmt, qs in (("JPEG", JPEG_QUALITIES), ("AVIF", AVIF_QUALITIES), ("WEBP", WEBP_QUALITIES)):
182
  for q in qs:
 
 
183
  tag = f"{fmt.lower()}_q{q}"
184
  if tag in have:
185
  continue
@@ -187,6 +214,9 @@ def ensure_baselines(study, images):
187
  try:
188
  _new_bar()
189
  rows = _eval_codec(fmt, q, images, on_image=_bar_tick)
 
 
 
190
  except Exception as e:
191
  _log(f"baseline {tag} failed: {e}")
192
  continue
@@ -194,41 +224,30 @@ def ensure_baselines(study, images):
194
  _add_completed(study, values, {
195
  "baseline": tag, "kind": "baseline",
196
  "config": {"codec": fmt, "q": q}, "per_image": rows})
197
- _log_result(f"baseline {tag} done", values, _is_pareto_candidate(study, values))
198
-
199
 
200
- # ---------------- runner state / control ----------------
201
- _LOCK = threading.Lock()
202
- _STOP = threading.Event()
203
- _THREAD = None
204
- _STATE = {"running": False, "mode": None, "done": 0, "total": None,
205
- "current": None, "started": None, "error": None, "log": []}
206
 
207
-
208
- def _log(msg):
209
- with _LOCK:
210
- _STATE["log"] = (_STATE["log"] + [f"{time.strftime('%H:%M:%S')} {msg}"])[-60:]
211
- print(f"[sweep] {msg}", flush=True)
212
-
213
-
214
- def _new_bar():
215
- """Append a fresh, empty progress line that _bar_tick grows in place."""
216
- with _LOCK:
217
- _STATE["log"] = (_STATE["log"] + [""])[-60:]
218
-
219
-
220
- def _bar_tick():
221
- """Add one '|' to the current progress line (called once per image)."""
222
- with _LOCK:
223
- if _STATE["log"]:
224
- _STATE["log"][-1] += "|"
225
-
226
-
227
- def status():
228
- with _LOCK:
229
- s = dict(_STATE)
230
- s["log"] = list(_STATE["log"])
231
- return s
232
 
233
 
234
  def _grid_trials(spec):
@@ -244,7 +263,7 @@ def start(mode, spec=None):
244
  if _STATE["running"]:
245
  return {"error": "A sweep is already running."}
246
  if not os.path.isdir(DATA_DIR):
247
- return {"error": f"Dataset folder missing: hpt_data/"}
248
  _STOP.clear()
249
  _THREAD = threading.Thread(target=_run, args=(mode, spec or {}), daemon=True)
250
  _THREAD.start()
@@ -267,6 +286,8 @@ def _run(mode, spec):
267
  _log(f"loaded {len(images)} images")
268
  study = get_study()
269
  ensure_baselines(study, images)
 
 
270
  ensure_pbc_baselines(study, images)
271
  if _STOP.is_set():
272
  return
 
30
  AVIF_QUALITIES = (0, 1, 3, 5, 10, 20, 40, 70, 95)
31
  WEBP_QUALITIES = (0, 1, 3, 5, 10, 20, 40, 70, 95)
32
 
 
33
  SEARCH_SPACE = [
34
+ ("patch_count", "cat", [1, 5, 10, 25, 50, 100, 200], True),
35
+ ("search_depth", "cat", [20, 50, 100, 500, 1000], True),
36
+ ("proposal_depth", "cat", [1, 5, 10, 20, 50, 100, 200], False),
37
+ ("exact_depth", "cat", [1, 10, 20, 30, 50], False),
38
+ ("top_k", "cat", [4, 8, 16, 32, 64], False),
39
+ ("anchor_block_size", "cat", [2, 4, 8, 16, 32], False),
40
+ ("cell_sizes_per_candidate", "cat", [1, 2, 3, 4], False),
41
+ ("search_q_start", "cat", [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], False),
42
+ ("search_q_end", "cat", [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], False),
43
+ ("q_init", "cat", [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], False),
44
+ ("q_start", "cat", [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], False),
45
+ ("q_end", "cat", [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], False),
46
+ ("patch_palette_bitcount", "cat", [1, 2, 3, 4, 5], False),
47
  ("mask_size", "cat", [4, 6, 8, 16], False),
48
  ]
49
 
50
  _DEFAULTS = PBC3Config().__dict__
 
51
  PRESETS = ("speed", "balanced", "quality", "compression", "high_quality")
52
 
53
+ _LOCK = threading.Lock()
54
+ _STOP = threading.Event()
55
+ _THREAD = None
56
+ _STATE = {"running": False, "mode": None, "done": 0, "total": None,
57
+ "current": None, "started": None, "error": None, "log": []}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
 
60
+ def _log(msg):
61
+ with _LOCK:
62
+ _STATE["log"] = (_STATE["log"] + [f"{time.strftime('%H:%M:%S')} {msg}"])[-60:]
63
+ print(f"[sweep] {msg}", flush=True)
 
 
 
 
 
 
64
 
65
 
66
+ def _new_bar():
67
+ with _LOCK:
68
+ _STATE["log"] = (_STATE["log"] + [""])[-60:]
69
+
70
+
71
+ def _bar_tick():
72
+ with _LOCK:
73
+ if _STATE["log"]:
74
+ _STATE["log"][-1] += "|"
75
+
76
+
77
+ def status():
78
+ with _LOCK:
79
+ s = dict(_STATE)
80
+ s["log"] = list(_STATE["log"])
81
+ return s
82
 
83
 
84
  def dataset():
 
100
  return [float(np.mean([r[k] for r in rows])) for k in ("seconds", "bpp", "mse")]
101
 
102
 
103
+ def _is_pbc3_trial(t):
104
+ cfg = t.user_attrs.get("config") or {}
105
+ kind = t.user_attrs.get("kind") or ("baseline" if cfg.get("codec") else "pbc3")
106
+ return kind == "pbc3"
107
+
108
+
109
  def _is_pareto_candidate(study, values):
110
  vals = np.asarray(values, dtype=np.float64)
111
  for t in study.trials:
112
+ if not _is_pbc3_trial(t) or t.state != optuna.trial.TrialState.COMPLETE or not t.values or len(t.values) != 3:
113
  continue
114
  other = np.asarray(t.values, dtype=np.float64)
115
  if np.all(other <= vals) and np.any(other < vals):
 
117
  return True
118
 
119
 
120
+ def _log_result(prefix, values, pareto=None):
121
  seconds, bpp, mse = values
122
+ msg = f"{prefix} | MSE {mse:.3f} | bpp {bpp:.5f} | speed {seconds:.3f}s"
123
+ if pareto is not None:
124
+ msg += f" | pareto {'yes' if pareto else 'no'}"
125
+ _log(msg)
126
+
127
+
128
+ def suggest_config(trial):
129
+ cfg = {}
130
+ for name, kind, *a in SEARCH_SPACE:
131
+ if kind == "int":
132
+ cfg[name] = trial.suggest_int(name, a[0], a[1], log=a[2])
133
+ elif kind == "float":
134
+ cfg[name] = trial.suggest_float(name, a[0], a[1])
135
+ else:
136
+ cfg[name] = trial.suggest_categorical(name, a[0])
137
+ return cfg
138
+
139
+
140
+ def _coerce(params):
141
+ out = {}
142
+ for k, v in params.items():
143
+ d = _DEFAULTS.get(k)
144
+ if isinstance(d, bool):
145
+ out[k] = v if isinstance(v, bool) else str(v).lower() in ("true", "1")
146
+ elif isinstance(d, int) and not isinstance(d, bool):
147
+ out[k] = int(v)
148
+ elif isinstance(d, float):
149
+ out[k] = float(v)
150
+ else:
151
+ out[k] = v
152
+ return out
153
 
154
 
155
  def _eval_pbc3(cfg, images, on_image=None):
 
169
  def _eval_codec(fmt, q, images, on_image=None):
170
  rows = []
171
  for im in images:
172
+ if _STOP.is_set():
173
+ raise InterruptedError("sweep stopped")
174
  arr = im["arr"]
175
  pixels = arr.shape[0] * arr.shape[1]
176
  buf = io.BytesIO()
 
205
  have = {t.user_attrs.get("baseline") for t in study.trials}
206
  for fmt, qs in (("JPEG", JPEG_QUALITIES), ("AVIF", AVIF_QUALITIES), ("WEBP", WEBP_QUALITIES)):
207
  for q in qs:
208
+ if _STOP.is_set():
209
+ return
210
  tag = f"{fmt.lower()}_q{q}"
211
  if tag in have:
212
  continue
 
214
  try:
215
  _new_bar()
216
  rows = _eval_codec(fmt, q, images, on_image=_bar_tick)
217
+ except InterruptedError:
218
+ _log(f"baseline {tag} stopped")
219
+ return
220
  except Exception as e:
221
  _log(f"baseline {tag} failed: {e}")
222
  continue
 
224
  _add_completed(study, values, {
225
  "baseline": tag, "kind": "baseline",
226
  "config": {"codec": fmt, "q": q}, "per_image": rows})
227
+ _log_result(f"baseline {tag} done", values)
 
228
 
 
 
 
 
 
 
229
 
230
+ def ensure_pbc_baselines(study, images):
231
+ have = {t.user_attrs.get("baseline") for t in study.trials}
232
+ for name in PRESETS:
233
+ if _STOP.is_set():
234
+ return
235
+ tag = f"preset_{name}"
236
+ if tag in have:
237
+ continue
238
+ _log(f"PBC preset baseline: {name} started")
239
+ cfg = dict(vars(getattr(PBC3Config, name)()))
240
+ try:
241
+ _new_bar()
242
+ rows = _eval_pbc3(cfg, images, on_image=_bar_tick)
243
+ except Exception as e:
244
+ _log(f"preset {name} failed: {e}")
245
+ continue
246
+ values = _avg(rows)
247
+ _add_completed(study, values,
248
+ {"kind": "pbc3", "baseline": tag, "preset": name,
249
+ "config": cfg, "per_image": rows})
250
+ _log_result(f"PBC preset baseline: {name} done", values, _is_pareto_candidate(study, values))
 
 
 
 
251
 
252
 
253
  def _grid_trials(spec):
 
263
  if _STATE["running"]:
264
  return {"error": "A sweep is already running."}
265
  if not os.path.isdir(DATA_DIR):
266
+ return {"error": "Dataset folder missing: hpt_data/"}
267
  _STOP.clear()
268
  _THREAD = threading.Thread(target=_run, args=(mode, spec or {}), daemon=True)
269
  _THREAD.start()
 
286
  _log(f"loaded {len(images)} images")
287
  study = get_study()
288
  ensure_baselines(study, images)
289
+ if _STOP.is_set():
290
+ return
291
  ensure_pbc_baselines(study, images)
292
  if _STOP.is_set():
293
  return
server.py CHANGED
@@ -516,6 +516,9 @@ def _pbc3_trials(study, mp_min=None, mp_max=None):
516
  if t.state == optuna.trial.TrialState.COMPLETE and t.values and len(t.values) == 3]
517
  rows = []
518
  for t in completed:
 
 
 
519
  if mp_min is not None or mp_max is not None:
520
  sub = [r for r in (t.user_attrs.get("per_image") or []) if _in_range(r["mp"], mp_min, mp_max)]
521
  if not sub:
@@ -525,22 +528,24 @@ def _pbc3_trials(study, mp_min=None, mp_max=None):
525
  mse = float(np.mean([r["mse"] for r in sub]))
526
  else:
527
  seconds, bpp, mse = t.values
528
- rows.append((t, seconds, bpp, mse))
529
 
530
- if rows:
531
- orient = np.array([[-s, -b, -m] for _, s, b, m in rows], dtype=np.float64) # minimize all three
 
 
 
 
532
  mask = _pareto_mask(orient)
533
- else:
534
- mask = []
535
 
536
  out = []
537
- for i, (t, s, b, m) in enumerate(rows):
538
- cfg = t.user_attrs.get("config") or {}
539
  out.append({
540
- "number": t.number, "speed": s, "bpp": b, "mse": m, # "speed" key carries seconds
541
- "pareto": bool(mask[i]), "baseline": t.user_attrs.get("baseline"),
542
  "preset": t.user_attrs.get("preset"),
543
- "kind": t.user_attrs.get("kind") or ("baseline" if cfg.get("codec") else "pbc3"),
544
  "codec": cfg.get("codec"), "q": cfg.get("q"), "params": cfg,
545
  })
546
  return out
 
516
  if t.state == optuna.trial.TrialState.COMPLETE and t.values and len(t.values) == 3]
517
  rows = []
518
  for t in completed:
519
+ cfg = t.user_attrs.get("config") or {}
520
+ kind = t.user_attrs.get("kind") or ("baseline" if cfg.get("codec") else "pbc3")
521
+
522
  if mp_min is not None or mp_max is not None:
523
  sub = [r for r in (t.user_attrs.get("per_image") or []) if _in_range(r["mp"], mp_min, mp_max)]
524
  if not sub:
 
528
  mse = float(np.mean([r["mse"] for r in sub]))
529
  else:
530
  seconds, bpp, mse = t.values
 
531
 
532
+ rows.append((t, seconds, bpp, mse, kind, cfg))
533
+
534
+ pareto = [False] * len(rows)
535
+ pbc_rows = [(i, s, b, m) for i, (_, s, b, m, kind, _) in enumerate(rows) if kind == "pbc3"]
536
+ if pbc_rows:
537
+ orient = np.array([[-s, -b, -m] for _, s, b, m in pbc_rows], dtype=np.float64)
538
  mask = _pareto_mask(orient)
539
+ for (i, _, _, _), is_pareto in zip(pbc_rows, mask):
540
+ pareto[i] = bool(is_pareto)
541
 
542
  out = []
543
+ for i, (t, s, b, m, kind, cfg) in enumerate(rows):
 
544
  out.append({
545
+ "number": t.number, "speed": s, "bpp": b, "mse": m,
546
+ "pareto": pareto[i], "baseline": t.user_attrs.get("baseline"),
547
  "preset": t.user_attrs.get("preset"),
548
+ "kind": kind,
549
  "codec": cfg.get("codec"), "q": cfg.get("q"), "params": cfg,
550
  })
551
  return out