การแยกผู้พูด
ระบุว่าใครพูดเมื่อใดในไฟล์บันทึกที่มีผู้พูดหลายคน มีเอนจินการแยกผู้พูดสองตัวให้เลือก: 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 |
|---|---|---|---|
| Segmentation | 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 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
)