(() => { const $ = (selector) => document.querySelector(selector); const runRoot = $("#view-train"); const checkpointRoot = $("#view-train-checkpoints"); if (!runRoot || !checkpointRoot) return; let poll = null; let checkpoints = []; let activeTab = "run"; let latestStatus = {}; let customOutputName = false; const presets = [ ["compression", "Compression"], ["balanced", "Balanced"], ["quality", "Quality"], ["high_quality", "High quality"], ]; const esc = (value) => String(value ?? "").replace(/[&<>\"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", "\"": """ }[c])); const api = async (path, options) => { const response = await fetch(path, options); const data = await response.json().catch(() => ({})); if (!response.ok) throw new Error(data.error || `HTTP ${response.status}`); return data; }; const metricColor = (value, baseline, observed) => { if (value == null || baseline == null) return ""; const values = observed.filter((item) => Number.isFinite(Number(item))); const best = Math.min(Number(baseline), ...(values.length ? values : [Number(baseline)])); const worst = Math.max(Number(baseline), ...(values.length ? values : [Number(baseline)])); const current = Number(value); if (current <= Number(baseline)) { const span = Number(baseline) - best; const amount = span > 0 ? Math.min(1, (Number(baseline) - current) / span) : 0; return `rgb(${Math.round(250 - 198 * amount)}, ${Math.round(204 + 31 * amount)}, ${Math.round(21 + 79 * amount)})`; } const span = worst - Number(baseline); const amount = span > 0 ? Math.min(1, (current - Number(baseline)) / span) : 0; return `rgb(${Math.round(250 - 11 * amount)}, ${Math.round(204 - 136 * amount)}, ${Math.round(21 + 47 * amount)})`; }; const metricValue = (metric, value, baseline, observed, digits) => { const color = metricColor(value, baseline, observed); return color ? `${fmt(value, digits)}` : fmt(value, digits); }; const download = (path) => window.open(`/api/train/download_checkpoint?path=${encodeURIComponent(path)}`, "_blank"); const fmt = (value, digits = 4) => value == null ? "—" : Number(value).toFixed(digits); function showLab() { ["landing", "app", "sweep-lab"].forEach((id) => { const el = $(`#${id}`); if (el) el.hidden = true; }); $("#train-lab").hidden = false; renderRun(); loadCheckpoints(); refresh(); } function showLanding() { $("#train-lab").hidden = true; $("#landing").hidden = false; if (poll) clearInterval(poll); poll = null; } $("#enter-train")?.addEventListener("click", showLab); $("#train-back")?.addEventListener("click", showLanding); document.querySelectorAll("[data-tr-tab]").forEach((tab) => tab.addEventListener("click", () => { activeTab = tab.dataset.trTab; document.querySelectorAll("[data-tr-tab]").forEach((item) => item.classList.toggle("active", item.dataset.trTab === activeTab)); runRoot.hidden = activeTab !== "run"; checkpointRoot.hidden = activeTab !== "checkpoints"; if (activeTab === "checkpoints") renderCheckpoints(); })); function renderRun() { customOutputName = false; runRoot.innerHTML = `
Runs the current filler trainer in a resumable subprocess. Start the same output again to continue from its optimizer and RNG checkpoint.
| Checkpoint | Train reward | Val reward | Aggregate score | Train MSE | Val MSE | Train bpp | Val bpp | Train grid coefficients | Val grid coefficients | Train encode s | Val encode s |
|---|
No validation rows yet. The first baseline pass can take a few minutes.
`; } return; const legacyKey = $("#tr-sort")?.value || "epoch"; const legacyDirection = Number($("#tr-sort-dir")?.dataset.direction || (legacyKey === "epoch" ? "1" : "-1")); const legacySorted = history.filter((row) => row.epoch || row.validation).slice().sort((a, b) => { const value = (row) => legacyKey === "epoch" ? (row.epoch ?? -1) : (legacyKey.startsWith("train_") ? row : (row.validation || row))[legacyKey]; const av = value(a); const bv = value(b); return (Number(av ?? -Infinity) - Number(bv ?? -Infinity)) * legacyDirection; }); const observed = (field, training = false) => sorted.map((row) => training ? row[field] : (row.validation || row)[field]); const valMse = observed("mse"); const valBpp = observed("bpp"); const valWork = observed("work"); const trainMse = observed("train_mse", true); const trainBpp = observed("train_bpp", true); const trainWork = observed("train_work", true); const legacyRows = legacySorted.map((row) => { const v = row.validation || row; return `| Checkpoint | Train reward | Val reward | Aggregate score | Train MSE | Val MSE | Train bpp | Val bpp | Train work | Val work |
|---|
No validation rows yet. The first baseline pass can take a few minutes.
`; } function resultText() { const history = latestStatus.history || []; return history.map((row) => { const v = row.validation || row; return [row.epoch ? `Epoch ${row.epoch}` : "Initial", fmt(row.train_reward, 5), fmt(row.reward ?? v.reward, 5), fmt(v.aggregate_score, 5), fmt(row.train_mse, 3), fmt(v.mse, 3), fmt(row.train_bpp, 6), fmt(v.bpp, 6), fmt(row.train_work, 0), fmt(v.work, 0), fmt(row.train_encode_seconds, 3), fmt(v.encode_seconds, 3)].join("\t"); }).join("\n"); } async function copyResults() { const text = ["Checkpoint\tTrain reward\tVal reward\tAggregate score\tTrain MSE\tVal MSE\tTrain bpp\tVal bpp\tTrain grid coefficients\tVal grid coefficients\tTrain encode s\tVal encode s", resultText()].filter(Boolean).join("\n"); try { await navigator.clipboard.writeText(text); $("#tr-pause").checked = true; } catch { window.prompt("Copy training results", text); } } function downloadResults() { const blob = new Blob([JSON.stringify(latestStatus, null, 2)], { type: "application/json" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = "pbc_training_results.json"; link.click(); URL.revokeObjectURL(link.href); } async function refresh() { const data = await api("/api/train/status").catch((error) => ({ running: false, log_tail: error.message, history: [] })); latestStatus = data; const status = $("#tr-status"); if (!status) return; const state = data.running ? "Running" : data.return_code == null ? "Idle" : data.return_code === 0 ? "Finished" : `Stopped / failed (${data.return_code})`; const output = data.output ? `${esc(data.output)}` : "No output selected";
status.innerHTML = `${state} · ${output}${data.spec?.presets ? ` · ${esc(data.spec.presets)}` : ""}`;
if (!$("#tr-pause")?.checked) {
$("#tr-log-tail").textContent = data.log_tail || "";
renderHistory(data.history || []);
drawCharts(data.history || []);
}
renderBestCheckpoints(data.checkpoint_groups || {});
if (data.running && !poll) poll = setInterval(refresh, 3000);
if (!data.running && poll) { clearInterval(poll); poll = null; loadCheckpoints(); }
}
function chartLayout(title, yTitle, extra = {}) {
return Object.assign({ title, height: 290, margin: { l: 52, r: 48, t: 38, b: 42 }, paper_bgcolor: "rgba(0,0,0,0)", plot_bgcolor: "rgba(0,0,0,0)", font: { color: "#cfd3dc", size: 11 }, xaxis: { title: "Epoch", gridcolor: "rgba(255,255,255,.07)" }, yaxis: { title: yTitle, gridcolor: "rgba(255,255,255,.07)" }, legend: { orientation: "h" } }, extra);
}
function drawCharts(history) {
if (!window.Plotly) return;
const points = history.filter((row) => row.epoch || row.validation);
if (!points.length) return;
const x = points.map((row) => row.epoch ?? 0);
const values = points.map((row) => row.validation || row);
const color = points.map((_, i) => i);
Plotly.react($("#tr-chart-reward"), [
{ x, y: points.map((row) => row.train_reward), name: "train reward", mode: "lines", line: { color: "#60a5fa" } },
{ x, y: points.map((row) => row.reward ?? (row.validation || {}).reward), name: "validation reward", mode: "lines", line: { color: "#f59e0b" } },
{ x, y: values.map((row) => row.aggregate_score), name: "aggregate score", mode: "lines", line: { color: "#22c55e" } },
], chartLayout("Reward and aggregate score", "score"), { responsive: true, displayModeBar: false });
Plotly.react($("#tr-chart-metrics"), [
{ x, y: points.map((row) => row.train_mse), name: "train MSE", mode: "lines", line: { color: "#93c5fd" } },
{ x, y: values.map((row) => row.mse), name: "val MSE", mode: "lines", line: { color: "#ef4444" } },
{ x, y: values.map((row) => row.baseline_mse), name: "PBC3.0 MSE", mode: "lines", line: { color: "#fca5a5", dash: "dot" } },
], chartLayout("MSE", "MSE"), { responsive: true, displayModeBar: false });
Plotly.react($("#tr-chart-work"), [
{ x, y: points.map((row) => row.train_work), name: "train grid coefficients", mode: "lines", line: { color: "#a78bfa" } },
{ x, y: values.map((row) => row.work), name: "val grid coefficients", mode: "lines", line: { color: "#c084fc" } },
], chartLayout("Grid coefficients", "coefficients"), { responsive: true, displayModeBar: false });
Plotly.react($("#tr-chart-train"), [
{ x, y: points.map((row) => row.train_bpp), name: "train bpp", mode: "lines", line: { color: "#93c5fd" } },
{ x, y: values.map((row) => row.bpp), name: "val bpp", mode: "lines", line: { color: "#38bdf8" } },
{ x, y: values.map((row) => row.baseline_bpp), name: "PBC3.0 bpp", mode: "lines", line: { color: "#7dd3fc", dash: "dot" } },
], chartLayout("Bitrate", "bits / pixel"), { responsive: true, displayModeBar: false });
Plotly.react($("#tr-chart-speed"), [
{ x, y: points.map((row) => row.train_encode_seconds), name: "train encode seconds", mode: "lines", line: { color: "#f59e0b" } },
{ x, y: values.map((row) => row.encode_seconds), name: "val encode seconds", mode: "lines", line: { color: "#ef4444" } },
{ x, y: values.map((row) => row.baseline_speed_seconds), name: "reference seconds", mode: "lines", line: { color: "#fca5a5", dash: "dot" } },
], chartLayout("Encode speed", "seconds"), { responsive: true, displayModeBar: false });
const baseline = values[0];
Plotly.react($("#tr-chart-rd"), [
{ x: values.map((row) => row.bpp), y: values.map((row) => row.mse), text: x.map((epoch) => `Epoch ${epoch}`), mode: "markers", name: "validation RD", marker: { size: 9, color, colorscale: "YlOrRd", showscale: true, colorbar: { title: "newer" } } },
{ x: [baseline.baseline_bpp], y: [baseline.baseline_mse], text: ["PBC3.0 baseline"], mode: "markers+text", name: "PBC3.0 baseline", textposition: "top center", marker: { size: 13, color: "#fff", symbol: "diamond", line: { color: "#ef4444", width: 2 } } },
], chartLayout("Rate–distortion history", "MSE", { xaxis: { title: "bpp", gridcolor: "rgba(255,255,255,.07)" } }), { responsive: true, displayModeBar: false });
}
function renderBestCheckpoints(groups) {
const root = $("#tr-best-checkpoints");
if (!root) return;
const normalized = groups.validation || groups.training ? groups : { validation: groups, training: {} };
const labels = { mse: "Best MSE checkpoints", bpp: "Best bpp checkpoints", work: "Best grid-coefficient checkpoints", rd: "Best RD checkpoints", overall: "Best overall checkpoint" };
root.innerHTML = ["validation", "training"].map((split) => {
const splitGroups = normalized[split] || {};
const allEntries = Object.values(splitGroups).flat();
const observed = (metric) => allEntries.map((entry) => entry[metric]);
const content = Object.entries(labels).map(([metric, label]) => {
const entries = (splitGroups[metric] || []).filter((entry, index, all) => index === all.findIndex((other) => other.mse === entry.mse && other.bpp === entry.bpp && other.work === entry.work));
const rows = entries.map((entry) => `No checkpoint yet.
`}Download an exported .npz after reviewing its validation history, then upload it into the main project when you want to test or promote it.
No matching checkpoints.
`; } setInterval(() => { if (!$("#train-lab").hidden && activeTab === "run") refresh(); }, 5000); })();