news 2026/2/7 11:24:33

如何清洗原始标签?SenseVoiceSmall postprocess函数解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何清洗原始标签?SenseVoiceSmall postprocess函数解析

如何清洗原始标签?SenseVoiceSmall postprocess函数解析

1. 为什么需要清洗原始标签?

你刚用SenseVoiceSmall跑完一段粤语采访录音,结果弹出这样一行文字:

<|HAPPY|>大家好<|LAUGHTER|>今天聊AI<|BGM|>背景音乐渐入<|SAD|>不过最近模型训练有点卡...

看起来信息量很足——情绪、事件、内容全都有。但问题来了:这根本没法直接放进报告、发给客户,或者嵌入字幕系统。方括号标签混在文字里,既不美观,也不利于后续处理。

这就是我们今天要解决的核心问题:原始识别输出是“带标记的富文本”,而真实业务场景需要的是“干净、可读、结构化”的结果
rich_transcription_postprocess这个函数,就是 SenseVoiceSmall 官方提供的“标签清洗器”——它不负责识别,只负责把识别结果“翻译”成人类和程序都爱看的样子。

它不是可有可无的装饰,而是从实验室模型走向落地应用的关键一环。
下面我们就一层层拆开它,看看它到底做了什么、怎么用、以及哪些地方你得自己动手补。

2. rich_transcription_postprocess 函数到底干了什么?

2.1 它不是黑箱,而是一套清晰的规则链

很多人以为postprocess是个神秘的AI后处理模块,其实恰恰相反——它的逻辑非常直白,本质是一系列字符串替换 + 格式重组操作。打开 FunASR 源码里的postprocess_utils.py,你会发现它主要做三件事:

  • 第一步:提取并归类标签
    扫描原始文本,把<|HAPPY|><|APPLAUSE|>这类标记单独拎出来,区分成三类:

    • 情感类(HAPPY / ANGRY / SAD / NEUTRAL / FEAR / SURPRISE)
    • 事件类(BGM / LAUGHTER / APPLAUSE / CRY / CHEERING / NOISE / OTHER)
    • 语言/模式类(zh / en / yue / ja / ko / auto)
  • 第二步:按预设规则转换格式
    不同类型标签,转换方式完全不同:

    • 情感标签 → 转为中文括号标注,如<|HAPPY|>[开心]
    • 事件标签 → 转为小写+中文说明,如<|LAUGHTER|>[笑声]
    • 语言标签 → 直接忽略(因为识别时已指定语言,无需重复标注)
  • 第三步:合并相邻纯文本,压缩冗余空格
    大家好<|LAUGHTER|>今天聊AI变成大家好 [笑声] 今天聊AI,中间自动加空格,避免粘连。

整个过程没有调用任何模型,不依赖GPU,纯CPU秒级完成。你可以把它理解成一个“智能版查找替换”,但比普通替换更懂语音场景的语义习惯。

2.2 看一眼它的输入输出对比

我们用一段真实测试音频的原始输出来演示(已脱敏):

原始输出: <|HAPPY|>欢迎收听科技早报<|BGM|>轻快钢琴声<|NEUTRAL|>今天有三条快讯<|APPLAUSE|><|EN|>First<|ZH|>第一条<|SAD|>某大模型API突然限频<|NOISE|>电流杂音 清洗后: [开心] 欢迎收听科技早报 [BGM] 轻快钢琴声 [中性] 今天有三条快讯 [掌声] [英文] First [中文] 第一条 [悲伤] 某大模型API突然限频 [噪音]

注意几个细节:

  • <|EN|><|ZH|>这类语言标签被保留为[英文][中文],方便多语种字幕对齐;
  • <|NOISE|>被转为[噪音],而不是[NOISE],说明它做了语义映射;
  • 所有标签统一用中文方括号包裹,风格一致,视觉清爽;
  • 标签与文字之间自动添加空格,阅读节奏自然。

这正是它比手动正则替换更可靠的地方:它知道哪些标签该留、哪些该转、哪些该删,且规则已针对语音场景反复打磨过

