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.

Not

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.

Henüz desteklenmiyor

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çenekAçıklama
--target-speakerHedef konuşmacı çıkarımı için kayıt sesi (yalnızca pyannote)
--embedding-engineKonuşmacı embedding motoru: mlx veya coreml (yalnızca pyannote)
--vad-filterSilero VAD ile ön filtreleme (yalnızca pyannote)
--rttmRTTM formatında çıktı
--jsonJSON çıktı formatı
--score-againstDER değerlendirmesi için referans RTTM dosyası
Önemli

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şenModelBoyutHuggingFace
SegmentationPyannote-Segmentation-3.0~5.7 MBaufklarer/Pyannote-Segmentation-MLX
Konuşmacı embeddingWeSpeaker-ResNet34-LM (MLX)~25 MBaufklarer/WeSpeaker-ResNet34-LM-MLX
Konuşmacı embeddingWeSpeaker-ResNet34-LM (CoreML)~25 MBaufklarer/WeSpeaker-ResNet34-LM-CoreML
SortformerSortformer Diarization (CoreML)~240 MBaufklarer/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
)