تحديد المتحدّثين
تعرّف على من تحدّث ومتى في تسجيل يحوي عدّة متحدّثين. تتوفّر ثلاثة محرّكات: Pyannote الخفيف والافتراضي، ومسار Community-1 الكامل (CoreML + PLDA/VBx أصلي)، وSortformer للبث.
المحرّكات
اختر --engine pyannote (افتراضي)، أو --engine community1، أو --engine sortformer.
Pyannote (افتراضي)
خطّ أنابيب من مرحلتين: تعالج تجزئة Pyannote نوافذ متداخلة مع ربط المتحدّثين القائم على النشاط (ارتباط Pearson في مناطق التداخل) لإسناد تسميات متحدّثين عامّة. يتيح استخراج embeddings WeSpeaker اللاحق تحديد متحدّث مستهدف عبر صوت تسجيل مرجعي.
Community-1 (CoreML + VBx أصلي)
تستخدم حزمة Community-1 CoreML سياقًا مدته 10 ثوانٍ وخطوة ثانية واحدة وتضمينات WeSpeaker مقنّعة وPLDA/VBx أصليًا وإسنادًا مقيّدًا؛ وتعيد متجهًا مركزيًا من 256 بُعدًا لكل متحدث. بلغ اختبار التكافؤ على خمسة ملفات 4.66% DER / 21.43% JER.
باستخدام مقيّم شبكة الإطارات في speech-swift على المجموعة الفرعية نفسها، حقق Community-1 نسبة 9.31% DER / 16.20% JER وسرعة 25.5× من الزمن الحقيقي وذروة RSS تبلغ 1.54 GB؛ بينما حقق محرك Pyannote الافتراضي 5.03% / 9.28% وسرعة 105.7× وذاكرة 393 MB. لذا فإن Community-1 مسار اختياري للتكافؤ مع المنبع يوفر حدودًا لعدد المتحدثين ومتجهات مركزية، وليس ترقية شاملة للدقة أو السرعة. لا يمكن مقارنة نتائج طريقتي التقييم مباشرة.
Sortformer (CoreML)
نموذج NVIDIA العصبي لتحديد المتحدّثين من طرف إلى طرف. يتنبّأ مباشرةً بنشاط كلّ متحدّث لكلّ إطار لما يصل إلى 4 متحدّثين دون مراحل منفصلة للـ embedding أو التجميع. يعمل على Neural Engine عبر CoreML مع مخازن حالة بثّ (FIFO + ذاكرة مؤقّتة للمتحدّثين).
للصوت المباشر، يشغّل SortformerStreamingSession نموذج Sortformer ذي الإعداد .streaming تدريجيًا مع مخازن PCM أحادية القناة بتردد 16 كيلوهرتز وبأي حجم. بعد توافر 560 مللي ثانية من السياق الاستباقي، يتقدّم بخطوات صوتية قدرها 480 مللي ثانية؛ وفي تجربة من خمسة ملفات على Neural Engine من سلسلة M بلغ وسيط زمن الاستجابة 12.4 مللي ثانية لكل push من 500 مللي ثانية. تتبع تسميات المتحدثين Arrival-Order Speaker Cache بدل إعادة تجميع كل نافذة، وتعيد كل عملية push لقطة للتدفق بأكمله. تعالج finish() الذيل المتبقي في المخزن. وفي اختبار التكافؤ المعتمد على النموذج، تطابقت المقاطع النهائية مع diarization كامل المخزن بإعداد .streaming ضمن إطار واحد تقريبًا مدته 80 مللي ثانية.
لا يُنتج Sortformer embeddings للمتحدّث. الخياران --target-speaker و--embedding-engine متاحان فقط مع محرّك Pyannote.
خط أساس CALLHOME المنشور
يعرض الجدول خط الأساس الخام المنشور من NVIDIA لتحديد المتحدثين المجهولين باستخدام diar_streaming_sortformer_4spk-v2. وهو مرجع خارجي، وليس قياسًا محليًا لتحويل CoreML في speech-swift ولا نتيجة لإسناد أسماء إلى المتحدثين.
| مجموعة البيانات | الشرط | DER |
|---|---|---|
| CALLHOME Part 2 | متحدثان مرجعيان | 6.57% |
| CALLHOME Part 2 | 3 متحدثين مرجعيين | 10.05% |
| CALLHOME Part 2 | 4 متحدثين مرجعيين | 12.44% |
تستخدم كل الصفوف زمن تأخير لمخزن الإدخال قدره 1.04 ثانية، وتشمل الكلام المتداخل، وتستخدم هامشًا قدره 0.25 ثانية ومعالجة لاحقة ضُبطت على CALLHOME Part 1 المنفصل. تتطلب CALLHOME ترخيصًا ولا تُضمّن. يعرض diarization-bench المحلي النتائج القابلة للمقارنة منفصلة حسب عدد المتحدثين المرجعيين في الطرفية وفي حقل JSON المسمى byReferenceSpeakerCount.
خطّ أنابيب 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
)