news 2026/4/22 21:11:28

语音情感可视化:SenseVoiceSmall ECharts图表展示案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
语音情感可视化:SenseVoiceSmall ECharts图表展示案例

语音情感可视化:SenseVoiceSmall ECharts图表展示案例

1. 为什么语音识别需要“看情绪”?

你有没有遇到过这样的情况:客服电话里对方说“好的,没问题”,语气却冷冰冰;会议录音中一句“这个方案很棒”,尾音微微上扬带着真诚的兴奋;短视频配音里“太可惜了”三个字,配上叹息和停顿,瞬间让观众心头一紧——文字记录不了这些,但声音记得

传统语音转文字(ASR)只关心“说了什么”,而真实沟通中,“怎么说”往往比“说什么”更重要。情绪是语言的隐形语法,掌声、笑声、背景音乐则是语境的无声注脚。当AI只能输出干巴巴的文字,它就错过了90%的沟通信息。

SenseVoiceSmall 正是为填补这一空白而生。它不是又一个“更高准确率”的ASR模型,而是一次对语音理解维度的升级:从“听清”走向“听懂”,从“转写”迈向“共情”。更关键的是,它把这种能力做进了轻量级模型里——不依赖大显存、不拖慢响应速度,真正能跑在日常开发环境中的情感感知引擎。

本文不讲论文公式,也不堆参数指标。我们直接动手,用一段真实音频,把模型识别出的情绪标签、声音事件,实时渲染成动态ECharts折线图与词云图。你会看到:开心的情绪如何在时间轴上起伏,掌声何时打断讲话,BGM怎样悄然铺满后半段……语音不再是一串文字,而是一幅可读、可感、可分析的声纹画卷。

2. SenseVoiceSmall 是什么?一句话说清

SenseVoiceSmall 是阿里巴巴达摩院开源的一款多语言富文本语音理解模型。名字里的“Small”不是功能缩水,而是指它在保持强大能力的同时,模型体积小、推理快、部署轻——4090D显卡上,1分钟音频秒级出结果。

它和你用过的普通语音识别工具,有本质区别:

  • 普通ASR:输入音频 → 输出文字(如:“今天天气真好”)
  • SenseVoiceSmall:输入音频 → 输出带结构的富文本(如:“<|HAPPY|>今天天气真好<|LAUGHTER|>”)

这个“富文本”,就是它真正的价值所在。它不是简单打标签,而是把语音中所有可感知的语义层都结构化呈现出来:

  • 语言层:自动识别中/英/日/韩/粤五种语言,支持自动检测
  • 情感层:识别 HAPPY(开心)、ANGRY(愤怒)、SAD(悲伤)、NEUTRAL(中性)等情绪状态
  • 事件层:检测 LAUGHTER(笑声)、APPLAUSE(掌声)、BGM(背景音乐)、CRY(哭声)、NOISE(噪音)等非语音事件

这些标签不是孤立存在的。它们按时间顺序嵌入识别结果,天然带有时间戳信息。这正是我们能做可视化图表的基础——每个情感标签,都对应着音频里确切的某几秒钟

镜像已预装 Gradio WebUI,开箱即用。但本文要带你走得更远:绕过界面点击,用 Python 直接调用模型 API,提取原始结构化结果,并用 ECharts 将其变成一张会呼吸的交互图表。

3. 从音频到图表:三步打通全流程

3.1 提取结构化情感与事件数据

Gradio 界面很友好,但它输出的是清洗后的富文本字符串(如“[开心]今天天气真好[笑声]”),这对可视化不够友好。我们需要的是原始结构化数据——包含时间戳、标签类型、置信度的字典列表。

幸运的是,model.generate()的返回结果里,藏着未被rich_transcription_postprocess处理的“原生数据”。我们只需稍作调整,就能拿到带时间信息的完整事件流:

# 从 model.generate() 原始输出中提取结构化事件 def extract_events_from_result(res): """ 从 SenseVoice 返回的原始结果中提取结构化事件 返回格式:[{"type": "HAPPY", "start": 2.3, "end": 3.1, "score": 0.92}, ...] """ if not res or len(res) == 0: return [] events = [] # res[0]["text"] 是富文本字符串,但真正的时间信息在 res[0]["segments"] 中 segments = res[0].get("segments", []) for seg in segments: text = seg.get("text", "") start = seg.get("start", 0.0) end = seg.get("end", 0.0) # 解析 text 中的标签,如 "<|HAPPY|>你好<|LAUGHTER|>" import re # 匹配 <|XXX|> 格式的标签 tags = re.findall(r"<\|(.*?)\|>", text) for tag in tags: # 过滤掉纯语言标签(如<|zh|>),只保留情感和事件 if tag in ["HAPPY", "ANGRY", "SAD", "NEUTRAL", "LAUGHTER", "APPLAUSE", "BGM", "CRY", "NOISE"]: events.append({ "type": tag, "start": start, "end": end, "score": seg.get("confidence", 0.8) }) return events # 调用示例(接在原有 generate 后) res = model.generate( input=audio_path, language=language, use_itn=True, batch_size_s=60, merge_vad=True, merge_length_s=15, ) events = extract_events_from_result(res) # ← 新增:提取结构化事件 print("识别到的情感与事件:", events)

