Konuşmacı ayrımı
Çok konuşmacılı bir kayıtta kimin ne zaman konuştuğunu belirleyin. Üç motor mevcuttur: hafif ve varsayılan Pyannote, tam işlem hattı Community-1 (CoreML + yerel PLDA/VBx) ve streaming için Sortformer.
Motorlar
--engine pyannote (varsayılan), --engine community1 veya --engine sortformer seçin.
Pyannote (varsayılan)
İki aşamalı pipeline: Pyannote segmentation, çakışan pencereleri etkinliğe dayalı konuşmacı zincirlemesi (çakışma bölgelerinde Pearson korelasyonu) ile işleyerek genel konuşmacı etiketlerini atar. Hesap sonrası WeSpeaker embedding çıkarımı, kayıt sesi aracılığıyla hedef konuşmacı tanımayı mümkün kılar.
Community-1 (CoreML + yerel VBx)
Community-1 CoreML paketi 10 saniye bağlam, 1 saniye adım, maskelenmiş WeSpeaker embedding'leri, yerel PLDA/VBx ve kısıtlı atama kullanır; konuşmacı başına 256 boyutlu centroid döndürür. Beş dosyalı eşdeğerlik kontrolü 4,66% DER / 21,43% JER verdi.
Aynı alt kümede speech-swift'in frame-grid puanlayıcısıyla Community-1 9,31% DER / 16,20% JER, 25,5× gerçek zaman ve 1,54 GB tepe RSS; varsayılan Pyannote motoru ise 5,03% / 9,28%, 105,7× gerçek zaman ve 393 MB ölçtü. Bu nedenle Community-1, konuşmacı sayısı sınırları ve centroid'ler sunan isteğe bağlı bir upstream eşdeğerlik işlem hattıdır; her durumda doğruluk veya hız yükseltmesi değildir. İki puanlama yönteminin sonuçları doğrudan karşılaştırılamaz.
Sortformer (CoreML)
NVIDIA'nın uçtan uca nöral konuşmacı ayrımı modeli. Ayrı embedding veya clustering aşaması olmadan, 4 konuşmacıya kadar frame başına konuşmacı etkinliğini doğrudan tahmin eder. Streaming durum buffer'larıyla (FIFO + konuşmacı cache) CoreML üzerinden Neural Engine'de çalışır.
Canlı ses için SortformerStreamingSession, .streaming Sortformer modelini her boyutta 16 kHz mono PCM arabelleğiyle artımlı olarak çalıştırır. 560 ms ileri bağlam hazır olduğunda 480 ms ses adımlarıyla ilerler; M serisi Neural Engine üzerinde beş dosyalık denemede her 500 ms push için medyan gecikme 12,4 ms ölçüldü. Konuşmacı etiketleri her pencereyi yeniden kümelemek yerine Arrival-Order Speaker Cache'i izler ve her push tüm akışın anlık görüntüsünü döndürür. finish() arabellekte kalan kuyruğu işler. Model kullanan eşdeğerlik testinde son segmentler, tam arabellekli .streaming diarization ile yaklaşık bir 80 ms kare içinde eşleşti.
Sortformer konuşmacı embedding üretmez. --target-speaker ve --embedding-engine bayrakları yalnızca Pyannote motoruyla kullanılabilir.
Yayımlanmış CALLHOME baseline
Tablo, NVIDIA'nın diar_streaming_sortformer_4spk-v2 için yayımladığı anonim konuşmacı ham diarization baseline'ını gösterir. Bu harici bir referanstır; speech-swift CoreML dönüşümünün yerel ölçümü veya isimli konuşmacı atıf sonucu değildir.
| Veri kümesi | Koşul | DER |
|---|---|---|
| CALLHOME Part 2 | 2 referans konuşmacı | 6,57% |
| CALLHOME Part 2 | 3 referans konuşmacı | 10,05% |
| CALLHOME Part 2 | 4 referans konuşmacı | 12,44% |
Tüm satırlar 1,04 sn giriş arabelleği gecikmesi, örtüşen konuşma dahil değerlendirme, 0,25 sn collar ve ayrık CALLHOME Part 1 üzerinde ayarlanmış son işlem kullanır. CALLHOME lisanslıdır ve pakete dahil değildir. Yerel diarization-bench, referans konuşmacı sayısına göre karşılaştırılabilir sonuçları konsolda ve JSON içindeki byReferenceSpeakerCount alanında ayrı ayrı raporlar.
Pyannote pipeline
Varsayılan pipeline iki aşamada çalışır:
Aşama 1: Segmentation + konuşmacı zincirleme
Pyannote segmentation-3.0, %50 çakışmalı 10 saniyelik kayar pencereleri işler. Bir powerset decoder, 7 sınıflı çıktıyı konuşmacı başına olasılıklara dönüştürür (pencere başına en fazla 3 yerel konuşmacı). Komşu pencereler 5 saniyelik bir çakışma paylaşır — konuşmacı kimliği, çakışma bölgesindeki olasılık yörüngeleri arasında Pearson korelasyonu hesaplanarak pencereler arası yayılır; tutarlı genel konuşmacı kimlikleri için açgözlü özel eşleme kullanılır.
Aşama 2: Hesap sonrası embedding
Ayrım sonrasında WeSpeaker ResNet34-LM, konuşmacı başına 256 boyutlu bir centroid embedding çıkarır. Bu embedding'ler hedef konuşmacı çıkarımını (--target-speaker) mümkün kılar ancak konuşmacı atamasının kendisini belirlemez.
pyannote.audio'dan geçiş
Python pyannote.audio kütüphanesinden geliyorsanız — pipeline.segmentation = ... tanımlayan bir Pipeline alt sınıfını değiştiriyorsanız ya da pyannote/speaker-diarization-3.1 barındıran bir sunucudan ayrılıyorsanız — Soniqo aynı Pyannote-Segmentation-3.0 modelini sarmalar ve Apple Silicon üzerinde tamamen cihazda çalıştırır. Python runtime yok, CUDA yok, çıkarım anında Hugging Face token yok.
| 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 = ... (özel alt sınıf) |
Sabit: Pyannote-Segmentation-3.0 (MLX veya CoreML, otomatik seçilir) |
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 ağırlıkları, üst akıştaki HuggingFace checkpoint'inden dönüştürülmüştür; dolayısıyla segmentation logits, float kesinliği toleransı içinde sayısal olarak eşdeğerdir. Segmentation sonrası zincirleme (çakışan pencerelerde Pearson korelasyonu + açgözlü özel eşleme) ve hesap sonrası WeSpeaker embedding aşamaları Swift'te yeniden uygulanmıştır ancak referans Python pipeline'ı ile karşılaştırılabilir RTTM çıktısı üretir.
Pyannote motoru için streaming OnlineSpeakerDiarization karşılığı yoktur. Gerçek zamanlı konuşmacı ayrımı için onun yerine --engine sortformer kullanın; bu motor Sortformer modelini FIFO ve konuşmacı cache durum buffer'larıyla çalıştırır.
CLI kullanımı
# 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
Hedef konuşmacı çıkarımı
Bilinen bir konuşmacının kayıt sesini sağlayın; pipeline kayıttan yalnızca o kişiye ait segmentleri çıkarır. Pipeline, kayıt sesinin konuşmacı embedding'ini hesaplar ve en yüksek kosinüs benzerliğine sahip cluster'ı bulur.
# Extract segments for a specific speaker
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav
DER puanlaması
Konuşmacı ayrımının kalitesini, referans bir RTTM dosyasıyla karşılaştırarak değerlendirin. Pipeline, yanlış atfedilen zamanın oranını ölçen Diarization Error Rate (DER) değerini hesaplar.
# Score against reference RTTM
.build/release/speech diarize meeting.wav --score-against reference.rttm
RTTM çıktısı
--rttm bayrağı, konuşmacı ayrımı değerlendirmesinde kullanılan standart bir format olan Rich Transcription Time Marked çıktısı üretir. Her satır şu biçimi izler:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
Seçenekler
| Seçenek | Açıklama |
|---|---|
--target-speaker | Hedef konuşmacı çıkarımı için kayıt sesi (yalnızca pyannote) |
--embedding-engine | Konuşmacı embedding motoru: mlx veya coreml (yalnızca pyannote) |
--vad-filter | Silero VAD ile ön filtreleme (yalnızca pyannote) |
--rttm | RTTM formatında çıktı |
--json | JSON çıktı formatı |
--score-against | DER değerlendirmesi için referans RTTM dosyası |
Konuşmacı ayrımı, net konuşmacı geçişleri olan kayıtlarda en iyi sonucu verir. Çok yoğun şekilde çakışan konuşma doğruluğu azaltabilir. Konuşmacı sayısı otomatik belirlenir.
Model indirmeleri
Modeller ilk kullanımda otomatik olarak indirilir:
| Bileşen | Model | Boyut | HuggingFace |
|---|---|---|---|
| Segmentation | Pyannote-Segmentation-3.0 | ~5.7 MB | aufklarer/Pyannote-Segmentation-MLX |
| Konuşmacı embedding | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| Konuşmacı 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
)