Sprecherdiarisierung — Diarisierung in Swift
Identifiziere, wer wann in einer Mehrsprecheraufnahme gesprochen hat. Drei Engines sind verfügbar: das leichte Standardmodell Pyannote, die vollständige Community-1-Pipeline (CoreML + natives PLDA/VBx) und das Streaming-Modell Sortformer.
Engines
Wähle --engine pyannote (Standard), --engine community1 oder --engine sortformer.
Pyannote (Standard)
Zweistufige Pipeline: Pyannote-Segmentierung verarbeitet überlappende Fenster mit aktivitätsbasierter Sprecherverkettung (Pearson-Korrelation in Überlappungsbereichen), um globale Sprecherlabels zu vergeben. Die Post-hoc-WeSpeaker-Embedding-Extraktion ermöglicht die Identifikation von Zielsprechern per Enrollment-Audio.
Community-1 (CoreML + natives VBx)
Das Community-1-CoreML-Bundle nutzt 10 s Kontext, 1 s Schrittweite, maskierte WeSpeaker-Embeddings, natives PLDA/VBx und beschränkte Zuordnung; es liefert einen 256-dimensionalen Zentroid pro Sprecher. Der Paritätstest mit fünf Dateien ergab 4,66% DER / 21,43% JER.
Mit dem Frame-Grid-Scorer von speech-swift auf demselben Subset erreichte Community-1 9,31% DER / 16,20% JER, 25,5× Echtzeit und 1,54 GB Spitzen-RSS; die standardmäßige Pyannote-Engine erreichte 5,03% / 9,28%, 105,7× Echtzeit und 393 MB. Community-1 ist damit eine optionale Upstream-Paritäts-Pipeline mit Sprecherzahlgrenzen und zurückgegebenen Zentroiden, keine universelle Verbesserung von Genauigkeit oder Geschwindigkeit. Die Ergebnisse der beiden Bewertungsmethoden sind nicht direkt vergleichbar.
Sortformer (CoreML)
NVIDIAs durchgängiges neuronales Diarisierungsmodell. Sagt direkt Per-Frame-Sprecheraktivität für bis zu 4 Sprecher voraus, ohne separate Embedding- oder Clustering-Stufen. Läuft auf der Neural Engine über CoreML mit Streaming-Zustandspuffern (FIFO + Sprecher-Cache).
Für Live-Audio führt SortformerStreamingSession das .streaming-Sortformer-Modell inkrementell mit beliebig großen 16 kHz-Mono-PCM-Puffern aus. Sobald 560 ms Lookahead verfügbar sind, arbeitet es in 480 ms Audioschritten; ein Test mit fünf Dateien auf einer M-Serie Neural Engine ergab eine mediane Latenz von 12,4 ms pro 500 ms Push. Sprecherlabels folgen dem Arrival-Order Speaker Cache, statt jedes Fenster neu zu clustern, und jeder Push liefert einen Schnappschuss des gesamten Streams. finish() verarbeitet den gepufferten Rest. Im modellgestützten Äquivalenztest stimmten die finalen Segmente bis auf etwa einen 80 ms-Frame mit der Ganzpuffer-.streaming-Diarisierung überein.
Sortformer erzeugt keine Sprechereinbettungen. Die Flags --target-speaker und --embedding-engine sind nur mit der Pyannote-Engine verfügbar.
Pyannote-Pipeline
Die Standard-Pipeline läuft in zwei Stufen:
Stufe 1: Segmentierung + Sprecherverkettung
Pyannote-segmentation-3.0 verarbeitet 10-Sekunden-Gleitfenster mit 50 % Überlappung. Ein Powerset-Decoder wandelt die 7-Klassen-Ausgabe in Per-Sprecher-Wahrscheinlichkeiten um (bis zu 3 lokale Sprecher pro Fenster). Benachbarte Fenster teilen sich eine 5-Sekunden-Überlappung — die Sprecheridentität wird über Fenster hinweg propagiert, indem die Pearson-Korrelation zwischen Wahrscheinlichkeitsspuren im Überlappungsbereich berechnet wird, mit gieriger exklusiver Zuordnung für konsistente globale Sprecher-IDs.
Stufe 2: Post-hoc-Embedding
Nach der Diarisierung extrahiert WeSpeaker ResNet34-LM ein 256-dimensionales Zentroid-Embedding pro Sprecher. Diese Embeddings ermöglichen die Zielsprecher-Extraktion (--target-speaker), steuern aber nicht die Sprecherzuordnung selbst.
Migration von pyannote.audio
Wenn du von der Python-Bibliothek pyannote.audio kommst — eine Pipeline-Subklasse mit pipeline.segmentation = ... ersetzt oder von einem Server mit pyannote/speaker-diarization-3.1 migrierst — Soniqo umhüllt dasselbe Pyannote-Segmentation-3.0-Modell und führt es vollständig auf dem Gerät auf Apple Silicon aus. Keine Python-Runtime, kein CUDA, kein Hugging-Face-Token zur Inferenzzeit.
| pyannote.audio (Python) | Soniqo (Swift) |
|---|---|
Pipeline.from_pretrained("pyannote/speaker-diarization-3.1") |
DiarizationPipeline.fromPretrained() |
pipeline(audio_file) |
pipeline.diarize(audio: samples, sampleRate: 16000) |
pipeline.segmentation = ... (benutzerdefinierte Subklasse) |
Fest: Pyannote-Segmentation-3.0 (MLX oder CoreML, automatisch gewählt) |
diarization.itertracks(yield_label=True) |
for seg in result.segments { ... } |
diarization.write_rttm(file) |
CLI: --rttm |
pyannote.metrics.diarization.DiarizationErrorRate |
CLI: --score-against reference.rttm |
Die Pyannote-Segmentation-3.0-Gewichte sind aus dem Upstream-HuggingFace-Checkpoint konvertiert, sodass die Segmentation-Logits innerhalb der Float-Präzisionstoleranz numerisch äquivalent sind. Die Nachsegmentierungsverkettung (Pearson-Korrelation über überlappende Fenster + gieriges exklusives Matching) und die Post-hoc-WeSpeaker-Embedding-Stufen sind in Swift reimplementiert, erzeugen aber RTTM-Ausgabe vergleichbar mit der Python-Referenzpipeline.
Es gibt kein Streaming-Äquivalent zu OnlineSpeakerDiarization für die Pyannote-Engine. Für Echtzeit-Diarisierung verwende stattdessen --engine sortformer, das das Sortformer-Modell mit FIFO- und Sprecher-Cache-Zustandspuffern ausführt.
CLI-Verwendung
# Basic diarization (pyannote, default)
.build/release/speech diarize meeting.wav
# End-to-end Sortformer (CoreML)
.build/release/speech diarize meeting.wav --engine sortformer
# RTTM output format (for evaluation)
.build/release/speech diarize meeting.wav --rttm
# JSON output
.build/release/speech diarize meeting.wav --json
Zielsprecher-Extraktion
Gib Enrollment-Audio eines bekannten Sprechers an, um nur dessen Segmente aus einer Aufnahme zu extrahieren. Die Pipeline berechnet das Sprecher-Embedding des Enrollment-Audios und findet das Cluster mit der höchsten Kosinusähnlichkeit.
# Extract segments for a specific speaker
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav
DER-Scoring
Bewerte die Diarisierungsqualität durch Vergleich mit einer Referenz-RTTM-Datei. Die Pipeline berechnet die Diarization Error Rate (DER), die den Anteil der falsch zugeordneten Zeit misst.
# Score against reference RTTM
.build/release/speech diarize meeting.wav --score-against reference.rttm
RTTM-Ausgabe
Das Flag --rttm erzeugt eine Rich-Transcription-Time-Marked-Ausgabe, ein Standardformat zur Diarisierungsauswertung. Jede Zeile folgt dem Format:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
Optionen
| Option | Beschreibung |
|---|---|
--target-speaker | Enrollment-Audio für die Zielsprecher-Extraktion (nur pyannote) |
--embedding-engine | Sprecher-Embedding-Engine: mlx oder coreml (nur pyannote) |
--vad-filter | Vorfilterung mit Silero VAD (nur pyannote) |
--rttm | Ausgabe im RTTM-Format |
--json | JSON-Ausgabeformat |
--score-against | Referenz-RTTM-Datei für DER-Auswertung |
Diarisierung funktioniert am besten mit Aufnahmen, die klare Sprecherwechsel aufweisen. Stark überlappende Sprache kann die Genauigkeit verringern. Die Sprecherzahl wird automatisch bestimmt.
Modell-Downloads
Modelle werden beim ersten Gebrauch automatisch heruntergeladen:
| Komponente | Modell | Größe | HuggingFace |
|---|---|---|---|
| Segmentierung | Pyannote-Segmentation-3.0 | ~5,7 MB | aufklarer/Pyannote-Segmentation-MLX |
| Sprecher-Embedding | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| Sprecher-Embedding | WeSpeaker-ResNet34-LM (CoreML) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
| Sortformer | Sortformer Diarization (CoreML) | ~240 MB | aufklarer/Sortformer-Diarization-CoreML |
Swift-API
import SpeechVAD
let pipeline = try await DiarizationPipeline.fromPretrained()
let result = pipeline.diarize(audio: samples, sampleRate: 16000)
for seg in result.segments {
print("Speaker \(seg.speakerId): [\(seg.startTime)s - \(seg.endTime)s]")
}
// Target speaker extraction
let targetEmb = pipeline.embeddingModel.embed(audio: enrollmentAudio, sampleRate: 16000)
let segments = pipeline.extractSpeaker(
audio: meetingAudio, sampleRate: 16000,
targetEmbedding: targetEmb
)