/* Model Training screen — globale gap-analyse voor het contour fine-tune model. Spiegelt tools/export_training.py --stats (endpoint GET /api/training/stats). */ const VLAG_KLEUR = { ok: 'var(--color-success)', krap: 'var(--color-warning)', tekort: 'var(--color-error)', }; const VLAG_KIND = { ok: 'success', krap: 'warning', tekort: 'danger' }; const VLAG_LABEL = { ok: 'Op orde', krap: 'Krap', tekort: 'Tekort' }; const STATUS_LABEL = { ok: 'Bruikbaar (compleet gereviewd)', incompleet_gereviewd:'Nog te reviewen', geen_componenten: 'Geen genummerde componenten', label_onbepaald: 'Label onbepaald', ongeldig_beeldpad: 'Ongeldig beeldpad', beeld_ontbreekt: 'Beeld ontbreekt', }; function TStatCard({ label, value, hint, icon, kind = 'neutral' }) { return (
{label}
{value}
{hint &&
{hint}
}
); } function SubtypeBar({ subtype, n, vlag, signaal, fatsoenlijk, schaalMax }) { const kleur = VLAG_KLEUR[vlag] || 'var(--color-border)'; // Gedeelde schaal: staaflengte = werkelijk aantal t.o.v. de grootste klasse/streefwaarde, // zodat de staven onderling vergelijkbaar zijn (echte staafdiagram). Twee referentielijnen // tonen signaal- en streefwaarde. const pct = Math.round(100 * n / schaalMax); const signaalPct = Math.round(100 * signaal / schaalMax); const doelPct = Math.round(100 * fatsoenlijk / schaalMax); return (
{CONTOUR_SUBTYPE_LABEL[subtype] || subtype}
{/* signaal-referentielijn */}
{/* streef-referentielijn */}
{n}
); } function SchermTraining() { const [stats, setStats] = useState(null); const [laden, setLaden] = useState(true); const [fout, setFout] = useState(false); const [dsStatus, setDsStatus] = useState(null); const [dsBezig, setDsBezig] = useState(false); async function laad() { setLaden(true); setFout(false); try { const r = await fetch(`${API}/training/stats`); if (!r.ok) throw new Error('status ' + r.status); setStats(await r.json()); } catch (e) { setFout(true); } setLaden(false); } useEffect(() => { laad(); }, []); // Crop-dataset bouwen + voortgang pollen. function pollDataset() { fetch(`${API}/training/dataset/status`) .then(r => r.json()) .then(s => { setDsStatus(s); if (s.status === 'bezig') { setDsBezig(true); setTimeout(pollDataset, 2000); } else setDsBezig(false); }) .catch(() => setDsBezig(false)); } useEffect(() => { pollDataset(); }, []); async function bouwDataset() { setDsBezig(true); try { await fetch(`${API}/training/dataset/bouw`, { method: 'POST' }); } catch (e) {} pollDataset(); } return (

Model Training

Hoever staat de contour-trainingsdata? Gebaseerd op mens-gereviewde clusters, over alle gemeenten.

{dsBezig ? `Klaarzetten… ${dsStatus ? dsStatus.verwerkt : 0}/${dsStatus ? dsStatus.totaal : 0}` : 'Trainingsset klaarzetten'} Verversen
{laden ? ( ) : fout ? ( ) : !stats ? null : ( <>
0 ? 'warning' : 'neutral'} />
{dsStatus && dsStatus.status !== 'leeg' && (() => { const drem = stats ? stats.drempels : { signaal: 50, fatsoenlijk: 150 }; const entries = Object.entries(dsStatus.per_klasse || {}); const maxN = Math.max(0, ...entries.map(([, n]) => n)); const schaalMax = Math.max(drem.fatsoenlijk, maxN); const vlagVan = n => n >= drem.fatsoenlijk ? 'ok' : n >= drem.signaal ? 'krap' : 'tekort'; const over = dsStatus.overgeslagen || {}; const overTotaal = Object.values(over).reduce((a, b) => a + b, 0); return (

Crop-trainingsset

{dsStatus.status === 'klaar' ? `${dsStatus.verwerkt} crops · ${dsStatus.klaar_op}` : dsStatus.status === 'fout' ? 'Fout' : `Bezig… ${dsStatus.verwerkt}/${dsStatus.totaal}`}
{dsStatus.status === 'fout' && (
Fout: {dsStatus.fout}
)} {[...entries].sort((a, b) => b[1] - a[1]).map(([k, n]) => ( ))} {dsStatus.status === 'klaar' && (
Map: {dsStatus.out_dir} {overTotaal > 0 && Overgeslagen: {overTotaal}}
)}
); })()}

Gereviewde voorbeelden per subtype

streefwaarde {stats.drempels.fatsoenlijk} · signaal {stats.drempels.signaal}
{(() => { const maxN = Math.max(0, ...stats.per_subtype.map(s => s.n)); const schaalMax = Math.max(stats.drempels.fatsoenlijk, maxN); return [...stats.per_subtype].sort((a, b) => b.n - a.n).map(s => ( )); })()}
Op orde (≥{stats.drempels.fatsoenlijk}) Krap (≥{stats.drempels.signaal}) Tekort Lijnen = signaal ({stats.drempels.signaal}) & streef ({stats.drempels.fatsoenlijk})

Detectie-balans

Binair doel: échte contourafwijking (bouwwerk) vs. geen afwijking. Gebalanceerd (~50/50) is ideaal.

{(() => { const a = stats.balans.afwijking, g = stats.balans.geen, tot = a + g || 1; return ( <>
Afwijking {fmtNum(a)} {fmtNum(g)} Geen
); })()}

Status-verdeling

{Object.entries(stats.status_verdeling) .sort((a, b) => b[1] - a[1]) .map(([status, n]) => ( ))}
{STATUS_LABEL[status] || status} {fmtNum(n)}

Exporteren naar een fine-tune dataset: python tools/export_training.py --out bag_contour.jsonl

)}
); } window.SchermTraining = SchermTraining;