
LocalAI speaker-recognition 后端实战说话人向量、1:1 声纹核验与语音属性分析【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI本文以 backend/python/speaker-recognition/README.md 为主线结合 backend.py 与 engines.py 的源码实现讲解 LocalAI 的说话人识别声纹后端SpeechBrain 与 ONNX 双引擎的选择机制、/v1/voice/verify、/v1/voice/embed、/v1/voice/analyze三个端点的请求语义与参数细节、情感/年龄/性别分析头的加载策略以及从 HTTP 音频输入到 gRPC 调用的完整数据通路。读完本文后你可以独立完成该后端的双引擎选型、gallery 模型条目配置、阈值调参与单元验证。1. 后端定位insightface 的“音频版”speaker-recognition 是 LocalAI 的说话人声纹识别后端官方 README 将其定位为insightface人脸识别后端在音频领域的对应物它产出说话人向量speaker embeddings支持 1:1 声纹核验voice verification与语音人口属性分析voice demographic analysis。从源码结构看该后端遵循 LocalAI Python 后端的通用骨架backend.py 是 gRPC 服务进程实现Health/LoadModel/Status以及三个语音专属 RPCVoiceVerify、VoiceAnalyze、VoiceEmbed这些 RPC 定义在 backend/backend.proto 中rpc VoiceVerify / VoiceAnalyze / VoiceEmbedengines.py 承载全部模型推理逻辑即 README 所说的 “The heavy lifting lives in engines.py — this file is just the gRPC plumbing”文件头部注释明确说明这种“gRPC 壳 双引擎”的拆分正是镜像了 insightface 后端的组织方式。gRPC 接口契约由 backend/backend.proto 固化关键消息定义包括VoiceVerifyRequestaudio1、audio2、threshold以及预留的anti_spoofing字段、VoiceVerifyResponseverified、distance、threshold、confidence、model、processing_time_ms、VoiceAnalyzeRequestaudio、actions与VoiceEmbedRequest/Responseembedding浮点数组 model。proto 注释中特别指出threshold为 0 时取后端默认值后端收到的一律是文件路径而非原始音频字节。2. 双引擎设计SpeechBrainEngine 与 OnnxDirectEngineREADME 的 Engines 一节定义了两种引擎engines.py 中的实现与之完全对应2.1 SpeechBrainEngine默认使用在 VoxCeleb 上训练的ECAPA-TDNN模型speechbrain/spkrec-ecapa-voxceleb输出192 维 L2 归一化向量核验采用余弦距离1 - cosine_similarity首次LoadModel时从 HuggingFace 自动下载 checkpoint。源码层面的补充细节构造时读取source选项回退链为options[source] → model_name → speechbrain/spkrec-ecapa-voxceleb下载目录savedir优先取 LocalAI 传入的ModelPath即 options 中的_model_path否则回退HF_HOME再否则./pretrained_models——这样 checkpoint 会与其他 gallery 管理的资产放在一起README 中 “Auto-downloads from HuggingFace on first LoadModel” 落地的就是这一机制音频加载刻意绕开torchaudio.load新版 torchaudio 解码依赖 torchcodec/ffmpeg 链路改用soundfile读 WAV/FLAC非 16kHz 采样率时用np.interp做线性重采样到 16kHz源码注释说明生产环境建议调用方预先重采样。2.2 OnnxDirectEngineCPU 友好的 ONNX 直跑面向已导出 ONNX 的说话人编码器WeSpeaker ResNet、3D-Speaker ERes2Net、CAM 等。模型路径来自 gallery 的files:条目——gallery 安装流程会把 ONNX 文件放进 models 目录README 与 install.sh 中 “No pre-baked model weights. Weights flow through LocalAIs galleryfiles:mechanism” 说的就是这条权重流转路径。该引擎的完整可配置参数均在 options 中解析见 engines.py 的OnnxDirectEngine.__init__选项必填默认值作用model_path/onnx是二选一无ONNX 文件路径相对路径基于_model_path解析providers否CPUExecutionProvideronnxruntime 执行提供器逗号分隔sample_rate否16000编码器期望的输入采样率fbank_num_mel_bins否80Kaldi FBank 的 mel 维数fbank_frame_length_ms否25FBank 帧长毫秒fbank_frame_shift_ms否10FBank 帧移毫秒fbank_cmn否true是否做逐句谱减均值归一化Cepstral Mean Normalisation一个值得注意的实现细节是输入张量秩的自动探测预导出的说话人编码器有两类输入形状——rank-2 的[batch, samples]部分 3D-Speaker 导出直接吃原始波形与 rank-3 的[batch, frames, n_mels]WeSpeaker 及多数 Kaldi 系编码器需要预计算好的 Kaldi FBank 特征。引擎在加载时通过session.get_inputs()[0]探测秩rank-3 时在embed()中先用torchaudio.compliance.kaldi.fbank做特征提取再喂入源码注释说明正是把原始音频直接喂给 rank-3 图会触发Invalid rank for input: feats Got: 2 Expected: 3这一错误才引入了该分支。2.3 引擎选择机制gallery 驱动build_engine()的判定逻辑只有三行engine_kind (options.get(engine) or ).lower() if engine_kind onnx or options.get(model_path) or options.get(onnx): return OnnxDirectEngine(model_name, options), OnnxDirectEngine.name return SpeechBrainEngine(model_name, options), SpeechBrainEngine.name即 README 所述模型配置提供了model_path:/onnx:或显式engine:onnx就走 ONNX 引擎否则默认 SpeechBrain。gallery 条目正是按此约定下发的gallery/index.yaml 中有两个对应实例speechbrain-ecapa-tdnn 条目options为engine:speechbrain、source:speechbrain/spkrec-ecapa-voxcelebparameters.model即 HuggingFace 仓库名wespeaker-resnet34 条目options为engine:onnx、model_path:wespeaker_voxceleb_resnet34.onnx、sample_rate:16000并通过files:声明 ONNX 文件名、sha256 与下载 URI来自 HuggingFace实现“gallery 安装即权重就位”。3. 三个 HTTP 端点与 gRPC 语义README 的 Endpoints 一节列出的端点与 proto、servicer 实现一一对应3.1 POST /v1/voice/verify — 1:1 同说话人核验底层调用VoiceVerify要求audio1、audio2均非空否则返回INVALID_ARGUMENT阈值请求中threshold 0时优先使用否则用后端默认值。默认值定义在 backend.py 的DEFAULT_VERIFY_THRESHOLD 0.25源码注释说明该值是针对 ECAPA-TDNN/VoxCeleb 的余弦距离调定的客户端可通过 options 的verify_threshold在 LoadModel 阶段覆盖置信度换算confidence clamp((1 - distance / threshold) * 100, 0, 100)即距离为 0 时 100 分、达到阈值时 0 分线性插值verified判定为distance threshold响应还回显model模型名与processing_time_ms便于调用方做耗时观测。3.2 POST /v1/voice/embed — 提取说话人向量调用VoiceEmbed入参audio为文件路径响应是embedding浮点数组与model名称。SpeechBrain 引擎下为 192 维ONNX 引擎下维度取决于具体编码器如 WeSpeaker ResNet34 为 256 维见 gallery/index.yaml 中该条目的描述。3.3 POST /v1/voice/analyze — 语音属性分析懒加载调用VoiceAnalyzeactions可取age、gender、emotion的子集为空时默认执行全部源码中list(request.actions) or [age, gender, emotion]。响应是segments列表每个片段含start/end、age、dominant_gendergender概率映射、dominant_emotionemotion概率映射。当前实现返回单个覆盖整段音频的片段start0.0end时长README 与引擎注释均说明分段/说话人分离是后续增强。该端点有两个关键行为值得注意懒加载分析头AnalysisHead在第一次 analyze 调用时才加载模型权重README 的 “loaded lazily on the first analyze call” 对应_ensure_age_gender()/_ensure_emotion()中的“首次调用才 import torch/transformers 并加载 checkpoint”实现501 行为README 明确 “Both heads are optional. When nothing loads, the engine returns 501。” 源码印证了这一点analyze()在两个分析头都未成功加载时抛出NotImplementedErrorservicer 捕获后返回grpc.StatusCode.UNIMPLEMENTED映射到 HTTP 即 501而非让整个请求 500。4. AnalysisHead情感默认开、年龄/性别默认关engines.py 的AnalysisHead类把 README Endpoints 一节的情感/年龄/性别规则落实为两个可独立启停的分析头4.1 情感分析默认开启可退出默认 checkpoint 为superb/wav2vec2-base-superb-erApache-2.04 类分类neutral / happy / angry / sad通过transformers.pipeline(audio-classification, model...)加载以top_k8取回各标签概率并选出主导情感用空字符串emotion_model:可禁用该头if not model_id: self._emotion_error disabled。4.2 年龄 性别默认关闭显式开启DEFAULT_AGE_GENDER_MODEL ——没有默认模型README 的 “no default — wire a checkpoint viaage_gender_model:repoin options” 正是指这一点README 特别解释了一条重要的坑Audeering 的 age-gender 模型不能直接作为 drop-in 使用因为其多任务头无法通过AutoModelForAudioClassification加载。源码注释给出了更具体的失败机理AutoModelForAudioClassification会把(hidden_states, logits_age, logits_gender)压平成一个[batch, 4]的logits张量age 权重被丢弃、分类头被随机初始化“so the output is noise”因此正确姿势是接入一个带标准Wav2Vec2ForSequenceClassification头的 checkpoint通过age_gender_model:repo选项指定标签集为(female, male, child)输出解析兼容两种形状标准 tuplehidden[0]为 age 回归、hidden[1]为性别 logitsage 乘以 100 还原为岁与 Audeering 压平后的单张量[age, female, male, child]健壮性设计age/gender 头推理失败只会记录错误并返回空 dict不阻断同一次调用中 emotion 分支的执行“never take down the whole analyze call”。4.3 可配置选项汇总结合 README 与源码analyze 相关的 options 共两个age_gender_model:repo缺省为禁用、emotion_model:repo缺省为superb/wav2vec2-base-superb-er空串禁用。两个头都禁用时 analyze 请求将得到 501 未实现错误。5. 音频输入通路从 URL / contenteditable="false">【免费下载链接】LocalAILocalAI is the open-source AI engine. Run any model - LLMs, vision, voice, image, video - on any hardware. No GPU required.项目地址: https://gitcode.com/GitHub_Trending/lo/LocalAI创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考