การแยกผู้พูด

ระบุว่าใครพูดเมื่อใดในไฟล์บันทึกที่มีผู้พูดหลายคน มีเอนจินการแยกผู้พูดสองตัวให้เลือก: pipeline Pyannote สองขั้นตอน (segmentation + การเรียงลำดับผู้พูดตามกิจกรรม จากนั้นทำ embedding ภายหลัง) และโมเดล Sortformer แบบ end-to-end (CoreML, Neural Engine)

เอนจิน

เลือกเอนจินด้วย --engine pyannote (ค่าเริ่มต้น) หรือ --engine sortformer

Pyannote (ค่าเริ่มต้น)

Pipeline สองขั้นตอน: Pyannote segmentation ประมวลผลหน้าต่างที่ซ้อนทับด้วยการเรียงลำดับผู้พูดตามกิจกรรม (Pearson correlation ในบริเวณที่ซ้อนทับ) เพื่อกำหนดป้ายผู้พูดในระดับ global การสกัด embedding ของ WeSpeaker ภายหลังช่วยให้สามารถระบุผู้พูดเป้าหมายผ่านเสียงลงทะเบียน

Sortformer (CoreML)

โมเดลการแยกผู้พูดแบบ neural end-to-end ของ NVIDIA ทำนายกิจกรรมของผู้พูดสูงสุด 4 คนต่อ frame โดยตรง โดยไม่ต้องมีขั้นตอน embedding หรือ clustering แยก ทำงานบน Neural Engine ผ่าน CoreML ด้วยบัฟเฟอร์สถานะ streaming (FIFO + cache ผู้พูด)

หมายเหตุ

Sortformer ไม่สร้าง embedding ของผู้พูด แฟล็ก --target-speaker และ --embedding-engine มีให้ใช้เฉพาะกับเอนจิน Pyannote เท่านั้น

Pipeline ของ Pyannote

Pipeline เริ่มต้นทำงานในสองขั้นตอน:

ขั้นตอนที่ 1: Segmentation + การเรียงลำดับผู้พูด

Pyannote segmentation-3.0 ประมวลผลหน้าต่างเลื่อนยาว 10 วินาทีโดยซ้อนทับ 50% decoder powerset แปลงเอาต์พุต 7 คลาสเป็นความน่าจะเป็นต่อผู้พูด (สูงสุด 3 ผู้พูดในระดับ local ต่อหน้าต่าง) หน้าต่างที่อยู่ติดกันแบ่งบริเวณซ้อนทับ 5 วินาที — ตัวตนของผู้พูดถูกส่งต่อระหว่างหน้าต่างโดยคำนวณ Pearson correlation ระหว่างวิถีความน่าจะเป็นในบริเวณที่ซ้อนทับ พร้อมการ matching แบบ exclusive greedy เพื่อให้ได้ ID ผู้พูดในระดับ global ที่สอดคล้อง

ขั้นตอนที่ 2: Embedding ภายหลัง

หลังการแยก WeSpeaker ResNet34-LM สกัด embedding centroid 256 มิติสำหรับผู้พูดแต่ละคน embedding เหล่านี้ช่วยให้สามารถสกัดผู้พูดเป้าหมาย (--target-speaker) แต่ไม่ตัดสินใจการกำหนดผู้พูด

การย้ายจาก pyannote.audio

หากคุณมาจากไลบรารี Python pyannote.audio — การแทนที่ subclass ของ Pipeline ที่ตั้งค่า pipeline.segmentation = ... หรือย้ายออกจากเซิร์ฟเวอร์ที่รัน pyannote/speaker-diarization-3.1 — Soniqo wrap ตัวโมเดล Pyannote-Segmentation-3.0 เองและรันทั้งหมดบนอุปกรณ์ Apple Silicon ไม่ต้องใช้ Python runtime ไม่ต้องใช้ CUDA ไม่ต้องใช้ Hugging Face token ตอน inference

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 = ... (subclass แบบกำหนดเอง) คงที่: 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 ถูกแปลงจาก checkpoint ต้นทางของ HuggingFace ดังนั้น logits ของ segmentation จึงเทียบเท่าเชิงตัวเลขภายในขีดจำกัดความคลาดเคลื่อนของ float ส่วนการเรียงลำดับหลัง segmentation (Pearson correlation บนหน้าต่างที่ซ้อนทับ + matching แบบ exclusive greedy) และขั้นตอน embedding ภายหลังด้วย WeSpeaker ถูกเขียนใหม่ใน Swift แต่ให้เอาต์พุต RTTM ที่เทียบเท่ากับ pipeline Python อ้างอิง

ยังไม่รองรับ

ยังไม่มีเวอร์ชัน streaming OnlineSpeakerDiarization สำหรับเอนจิน Pyannote สำหรับการแยกผู้พูดแบบเรียลไทม์ ให้ใช้ --engine sortformer แทน เอนจินนี้รันโมเดล Sortformer ด้วยบัฟเฟอร์สถานะ FIFO + cache ผู้พูด

การใช้งาน 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

การสกัดผู้พูดเป้าหมาย

ให้เสียงลงทะเบียนของผู้พูดที่ทราบเพื่อสกัดเฉพาะ segment ของพวกเขาออกจากไฟล์บันทึก Pipeline คำนวณ embedding ของเสียงลงทะเบียนและค้นหา cluster ที่มีความคล้ายคลึง cosine สูงสุด

# Extract segments for a specific speaker
.build/release/speech diarize meeting.wav --target-speaker enrollment.wav

การให้คะแนน DER

ประเมินคุณภาพการแยกโดยเทียบกับไฟล์ RTTM อ้างอิง Pipeline คำนวณ Diarization Error Rate (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
SegmentationPyannote-Segmentation-3.0~5.7 MBaufklarer/Pyannote-Segmentation-MLX
Embedding ผู้พูดWeSpeaker-ResNet34-LM (MLX)~25 MBaufklarer/WeSpeaker-ResNet34-LM-MLX
Embedding ผู้พูดWeSpeaker-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
)