运行后,你会得到类似这样的数据:

[ {"type": "HAPPY", "start": 1.2, "end": 2.5, "score": 0.94}, {"type": "LAUGHTER", "start": 3.1, "end": 3.8, "score": 0.87}, {"type": "BGM", "start": 15.0, "end": 58.2, "score": 0.91} ]

这才是可视化需要的“燃料”。

3.2 构建时间轴情感热力图

有了带时间戳的事件数据,我们就可以绘制最直观的图表:X轴为时间(秒),Y轴为事件类型,点的大小代表置信度,颜色区分情绪倾向

我们用 ECharts 的scatter图实现,代码简洁清晰:

<!-- echarts_emotion_timeline.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>SenseVoice 情感时间轴</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> <style>body { margin: 0; padding: 20px; font-family: "Segoe UI", sans-serif; }</style> </head> <body> <div id="main" style="width: 100%; height: 500px;"></div> <script type="text/javascript"> // 模拟从 Python 后端传来的数据(实际项目中用 AJAX 获取) const eventData = [ {"type": "HAPPY", "start": 1.2, "end": 2.5, "score": 0.94}, {"type": "LAUGHTER", "start": 3.1, "end": 3.8, "score": 0.87}, {"type": "ANGRY", "start": 8.5, "end": 9.2, "score": 0.76}, {"type": "BGM", "start": 15.0, "end": 58.2, "score": 0.91}, {"type": "APPLAUSE", "start": 59.1, "end": 60.3, "score": 0.82} ]; // 计算每个事件的中心时间点 const points = eventData.map(item => ({ name: item.type, value: [item.start + (item.end - item.start) / 2, item.type, item.score * 100], start: item.start, end: item.end, score: item.score })); // 情绪颜色映射(开心绿、愤怒红、悲伤蓝、中性灰) const colorMap = { "HAPPY": "#52c418", "ANGRY": "#f5222d", "SAD": "#1890ff", "NEUTRAL": "#bfbfbf", "LAUGHTER": "#faad14", "APPLAUSE": "#722ed1", "BGM": "#13c2c2", "CRY": "#eb2f96", "NOISE": "#d9d9d9" }; const chartDom = document.getElementById('main'); const myChart = echarts.init(chartDom); const option = { title: { text: '语音情感与事件时间分布', subtext: '横轴:时间(秒)| 纵轴:事件类型| 圆点大小:置信度' }, tooltip: { trigger: 'item', formatter: '{a}<br/>{b}: {c}%<br/>时段:{d} - {e}秒' }, grid: { left: '10%', right: '10%', bottom: '15%' }, xAxis: { type: 'value', name: '时间(秒)', min: 0, max: 65, splitLine: { show: true } }, yAxis: { type: 'category', data: ['HAPPY', 'ANGRY', 'SAD', 'NEUTRAL', 'LAUGHTER', 'APPLAUSE', 'BGM', 'CRY', 'NOISE'], name: '事件类型' }, series: [{ name: '情感/事件', type: 'scatter', symbolSize: function (val) { return val[2] * 2; // 置信度越高,点越大 }, itemStyle: { color: function(params) { return colorMap[params.value[1]] || '#ccc'; } }, data: points.map(p => [p.value[0], p.value[1], p.value[2]]) }], legend: { show: false } }; myChart.setOption(option); window.addEventListener('resize', () => { myChart.resize(); }); </script> </body> </html>

打开这个 HTML 文件,你会看到一张清晰的时间轴散点图:绿色圆点代表开心时刻,红色代表愤怒,青色长条是持续播放的背景音乐……所有信息一目了然。

3.3 生成动态情感词云图

除了时间分布,我们还关心整体情绪基调。哪个情绪出现最多?哪些事件最频繁?这时,词云图是最直观的选择。

我们用 Python 的wordcloud库生成静态词云,再用 ECharts 的wordCloud图实现动态交互效果(支持点击跳转到对应时间段):

