화자 분리
다화자 녹음에서 누가 언제 말했는지 식별합니다. 두 가지 화자 분리 엔진을 제공합니다: 두 단계의 Pyannote 파이프라인(분할 + 활동 기반 화자 체이닝, 이후 사후 임베딩)과 엔드투엔드 Sortformer 모델(CoreML, Neural Engine)입니다.
엔진
--engine pyannote (기본) 또는 --engine sortformer로 엔진을 선택합니다.
Pyannote (기본)
두 단계 파이프라인: Pyannote 분할이 활동 기반 화자 체이닝(오버랩 존에서의 Pearson 상관)과 함께 겹치는 윈도를 처리하여 전역 화자 레이블을 할당합니다. 사후 WeSpeaker 임베딩 추출로 등록 오디오를 통한 타겟 화자 식별이 가능합니다.
Sortformer (CoreML)
NVIDIA의 엔드투엔드 신경 화자 분리 모델. 별도의 임베딩이나 클러스터링 단계 없이 최대 4명의 화자에 대해 프레임별 화자 활동을 직접 예측합니다. 스트리밍 상태 버퍼(FIFO + 화자 캐시)와 함께 CoreML을 통해 Neural Engine에서 실행됩니다.
Sortformer는 화자 임베딩을 생성하지 않습니다. --target-speaker와 --embedding-engine 플래그는 Pyannote 엔진에서만 사용할 수 있습니다.
Pyannote 파이프라인
기본 파이프라인은 두 단계로 실행됩니다:
1단계: 분할 + 화자 체이닝
Pyannote segmentation-3.0은 50% 오버랩의 10초 슬라이딩 윈도를 처리합니다. powerset 디코더가 7-클래스 출력을 화자별 확률로 변환합니다(윈도당 최대 3명의 로컬 화자). 인접한 윈도는 5초의 오버랩을 공유하며, 오버랩 존에서 확률 트랙 간 Pearson 상관을 계산하고 greedy 배타적 매칭을 통해 일관된 전역 화자 ID를 윈도 전체에 전파합니다.
2단계: 사후 임베딩
화자 분리 이후 WeSpeaker ResNet34-LM이 화자당 256차원 centroid 임베딩을 추출합니다. 이 임베딩은 타겟 화자 추출(--target-speaker)을 가능하게 하지만 화자 할당 자체를 결정하지는 않습니다.
CLI 사용법
# 기본 화자 분리 (pyannote, 기본)
.build/release/audio diarize meeting.wav
# 엔드투엔드 Sortformer (CoreML)
.build/release/audio diarize meeting.wav --engine sortformer
# RTTM 출력 형식 (평가용)
.build/release/audio diarize meeting.wav --rttm
# JSON 출력
.build/release/audio diarize meeting.wav --json
타겟 화자 추출
알려진 화자의 등록 오디오를 제공하여 녹음에서 해당 화자의 세그먼트만 추출합니다. 파이프라인은 등록 오디오의 화자 임베딩을 계산하고 가장 높은 코사인 유사도를 가진 클러스터를 찾습니다.
# 특정 화자의 세그먼트 추출
.build/release/audio diarize meeting.wav --target-speaker enrollment.wav
DER 스코어링
레퍼런스 RTTM 파일에 대해 스코어링하여 화자 분리 품질을 평가합니다. 파이프라인은 잘못 귀속된 시간의 비율을 측정하는 Diarization Error Rate (DER)를 계산합니다.
# 레퍼런스 RTTM에 대해 스코어링
.build/release/audio 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 | 화자 임베딩 엔진: mlx 또는 coreml (pyannote 전용) |
--vad-filter | Silero VAD로 사전 필터링 (pyannote 전용) |
--rttm | RTTM 형식으로 출력 |
--json | JSON 출력 형식 |
--score-against | DER 평가를 위한 레퍼런스 RTTM 파일 |
화자 분리는 명확한 화자 턴이 있는 녹음에서 가장 잘 동작합니다. 심하게 겹치는 음성은 정확도를 떨어뜨릴 수 있습니다. 화자 수는 자동으로 결정됩니다.
모델 다운로드
모델은 첫 사용 시 자동으로 다운로드됩니다:
| 구성 요소 | 모델 | 크기 | HuggingFace |
|---|---|---|---|
| 분할 | Pyannote-Segmentation-3.0 | 약 5.7 MB | aufklarer/Pyannote-Segmentation-MLX |
| 화자 임베딩 | WeSpeaker-ResNet34-LM (MLX) | 약 25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| 화자 임베딩 | 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]")
}
// 타겟 화자 추출
let targetEmb = pipeline.embeddingModel.embed(audio: enrollmentAudio, sampleRate: 16000)
let segments = pipeline.extractSpeaker(
audio: meetingAudio, sampleRate: 16000,
targetEmbedding: targetEmb
)