#74 (22 Teilnehmer) ergab automatisch 44 Sprecher, dieselbe Stimme mehrfach. Ursache gemessen: ein Zentroid pro sherpa-Cluster aus max. 3 Schnitten, fester Kosinus auf rohen Embeddings (Kreuz-Turn-Kosinus ~0,4 = keine Trennschaerfe). v2: sherpa liefert nur Turn-Grenzen, Identitaet aus 3-s-Fenster-Embeddings, Mittelwert-Zentrierung, Average-Linkage-AHC, Weak-Fold, Schwelle pro Aufnahme per 2-Gauss-EM gelernt. Validiert: #74 44->20, #70 ->4, #68 ->2. Live-Namensmarker: Namen waehrend der Aufnahme tippen; Zeitstempel benennt spaeter den Cluster und dient als must-/cannot-link-Zwangsbedingung.
24 lines
1.4 KiB
JavaScript
24 lines
1.4 KiB
JavaScript
// Replay the REAL production clustering (diar.js clusterWindows) on cached
|
|
// window embeddings, so thresholds can be tuned without re-running sherpa or the
|
|
// embedding pass. Usage:
|
|
// [DIAR_COS_FINE=..] [DIAR_COS_FINAL=..] node lab-eval.mjs <emb.json> [expect] [numSpeakers] [markers.json]
|
|
import fs from 'node:fs';
|
|
import { clusterWindows } from './diar-v2.js';
|
|
|
|
const [file, expect, numSp, markerFile] = process.argv.slice(2);
|
|
const d = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
const ok = d.wins.filter(w => w.v).map(w => ({ ti: w.ti, start: w.s, end: w.e, vec: w.v }));
|
|
const markers = markerFile ? JSON.parse(fs.readFileSync(markerFile, 'utf8')) : [];
|
|
const segs = clusterWindows(d.turns, ok, +(numSp || 0), markers);
|
|
|
|
const talk = new Map(), first = new Map(), last = new Map();
|
|
for (const s of segs) {
|
|
talk.set(s.speaker, (talk.get(s.speaker) || 0) + (s.end - s.start));
|
|
if (!first.has(s.speaker)) first.set(s.speaker, s.start);
|
|
last.set(s.speaker, s.end);
|
|
}
|
|
const order = [...talk.entries()].sort((a, b) => b[1] - a[1]);
|
|
console.log(`RESULT ${d.model.split('/').pop()} fine=${process.env.DIAR_COS_FINE || 0.35} final=${process.env.DIAR_COS_FINAL || 0.30}` +
|
|
` → speakers=${order.length}${expect ? ` (expect ${expect})` : ''} segs=${segs.length}`);
|
|
console.log(' ' + order.map(([sp, t]) => `${sp.replace('speaker_', '')}:${t.toFixed(0)}s[${(first.get(sp) / 60).toFixed(1)}-${(last.get(sp) / 60).toFixed(1)}]`).join(' '));
|