# generate_wordcloud.py from wordcloud import WordCloud import matplotlib.pyplot as plt from collections import Counter import numpy as np def generate_emotion_wordcloud(events, output_path="emotion_wordcloud.png"): """根据事件列表生成情感词云""" # 统计各类型出现频次 types = [e["type"] for e in events] counter = Counter(types) # 中文支持需指定字体 wc = WordCloud( font_path="/System/Library/Fonts/PingFang.ttc", # macOS # font_path="simhei.ttf", # Windows width=800, height=400, background_color='white', colormap='tab10', max_words=20 ) # 生成词云 wc.generate_from_frequencies(counter) plt.figure(figsize=(10, 5)) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.title("语音情感与事件分布词云", fontsize=16, pad=20) plt.savefig(output_path, bbox_inches='tight', dpi=150) print(f"词云图已保存至:{output_path}") # 调用 generate_emotion_wordcloud(events)

生成的 PNG 图可直接嵌入报告。但若想更进一步,我们用 ECharts 实现可交互词云:

// 在 echarts_emotion_timeline.html 中追加 const wordCloudData = [ {name: 'HAPPY', value: 12, itemStyle: {color: '#52c418'}}, {name: 'LAUGHTER', value: 8, itemStyle: {color: '#faad14'}}, {name: 'BGM', value: 5, itemStyle: {color: '#13c2c2'}}, {name: 'ANGRY', value: 3, itemStyle: {color: '#f5222d'}}, {name: 'APPLAUSE', value: 2, itemStyle: {color: '#722ed1'}} ]; // 添加词云图配置(在原有 script 标签内) const wordCloudChart = echarts.init(document.getElementById('wordcloud')); wordCloudChart.setOption({ title: { text: '整体情感与事件热度', textStyle: { fontSize: 16 } }, tooltip: { show: true }, series: [{ type: 'wordCloud', gridSize: 2, sizeRange: [12, 50], rotationRange: [-45, 45], shape: 'pentagon', width: '100%', height: '100%', data: wordCloudData }] });

这张词云不仅告诉你“开心最多”,还能点击“LAUGHTER”立刻高亮所有笑声发生的时间段——数据、图表、原始音频,三位一体

4. 实战技巧:让可视化更实用、更稳定

4.1 处理长音频的分段策略

SenseVoiceSmall 对单次输入时长有限制(通常建议≤60秒)。面对10分钟会议录音,不能一股脑喂给模型。我们采用“滑动窗口+重叠合并”策略:

import numpy as np def split_audio_by_vad(audio_path, max_duration=45.0, overlap=5.0): """ 基于 VAD(语音活动检测)智能分段,避免在句子中间切断 """ # 使用 funasr 内置 VAD 工具 from funasr.utils.vad_utils import SileroVAD vad = SileroVAD() speech_timestamps = vad(audio_path, return_seconds=True) # 合并相邻短片段,生成长度约 max_duration 的段 segments = [] current_start = 0.0 for i, ts in enumerate(speech_timestamps): if i == 0: current_start = ts['start'] continue duration = ts['end'] - current_start if duration >= max_duration: segments.append({ "start": current_start, "end": ts['end'], "duration": duration }) current_start = ts['start'] - overlap # 重叠5秒,避免丢失上下文 return segments # 使用 segments = split_audio_by_vad("meeting.wav") for seg in segments: print(f"处理片段:{seg['start']:.1f}s - {seg['end']:.1f}s ({seg['duration']:.1f}s)")

这样分段,既保证模型高效运行,又让情感分析结果在时间轴上自然连贯。

4.2 情感强度分级可视化

原始模型输出的score是0~1之间的置信度,但“0.85的开心”和“0.92的开心”对业务意义不同。我们可以将其映射为三级强度:

def map_emotion_intensity(score): """将置信度映射为强度等级""" if score >= 0.9: return "强烈" elif score >= 0.75: return "中等" else: return "轻微" # 在图表中用不同形状表示强度 symbolMap = { "强烈": "diamond", "中等": "circle", "轻微": "rect" } // ECharts 配置中 symbol: function(params) { const intensity = map_emotion_intensity(params.data.score); return symbolMap[intensity]; }

用户一眼就能分辨:哪个开心是发自肺腑,哪个只是礼貌性微笑。

4.3 一键导出分析报告

最后,把所有图表打包成一份可分享的 HTML 报告:

def generate_report_html(events, audio_filename, output_html="report.html"): """生成含时间轴图、词云图、统计摘要的完整HTML报告""" # 统计摘要 total_duration = max(e["end"] for e in events) if events else 0 type_count = {} for e in events: type_count[e["type"]] = type_count.get(e["type"], 0) + 1 # 渲染 HTML 模板(此处省略 Jinja2 模板细节) report_html = f""" <!DOCTYPE html> <html><head><title>语音分析报告 - {audio_filename}</title> <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script> </head><body> <h1> {audio_filename} 情感与事件分析报告</h1> <p><strong>总时长:</strong>{total_duration:.1f} 秒</p> <p><strong>识别事件总数:</strong>{len(events)}</p> <h2> 情感时间轴</h2> <div id="timeline" style="height:400px;"></div> <h2>☁ 情感热度词云</h2> <div id="wordcloud" style="height:400px;"></div> <script> // 这里插入前面定义的 timeline 和 wordcloud 初始化代码 // (实际使用时,将 JS 逻辑注入此模板) </script> </body></html> """ with open(output_html, "w", encoding="utf-8") as f: f.write(report_html) print(f"分析报告已生成:{output_html}") # 调用 generate_report_html(events, "customer_call.wav")

