资讯动态

LocalAI 说话人(声纹)识别全指南:从声纹注册、1:1 语音验证到 1:N 说话人识别

发布时间:2026/9/9 12:36:42 来源:尧图企业网站定制
LocalAI 说话人声纹识别全指南从声纹注册、1:1 语音验证到 1:N 说话人识别【免费下载链接】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/LocalAILocalAI 内置了一整套与 Face Recognition 对偶的说话人识别Speaker/Voice Recognition能力基于自建向量存储完成声纹嵌入embedding、1:1 语音验证verify、1:N 说话人识别identify以及对语音的人口学属性分析age / gender / emotion全部通过统一的/v1/voice/*HTTP API 暴露。本文以 docs/content/features/voice-recognition.md 为骨架结合仓库源码、gallery 条目与 gRPC 协议定义给出可直接上手的注册—识别—删除实战流程、完整 API 参数参考以及两套后端C/ggml 的voice-detect与 Python 的speaker-recognition的选型与原理。一、功能全景验证、识别、嵌入与人口学分析LocalAI 的 voice recognition 是 Face Recognition 的音频孪生——同一个基于向量存储的注册中心设计只是把人脸特征向量换成了声纹特征向量voiceprint / speaker embedding。它支持四类能力能力HTTP 端点一句话说明1:1 声纹验证POST /v1/voice/verify这两段音频是否出自同一人1:N 说话人识别POST /v1/voice/identify这段未知音频在已注册说话人里是谁声纹登记 / 删除POST /v1/voice/register、/v1/voice/forget往向量存储中注册/移除某个说话人声纹声纹嵌入提取POST /v1/voice/embed返回 L2 归一化声纹向量人口学分析POST /v1/voice/analyze从语音推断年龄、性别、情绪这些端点由一个后端的多个 gRPC RPC 驱动。看路由注册代码 core/http/routes/localai.go#L140-L160 可以清楚看到/v1/voice/{verify,analyze,embed,register,identify}都经过requestExtractor.SetModelAndConfig会按speaker_recognitionusecase 过滤并解析出当前模型配置而/v1/voice/forget因为不加载语音模型、只需要注册中心被特殊处理直接绑到app.VoiceRegistry()。服务同一套 API 的两套后端后端运行时形态定位voice-detect推荐默认独立 C/ggml 引擎经 Go 侧 purego 桥接无 Python、无 onnxruntime、无 torch单文件 GGUF一条 gallery 命令即可完成引擎选择speaker-recognitionPython原始 SpeechBrain / ONNX 后端单镜像内提供双引擎仍是受支持的选项兼容早期部署两套后端暴露完全一致的线上协议wire format本页所有 curl 示例两者通用唯一区别是请求里model字段的 gallery 条目名不同。HTTP 层之上的一切行为输入解码、阈值、向量存储注册流程与后端无关。二、voice-detectggml后端免 Python 运行时的默认方案voice-detect是文档明确推荐的新部署选项。它的核心理念是把说话人嵌入或分析架构直接写进 GGUF 元数据的voicedetect.arch字段因此安装某个 gallery 条目就等于选择某个引擎无需任何额外配置。从仓库后端实现看这套机制是双进程协作backend/go/voice-detect/main.go 里 LocalAI 为每个加载的模型启动一个 gRPC 服务进程通过 puregodlopen加载libvoicedetect.so可用环境变量VOICEDETECT_LIBRARY覆盖默认库名把voicedetect_capi.h声明的 C 入口逐一绑定为 Go 函数指针backend/go/voice-detect/govoicedetect.go#L95-L196 则实现VoiceEmbed/VoiceVerify/VoiceAnalyze三个 RPC。C 侧engine保持单模型单状态且不可重入所以 Go 侧通过base.SingleThread串行化每一次调用LocalAI 的 per-model 线程预算会通过环境变量VOICEDETECT_THREADS透传给引擎避免与硬件并发数冲突。gallery 条目速查Gallery 条目模型嵌入维度Licensevoice-detect-ecapa-tdnnSpeechBrain ECAPA-TDNNVoxCeleb192Apache 2.0 - 可商用voice-detect-wespeaker-resnet34WeSpeaker ResNet34VoxCeleb256CC-BY-4.0voice-detect-eres2net3D-Speaker ERes2NetVoxCeleb192Apache 2.0 - 可商用voice-detect-campplus3D-Speaker CAMVoxCeleb192Apache 2.0 - 可商用voice-detect-emotion-wav2vec2audEERING wav2vec2年龄/性别/情绪analyze 头CC-BY-NC-SA-4.0 - 仅限非商用研究前四个条目驱动 verify / embed / identify 管线voice-detect-emotion-wav2vec2是/v1/voice/analyze背后的分析头连续年龄估计 性别与情绪类别得分非商用 / 仅限研究。这些条目在 gallery/index.yaml#L18924-L19101 中有完整的下载地址、SHA-256 与overrides元数据例如voice-detect-ecapa-tdnn从 HuggingFacemudler/voice-detect-gguf下载ecapa-tdnn-voxceleb.gguf并声明backend: voice-detect、verify_threshold:0.25每个 GGUF 文件都带校验和属于self-describing 单文件形态。快速上手安装默认条目推荐直接复制local-ai models install voice-detect-ecapa-tdnn验证两段音频是否出自同一个人curl -sX POST http://localhost:8080/v1/voice/verify \ -H Content-Type: application/json \ -d { model: voice-detect-ecapa-tdnn, audio1: https://example.com/alice_1.wav, audio2: https://example.com/alice_2.wav }分析年龄 / 性别 / 情绪需先安装 analyze 条目local-ai models install voice-detect-emotion-wav2vec2 curl -sX POST http://localhost:8080/v1/voice/analyze \ -H Content-Type: application/json \ -d {model: voice-detect-emotion-wav2vec2, audio: https://example.com/alice.wav}1:N 的 register / identify / forget 流程以及其余 API 与下方 API 参考 完全一致——只需把模型名换成voice-detect-*条目。默认 verify 阈值ECAPA-TDNN / ERes2Net / CAM 约为 0.25WeSpeaker ResNet34 约为 0.30详见 阈值参考。值得注意的源码细节虽然请求里可以带threshold但0 时后端会回落到模型配置的默认阈值见 backend/go/voice-detect/govoicedetect.go#L133-L171。该默认值来自verify_threshold/threshold选项解析逻辑见 backend/go/voice-detect/options.go代码内建默认0.25与 Python 后端对齐而 gallery 的overrides.options通常已写入verify_threshold:0.25。因此切换识别器时更稳妥的做法是在请求里显式传 threshold见阈值参考表不要依赖未传即默认的隐式行为。置信度confidence的计算口径两个后端对confidence的计算完全一致为线性衰减距离为 0 时 100距离等于阈值时降到 0。Go 侧实现见 backend/go/voice-detect/govoicedetect.go#L156-L161Python 侧见 backend/python/speaker-recognition/backend.py#L108-L110confidence max(0, min(100, (1 - distance / threshold) * 100))三、speaker-recognitionPython后端SpeechBrain / ONNX 双引擎speaker-recognition遵循与voice-detect相同的双引擎、同一镜像模式是项目的原始后端。它的结构是薄 gRPC 外壳 引擎库 backend/python/speaker-recognition/backend.py 只做 RPC 装配VoiceVerify/VoiceAnalyze/VoiceEmbed加上 Health / LoadModel / Status真正的推理逻辑在engines.py。引擎与 gallery 条目Gallery 条目模型大小Licensespeechbrain-ecapa-tdnnECAPA-TDNN on VoxCelebSpeechBrain~17 MBApache 2.0 - 可商用wespeaker-resnet34WeSpeaker ResNet34 ONNX~26 MBApache 2.0 - 可商用两者都可商用Apache-2.0。SpeechBrain 是默认引擎轻量纯 PyTorch checkpoint首次 LoadModel 时自动从 HuggingFace 下载——这一点在 gallery 条目描述中写得很明确gallery/index.yaml#L18855-L18887auto-downloaded from HuggingFace on first LoadModel无独立权重文件直接指向上游 SpeechBrain HF 仓库保证每次部署字节一致。wespeaker-resnet34条目则接直连 ONNX 路径engine:onnx、model_path:wespeaker_voxceleb_resnet34.onnx、sample_rate:16000适合不需要 torch 运行时的纯 CPU 部署见 gallery/index.yaml#L18888-L18923。引擎选择是gallery 驱动的从 backend/python/speaker-recognition/README.md 可以确认若模型配置提供了model_path:/onnx:则走 ONNX 引擎否则走 SpeechBrain 引擎。快速上手安装默认后端与模型local-ai models install speechbrain-ecapa-tdnn验证两段音频是否出自同一个人curl -sX POST http://localhost:8080/v1/voice/verify \ -H Content-Type: application/json \ -d { model: speechbrain-ecapa-tdnn, audio1: https://example.com/alice_1.wav, audio2: https://example.com/alice_2.wav }响应示例{ verified: true, distance: 0.18, threshold: 0.25, confidence: 28.0, model: speechbrain-ecapa-tdnn, processing_time_ms: 340.0 }Python 侧的阈值优先级与 voice-detect 完全一致请求threshold 0用请求值否则回落到模型加载时解析出的verify_threshold选项Python 内建默认同样0.25见 backend/python/speaker-recognition/backend.py#L71-L99。四、API 参考逐端点字段与语义所有/v1/voice/*请求体定义集中在 core/schema/localai.go#L398-L500先读这个文件能一次看清全部字段下面是端点级详解。POST /v1/voice/verify1:1 验证字段类型说明modelstringgallery 条目名如speechbrain-ecapa-tdnnaudio1、audio2string音频文件的 URL、裸 base64 或>name: my-voice-analyzer backend: speaker-recognition options: - age_gender_model:your-org/your-age-gender-checkpoint - emotion_model:优雅降级某个头加载失败离线、磁盘满、缺transformers时引擎不报错仍返回它能算出的属性当所有属性都算不出来时后端返回501 Unimplemented。analyze 对speechbrain-ecapa-tdnn与wespeaker-resnet34都支持——说话人识别器与分析头相互独立互不依赖。协议层值得注意VoiceAnalyzeResponse是段segments结构见 backend/backend.proto#L1041-L1053每段含start/end时间、连续age、dominant_gender 各性别得分、dominant_emotion 各情绪得分HTTP 响应字段定义见 core/schema/localai.go#L429-L441。不过在 voice-detectggml实现里C-API 总是评估所有支持的头actions过滤仅是建议性的分析结果以单段single-utterance返回见 backend/go/voice-detect/govoicedetect.go#L173-L196。POST /v1/voice/register1:N 声纹登记字段类型说明modelstring语音识别模型audiostring待登记的说话人音频namestring可读标签必填为空返回 400labelsmap[string]string可选任意元数据storestring可选向量存储模型默认 local-store返回{id, name, registered_at}。id是不透明 UUID供/v1/voice/identify与/v1/voice/forget使用。从 core/http/endpoints/localai/voice_register.go 看登记的真实链路是两步走先调用backend.VoiceEmbed把音频编码成声纹向量再registry.Register(...)把向量连同元数据写进向量存储。底层 core/services/voicerecognition/store_registry.go#L51-L81 会用 UUIDv4 生成id、UTC 时间填充registered_at、把Metadata序列化成 JSON 后经store.SetSingle写入同时在进程内idIndex里保留id → embedding的映射供后续按 id 删除。POST /v1/voice/identify1:N 说话人识别字段类型说明modelstring语音识别模型audiostring探针音频top_kint可选最多返回的匹配数默认 5thresholdfloat可选余弦距离截断值默认 0.25storestring可选向量存储模型返回按距离升序排列的匹配列表每个匹配含id、name、labels、distance、confidence与matchmatch distance ≤ threshold。两个默认值在 core/http/endpoints/localai/voice_identify.go#L17-L21 中硬编码为常量top_k默认 5defaultVoiceIdentifyThreshold为0.25注释说明是针对 VoxCeleb 上 ECAPA-TDNN 调优的WeSpeaker、ERes2Net 可能需覆盖。查询时 handler 先对探针音频做VoiceEmbed再调用registry.Identify做 Top-K 相似检索服务端置信度同样用线性公式(1 - distance/threshold) * 100计算并夹在 0–100。底层的相似度检索在 core/services/voicerecognition/store_registry.go#L83-L120通过store.Find拿回原始嵌入与相似度后把Distance定义为1 - similarities[i]再稳定排序距离小者在前对共享存储里无法反序列化成语音Metadata的无关记录会直接跳过——这意味着同一向量存储命名空间理论上可以承载多种记录但要小心维度一致性问题。POST /v1/voice/forget删除说话人字段类型说明idstring/v1/voice/register返回的 ID成功返回204 No ContentID 未知返回404 Not Found。实现细节见 core/http/endpoints/localai/voice_forget.go这个端点不加载模型当 request extractor 未运行时还会退化为裸 bind删除时先查idIndex拿到该 id 对应的嵌入再store.DeleteSingle从向量存储删除并清理索引。由于底层 local-store 只按向量 key 索引、没有list all接口这个进程内idIndex是按 id 删除能力的前提——也正因如此它每次重启都会重建。POST /v1/voice/embed声纹嵌入返回 L2 归一化的说话人嵌入向量字段类型说明modelstring语音模型audiostringURL / base64 />curl -sX POST http://localhost:8080/v1/voice/register \ -H Content-Type: application/json \ -d { model: speechbrain-ecapa-tdnn, name: Alice, audio: https://example.com/alice.wav } # → {id: b2f..., name: Alice, registered_at: 2026-04-22T...}2. 识别未知探针音频curl -sX POST http://localhost:8080/v1/voice/identify \ -H Content-Type: application/json \ -d { model: speechbrain-ecapa-tdnn, audio: https://example.com/unknown.wav, top_k: 5 } # → {matches: [{id:b2f...,name:Alice,distance:0.19,match:true,...}]}3. 按 ID 删除某位说话人curl -sX POST http://localhost:8080/v1/voice/forget \ -d {id: b2f...} # → 204 No Content向量存储的注册中心设计从仓库源码可以确认这是一个可与 Face Recognition 共享设计的可替换注册中心接口抽象core/services/voicerecognition/registry.go 定义了Registry接口Register/Identify/Forget并声明其设计与facerecognition包对偶、接口形状完全一致——未来落地共享的通用生物识别注册中心时HTTP handler 无需改动。默认实现NewStoreRegistry把 LocalAI 泛化向量存储StoresSet/StoresFind/StoresDeletegRPC 面包装成注册中心。命名空间隔离core/application/application.go#L174-L189 里显式声明人脸用localai-face-biometrics、语音用localai-voice-biometrics两个独立命名空间。这是必须的local-store gRPC 面拒绝同一命名空间内的混合维度Try to add key with length N when existing length is M而 ArcFace 的 512 维与人脸向量和 ECAPA-TDNN 的 192 维声纹向量不可比、也不能混存。维度开关NewStoreRegistry(resolve, storeName, dim)的dim参数传入0表示接受任意维度——voice 应用正是传0voiceEmbeddingDim 0见 core/application/application.go#L49-L53以便同一注册中心兼容 ECAPA-TDNN(192) 与 ResNet(256) 等不同识别器。哨兵错误ErrNotFound/ErrEmptyEmbedding/ErrDimensionMismatch三个哨兵错误供调用方用errors.Is判断如 forget 端点把ErrNotFound映射为 404。{{notice 存储限制}}存储注意事项。默认向量存储是进程内内存存储。LocalAI 重启后所有已登记说话人都会丢失。持久化存储pgvector是与人脸识别共享的已列入规划的未来增强方向——voice-recognition 的 HTTP API 被刻意设计成更换底层存储时线上格式不变。 {{/notice}}六、阈值参考跨识别器的取舍由于distance 1 - cosine_similarity阈值越低要求越严格拒绝更远的向量。文档给出如下跨识别器参考识别器余弦距离阈值ECAPA-TDNNSpeechBrainVoxCeleb~0.25WeSpeaker ResNet34~0.303D-Speaker ERes2Net~0.28切换识别器时请显式传入threshold——未传才使用 per-model 默认值隐式默认并不自动跟随你换的新识别器。无论后端如何per-model 默认都可以通过模型配置的verify_threshold选项覆盖voice-detect 在 backend/go/voice-detect/options.go#L29-L46 解析它Python 后端在 backend/python/speaker-recognition/backend.py#L71-L76 解析它对应 gallery 条目也已在overrides.options里预置了verify_threshold:0.25。七、音频输入三种形态与统一解码音频由HTTP 层在调用 gRPC 之前统一物化成临时 WAV 文件后端永远只拿到一个文件系统路径——这与 Whisper / Voxtral 等转录后端的约定一致backend/backend.proto#L1005-L1010 注释写明Audio fields accept a filesystem path (same convention as TranscriptRequest.dst)。所有音频字段都接受三种形态http:///https://URL——服务端下载且受ValidateExternalURL安全检查约束裸 base64无前缀Data URIdata:audio/wav;base64,...。解码实现位于 core/http/endpoints/localai/audio.go#L40-L92值得留意的细节对http(s)://前缀走 URL 下载分支先做utils.ValidateExternalURL校验失败/下载失败一律报400而非 500让调用方区分客户端错误与服务端故障否则走 base64 分支先剥离data:audio/...;base64,前缀再解码解码结果为空或零字节同样报 400最终os.CreateTemp(, localai-voice-*.wav)落成临时文件返回路径 cleanup 闭包调用方defer cleanup()防止临时文件泄漏。也因此若你的音频是 PCM 裸流之类非 WAV 数据HTTP 层物化出的.wav后缀只是约定实际能否解码取决于后端引擎对容器格式的支持——实践中请尽量提供标准 WAV/容器音频。八、相关功能Face Recognition —— 图像对应物两者共享注册中心设计localai-*独立命名空间 同一Registry接口形状。Audio to Text —— 语音转写Whisper、Voxtral、faster-whisper与说话人识别是并行叠加关系而非替代。Stores —— 支撑人脸与语音 1:N 识别管线的通用向量存储。Embeddings —— 纯文本的 OpenAI 兼容嵌入端点音频嵌入请用/v1/voice/embed。想继续深入源码推荐按此顺序阅读先看 core/schema/localai.go#L398-L500请求/响应契约→ 再看 core/http/endpoints/localai 目录下的六个 voice handler端点语义→ 进入 core/backend 的voice_*.go模型加载与 gRPC 调用编排→ 最后到 core/services/voicerecognition注册中心实现与 backend/go/voice-detect / backend/python/speaker-recognition两套引擎。结合 gallery/index.yaml 中speechbrain-ecapa-tdnn到voice-detect-age-gender-wav2vec2的完整条目定义即可对 LocalAI 说话人识别从 HTTP 到 C/Python 引擎的整条链路建立完整的理解。【免费下载链接】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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价