pentanet-124m / scripts /generate_dashboard.py
Kyworn's picture
Upload folder using huggingface_hub
80603a0 verified
import glob
import json
import os
def generate_dashboard():
# Only read completed or valid structured log files (with valid seed)
files = glob.glob('models/*_s[0-9]*_results.json')
results = []
for f in files:
with open(f, 'r') as file:
try:
results.append(json.load(file))
except Exception:
pass
# Sort results to be predictable
results.sort(key=lambda x: (x.get('mode', ''), x.get('seed', 0)))
html_template = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PentaNet vs BitNet - NeurIPS Dashboard</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
:root {
--bg-color: #0f172a;
--surface-color: #1e293b;
--text-main: #f8fafc;
--text-muted: #94a3b8;
--border: #334155;
--accent: #38bdf8;
}
body { font-family: 'Inter', system-ui, sans-serif; background-color: var(--bg-color); color: var(--text-main); margin: 0; padding: 40px 20px; }
.container { max-width: 1200px; margin: 0 auto; }
.header { text-align: center; margin-bottom: 40px; }
.header h1 { font-size: 2.5rem; margin: 0 0 10px 0; background: linear-gradient(135deg, #38bdf8, #818cf8); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
.header p { color: var(--text-muted); font-size: 1.1rem; }
.card { background-color: var(--surface-color); border-radius: 16px; padding: 30px; margin-bottom: 30px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3); border: 1px solid var(--border); }
.card h2 { margin-top: 0; margin-bottom: 20px; font-size: 1.4rem; display: flex; align-items: center; gap: 10px; }
.chart-container { position: relative; height: 400px; width: 100%; }
table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
th, td { padding: 16px; text-align: left; border-bottom: 1px solid var(--border); }
th { color: var(--text-muted); font-weight: 600; text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.05em; }
tr:last-child td { border-bottom: none; }
.badge { background: #334155; padding: 4px 8px; border-radius: 6px; font-size: 0.85rem; font-weight: bold; }
.badge.pentanet { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
.badge.bitnet { background: rgba(244, 63, 94, 0.2); color: #f43f5e; }
.controls { display: flex; flex-wrap: wrap; gap: 25px; margin-bottom: 20px; background: rgba(0,0,0,0.2); padding: 15px; border-radius: 8px; align-items: center; }
.controls strong { margin-right: 10px; color: var(--accent); }
.controls label { margin-right: 15px; cursor: pointer; user-select: none; font-size: 0.95rem; }
.controls input[type="checkbox"] { accent-color: var(--accent); margin-right: 5px; transform: scale(1.1); cursor: pointer; }
select { background-color: #0f172a; color: var(--text-main); border: 1px solid var(--border); padding: 6px 12px; border-radius: 6px; font-size: 1rem; cursor: pointer; }
select:focus { outline: none; border-color: var(--accent); }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>PentaNet Metrics Dashboard</h1>
<p>Live interactive visualization of Model Training.</p>
</div>
<!-- Summary Table -->
<div class="card">
<h2>📊 Training Results Summary</h2>
<table id="summaryTable">
<thead>
<tr>
<th>Model Architecture</th>
<th>Seed</th>
<th>Parameters</th>
<th>Total Time</th>
<th>Best Perplexity (PPL)</th>
<th>Best Val Loss</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<!-- PPL Chart -->
<div class="card">
<h2>📉 Perplexity Convergence</h2>
<div class="controls" id="pplControls">
<div>
<strong>Algorithms:</strong>
<label><input type="checkbox" class="model-filter" value="pentanet" checked> PentaNet</label>
<label><input type="checkbox" class="model-filter" value="bitnet" checked> BitNet</label>
</div>
<div id="seedFilters">
<strong>Seeds:</strong>
<!-- Populated by JS -->
</div>
</div>
<div class="chart-container">
<canvas id="pplChart"></canvas>
</div>
</div>
<!-- Weights Chart -->
<div class="card">
<h2>⚖️ PentaNet Weight Entropy (Evolution of {-2 .. 2})</h2>
<div class="controls">
<div>
<strong>PentaNet Explorer: </strong>
<select id="weightSeedSelect"></select>
</div>
</div>
<div class="chart-container">
<canvas id="weightChart"></canvas>
</div>
</div>
</div>
<script>
const rawData = DATA_PLACEHOLDER;
// --- 1. Populate Table ---
const tbody = document.querySelector('#summaryTable tbody');
rawData.forEach(run => {
if (!run.log || run.log.length === 0) return;
const ppl = Math.exp(run.best_val_loss).toFixed(2);
const tr = document.createElement('tr');
tr.innerHTML = `
<td><span class="badge ${run.mode}">${run.mode.toUpperCase()}</span></td>
<td>${run.seed}</td>
<td>${run.params_M.toFixed(1)}M</td>
<td>${Math.round(run.total_time)}s</td>
<td style="font-weight: bold; color: ${run.mode === 'pentanet' ? '#38bdf8' : '#f43f5e'}">${ppl}</td>
<td>${run.best_val_loss.toFixed(4)}</td>
`;
tbody.appendChild(tr);
});
// Extract unique seeds from all data
const allSeeds = [...new Set(rawData.map(r => r.seed))].sort((a,b)=>a-b);
const seedFiltersDiv = document.getElementById('seedFilters');
allSeeds.forEach(seed => {
seedFiltersDiv.innerHTML += `<label><input type="checkbox" class="seed-filter" value="${seed}" checked> ${seed}</label>`;
});
// Extract pentanet runs for the dropdown
const weightSelect = document.getElementById('weightSeedSelect');
const pentanetRuns = rawData.filter(r => r.mode === 'pentanet' && r.log && r.log.length > 2);
pentanetRuns.forEach(run => {
weightSelect.innerHTML += `<option value="${run.seed}">Seed ${run.seed}</option>`;
});
// --- 2. Chart: PPL Convergence ---
const ctxPpl = document.getElementById('pplChart').getContext('2d');
const pplDatasetsAll = [];
const knownColors = {
'pentanet': ['#38bdf8', '#0ea5e9', '#6366f1', '#a855f7'],
'bitnet': ['#f43f5e', '#e11d48', '#be123c', '#9f1239']
};
let colorIndex = {'pentanet': 0, 'bitnet': 0};
let maxItersPpl = [];
rawData.forEach(run => {
if (!run.log || run.log.length < 2) return;
const logs = run.log.slice(1);
const dataPoints = logs.map(l => l.ppl);
const iterPoints = logs.map(l => l.iter);
if (iterPoints.length > maxItersPpl.length) maxItersPpl = iterPoints;
const color = knownColors[run.mode][colorIndex[run.mode] % knownColors[run.mode].length];
colorIndex[run.mode]++;
pplDatasetsAll.push({
_meta_mode: run.mode,
_meta_seed: String(run.seed),
label: `${run.mode.toUpperCase()} (Seed ${run.seed})`,
data: dataPoints,
borderColor: color,
backgroundColor: `${color}22`,
borderWidth: 2,
pointRadius: 0,
pointHoverRadius: 5,
tension: 0.3,
fill: false
});
});
const pplChart = new Chart(ctxPpl, {
type: 'line',
data: { labels: maxItersPpl, datasets: [...pplDatasetsAll] },
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
scales: {
y: { type: 'logarithmic', title: { display: true, text: 'Validation PPL (Log)' }, grid: { color: '#334155' } },
x: { title: { display: true, text: 'Iterations' }, grid: { color: '#334155', display: false } }
},
plugins: { legend: { labels: { color: '#f8fafc' } } }
}
});
// Update Chart Logic for Toggles
function updatePplChart() {
const activeModels = Array.from(document.querySelectorAll('.model-filter:checked')).map(cb => cb.value);
const activeSeeds = Array.from(document.querySelectorAll('.seed-filter:checked')).map(cb => cb.value);
pplChart.data.datasets = pplDatasetsAll.filter(ds =>
activeModels.includes(ds._meta_mode) && activeSeeds.includes(ds._meta_seed)
);
pplChart.update();
}
document.querySelectorAll('.model-filter, .seed-filter').forEach(cb => {
cb.addEventListener('change', updatePplChart);
});
// --- 3. Chart: Weight Distribution ---
const ctxWeights = document.getElementById('weightChart').getContext('2d');
const bucketColors = ['#e11d48', '#38bdf8', '#22c55e', '#0284c7', '#be123c'];
const weightKeys = ['-2', '-1', '0', '1', '2'];
let weightChart;
function renderWeightChart(seed) {
const run = pentanetRuns.find(r => String(r.seed) === String(seed));
if (!run) return;
const logs = run.log;
const iters = logs.map(l => l.iter);
const datasets = weightKeys.map((key, i) => {
const dataPts = logs.map(l => {
const w = l.weights;
const total = Object.values(w).reduce((a,b)=>a+b, 0);
return (w[key] / total) * 100;
});
return {
label: `Weight Bucket [${key}]`,
data: dataPts,
borderColor: bucketColors[i],
backgroundColor: bucketColors[i],
fill: false,
tension: 0.3,
pointRadius: 0,
pointHoverRadius: 5,
borderWidth: 3
};
});
if (weightChart) weightChart.destroy();
weightChart = new Chart(ctxWeights, {
type: 'line',
data: { labels: iters, datasets: datasets },
options: {
responsive: true,
maintainAspectRatio: false,
interaction: { mode: 'index', intersect: false },
scales: {
y: { min: 0, max: 100, title: { display: true, text: 'Percentage (%)' }, grid: { color: '#334155' } },
x: { title: { display: true, text: 'Iterations' }, grid: { color: '#334155', display: false } }
},
plugins: {
legend: { labels: { color: '#f8fafc' } },
tooltip: { callbacks: { label: (ctx) => ` ${ctx.dataset.label}: ${ctx.raw.toFixed(1)}%` } }
}
}
});
}
if (pentanetRuns.length > 0) {
renderWeightChart(pentanetRuns[0].seed);
}
weightSelect.addEventListener('change', (e) => {
renderWeightChart(e.target.value);
});
</script>
</body>
</html>
"""
html_content = html_template.replace("DATA_PLACEHOLDER", json.dumps(results))
with open('dashboard.html', 'w') as f:
f.write(html_content)
print("✅ Interactive dashboard written to dashboard.html")
if __name__ == "__main__":
generate_dashboard()