双击打开report.html,一份专业、直观、无需安装任何软件的语音分析报告就呈现在眼前。

5. 总结:让语音理解真正“看得见”

回看整个过程,我们没有发明新模型,也没有训练新参数。我们只是做了一件简单却关键的事:把 SenseVoiceSmall 模型输出的结构化语音理解结果,用人类最易感知的方式——图表——重新表达出来

  • 时间轴散点图,让情绪起伏变成可视的波形;
  • 动态词云图,让抽象频次变成具象的视觉权重;
  • 强度分级与分段策略,让技术细节服务于真实业务判断。

这背后体现的,是一种务实的技术观:AI的价值不在于参数多大、指标多高,而在于它能否被一线产品、运营、客服人员真正“看见”、真正“用上”。当销售主管点开一份会议录音的分析报告,3秒内就看到客户在哪个节点露出明显不满(ANGRY),在哪个产品介绍后传来真诚笑声(LAUGHTER),他获得的就不再是“一段音频”,而是一份可行动的洞察。

SenseVoiceSmall 的轻量与富文本特性,恰好为这种“最后一公里”的落地提供了理想载体。它不高不可攀,不重不可及,就在你的笔记本电脑、开发服务器上安静运行,等待你用一行代码、一张图表,把它沉睡的能力唤醒。

下一次,当你再听到一段语音,别只想着“它说了什么”。试着问一句:“它的情绪是什么?它的背景在发生什么?如果画成图,会是什么样子?”

答案,就藏在 SenseVoiceSmall 为你标记的每一个<|HAPPY|>里。


获取更多AI镜像

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

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

AI工程实践指南:《AI Engineering》系统学习与落地方法论

AI工程实践指南&#xff1a;《AI Engineering》系统学习与落地方法论 【免费下载链接】aie-book [WIP] Resources for AI engineers. Also contains supporting materials for the book AI Engineering (Chip Huyen, 2025) 项目地址: https://gitcode.com/GitHub_Trending/ai…

作者头像 李华
网站建设 2026/4/22 21:30:38

服务器管理工具XPipe:提升远程连接与运维效率的全栈解决方案

服务器管理工具XPipe&#xff1a;提升远程连接与运维效率的全栈解决方案 【免费下载链接】xpipe Your entire server infrastructure at your fingertips 项目地址: https://gitcode.com/GitHub_Trending/xp/xpipe 在现代IT架构中&#xff0c;服务器集群的管理复杂度随着…

作者头像 李华
网站建设 2026/4/23 11:35:57

YOLO11模型推理实战,效果远超预期

YOLO11模型推理实战&#xff0c;效果远超预期 你是否试过刚跑完第一次推理&#xff0c;就忍不住截图发给同事&#xff1a;“这分割边界也太干净了吧&#xff1f;” 不是幻觉——YOLO11在实例分割任务上的表现&#xff0c;确实刷新了我们对实时精度平衡的认知。它不只快&#x…

作者头像 李华
网站建设 2026/4/23 13:16:07

ASP.NET Core面试精讲系列五

目录 71. 模型绑定如何工作&#xff1a;会用哪些数据源 72. 绑定复杂类型 vs 简单类型 73. 自定义模型绑定器 74. 多个绑定源特性&#xff08;[FromBody]、[FromQuery] 等&#xff09; 75. 模型验证&#xff1a;数据注解 76. 服务端验证与客户端验证&#xff08;非侵入式…

作者头像 李华
网站建设 2026/4/23 16:16:52

ASP.NET Core面试精讲系列四

目录 51. 什么是 REST&#xff1f;如何在 ASP.NET Core 设计 RESTful API REST 定义 RESTful API 设计原则 总结 52. [ApiController] 特性及其优势 主要优势 示例 总结 53. Web API 路由约定&#xff08;特性路由、路由模板&#xff09; 特性路由&#xff08;推荐方…

作者头像 李华
网站建设 2026/4/23 14:49:22

亲测TurboDiffusion图生视频效果,1.9秒生成超惊艳

亲测TurboDiffusion图生视频效果&#xff0c;1.9秒生成超惊艳 1. 这不是科幻&#xff0c;是已经跑在你显卡上的现实 你有没有试过盯着一张静态图片&#xff0c;心里想着“要是它能动起来该多好”&#xff1f; 上周我上传了一张自己拍的咖啡馆窗景照片——木桌、手冲壶、阳光斜…

作者头像 李华