Konuşmacı ayrımı
Çok konuşmacılı bir kayıtta kimin ne zaman konuştuğunu belirleyin. İki konuşmacı ayrımı motoru mevcuttur: iki aşamalı bir Pyannote pipeline'ı (segmentation + etkinliğe dayalı konuşmacı zincirleme, ardından hesap sonrası embedding) ve uçtan uca bir Sortformer modeli (CoreML, Neural Engine).
Motorlar
Motoru --engine pyannote (varsayılan) ya da --engine sortformer ile 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.
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.
Sortformer konuşmacı embedding üretmez. --target-speaker ve --embedding-engine bayrakları yalnızca Pyannote motoruyla kullanılabilir.
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
)