تحديد المتحدّثين
تعرّف على من تحدّث ومتى في تسجيل يحوي عدّة متحدّثين. يتوفّر محرّكان لتحديد المتحدّثين: خطّ أنابيب Pyannote من مرحلتين (تجزئة + ربط للمتحدّثين بناءً على النشاط، ثم استخراج embedding لاحقًا)، ونموذج Sortformer من طرف إلى طرف (CoreML، Neural Engine).
المحرّكات
اختر المحرّك باستخدام --engine pyannote (افتراضي) أو --engine sortformer.
Pyannote (افتراضي)
خطّ أنابيب من مرحلتين: تعالج تجزئة Pyannote نوافذ متداخلة مع ربط المتحدّثين القائم على النشاط (ارتباط Pearson في مناطق التداخل) لإسناد تسميات متحدّثين عامّة. يتيح استخراج embeddings WeSpeaker اللاحق تحديد متحدّث مستهدف عبر صوت تسجيل مرجعي.
Sortformer (CoreML)
نموذج NVIDIA العصبي لتحديد المتحدّثين من طرف إلى طرف. يتنبّأ مباشرةً بنشاط كلّ متحدّث لكلّ إطار لما يصل إلى 4 متحدّثين دون مراحل منفصلة للـ embedding أو التجميع. يعمل على Neural Engine عبر CoreML مع مخازن حالة بثّ (FIFO + ذاكرة مؤقّتة للمتحدّثين).
لا يُنتج Sortformer embeddings للمتحدّث. الخياران --target-speaker و--embedding-engine متاحان فقط مع محرّك Pyannote.
خطّ أنابيب Pyannote
يعمل خطّ الأنابيب الافتراضي على مرحلتين:
المرحلة 1: التجزئة + ربط المتحدّثين
يعالج Pyannote segmentation-3.0 نوافذ منزلقة من 10 ثوانٍ بتداخل 50%. يحوّل مُفكِّك powerset الخرج المؤلَّف من 7 فئات إلى احتمالات لكلّ متحدّث (حتى 3 متحدّثين محليين لكلّ نافذة). تتشارك النوافذ المتجاورة تداخلاً مدّته 5 ثوانٍ — وتُنشر هويّة المتحدّث عبر النوافذ بحساب ارتباط Pearson بين مسارات الاحتمال في منطقة التداخل، مع مطابقة حصرية جشعة للحصول على معرّفات متحدّثين عامّة متّسقة.
المرحلة 2: embedding لاحق
بعد تحديد المتحدّثين، يستخرج WeSpeaker ResNet34-LM embedding مركزياً بأبعاد 256 لكلّ متحدّث. تتيح هذه الـ embeddings استخراج متحدّث مستهدف (--target-speaker) لكنّها لا تُحدّد إسناد المتحدّثين ذاته.
الانتقال من pyannote.audio
إن كنت قادمًا من مكتبة بايثون pyannote.audio — تستبدل صنفًا فرعيًا من Pipeline يضبط pipeline.segmentation = ...، أو تنتقل من خادم يستضيف pyannote/speaker-diarization-3.1 — فإنّ Soniqo يغلّف نفس نموذج Pyannote-Segmentation-3.0 ويُشغّله بالكامل على الجهاز على Apple Silicon. بدون بيئة تشغيل بايثون، بدون CUDA، وبدون رمز Hugging Face عند الاستدلال.
| 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 = ... (صنف فرعي مخصّص) |
ثابت: Pyannote-Segmentation-3.0 (MLX أو CoreML، يُختار تلقائيًا) |
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 محوَّلة من نقطة التحقّق الأصلية على HuggingFace، لذلك فإنّ logits التجزئة متكافئة عدديًا ضمن حدود دقّة الفاصلة العائمة. وقد أُعيد تنفيذ الربط اللاحق للتجزئة (ارتباط Pearson عبر النوافذ المتداخلة + مطابقة حصرية جشعة) ومراحل embedding WeSpeaker اللاحقة في Swift، لكنّها تُنتج خرج RTTM مقارنًا لخطّ أنابيب بايثون المرجعي.
لا يوجد مكافئ بثّ لـ OnlineSpeakerDiarization لمحرّك Pyannote. لتحديد المتحدّثين في الوقت الحقيقي استخدم --engine sortformer بدلاً من ذلك، فهو يُشغّل نموذج Sortformer مع مخازن حالة FIFO وذاكرة مؤقّتة للمتحدّثين.
استخدام 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
استخراج المتحدّث المستهدف
زوّد بصوت تسجيل مرجعي لمتحدّث معروف لاستخراج مقاطعه فقط من تسجيل ما. يحسب خطّ الأنابيب embedding المتحدّث للصوت المرجعي ويعثر على العنقود الأعلى تشابهًا جيب التمام.
# Extract segments for a specific speaker
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav
تقييم DER
قِس جودة تحديد المتحدّثين بمقارنتها بملفّ RTTM مرجعي. يحسب خطّ الأنابيب معدّل خطأ تحديد المتحدّثين (DER)، الذي يقيس نسبة الوقت المُسنَد بشكل خاطئ.
# Score against reference RTTM
.build/release/speech diarize meeting.wav --score-against reference.rttm
خرج RTTM
يُنتج الخيار --rttm خرجًا بصيغة Rich Transcription Time Marked، وهي صيغة قياسية تُستخدم لتقييم تحديد المتحدّثين. يتبع كلّ سطر الصيغة التالية:
SPEAKER filename 1 start_time duration <NA> <NA> speaker_id <NA> <NA>
الخيارات
| الخيار | الوصف |
|---|---|
--target-speaker | صوت تسجيل مرجعي لاستخراج متحدّث مستهدف (pyannote فقط) |
--embedding-engine | محرّك embedding المتحدّث: mlx أو coreml (pyannote فقط) |
--vad-filter | تصفية مُسبقة بـ Silero VAD (pyannote فقط) |
--rttm | خرج بصيغة RTTM |
--json | صيغة خرج JSON |
--score-against | ملفّ RTTM مرجعي لتقييم DER |
يعمل تحديد المتحدّثين بأفضل صورة مع التسجيلات التي تحوي تناوبًا واضحًا بين المتحدّثين. قد يُقلّل التداخل الكبير بين الأصوات الدقّة. يُحدَّد عدد المتحدّثين تلقائيًا.
تنزيلات النماذج
تُنزَّل النماذج تلقائيًا عند أوّل استخدام:
| المكوّن | النموذج | الحجم | HuggingFace |
|---|---|---|---|
| التجزئة | Pyannote-Segmentation-3.0 | ~5.7 MB | aufklarer/Pyannote-Segmentation-MLX |
| embedding المتحدّث | WeSpeaker-ResNet34-LM (MLX) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| embedding المتحدّث | WeSpeaker-ResNet34-LM (CoreML) | ~25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
| Sortformer | Sortformer Diarization (CoreML) | ~240 MB | aufklarer/Sortformer-Diarization-CoreML |
واجهة Swift
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
)