说话人嵌入向量
使用 WeSpeaker ResNet34-LM 提取 256 维 L2 归一化的说话人向量。这些 embedding 捕获说话人独特的声学特征,可用于识别、验证和声音检索。
架构
WeSpeaker ResNet34-LM 是一个深度残差网络,用于说话人表示学习。
| 阶段 | 细节 |
|---|---|
| 输入 | Conv2d(1 到 32 通道) |
| ResNet34 | [3, 4, 6, 3] 残差块 |
| 统计池化 | 时间维度上的均值 + 标准差 |
| 投影 | Linear(5120 到 256) |
| 输出 | L2 归一化的 256 维 embedding |
模型大小:约 6.6M 参数,磁盘约 25 MB。
Mel 特征
模型使用由 Hamming 窗计算的 80 维 mel 频率特征。对数缩放使用简单的 log(max(mel, 1e-10)) 公式,无需额外归一化。为提高推理效率,batch normalization 在转换时就已融合到 Conv2d 层中。
CLI 使用
# Extract speaker embedding
.build/release/speech embed-speaker voice.wav
# JSON output (includes the 256-dim vector)
.build/release/speech embed-speaker voice.wav --json
# Choose inference engine
.build/release/speech embed-speaker voice.wav --engine coreml
选项
| 选项 | 说明 |
|---|---|
--engine | 推理引擎:mlx 或 coreml |
--json | 含完整 embedding 向量的 JSON 输出格式 |
使用场景
说话人验证
对比两段音频,判断是否来自同一个说话人。分别提取 embedding 并计算余弦相似度。相似度越高,属于同一说话人的概率越大。
import SpeechVAD
let model = try await WeSpeakerModel.fromPretrained()
let samples1: [Float] = loadAudio("sample1.wav")
let samples2: [Float] = loadAudio("sample2.wav")
let embedding1 = model.embed(audio: samples1, sampleRate: 16000)
let embedding2 = model.embed(audio: samples2, sampleRate: 16000)
let similarity = WeSpeakerModel.cosineSimilarity(embedding1, embedding2)
print("Similarity: \(similarity)") // > 0.7 typically same speaker
说话人识别
将一段未知音频与已注册的说话人 embedding 数据库匹配。余弦相似度最高的已注册说话人即为预测身份。
声音检索
按说话人 embedding 为一组音频录音建立索引,然后用一段新音频查询,找出所有同一说话人的录音。
重要
说话人嵌入向量在至少 2-3 秒的干净语音上效果最佳。非常短的片段或嘈杂录音可能会得到不太可靠的 embedding。对于嘈杂音频,可考虑先应用语音增强。
模型下载
| 模型 | 后端 | 大小 | 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 WeSpeakerModel.fromPretrained()
// Extract embedding from audio samples
let samples: [Float] = loadAudio("voice.wav")
let embedding = model.embed(audio: samples, sampleRate: 16000)
print("Embedding dimensions: \(embedding.count)") // 256