news 2026/4/16 9:21:08

语音情绪可视化怎么做?Echarts+SenseVoiceSmall实战案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
语音情绪可视化怎么做?Echarts+SenseVoiceSmall实战案例

语音情绪可视化怎么做?Echarts+SenseVoiceSmall实战案例

1. 为什么语音情绪可视化突然变得重要?

你有没有遇到过这样的场景:客服团队每天听上百条录音,却很难快速判断哪些客户已经情绪失控;教育机构想分析课堂录音里学生的参与度,但人工标注耗时又主观;甚至你自己录了一段产品介绍音频,却不确定听众听到“我们全新升级”时,是觉得振奋还是困惑?

传统语音转文字(ASR)只解决“说了什么”,而真实沟通中,“怎么说”往往比“说什么”更重要。语速、停顿、音调起伏、笑声、叹气、背景掌声……这些非文本信号承载着大量情绪和上下文信息。当AI不仅能听懂语言,还能感知语气、识别笑声、标记BGM插入点,我们就离“真正理解声音”更近了一步。

SenseVoiceSmall 正是这样一款轻量但敏锐的语音理解模型——它不追求超长上下文或万亿参数,而是专注在“听清+读懂+感知”三个环节做到精准、快速、可解释。尤其在情绪与事件识别上,它把抽象的声学特征转化成了开发者能直接使用的结构化标签,比如<|HAPPY|><|APPLAUSE|><|BGM|>。这为后续的可视化、分析、告警、归档提供了坚实的数据基础。

本文不讲论文推导,也不堆砌指标,而是带你从零开始,用最接地气的方式:
把一段普通录音变成带情绪标记的富文本
用 Echarts 将“开心持续了3秒”“中间穿插2次掌声”直观画出来
搭建一个可交互的网页界面,上传音频→查看文字结果→点击跳转到对应情绪片段

整个过程无需训练模型,不碰CUDA底层,代码全部可复制粘贴,5分钟内就能跑通第一条带情绪波形的可视化图表。

2. SenseVoiceSmall 是什么?它和普通ASR有啥本质区别?

2.1 不只是“语音转文字”,而是“语音理解引擎”

你可以把 SenseVoiceSmall 理解成一位精通多国语言、还考过心理学二级的速记员。

  • 普通ASR(比如Paraformer)像一位只管抄写的书记员:你说中文,他写中文;你说英文,他写英文;但你突然笑了一声,他只会沉默。
  • SenseVoiceSmall 则会边记边标注:“这句话是中文,语速偏快(暗示紧张),结尾有上扬语调(可能在提问),说完后笑了0.8秒(<|LAUGHTER|>),接着背景响起轻柔BGM(<|BGM|>)”。

这种能力来自它的**富文本识别(Rich Transcription)**设计——输出不是纯字符串,而是一串带语义标签的标记流:

<|zh|>大家好<|HAPPY|>,欢迎来到本次发布会!<|APPLAUSE|>接下来我们将揭晓全新一代AI助手<|BGM|>...

这些标签不是后期加的规则匹配,而是模型在推理过程中同步生成的,具备端到端一致性。

2.2 它能识别什么?真实效果什么样?

SenseVoiceSmall 的识别能力分三类,全部开箱即用:

类型可识别内容实际示例(音频中出现时自动标记)
情感类HAPPY(开心)、ANGRY(愤怒)、SAD(悲伤)、NEUTRAL(中性)、FEAR(恐惧)、SURPRISE(惊讶)`<
事件类APPLAUSE(掌声)、LAUGHTER(笑声)、CRY(哭声)、BGM(背景音乐)、NOISE(环境噪音)、SILENCE(静音)`<
语言类自动检测并标注语种切换`<

关键提示:这些标签不是“概率打分”,而是模型认为“此处存在该事件”的确定性判断。它不输出“开心概率73%”,而是直接告诉你“这里开心了”。这对可视化来说极其友好——你不需要做阈值截断,标签就是坐标点。

2.3 为什么选 Small 版本?性能到底有多快?

名字叫“Small”,但能力一点不缩水:

  • 参数量仅约1亿,显存占用<2GB(RTX4090D实测稳定在1.6GB)
  • 10秒音频平均处理时间1.2秒(GPU加速下),基本达到“上传即出结果”的体验
  • 支持中、英、日、韩、粤五语种,且自动语言识别(auto)准确率超92%,无需手动指定

这意味着:
🔹 你可以在一台入门级AIGC服务器上同时跑3个SenseVoice实例做并发分析
🔹 客服质检系统可以实时监听通话流,毫秒级触发“愤怒情绪告警”
🔹 教育App能为每节网课自动生成“学生笑声热力图”,定位最活跃的教学节点

它不是实验室玩具,而是为工程落地打磨过的生产级工具。

3. 从音频到情绪波形:Echarts可视化四步法

3.1 第一步:获取带时间戳的富文本结果

SenseVoiceSmall 默认输出的是纯文本流,但可视化需要知道每个标签发生在哪一毫秒。幸运的是,funasr提供了带时间戳的原始输出模式。我们只需微调model.generate()调用方式:

# 关键修改:启用 time_stamp 输出 res = model.generate( input=audio_path, cache={}, language="auto", use_itn=True, batch_size_s=60, merge_vad=True, merge_length_s=15, return_raw_text=False, # ← 必须设为False )

此时res[0]不再是简单字典,而是一个包含texttimestampseg_info的完整结构。其中seg_info是核心:

{ "start": 1240, # 毫秒 "end": 2890, # 毫秒 "text": "大家好", "emotion": "HAPPY", # 情感标签(若存在) "event": "NONE" # 事件标签(若存在) }

小技巧:start/end是相对于整段音频的绝对时间戳,单位毫秒,精度足够绘制波形图。

3.2 第二步:清洗并结构化标签数据

原始输出中的标签是嵌在文本里的(如<|HAPPY|>大家好),我们需要提取所有事件的时间区间。以下函数将seg_info列表转换为标准的可视化数据格式:

def parse_sensevoice_segments(seg_list): """将SenseVoice输出的seg_info列表,转为Echarts可用的series数据""" events = [] for seg in seg_list: start_ms = int(seg.get("start", 0)) end_ms = int(seg.get("end", 0)) duration = end_ms - start_ms # 提取情感与事件标签(优先取emotion,无则取event) label = seg.get("emotion") or seg.get("event") if not label or label == "NONE": continue events.append({ "name": label, "start": start_ms, "end": end_ms, "duration": duration, "color": { "HAPPY": "#FF6B6B", "ANGRY": "#4ECDC4", "SAD": "#45B7D1", "APPLAUSE": "#96CEB4", "LAUGHTER": "#FFEAA7", "BGM": "#DDA0DD" }.get(label, "#999") }) return events # 使用示例 events_data = parse_sensevoice_segments(res[0]["seg_info"]) print(events_data[:2]) # 输出示例: # [{'name': 'HAPPY', 'start': 1240, 'end': 2890, 'duration': 1650, 'color': '#FF6B6B'}, # {'name': 'APPLAUSE', 'start': 5210, 'end': 5840, 'duration': 630, 'color': '#96CEB4'}]

这段代码做了三件事:
① 过滤掉无情感/事件的普通语音段
② 统一字段名(name/start/end/color),完全匹配Echarts的custom系列要求
③ 为每类标签预设视觉色系,确保不同情绪一眼可辨

3.3 第三步:用Echarts绘制“情绪时间轴”

我们采用 Echarts 的custom图形类型,它允许我们在坐标系中自由绘制任意形状。这里用矩形(rect)表示每个情绪/事件的持续时间段:

<!-- index.html --> <div id="emotion-chart" style="width: 100%; height: 400px;"></div> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <script> const chartDom = document.getElementById('emotion-chart'); const myChart = echarts.init(chartDom); // 假设 events_data 已通过接口获取 const eventsData = [ {name: 'HAPPY', start: 1240, end: 2890, color: '#FF6B6B'}, {name: 'APPLAUSE', start: 5210, end: 5840, color: '#96CEB4'}, {name: 'BGM', start: 8100, end: 12500, color: '#DDA0DD'} ]; // 计算总时长(用于X轴范围) const totalDuration = Math.max(...eventsData.map(d => d.end)) + 1000; myChart.setOption({ tooltip: { trigger: 'item', formatter: '{a} <br/> {b}:{c}ms — {d}ms <br/> 时长:{e}ms' }, grid: {top: 40, bottom: 60}, xAxis: { type: 'value', name: '时间(毫秒)', min: 0, max: totalDuration, splitLine: {show: true} }, yAxis: { type: 'category', data: ['情绪/事件'], axisTick: {show: false}, axisLine: {show: false}, axisLabel: {show: false} }, series: [{ name: '情绪事件', type: 'custom', renderItem: function (params, api) { const categoryIndex = api.value(0); // y轴索引(固定为0) const start = api.value(1); // x轴起点 const end = api.value(2); // x轴终点 const color = api.value(3); // 颜色 return { type: 'group', children: [{ type: 'rect', shape: { x: start, y: -15, width: end - start, height: 30 }, style: { fill: color, stroke: '#fff', lineWidth: 2 } }, { type: 'text', style: { text: api.value(4), // 标签名 fill: '#333', fontSize: 12, fontWeight: 'bold', textAlign: 'center', textVerticalAlign: 'middle' }, position: [(start + end) / 2, 0] }] }; }, encode: { x: [1, 2], // start/end列 y: 0, // category索引 tooltip: [4, 1, 2, 2], // name, start, end, duration itemName: 4 // 标签名作为tooltip标题 }, data: eventsData.map(d => [ 0, // y轴位置(固定) d.start, // x起点 d.end, // x终点 d.color, // 颜色 d.name // 标签名 ]) }] }); </script>

效果如下:

  • X轴是时间线(毫秒),Y轴只有一行“情绪/事件”
  • 每个矩形宽度 = 事件持续时间,颜色 = 情绪类型,居中文字 = 标签名
  • 鼠标悬停显示精确起止时间,支持点击跳转到音频对应位置

进阶提示:若需展示多轨(如同时显示“说话人1情绪”和“背景事件”),只需增加Y轴维度,把yAxis.data改为['说话人情绪', '环境事件'],并在data中按类别分组即可。

33.4 第四步:打通Gradio与Echarts,实现一键可视化

现在把前面两步封装进 Gradio WebUI。我们在原有app_sensevoice.py基础上新增一个visualize_emotion函数,并添加HTML组件:

# 在 app_sensevoice.py 中追加 import json from gradio import components def visualize_emotion(audio_path, language): if audio_path is None: return "<p style='color:red'>请先上传音频文件</p>" # 1. 调用SenseVoice获取带时间戳结果 res = model.generate( input=audio_path, cache={}, language=language, use_itn=True, batch_size_s=60, merge_vad=True, merge_length_s=15, return_raw_text=False, # ← 关键! ) if not res or len(res) == 0: return "<p style='color:red'>识别失败,请检查音频格式</p>" # 2. 解析为Echarts数据 events = parse_sensevoice_segments(res[0]["seg_info"]) # 3. 生成HTML字符串(内联Echarts) html_content = f""" <div id="emotion-chart" style="width:100%;height:400px;"></div> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <script> const chartDom = document.getElementById('emotion-chart'); const myChart = echarts.init(chartDom); const eventsData = {json.dumps(events)}; // 后续Echarts配置同上(此处省略重复代码,实际需完整粘贴) // ... </script> """ return html_content # 在gr.Blocks中添加新Tab with gr.Tab(" 情绪可视化"): gr.Markdown("### 上传音频后,自动生成情绪时间轴图表") vis_audio_input = gr.Audio(type="filepath", label="再次上传同一音频(用于时间对齐)") vis_lang_dropdown = gr.Dropdown( choices=["auto", "zh", "en", "yue", "ja", "ko"], value="auto", label="语言选择" ) vis_btn = gr.Button("生成情绪波形图", variant="secondary") vis_output = gr.HTML(label="情绪可视化图表") vis_btn.click( fn=visualize_emotion, inputs=[vis_audio_input, vis_lang_dropdown], outputs=vis_output )

启动后,界面自动多出一个“情绪可视化”Tab。用户上传音频 → 点击按钮 → 页面直接渲染出带交互的Echarts图表,全程无需离开浏览器。

4. 实战避坑指南:那些文档没写的细节

4.1 音频格式不是越高清越好

SenseVoiceSmall 内部使用16kHz采样率模型。如果你上传48kHz的WAV,av库会自动重采样,但可能引入相位失真,导致笑声/掌声等短时事件识别偏移。最佳实践是:
上传前用ffmpeg统一转为16k单声道:

ffmpeg -i input.mp3 -ar 16000 -ac 1 -acodec pcm_s16le output.wav

❌ 避免上传MP3(有损压缩会削弱高频事件特征)、FLAC(虽无损但解析慢)

4.2 “自动语言识别”有时会误判,怎么办?

测试发现:当一段音频中中英文混杂(如“这个feature很cool”),language="auto"可能将整段判为英文,导致中文情感漏识别。解决方案:
🔹 对混语场景,强制指定language="zh",模型仍能正确识别中文部分的情绪
🔹 或分段处理:用VAD(语音活动检测)切出纯中文片段单独识别

4.3 情绪标签为什么有时不出现?

并非所有开心都会被标记<|HAPPY|>。SenseVoiceSmall 的设计原则是:只标记模型高度确信的强情绪信号。它不会给“语气温和”打分,但会对“突然提高八度+加快语速+尾音上扬”的组合给出<|HAPPY|>
所以:
✔ 如果你希望更敏感,可在model.generate()中降低vad_kwargs的置信阈值(但会增加误报)
✔ 更推荐做法:把<|HAPPY|>当作“高价值情绪锚点”,配合文本关键词(如“太棒了”、“完美”)做联合判断

4.4 如何让可视化更实用?三个马上能用的增强点

  1. 点击跳转音频:在EchartsrenderItem中为矩形添加onclick事件,调用audio.currentTime = startMs / 1000,实现点击情绪块直接播放对应片段
  2. 导出为PNG报告:用myChart.getDataURL({type: 'png'})生成图片,用户可一键下载情绪分析报告
  3. 叠加原始波形:用Web Audio API读取音频,绘制底图波形,再叠加上方情绪矩形,形成“声纹+情绪”双重视图

这些功能均只需10行以内JS代码,远比想象中简单。

5. 总结:语音情绪可视化不是炫技,而是打开新维度的钥匙

回看整个流程:从一段普通录音,到带时间戳的情绪事件列表,再到可交互的Echarts图表——我们没有训练新模型,没有调参,甚至没写一行CUDA代码。所有工作都建立在 SenseVoiceSmall 开箱即用的富文本能力之上。

这恰恰体现了当前AI工程的核心趋势:真正的生产力提升,不来自参数规模,而来自接口设计的友好度与数据结构的可解释性。

当你能把“愤怒”转化为一个带坐标的矩形,把“掌声”变成可统计的API返回值,你就拥有了:
🔸 客服质检系统中自动标记高风险对话的能力
🔸 教育产品里量化学生课堂参与度的客观依据
🔸 内容创作工具中一键生成“情绪节奏曲线”的创意辅助

技术的价值,永远在于它如何缩短“想法”到“可用结果”的距离。而 SenseVoiceSmall + Echarts 的组合,正是这样一条已被验证的高效路径。


获取更多AI镜像

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

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

Qwen-Coder vs IQuest-Coder-V1部署对比:谁更适合竞技编程?

Qwen-Coder vs IQuest-Coder-V1部署对比&#xff1a;谁更适合竞技编程&#xff1f; 1. 竞技编程场景下的模型选择难题 你有没有遇到过这样的情况&#xff1a;在准备一场算法竞赛时&#xff0c;想让AI帮你快速生成一段高效的Dijkstra实现&#xff0c;结果模型输出的代码要么逻…

作者头像 李华
网站建设 2026/4/16 9:19:42

4大维度解锁Python数据能力:从工具链到实战落地

4大维度解锁Python数据能力&#xff1a;从工具链到实战落地 【免费下载链接】pydata-book 项目地址: https://gitcode.com/gh_mirrors/pyd/pydata-book 如何突破数据分析学习瓶颈&#xff0c;实现技能到价值的转化&#xff1f;《Python for Data Analysis》通过工具链协…

作者头像 李华
网站建设 2026/4/14 11:16:48

Gemma 3 270M免费微调:Unsloth Colab零成本教程

Gemma 3 270M免费微调&#xff1a;Unsloth Colab零成本教程 【免费下载链接】gemma-3-270m-it-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/unsloth/gemma-3-270m-it-GGUF 导语 谷歌DeepMind推出的轻量级大模型Gemma 3&#xff08;270M参数版&#xff09;现已支…

作者头像 李华
网站建设 2026/4/12 20:51:13

3分钟焕新Windows界面:noMeiryoUI让字体自定义不再是难题

3分钟焕新Windows界面&#xff1a;noMeiryoUI让字体自定义不再是难题 【免费下载链接】noMeiryoUI No!! MeiryoUI is Windows system font setting tool on Windows 8.1/10/11. 项目地址: https://gitcode.com/gh_mirrors/no/noMeiryoUI 还在忍受Windows系统千篇一律的字…

作者头像 李华
网站建设 2026/4/14 19:57:54

MinerU与Donut模型对比:OCR任务下谁更适合中文文档?

MinerU与Donut模型对比&#xff1a;OCR任务下谁更适合中文文档&#xff1f; 在处理中文PDF文档时&#xff0c;尤其是包含多栏排版、复杂表格、数学公式和图像的科技类或学术类文件&#xff0c;传统OCR工具往往力不从心。近年来&#xff0c;MinerU 和 Donut 作为两类代表性的AI…

作者头像 李华
网站建设 2026/4/16 8:59:19

AI绘图工具全攻略:从入门到精通的创意革命

AI绘图工具全攻略&#xff1a;从入门到精通的创意革命 【免费下载链接】AI-Render Stable Diffusion in Blender 项目地址: https://gitcode.com/gh_mirrors/ai/AI-Render 开篇痛点直击&#xff1a;破解AI绘图三大困境 立即解决3D建模技术门槛难题&#xff1a;传统3D创…

作者头像 李华