Spaces:
Runtime error
Runtime error
| /* Model Insights β renders everything from /static/data/model_card.json */ | |
| const pretty = (s) => s.replace(/_/g, ' '); | |
| const f1class = (v) => (v >= 0.8 ? 'good' : v >= 0.5 ? 'mid' : 'bad'); | |
| fetch('/static/data/model_card.json') | |
| .then(r => r.json()) | |
| .then(render) | |
| .catch(() => { | |
| document.getElementById('kpis').innerHTML = | |
| '<p class="muted">Could not load model_card.json</p>'; | |
| }); | |
| function render(card) { | |
| const m = card.metrics, ds = card.dataset, mdl = card.model, hp = card.hyperparameters; | |
| // ββ KPIs ββ | |
| const kpis = [ | |
| { v: (m.accuracy * 100).toFixed(1) + '%', l: 'Accuracy' }, | |
| { v: m.macro_f1.toFixed(3), l: 'Macro F1' }, | |
| { v: ds.classes, l: 'Classes' }, | |
| { v: ds.total_images.toLocaleString(), l: 'Images' }, | |
| { v: mdl.trainable_params_millions + 'M', l: 'Parameters' }, | |
| { v: mdl.weights_size_mb + 'MB', l: 'Model size' }, | |
| ]; | |
| document.getElementById('kpis').innerHTML = kpis.map(k => | |
| `<div class="kpi rise"><b>${k.v}</b><span>${k.l}</span></div>`).join(''); | |
| // ββ Pipeline ββ | |
| document.getElementById('pipeline').innerHTML = card.pipeline.map(p => | |
| `<div class="pipe-step"><b>${p.step}</b><span>${p.detail}</span></div>`).join(''); | |
| // ββ Dataset donut (train vs test) ββ | |
| const trainPct = Math.round(ds.train_images / ds.total_images * 100); | |
| const accent = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim() || '#2EA866'; | |
| document.getElementById('donut').style.background = | |
| `conic-gradient(${accent} 0 ${trainPct}%, var(--bg-tint) ${trainPct}% 100%)`; | |
| document.getElementById('donutLegend').innerHTML = ` | |
| <div><i style="background:${accent}"></i>Train Β· ${ds.train_images.toLocaleString()}</div> | |
| <div><i style="background:var(--bg-tint)"></i>Test Β· ${ds.test_images.toLocaleString()}</div>`; | |
| document.getElementById('dsFacts').innerHTML = ` | |
| <div><b>${ds.classes}</b><span>dish classes</span></div> | |
| <div><b>${ds.per_class}</b><span>images / class</span></div> | |
| <div><b>${ds.split}</b><span>split</span></div> | |
| <div><b>${ds.balanced ? 'Balanced' : 'Imbalanced'}</b><span>distribution</span></div>`; | |
| document.getElementById('augChips').innerHTML = | |
| card.augmentations.map(a => `<span class="pill">${a}</span>`).join(''); | |
| // ββ Model card + hyperparameters ββ | |
| const rows = (obj) => Object.entries(obj).map(([k, v]) => | |
| `<tr><td>${k}</td><td>${v}</td></tr>`).join(''); | |
| document.getElementById('modelSpec').innerHTML = rows({ | |
| 'Name': mdl.published_as, | |
| 'Architecture': mdl.architecture, | |
| 'Framework': mdl.framework, | |
| 'Parameters': mdl.trainable_params_millions + ' M', | |
| 'Weights size': mdl.weights_size_mb + ' MB', | |
| 'Input': mdl.input, | |
| 'Output': mdl.output, | |
| }); | |
| document.getElementById('hpSpec').innerHTML = rows({ | |
| 'Epochs': hp.epochs, | |
| 'Learning rate': hp.learning_rate, | |
| 'Train batch size': hp.train_batch_size, | |
| 'Eval batch size': hp.eval_batch_size, | |
| 'Weight decay': hp.weight_decay, | |
| 'Warmup steps': hp.warmup_steps, | |
| 'Optimizer steps': hp.optimizer_steps, | |
| 'Runtime': hp.runtime_min + ' min (GPU)', | |
| }); | |
| // ββ Train compare (baseline vs final) ββ | |
| document.getElementById('trainCompare').innerHTML = ` | |
| <div class="tc"><b>${(m.baseline_accuracy * 100).toFixed(1)}%</b><span>Baseline accuracy</span></div> | |
| <div class="tc final"><b>${(m.accuracy * 100).toFixed(1)}%</b><span>After fine-tuning</span></div> | |
| <div class="tc"><b>${m.macro_f1.toFixed(3)}</b><span>Macro F1</span></div>`; | |
| document.getElementById('curveNote').textContent = | |
| 'Per-epoch loss/accuracy curves are not plotted: the training run only logged baseline and final metrics. Enable epoch logging in the notebook to capture full curves.'; | |
| // ββ Per-class F1 bars ββ | |
| const sorted = [...card.per_class].sort((a, b) => b.f1 - a.f1); | |
| renderBars('bestBars', sorted.slice(0, 10)); | |
| renderBars('worstBars', sorted.slice(-10).reverse()); | |
| renderBars('allBars', sorted); | |
| // ββ Limitations ββ | |
| document.getElementById('limits').innerHTML = | |
| card.limitations.map(l => `<li>${l}</li>`).join(''); | |
| // animate bar widths after paint | |
| requestAnimationFrame(() => requestAnimationFrame(() => { | |
| document.querySelectorAll('.bar-fill').forEach(el => { el.style.width = el.dataset.w + '%'; }); | |
| })); | |
| } | |
| function renderBars(id, items) { | |
| document.getElementById(id).innerHTML = items.map(c => ` | |
| <div class="bar-row"> | |
| <span class="name" title="${pretty(c.label)}">${pretty(c.label)}</span> | |
| <div class="bar-track"><div class="bar-fill ${f1class(c.f1)}" data-w="${(c.f1 * 100).toFixed(0)}"></div></div> | |
| <span class="val">${c.f1.toFixed(2)}</span> | |
| </div>`).join(''); | |
| } | |