docs(dashboard): add how-to-read guide per panel
This commit is contained in:
@@ -46,15 +46,17 @@ ALPHA_C = "#9467bd" # alpha line on the losses panel
|
||||
RELOAD_JS = ('<script type="text/javascript"><![CDATA[ '
|
||||
'setTimeout(function(){ location.reload(); }, 60000); ]]></script>')
|
||||
W, H = 1400, 1720
|
||||
TITLE_H, GUIDE_H = 80, 180
|
||||
TITLE_H, GUIDE_H = 80, 80
|
||||
# rows: (header_y, panel_top_y, panel_bottom_y, x_left, x_right)
|
||||
# panel_top sits low enough to leave room under header+sub for the
|
||||
# per-panel how-to-read guide (3 italic lines, see panel_guide)
|
||||
C1_L, C1_R = 70, 697
|
||||
C2_L, C2_R = 747, 1375
|
||||
ROWS = {
|
||||
1: (100, 120, 720, C1_L, C1_R),
|
||||
2: (100, 120, 720, C2_L, C2_R),
|
||||
3: (940, 958, 1428, C1_L, C1_R),
|
||||
4: (940, 958, 1428, C2_L, C2_R),
|
||||
1: (100, 184, 784, C1_L, C1_R),
|
||||
2: (100, 184, 784, C2_L, C2_R),
|
||||
3: (940, 1024, 1494, C1_L, C1_R),
|
||||
4: (940, 1024, 1494, C2_L, C2_R),
|
||||
}
|
||||
PANEL_TITLES = [
|
||||
"Test matches - win % vs opponents",
|
||||
@@ -62,11 +64,22 @@ PANEL_TITLES = [
|
||||
"Throughput - games per hour",
|
||||
"Max score per eval cycle",
|
||||
]
|
||||
# per-panel how-to-read notes: (what it shows, axes, which direction is better)
|
||||
GUIDES = {
|
||||
1: ("How often the bot wins against each opponent in test battles.",
|
||||
"X = training progress (match number); Y = win rate, 0-100%.",
|
||||
"higher is better."),
|
||||
2: ("How well the brain is learning: losses should fall; alpha sets explore/exploit.",
|
||||
"X = training progress; left Y (log) = losses; right Y (linear) = alpha.",
|
||||
"lower losses = better; alpha falls over time as the bot gets confident."),
|
||||
3: ("How many games per hour the bot trains (its learning speed).",
|
||||
"X = training progress (10-game chunks); Y = games per hour.",
|
||||
"higher = faster learning; a steady line beats a spiky one."),
|
||||
4: ("Best single-round score the bot managed in each test cycle.",
|
||||
"X = eval cycle number; Y = best score achieved.",
|
||||
"higher is better; a rising trend means the bot is improving."),
|
||||
}
|
||||
GUIDE_LINES = [
|
||||
"Test matches: dots are single fights, thick line shows trend.",
|
||||
"Loss spikes are normal early; endless growth is bad.",
|
||||
"Throughput flat is healthy; dips mean something slowed.",
|
||||
"Max score: best single-round score the bot managed in that eval cycle.",
|
||||
"This file reloads itself in Chrome every sixty seconds.",
|
||||
"Regenerate anytime with tools/watch_dashboard.sh or the python command.",
|
||||
]
|
||||
@@ -289,6 +302,16 @@ def header(x, y, title, sub=None):
|
||||
return s
|
||||
|
||||
|
||||
def panel_guide(x0, y, guide):
|
||||
"""Italic how-to-read note between a panel's header and its plot area."""
|
||||
what, axes, better = guide
|
||||
s = ""
|
||||
for i, txt in enumerate((f"What: {what}", f"Axes: {axes}", f"Better: {better}")):
|
||||
s += (f'<text x="{x0}" y="{y + i * 16}" font-size="12" font-style="italic" '
|
||||
f'fill="#555">{esc(txt)}</text>\n')
|
||||
return s
|
||||
|
||||
|
||||
# ---------- panels ----------
|
||||
|
||||
def panel_test_matches(series, geo):
|
||||
@@ -455,21 +478,22 @@ def build_dashboard(campaign, metrics_f, out):
|
||||
f'(open this file in Chrome)</text>\n')
|
||||
|
||||
drawers = [
|
||||
(ROWS[1], PANEL_TITLES[0],
|
||||
(ROWS[1], PANEL_TITLES[0], GUIDES[1],
|
||||
"raw dots = single test matches, thick = rolling-mean-%d" % TREND_WINDOW,
|
||||
lambda: panel_test_matches(series, ROWS[1])),
|
||||
(ROWS[2], PANEL_TITLES[1],
|
||||
(ROWS[2], PANEL_TITLES[1], GUIDES[2],
|
||||
"training_metrics.jsonl - big early spikes are normal",
|
||||
lambda: panel_losses(rows, ROWS[2])),
|
||||
(ROWS[3], PANEL_TITLES[2],
|
||||
(ROWS[3], PANEL_TITLES[2], GUIDES[3],
|
||||
"method: training_metrics.jsonl 'epoch' deltas; 1 row = one 10-game chunk",
|
||||
lambda: panel_throughput(rows, ROWS[3])),
|
||||
(ROWS[4], PANEL_TITLES[3],
|
||||
(ROWS[4], PANEL_TITLES[3], GUIDES[4],
|
||||
"campaign_v4_stdout.log - max of the 10 deterministic round scores per eval",
|
||||
lambda: panel_max_score(maxes, ROWS[4])),
|
||||
]
|
||||
for geo, title, sub, drawer in drawers:
|
||||
for geo, title, guide, sub, drawer in drawers:
|
||||
s += header(geo[3], geo[0], title, sub)
|
||||
s += panel_guide(geo[3], geo[0] + 33, guide)
|
||||
s += drawer() or "" # panels bare-return None on their no-data path
|
||||
|
||||
s += guide_block(W, H, GUIDE_LINES)
|
||||
@@ -530,6 +554,10 @@ def selftest():
|
||||
assert text.count(PANEL_TITLES[0]) == 1
|
||||
assert "How to read" in text, "reading guide missing"
|
||||
assert 'width="1400"' in text and 'height="1720"' in text
|
||||
# every panel carries its own What/Axes/Better how-to-read note
|
||||
assert text.count("What:") == len(PANEL_TITLES), text.count("What:")
|
||||
for g in GUIDES.values():
|
||||
assert esc(g[0]) in text, f"panel guide missing: {g[0]}"
|
||||
# circles: 4 eval dots (panel 1) + 2 throughput rate dots (panel 3)
|
||||
# + 2 cycles x 2 opponents max-score dots (panel 4)
|
||||
assert text.count("<circle") == 10, text.count("<circle")
|
||||
|
||||
Reference in New Issue
Block a user