स्पीकर डायराइज़ेशन
एक multi-speaker रिकॉर्डिंग में पहचानें कि कौन कब बोला। तीन इंजन उपलब्ध हैं: हल्का डिफ़ॉल्ट Pyannote, पूर्ण-pipeline Community-1 (CoreML + native PLDA/VBx), और streaming Sortformer।
इंजन
--engine pyannote (डिफ़ॉल्ट), --engine community1, या --engine sortformer चुनें।
Pyannote (डिफ़ॉल्ट)
Two-stage पाइपलाइन: Pyannote segmentation overlapping windows को activity-based speaker chaining (overlap zones में Pearson correlation) के साथ प्रोसेस करता है ताकि global speaker labels असाइन किए जा सकें। Post-hoc WeSpeaker embedding extraction enrollment audio के माध्यम से target speaker पहचान सक्षम करता है।
Community-1 (CoreML + native VBx)
Community-1 CoreML bundle 10 सेकंड context, 1 सेकंड step, masked WeSpeaker embeddings, native PLDA/VBx और constrained assignment का उपयोग करता है; यह हर speaker के लिए 256-dimensional centroid लौटाता है। पाँच-file parity check में 4.66% DER / 21.43% JER मिला।
उसी subset पर speech-swift के frame-grid scorer से Community-1 ने 9.31% DER / 16.20% JER, 25.5× real-time और 1.54 GB peak RSS मापा; डिफ़ॉल्ट Pyannote ने 5.03% / 9.28%, 105.7× real-time और 393 MB मापा। इसलिए Community-1 speaker-count bounds और centroids देने वाला वैकल्पिक upstream-parity pipeline है, हर स्थिति में accuracy या speed upgrade नहीं। दोनों scoring methods के परिणाम सीधे तुलना योग्य नहीं हैं।
Sortformer (CoreML)
NVIDIA का end-to-end neural diarization मॉडल। अलग embedding या clustering चरणों के बिना 4 speakers तक के लिए प्रति-फ़्रेम speaker activity का सीधे अनुमान लगाता है। CoreML के माध्यम से Neural Engine पर streaming state buffers (FIFO + speaker cache) के साथ चलता है।
लाइव ऑडियो के लिए SortformerStreamingSession, .streaming Sortformer मॉडल को किसी भी आकार के 16 kHz मोनो PCM बफ़र के साथ क्रमिक रूप से चलाता है। 560 ms का lookahead उपलब्ध होने पर यह 480 ms ऑडियो स्टेप में आगे बढ़ता है; M-सीरीज़ Neural Engine पर पाँच फ़ाइलों के परीक्षण में हर 500 ms push की माध्यिका latency 12.4 ms मापी गई। स्पीकर लेबल हर विंडो को फिर से क्लस्टर करने के बजाय Arrival-Order Speaker Cache का अनुसरण करते हैं, और हर push पूरे स्ट्रीम का स्नैपशॉट लौटाता है। finish() बफ़र में बचा अंतिम हिस्सा प्रोसेस करता है। मॉडल-आधारित समानता परीक्षण में अंतिम सेगमेंट फुल-बफ़र .streaming diarization से लगभग एक 80 ms फ्रेम के भीतर मेल खाते थे।
Sortformer स्पीकर embeddings उत्पन्न नहीं करता है। --target-speaker और --embedding-engine flags केवल Pyannote इंजन के साथ उपलब्ध हैं।
प्रकाशित CALLHOME बेसलाइन
यह तालिका diar_streaming_sortformer_4spk-v2 के लिए NVIDIA की प्रकाशित raw anonymous-speaker diarization बेसलाइन दिखाती है। यह एक बाहरी संदर्भ है, speech-swift के CoreML conversion का local measurement या named-speaker attribution परिणाम नहीं।
| Dataset | स्थिति | DER |
|---|---|---|
| CALLHOME Part 2 | 2 reference speakers | 6.57% |
| CALLHOME Part 2 | 3 reference speakers | 10.05% |
| CALLHOME Part 2 | 4 reference speakers | 12.44% |
सभी पंक्तियों में 1.04 सेकंड input-buffer latency, overlap शामिल, 0.25 सेकंड collar और अलग CALLHOME Part 1 पर tune किया गया post-processing है। CALLHOME licensed है और bundle में शामिल नहीं है। Local diarization-bench console और JSON के byReferenceSpeakerCount में reference speaker count के अनुसार तुलनीय परिणाम अलग-अलग दिखाता है।
Pyannote पाइपलाइन
डिफ़ॉल्ट पाइपलाइन दो चरणों में चलती है:
चरण 1: Segmentation + Speaker Chaining
Pyannote segmentation-3.0 50% overlap के साथ 10-सेकंड sliding windows प्रोसेस करता है। एक powerset decoder 7-class आउटपुट को प्रति-speaker probabilities (प्रति window 3 local speakers तक) में कन्वर्ट करता है। आसन्न windows 5-सेकंड overlap साझा करते हैं — speaker identity को overlap zone में probability tracks के बीच Pearson correlation की गणना करके windows में प्रसारित किया जाता है, जिसमें सुसंगत global speaker IDs के लिए greedy exclusive matching होती है।
चरण 2: Post-hoc Embedding
डायराइज़ेशन के बाद, WeSpeaker ResNet34-LM प्रति speaker एक 256-dimensional centroid embedding निकालता है। ये embeddings target speaker extraction (--target-speaker) सक्षम करते हैं, लेकिन स्वयं speaker assignment को drive नहीं करते।
pyannote.audio से Migrate करना
यदि आप Python pyannote.audio library से आ रहे हैं — एक Pipeline subclass को replace कर रहे हैं जो pipeline.segmentation = ... set करती है, या pyannote/speaker-diarization-3.1 host करने वाले server से migrate कर रहे हैं — Soniqo उसी Pyannote-Segmentation-3.0 model को wrap करता है और इसे Apple Silicon पर पूरी तरह on-device चलाता है। कोई Python runtime, CUDA, या inference time पर Hugging Face token नहीं चाहिए।
| 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 = ... (custom subclass) |
Fixed: Pyannote-Segmentation-3.0 (MLX या CoreML, auto-selected) |
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 |
Pyannote-Segmentation-3.0 weights upstream HuggingFace checkpoint से convert किए गए हैं, इसलिए segmentation logits float-precision tolerance के भीतर numerically equivalent हैं। Post-segmentation chaining (overlapping windows पर Pearson correlation + greedy exclusive matching) और post-hoc WeSpeaker embedding stages Swift में reimplement किए गए हैं, लेकिन reference Python pipeline से comparable RTTM output produce करते हैं।
Pyannote engine के लिए OnlineSpeakerDiarization के बराबर कोई streaming API नहीं है। Real-time diarization के लिए --engine sortformer का use करें, जो Sortformer model को FIFO + speaker-cache state buffers के साथ चलाता है।
CLI उपयोग
# 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
Target Speaker Extraction
एक ज्ञात speaker का enrollment audio प्रदान करें ताकि उसके केवल segments को रिकॉर्डिंग से निकाला जा सके। पाइपलाइन enrollment audio का speaker embedding कंप्यूट करती है और उच्चतम cosine similarity वाले cluster को खोजती है।
# Extract segments for a specific speaker
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav
DER Scoring
एक reference RTTM फ़ाइल के विरुद्ध scoring करके डायराइज़ेशन गुणवत्ता का मूल्यांकन करें। पाइपलाइन Diarization Error Rate (DER) कंप्यूट करती है, जो समय के अनुपात को मापती है जो गलत तरीके से attributed है।
# Score against reference RTTM
.build/release/speech diarize meeting.wav --score-against reference.rttm
RTTM आउटपुट
--rttm flag Rich Transcription Time Marked आउटपुट उत्पन्न करता है, जो डायराइज़ेशन मूल्यांकन के लिए उपयोग किया जाने वाला एक मानक फ़ॉर्मैट है। प्रत्येक line इस फ़ॉर्मैट का अनुसरण करती है:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
विकल्प
| विकल्प | विवरण |
|---|---|
--target-speaker | Target speaker extraction के लिए enrollment ऑडियो (केवल pyannote) |
--embedding-engine | Speaker embedding इंजन: mlx या coreml (केवल pyannote) |
--vad-filter | Silero VAD के साथ pre-filter (केवल pyannote) |
--rttm | RTTM फ़ॉर्मैट में आउटपुट |
--json | JSON आउटपुट फ़ॉर्मैट |
--score-against | DER मूल्यांकन के लिए reference RTTM फ़ाइल |
डायराइज़ेशन उन रिकॉर्डिंग के साथ सबसे अच्छा काम करता है जिनमें स्पष्ट speaker turns होते हैं। अत्यधिक overlapping speech सटीकता को कम कर सकती है। Speaker count स्वचालित रूप से निर्धारित होता है।
मॉडल डाउनलोड
मॉडल पहले उपयोग पर स्वचालित रूप से डाउनलोड होते हैं:
| कॉम्पोनेंट | मॉडल | आकार | HuggingFace |
|---|---|---|---|
| Segmentation | Pyannote-Segmentation-3.0 | ~5.7 MB | aufklarer/Pyannote-Segmentation-MLX |
| Speaker Embedding | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| Speaker 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
)