3. 深度解析:postprocess 的源码逻辑与关键参数

3.1 函数签名与核心调用方式

rich_transcription_postprocess定义在funasr.utils.postprocess_utils中,最简调用只需一行:

from funasr.utils.postprocess_utils import rich_transcription_postprocess clean_text = rich_transcription_postprocess("<|HAPPY|>你好<|LAUGHTER|>") # 输出:"[开心] 你好 [笑声]"

但它也支持两个重要可选参数,直接影响清洗效果:

参数类型默认值作用说明
merge_longer_tagboolTrue是否将连续多个同类标签合并为一个,例如 `<
remove_puncboolFalse是否移除原始文本中的标点符号(如句号、问号),适合生成纯文本摘要场景

这两个参数看似简单,但在实际工程中非常实用。比如做客服对话分析时,你可能希望去掉所有标点,让NLP下游任务更稳定;而做播客字幕时,则必须保留标点,只清洗标签。

3.2 它内部用的不是正则,而是状态机式解析

你可能会想:“不就是字符串替换吗?我自己写个replace()不就行了?”
试试这段原始文本:

<|HAPPY|>会议开始<|APPLAUSE|><|HAPPY|>大家鼓掌<|BGM|>

如果用简单replace

  • 先替<|HAPPY|>[开心],得到[开心]会议开始<|APPLAUSE|>[开心]大家鼓掌<|BGM|>
  • 再替<|APPLAUSE|>[掌声],得到[开心]会议开始[掌声][开心]大家鼓掌<|BGM|>

问题来了:两个[开心]紧挨着,中间没空格,读起来像[开心]会议开始[掌声][开心]大家鼓掌—— 这显然不对。

rich_transcription_postprocess采用逐字符扫描 + 状态标记的方式:

  • 遇到<就进入“标签识别态”;
  • 扫到|>结束,记录完整标签名;
  • 在标签前后自动插入空格(除非前/后已是空格或标点);
  • 同类标签连续出现时,只保留第一个,避免冗余。

这种设计保证了输出永远“语义正确、格式整洁”,而不是机械拼接。

3.3 它的标签映射表是可扩展的

FunASR 把所有标签的中文映射关系定义在一个字典里:

TAG_MAP = { "HAPPY": "开心", "ANGRY": "愤怒", "SAD": "悲伤", "NEUTRAL": "中性", "FEAR": "恐惧", "SURPRISE": "惊讶", "BGM": "BGM", "LAUGHTER": "笑声", "APPLAUSE": "掌声", "CRY": "哭声", "CHEERING": "欢呼", "NOISE": "噪音", "OTHER": "其他", }

注意:BGM没被翻译成“背景音乐”,而是保留英文缩写。这是有意为之——在专业音频制作领域,“BGM”本身就是通用术语,比中文更准确。

如果你需要支持新标签(比如你自己微调模型加了<|CLAPPING|>),只需修改这个字典,再重载函数即可,无需动核心逻辑。

4. 实战技巧:三种常见清洗需求与对应方案

4.1 需求一:我要纯文字字幕(去掉所有标签,只留说话内容)

很多用户误以为postprocess是万能清洗器,其实它默认保留所有标签。如果你只需要干干净净的说话文本,得自己加一步过滤:

import re from funasr.utils.postprocess_utils import rich_transcription_postprocess def clean_subtitles(raw_text): # 先走官方清洗流程 processed = rich_transcription_postprocess(raw_text) # 再用正则剔除所有 [xxx] 格式内容 return re.sub(r'\[.*?\]', '', processed).strip() # 示例 raw = "<|HAPPY|>大家好<|LAUGHTER|>今天讲SenseVoice" print(clean_subtitles(raw)) # 输出:"大家好 今天讲SenseVoice"

优势:保留postprocess的空格修复能力,避免文字粘连
❌ 注意:re.sub会把[BGM][开心]一并删掉,无法区分保留哪类

4.2 需求二:我要结构化 JSON(把标签和文本分开存)

字幕系统、知识图谱、对话分析平台往往需要结构化数据。这时别只盯着字符串,直接解析原始model.generate()的返回结果:

res = model.generate(input=audio_path, language="zh") # res[0] 是主结果,结构类似: # { # "text": "<|HAPPY|>你好<|LAUGHTER|>", # "timestamp": [[0, 1200], [1250, 2100]], # 时间戳(毫秒) # "emo": ["HAPPY"], # 情感列表 # "event": ["LAUGHTER"] # 事件列表 # } def to_structured_output(res_item): text = res_item.get("text", "") clean_text = rich_transcription_postprocess(text) return { "clean_text": clean_text, "raw_text": text, "emotions": res_item.get("emo", []), "events": res_item.get("event", []), "timestamps": res_item.get("timestamp", []) } structured = to_structured_output(res[0]) print(structured) # 输出: # { # "clean_text": "[开心] 你好 [笑声]", # "raw_text": "<|HAPPY|>你好<|LAUGHTER|>", # "emotions": ["HAPPY"], # "events": ["LAUGHTER"], # "timestamps": [[0, 1200], [1250, 2100]] # }

优势:绕过字符串解析,直接拿到结构化字段,稳定可靠
补充:timestamp字段可用于生成 SRT 字幕文件,精准对齐

4.3 需求三:我要自定义标签样式(比如改成 emoji 或颜色标记)

有些团队做内部看板,希望用更直观的方式呈现情绪。postprocess本身不支持 emoji,但你可以基于它的输出二次加工:

EMOJI_MAP = { "开心": "😄", "愤怒": "😠", "悲伤": "😢", "中性": "😐", "恐惧": "😨", "惊讶": "😮", "笑声": "😂", "掌声": "", "BGM": "🎵" } def add_emoji(clean_text): for zh, emoji in EMOJI_MAP.items(): clean_text = clean_text.replace(f"[{zh}]", f"{emoji}[{zh}]") return clean_text # 输入:"[开心] 你好 [笑声]" # 输出:"😄[开心] 你好 😂[笑声]"

优势:完全自由定制,适配不同团队偏好
提示:emoji 可能影响某些终端或数据库的编码兼容性,生产环境建议加 try-except 包裹

5. 常见问题与避坑指南

5.1 为什么我的清洗结果里还有<|xxx|>?是不是函数没生效?

90% 的情况是:你传给rich_transcription_postprocess的根本不是原始text字段。

检查你的代码是否这样写:

# ❌ 错误:传了整个 res 对象 clean = rich_transcription_postprocess(res) # res 是 list,不是 str # ❌ 错误:传了 res[0]["text"] 但 res[0] 为空 if res: clean = rich_transcription_postprocess(res[0]["text"]) # 正确 else: clean = "识别失败"

更稳妥的写法:

def safe_postprocess(res): if not res or len(res) == 0: return "无识别结果" try: raw_text = res[0].get("text", "") if not raw_text.strip(): return "识别结果为空" return rich_transcription_postprocess(raw_text) except Exception as e: return f"清洗出错:{str(e)}" clean_result = safe_postprocess(res)

5.2 清洗后中文和英文混排,空格不统一怎么办?

SenseVoiceSmall 输出中,中英文之间有时缺空格(如今天[开心]Hello),postprocess默认不修复这点。解决方案很简单,在清洗后加一次智能空格修复:

import re def fix_zh_en_spacing(text): # 中文后紧跟英文,加空格:今天Hello → 今天 Hello text = re.sub(r'([\u4e00-\u9fa5])([a-zA-Z])', r'\1 \2', text) # 英文后紧跟中文,加空格:Hello今天 → Hello 今天 text = re.sub(r'([a-zA-Z])([\u4e00-\u9fa5])', r'\1 \2', text) return text final = fix_zh_en_spacing(clean_text)

5.3 我想把[BGM]改成[背景音乐],但改了 TAG_MAP 没生效?

FunASR 的TAG_MAP是模块级常量,修改后需重启 Python 解释器才生效。开发调试时推荐用“运行时覆盖”方式:

from funasr.utils.postprocess_utils import rich_transcription_postprocess, TAG_MAP # 临时覆盖 TAG_MAP["BGM"] = "背景音乐" TAG_MAP["LAUGHTER"] = "笑场" clean = rich_transcription_postprocess("<|BGM|>音乐起<|LAUGHTER|>") # 输出:"[背景音乐] 音乐起 [笑场]"

6. 总结:清洗不是终点,而是新流程的起点

rich_transcription_postprocess看似只是一个小小的工具函数,但它实际承担着语音理解结果从“模型输出”到“业务可用”的最后一公里

  • 它不是万能的,但足够聪明——知道什么时候该留、该转、该删;
  • 它不替代你的业务逻辑,而是为你省去80%的字符串脏活;
  • 它开放且可扩展,所有规则都明明白白写在源码里,改起来毫不费力。

真正决定落地效果的,从来不是模型有多强,而是你如何把识别结果变成真正能用的东西。
清洗原始标签,不是为了让文字变好看,而是为了让人和机器都能高效读懂声音背后的信息。

下一次当你看到<|HAPPY|>这样的标记时,别急着删掉它——先想想:这个标签,对你的用户、你的系统、你的下一个功能,意味着什么。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/6 1:16:00

Qwen-Image-2512-ComfyUI实战:输入中文秒出高清图

Qwen-Image-2512-ComfyUI实战&#xff1a;输入中文秒出高清图 阿里通义千问团队最新发布的Qwen-Image-2512&#xff0c;是当前中文图像生成领域少有的“真正懂中文”的大模型。它不是简单地把中文翻译成英文再生成&#xff0c;而是原生支持中文字词结构、文化意象和语义逻辑—…

作者头像 李华
网站建设 2026/2/6 7:17:42

Hanime1观影助手:重新定义Android平台视频体验

Hanime1观影助手&#xff1a;重新定义Android平台视频体验 【免费下载链接】Hanime1Plugin Android插件(https://hanime1.me) (NSFW) 项目地址: https://gitcode.com/gh_mirrors/ha/Hanime1Plugin 同样的网络环境&#xff0c;同样的视频资源&#xff0c;为什么有人能享受…

作者头像 李华
网站建设 2026/2/5 1:59:05

免费开源!Qwen-Image-Edit-2511本地部署全流程

免费开源&#xff01;Qwen-Image-Edit-2511本地部署全流程 你是否试过用AI修图&#xff0c;结果人物脸型变了、衣服颜色跑偏、背景线条扭曲&#xff1f;或者想给产品图换材质&#xff0c;却反复生成出完全不像原图的“抽象派”版本&#xff1f;别急——Qwen-Image-Edit-2511来…

作者头像 李华
网站建设 2026/2/5 2:34:38

解锁硬件潜能:SMU Debug Tool让普通用户掌握专业级调试技术

解锁硬件潜能&#xff1a;SMU Debug Tool让普通用户掌握专业级调试技术 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: https…

作者头像 李华
网站建设 2026/2/7 3:15:03

SGLang部署踩坑记录:这些错误千万别犯

SGLang部署踩坑记录&#xff1a;这些错误千万别犯 SGLang-v0.5.6 镜像上线后&#xff0c;不少开发者在本地或生产环境部署时遇到了各种“意料之外但情理之中”的问题。有人卡在启动失败&#xff0c;有人调用超时却查不到原因&#xff0c;还有人明明配置了多卡却只跑在单卡上……

作者头像 李华
网站建设 2026/2/4 16:05:57

TMSpeech:让每个人都能轻松拥有AI语音转文字能力

TMSpeech&#xff1a;让每个人都能轻松拥有AI语音转文字能力 【免费下载链接】TMSpeech 腾讯会议摸鱼工具 项目地址: https://gitcode.com/gh_mirrors/tm/TMSpeech 你是否曾在远程会议中手忙脚乱地记录要点&#xff1f;是否在在线课程中因来不及笔记而错过关键知识点&am…

作者头像 李华