说话人嵌入向量
使用 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 使用
# 提取说话人 embedding
.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 | 含完整 embedding 向量的 JSON 输出格式 |
使用场景
说话人验证
对比两段音频,判断是否来自同一个说话人。分别提取 embedding 并计算余弦相似度。相似度越高,属于同一说话人的概率越大。
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 通常为同一说话人
说话人识别
将一段未知音频与已注册的说话人 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 WeSpeaker.loadFromHub()
// 从文件提取 embedding
let embedding = try await model.embed(audioFile: "voice.wav")
print("Embedding dimensions: \(embedding.count)") // 256
// 从音频样本提取 embedding
let samples: [Float] = loadAudio("voice.wav")
let embedding = try await model.embed(samples: samples, sampleRate: 16000)