화자 임베딩
WeSpeaker ResNet34-LM을 사용해 L2 정규화된 256차원 화자 벡터를 추출합니다. 이러한 임베딩은 화자의 고유한 음성 특성을 포착하며 식별, 검증, 음성 검색에 사용할 수 있습니다.
아키텍처
WeSpeaker ResNet34-LM은 화자 표현 학습을 위해 훈련된 심층 residual 네트워크입니다.
| 단계 | 세부 내용 |
|---|---|
| 입력 | Conv2d (1 → 32 채널) |
| ResNet34 | [3, 4, 6, 3] residual 블록 |
| Stats Pooling | 시간에 대한 평균 + 표준편차 |
| Projection | Linear (5120 → 256) |
| 출력 | L2 정규화된 256차원 임베딩 |
모델 크기: 약 6.6M 파라미터, 디스크에서 약 25 MB.
멜 특징
이 모델은 Hamming 창으로 계산된 80차원 멜 주파수 특징을 사용합니다. 로그 스케일링은 추가 정규화 없이 단순한 log(max(mel, 1e-10)) 공식을 사용합니다. 배치 정규화는 추론 효율성을 위해 변환 시 Conv2d 레이어에 융합됩니다.
CLI 사용법
# 화자 임베딩 추출
.build/release/audio embed-speaker voice.wav
# JSON 출력 (256차원 벡터 포함)
.build/release/audio embed-speaker voice.wav --json
# 추론 엔진 선택
.build/release/audio embed-speaker voice.wav --engine coreml
옵션
| 옵션 | 설명 |
|---|---|
--engine | 추론 엔진: mlx 또는 coreml |
--json | 전체 임베딩 벡터를 포함한 JSON 출력 형식 |
사용 사례
화자 검증
두 개의 오디오 샘플을 비교하여 동일한 화자인지 판단합니다. 두 샘플에서 임베딩을 추출하고 코사인 유사도를 계산합니다. 유사도 점수가 높을수록 동일 화자일 확률이 높습니다.
import SpeechVAD
let model = try await WeSpeaker.loadFromHub()
let embedding1 = try await model.embed(audioFile: "sample1.wav")
let embedding2 = try await model.embed(audioFile: "sample2.wav")
let similarity = cosineSimilarity(embedding1, embedding2)
print("Similarity: \(similarity)") // > 0.7이면 일반적으로 동일 화자
화자 식별
미지의 오디오 샘플을 등록된 화자 임베딩 데이터베이스와 매칭합니다. 가장 높은 코사인 유사도를 가진 등록 화자가 예측된 식별자입니다.
음성 검색
오디오 녹음 컬렉션을 화자 임베딩으로 인덱싱한 후, 새 오디오 샘플로 쿼리해 동일 화자의 모든 녹음을 찾습니다.
중요
화자 임베딩은 최소 2-3초의 깨끗한 음성에서 가장 잘 동작합니다. 매우 짧은 클립이나 잡음 녹음은 덜 신뢰할 수 있는 임베딩을 생성할 수 있습니다. 잡음 오디오의 경우 먼저 음성 향상을 적용해 보세요.
모델 다운로드
| 모델 | 백엔드 | 크기 | HuggingFace |
|---|---|---|---|
| WeSpeaker-ResNet34-LM | MLX | 약 25 MB | aufklarer/WeSpeaker-ResNet34-LM-MLX |
| WeSpeaker-ResNet34-LM | CoreML | 약 25 MB | aufklarer/WeSpeaker-ResNet34-LM-CoreML |
Swift API
import SpeechVAD
let model = try await WeSpeaker.loadFromHub()
// 파일에서 임베딩 추출
let embedding = try await model.embed(audioFile: "voice.wav")
print("Embedding dimensions: \(embedding.count)") // 256
// 오디오 샘플에서 임베딩 추출
let samples: [Float] = loadAudio("voice.wav")
let embedding = try await model.embed(samples: samples, sampleRate: 16000)