十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FunASR 说话人与情感标签实战指南:声纹向量、匿名聚类与 SenseVoice 富标签的正确获取与保存

FunASR 说话人与情感标签实战指南:声纹向量、匿名聚类与 SenseVoice 富标签的正确获取与保存 FunASR 说话人与情感标签实战指南声纹向量、匿名聚类与 SenseVoice 富标签的正确获取与保存【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR导读本文围绕 FunASR 中说话人与情感标签这一核心主题讲清四类极易混淆的输出——声纹向量spk_embedding、匿名聚类编号spk、已注册人员身份与情感/事件标签——之间的本质区别与适用场景。你将掌握如何用 CAMPPlus / ERes2NetV2 从单段语音提取可序列化的声纹向量如何用 SenseVoiceSmall 完整保留转写、情感与音频事件标签并安全落盘以及为什么 VAD 说话人聚类、HTTP 转写服务与独立推理之间不能随意混用。全文以 docs/speaker_emotion.md 为主线并结合仓库源码给出实现级佐证。先选任务再选模型四类输出不可互相替代在动手调用任何一个模型之前必须明确你最终需要的是哪一种结果。声纹向量、匿名聚类标签、已注册人员身份与情感标签是四类不同性质的输出任何一类都不能替代另一类任务路径从选定语音提取一个说话人向量CAMPPlus 或 ERes2NetV2见下方示例为转写片段分配匿名说话人ASR/VAD/说话人流水线保留转写、情感和音频事件标签SenseVoiceSmall见下方示例联合转写与匿名说话人分离第三方 OpenMOSS MOSS 指南其中 CAMPPlus 与 ERes2NetV2 属于说话人识别SV模型返回值是spk_embedding这个 Tensor而不是人的姓名、匹配结论也不是通用 ASR 的text字段。从源码看两者的接口契约完全一致CAMPPlus 模型实现 明确注释 Output: 192-dimensional speaker embedding per utterance其inference方法返回[{spk_embedding: Tensor of shape (1, 192)}]ERes2NetV2 模型实现 同样返回{spk_embedding: Tensor of shape (1, 192)}并在注释中说明它对短时音频 3 秒的特征提取优于 CAM——这是两者选型时的重要参考。需要特别强调两个由源码结构可以确认的细节向量维度由检查点配置决定不是接口永远保证 192 维。虽然默认embedding_size192但该值来自模型构造参数换一个检查点就可能不同因此下游代码不应硬编码维度。单条输入通常对应一行但务必使用batch_size1。部分批处理路径会在一个字典内返回多行向量而不是每个输入对应一个带key的结果按每输入一个结果的假设取数会出错。边界澄清向量 ≠ 身份标签 ≠ 人员 ID向量本身不等于身份确认。注册enrollment、匹配matching、阈值校准、授权同意consent以及面向目标人群的评测都需要应用层单独设计与实现模型只负责输出向量。spk0或 MOSS 的S01只是录音内部的匿名标签不是跨录音稳定的人员 ID。它只在某一条音频/会议内部有意义。spk_embedding_center是聚类均值表示该聚类所有片段向量的中心点不代表已经完成身份注册。准备本地检查点环境、模型与音频格式环境与模型清单开始之前先完成安装检查并准备一个完整的本地模型快照——包括配置文件、前端frontend、适用时的 tokenizer 以及权重文件。同时建议记录以下信息以备复现解析后的模型 revision 或文件哈希、SDK 版本、实际导入的模块路径与源码 commit。本文对应的实现以文末列出的源码文件为准不保证所有旧版 wheel、导出后端或模型变体接口一致。本指南使用的三个模型路径CAMPPlusembeddingiic/speech_campplus_sv_zh-cn_16k-common其 ModelScope 别名为camERes2NetV2embeddingiic/speech_eres2netv2_sv_zh-cn_16k-commonSenseVoicesensevoiceiic/SenseVoiceSmall。注意embedding与sensevoice只是下方示例程序的两个运行模式选项并不是 FunASR 新增的模型别名或 CLI 子命令。不要把 ASR 检查点传给embedding模式也不要把 ERes2NetV2 的行为推广到所有 ERes2Net 变体。更多模型路径可查阅 Model Zoo。音频输入要求示例程序只接受非空、单声道、16 kHz 的 WAV 音频它读取归一化的float32波形对立体声、空音频或其它采样率的输入直接报错而不是隐式转换。对于声纹提取请使用选定说话人的单人有效语音——混合说话人、静音与极短片段都不能提供可靠的身份依据。需要说明的是下面的输入校验只做格式检查不是语音质量检测器。完整示例程序保存 SDK 结果且不丢失标签下面这段独立程序接收task、model_dir、audio和一个新的 JSON 输出路径。运行方式在脚本名之后python attributes.py embedding /models/campplus speaker.wav vector.json python attributes.py sensevoice /models/sensevoice utterance.wav tags.json两个模式都在 CPU 上处理完整片段不添加 VAD、标点、配套说话人模型或流式缓存程序内部也不会下载模型。完整代码即 docs/speaker_emotion.md 中的示例import argparse import json import os from pathlib import Path import soundfile as sf from funasr import AutoModel from funasr.utils.postprocess_utils import rich_transcription_postprocess def embedding_record(results): if not results: raise ValueError(No result from the speaker model) if len(results) ! 1: raise ValueError(Expected one speaker result for one input) vector results[0][spk_embedding] if vector.ndim ! 2 or vector.shape[0] ! 1 or vector.shape[1] 0: raise ValueError(Expected a nonempty single-row speaker embedding) return {spk_embedding: vector.detach().cpu().tolist()} def tagged_records(results): if not results: raise ValueError(No result from SenseVoice) records [] for item in results: raw item[text] if not isinstance(raw, str): raise ValueError(Expected SenseVoice text with its original tags) records.append({ key: item.get(key), raw_tagged_text: raw, display_text: rich_transcription_postprocess(raw), }) return records def write_result(path, record): payload json.dumps(record, ensure_asciiFalse, allow_nanFalse, indent2) fd os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) with os.fdopen(fd, w, encodingutf-8) as stream: stream.write(payload \n) parser argparse.ArgumentParser() parser.add_argument(task, choices[embedding, sensevoice]) parser.add_argument(model_dir) parser.add_argument(audio) parser.add_argument(output) args parser.parse_args() model_dir Path(args.model_dir).expanduser().resolve(strictTrue) if not model_dir.is_dir(): raise ValueError(Expected a complete local model directory) speech, sample_rate sf.read(args.audio, dtypefloat32) if sample_rate ! 16000 or speech.ndim ! 1 or len(speech) 0: raise ValueError(Expected nonempty mono 16 kHz audio) model AutoModel( modelstr(model_dir), devicecpu, ncpu1, disable_updateTrue, trust_remote_codeFalse, vad_modelNone, punc_modelNone, spk_modelNone, ) if args.task embedding: results model.generate(inputspeech, fssample_rate, batch_size1) result embedding_record(results) else: results model.generate( inputspeech, fssample_rate, batch_size1, languageauto, use_itnTrue, output_timestampFalse, ) result tagged_records(results) write_result(args.output, { task: args.task, model_dir: str(model_dir), sample_rate: sample_rate, result: result, })示例程序的设计要点源码级解读外层 JSON、raw_tagged_text与display_text是应用自己定义的字段不是 SDK 新增的响应字段。AutoModel.generate本身返回的是 AutoModel 的标准结果列表。向量序列化是显式的spk_embedding先detach()脱离计算图再.cpu()移到 CPU最后.tolist()转成嵌套列表才能写入 JSON——这与后文提到的NumPy 聚类中心数组也需要显式序列化是同一类问题。非有限数值在创建文件前就被拒绝json.dumps(..., allow_nanFalse)会抛出ValueError杜绝 NaN/Inf 悄悄进入结果文件。已有输出文件永远不会被覆盖os.O_EXCL标志保证如果目标路径已存在则直接失败FileExistsError每次运行需要选择新路径。权限控制在 POSIX 上文件以0o600仅所有者可读写模式创建此外仍需自行配置合适的目录权限与平台 ACL。数据安全提醒声纹向量和转写文本可能属于敏感个人信息只保留应用确实需要的字段不要公开私人音频样本或识别结果。正确读取 SenseVoice 标签原始标签在哪里SenseVoice 的原始带标签字符串通常在result[text]字段不能假设一定存在raw_text、emotion或emotion_score之类的字段。标签区分大小写例如|zh|、|HAPPY|、|Speech|、|withitn|某个标签是否出现及其含义取决于具体检查点一张展示映射表并不承诺每个模型都会输出表中全部标签。从 postprocess_utils.py 的映射表 可以看到仓库实际支持的标签体系情感标签|HAPPY|、|SAD|、|ANGRY|、|NEUTRAL|、|FEARFUL|、|DISGUSTED|、|SURPRISED|音频事件标签|BGM|、|Speech|、|Applause|、|Laughter|、|Cry|、|Sneeze|、|Breath|、|Cough|语言标签|zh|、|en|、|yue|、|ja|、|ko|、|nospeech|文本规范化开关|withitn|开启逆文本规范化与|woitn|关闭。rich_transcription_postprocess是有损展示函数rich_transcription_postprocess 是有损的展示处理它做四件事删除标签、按出现次数选择展示情感见format_str_v2中出现次数最多者胜出的逻辑、合并重复的展示标记以及执行若干文本替换例如把语言标签统一替换为|lang|、The.替换为空格。因此它不是结构化标签解析器也不是概率计算器——不要从展示符号或模型标签推导置信概率、真实心理状态或诊断结论必须先把原始字符串原样保存示例中的raw_tagged_text正是为此设计展示用display_text只是给人看的副产品静音、短片段与噪声音频不能提供可靠的情感依据emotion2vec是另一类模型和接口不是 SenseVoice 标签的别名不要混淆。生成参数的确切语义示例中显式选择了languageauto、use_itnTrue、output_timestampFalse这些参数并非全部必填。从 SenseVoice 模型 inference 实现 可以看到它们的底层语义language语言提示取值auto/zh/en/yue/ja/ko默认auto。它会通过self.lid_dict映射为语言嵌入language_query拼接到输入中从而引导解码use_itn控制逆文本规范化Inverse Text Normalization不是情感识别开关。源码中use_itnTrue时textnorm取withitn否则取woitn但如果显式传入了text_norm则以text_norm为准优先级更高output_timestamp控制是否额外输出逐词时间戳源码中只有在为True时才会走ctc_forced_align的强制对齐分支。每次调用都需要重复传入需要固定的选项不要依赖上次调用的状态。此外要明确本例是完整片段推理不是 KWS 的 EOS 协议也不是实时情绪监控——不要把它当作流式或长时监测接口使用。VAD、说话人分离与服务边界不要把聚类和直接推理混为一谈独立声纹提取和短片段 SenseVoice 推理不要求 VAD。但要注意通用 AutoModel 的说话人聚类功能位于其 VAD 流水线内部——只设置spk_model或return_spk_resTrue并不会给直接推理增加说话人分离。换言之model.generate直接推理不产出sentence_info[].spk要走匿名说话人聚类必须走完整的 SDK 流水线组合兼容的 ASR/VAD/标点/说话人模型。同时记住三点边界VAD 边界不保证对应说话人切换——静音切段和说话人切换是两个不同的事件长音频分段会改变情感标签所依据的上下文——同一句话在整段上下文里与单独成段时模型输出的标签可能不同sentence_info[].spk是录音内的匿名编号SDK 的start/end时间字段单位是毫秒NumPy 聚类中心数组也需要像 Tensor 一样显式序列化后才能写入 JSON。第三方 MOSS 与内置 HTTP 服务OpenMOSS MOSS-Transcribe-Diarize见 MOSS 指南提供自己的联合转写与说话人分离路径不需要外部vad_model或spk_model。使用它时不要叠加第二套聚类流水线也不要宣称其输出可识别已知人员身份后端、显存与返回格式的细节以该指南为准。当前内置的 HTTP 转写服务 中SenseVoice 的配置附带 VAD其 fallback 会剥离顶层及分段文本中的富标签且不会补建情感分数字段。它的spktrue属于服务自己的流水线HTTP 分段start/end单位是秒与 SDK 的毫秒不同。因此把本 SDK 示例平移到/v1/audio/transcriptions不代表仍能读到同样的标签或使用同样的参数。请查阅部署矩阵不要假设 vLLM、llama.cpp、ONNX 或 WebSocket 等导出路径都保留与 Python 接口相同的内容结构。源码与验证契约从哪来测试验什么契约来源文件本文描述的接口行为以仓库中以下实现为契约来源CAMPPlus 模型返回spk_embedding默认 192 维维度来自检查点配置ERes2NetV2 模型返回spk_embedding短时音频提取更优SenseVoice 模型language/use_itn/output_timestamp/text_norm的实际处理逻辑后处理工具emo_dict/event_dict/lang_dict/emoji_dict映射与rich_transcription_postprocess有损展示逻辑AutoModelgenerate的入口与参数传递HTTP 适配层funasr/bin/_server_app.pyHTTP 服务的标签剥离与spk流水线行为。指南测试验证了什么test_speaker_emotion_docs.py 使用记录调用参数的 SDK 替身Recording Doubles与真实的rich_transcription_postprocess函数逐字执行本文公开的示例程序重点验证字段保留raw_tagged_text与原始标签字符串逐字一致display_text为经过真实有损处理的展示结果例如|zh||HAPPY||Speech||withitn|你好→你好参数传递batch_size1、fs16000、devicecpu、trust_remote_codeFalse、vad_model/punc_model/spk_modelNone以及 SenseVoice 模式的languageauto、use_itnTrue、output_timestampFalse序列化安全向量必须经历detach → cpu → tolist才允许写出非有限数值、多行向量、空结果都会抛ValueError且不产生输出文件错误输入处理非 16 kHz、空音频、立体声、已有输出文件不覆盖、非字符串text等场景均被正确拒绝。需要明确测试的边界这些测试验证的是接口契约与序列化正确性不代表声学质量、身份匹配准确率、不同人群的表现差异或情感识别精度的评测结论。写在最后三个先问自己的检查清单我要的是哪类结果是向量CAMPPlus/ERes2NetV2、匿名聚类编号VAD/说话人流水线、富标签SenseVoiceSmall还是联合分离MOSS四者不可互相替代。我的结果会怎样被消费向量需要显式 detach/cpu/tolist 才能 JSON 化spk是录音内匿名编号不是人员 IDspk_embedding_center是聚类均值不是注册结果展示文本经过了有损处理原始标签必须先保存。我的部署路径是谁SDK 直接推理、VAD 聚类流水线、HTTP 转写服务、vLLM/llama.cpp/ONNX/WebSocket 导出路径之间的标签保留、时间单位毫秒 vs 秒与参数语义并不一致务必以部署矩阵和对应指南为准。【免费下载链接】FunASROpen-source speech recognition toolkit for training, inference, streaming ASR, VAD, punctuation, speaker diarization pipelines, and OpenAI-compatible/MCP serving.项目地址: https://gitcode.com/GitHub_Trending/fun/FunASR创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表