news 2026/1/31 17:07:07

语音情感数据可视化:结合SenseVoiceSmall输出生成图表教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
语音情感数据可视化:结合SenseVoiceSmall输出生成图表教程

语音情感数据可视化:结合SenseVoiceSmall输出生成图表教程

1. 为什么需要把语音情感“画出来”

你有没有试过听完一段客服录音,心里觉得“这人明显不耐烦”,但翻来覆去听三遍,还是没法准确告诉同事“愤怒情绪持续了27秒,中间夹杂两次短促笑声”?
这不是你观察力不够——而是原始语音识别结果太“扁平”:它只给你一行文字,比如:

<|HAPPY|>您好欢迎光临!<|APPLAUSE|><|SAD|>抱歉这个活动已经结束了...

这些标签藏在文本里,像散落的珠子,串不起来,也看不见分布规律。
而真实业务中,你需要的是:
某段3分钟会议录音里,开心、中性、愤怒分别占多少比例?
客服对话中,客户一开口就带愤怒情绪的占比有多高?
笑声和掌声集中在哪些时间点爆发?是否和产品介绍环节重合?

这就是本教程要解决的问题:不只让模型“听懂”语音,更要让它“说出”的情感变成一眼能看懂的图表
我们不用写复杂后端,不碰数据库,只用几行Python+现成的SenseVoiceSmall输出,就能把语音情感数据变成柱状图、时间热力图、词云图——真正实现“听得到,更看得清”。

2. 先跑通基础:从WebUI拿到带情感标签的原始结果

2.1 确认环境已就绪

你不需要从零安装所有依赖。本镜像已预装:

  • Python 3.11 + PyTorch 2.5(GPU加速已启用)
  • funasr(v1.1.0+)、modelscopegradioavffmpeg
  • SenseVoiceSmall 模型权重(自动从ModelScope下载)

只需验证Gradio服务是否正常启动。打开终端,输入:

nvidia-smi | grep "python" # 查看GPU是否被占用 ps aux | grep "app_sensevoice.py" # 查看服务是否在运行

如果没看到进程,执行:

python app_sensevoice.py

稍等几秒,终端会显示类似:

Running on local URL: http://0.0.0.0:6006

注意:镜像默认绑定0.0.0.0:6006,但平台安全策略限制外网直连。请按文档说明配置SSH隧道,在本地浏览器访问http://127.0.0.1:6006

2.2 上传一段测试音频,获取结构化输出

准备一个10–30秒的音频(MP3/WAV格式,采样率16kHz最佳)。推荐使用以下两类素材:

  • 自录对话:用手机录一段自己说“今天天气真好!”+“啊?什么?我没听清!”+“哈哈,开个玩笑~”
  • 公开语料:Common Voice中文数据集中任意片段

上传后点击“开始 AI 识别”,你会看到类似这样的结果:

<|HAPPY|>今天天气真好!<|NEUTRAL|>啊?什么?我没听清!<|HAPPY|>哈哈,开个玩笑~

关键确认点:

  • 结果中明确出现<|HAPPY|><|NEUTRAL|><|ANGRY|>等标签(不是纯文字)
  • 标签与文字紧密嵌套,没有丢失或错位
  • 如果结果全是<|NEUTRAL|>,尝试切换语言为autozh,避免自动识别偏差

这个带标签的字符串,就是我们后续可视化的唯一数据源——它轻量、结构清晰、无需额外标注。

3. 解析情感标签:用正则提取,不依赖模型内部API

3.1 为什么不用模型自带的JSON输出?

SenseVoiceSmall 的model.generate()默认返回字典列表,例如:

[{"text": "<|HAPPY|>你好<|LAUGHTER|>", "timestamp": [0, 1200]}]

但注意:timestamp字段在当前版本中不稳定,有时为空,有时单位不统一(毫秒/帧数),直接用于时间轴绘图风险高。
<|TAG|>格式是模型稳定输出的富文本标准,解析它更可靠、更轻量、小白也能看懂。

3.2 三行代码提取全部情感事件

新建一个parse_emotion.py文件,粘贴以下代码(无需安装新库):

import re def extract_emotions(text): """ 从SenseVoice输出中提取所有情感/事件标签及其位置 返回:[{"tag": "HAPPY", "start_pos": 0, "end_pos": 12}, ...] """ pattern = r"<\|(\w+)\|>" matches = [] for match in re.finditer(pattern, text): tag = match.group(1) start_pos = match.start() end_pos = match.end() matches.append({ "tag": tag, "start_pos": start_pos, "end_pos": end_pos }) return matches # 示例:用WebUI刚得到的结果测试 raw_output = "<|HAPPY|>今天天气真好!<|NEUTRAL|>啊?什么?我没听清!<|HAPPY|>哈哈,开个玩笑~" events = extract_emotions(raw_output) print(events)

运行后输出:

[ {'tag': 'HAPPY', 'start_pos': 0, 'end_pos': 12}, {'tag': 'NEUTRAL', 'start_pos': 24, 'end_pos': 38}, {'tag': 'HAPPY', 'start_pos': 50, 'end_pos': 62} ]

小技巧:start_posend_pos是字符位置,不是时间戳。但它能反映事件发生的相对顺序和密度——比如连续两个<|HAPPY|>间隔很近,说明情绪高涨;中间隔了很长文字,说明情绪有缓冲。这对分析用户情绪节奏已足够。

4. 生成三类实用图表:零代码也能看懂情感分布

我们用最精简的matplotlib+seaborn绘图(镜像已预装),不引入复杂框架,确保每张图都能在30秒内跑出。

4.1 图表一:情感类型占比饼图(一眼看清主情绪)

适用场景:快速判断整段音频的情绪基调(如客服质检中“愤怒占比超30%需复盘”)

import matplotlib.pyplot as plt def plot_emotion_pie(events, title="语音情感分布"): if not events: print(" 未检测到任何情感标签,请检查输入文本") return # 统计各标签出现次数 from collections import Counter tags = [e["tag"] for e in events] counter = Counter(tags) # 绘制饼图 plt.figure(figsize=(6, 6)) plt.pie( counter.values(), labels=counter.keys(), autopct='%1.1f%%', startangle=90, colors=plt.cm.Set3(range(len(counter))) ) plt.title(title, fontsize=14, pad=20) plt.tight_layout() plt.savefig("emotion_pie.png", dpi=150, bbox_inches='tight') plt.show() # 调用示例 plot_emotion_pie(events)

生成效果:

  • 一个清晰饼图,标出 HAPPY 占 66.7%,NEUTRAL 占 33.3%
  • 颜色区分明显(绿色=开心,灰色=中性,红色=愤怒)
  • 保存为emotion_pie.png,可直接插入报告

优势:不依赖音频时长,纯文本统计,结果稳定。

4.2 图表二:情感时间热力图(定位情绪爆发点)

适用场景:找出对话中情绪转折时刻(如“客户前10秒平静,第15秒突然提高音量并愤怒”)

虽然无精确时间戳,但我们用标签在文本中的位置比例模拟时间轴:

import numpy as np import seaborn as sns def plot_emotion_heatmap(events, text_length=100, title="情感分布热力图"): if not events: return # 将文本长度归一化为100单位(模拟100秒) # 每个标签的位置映射到0-100区间 positions = [] for e in events: # 用标签起始位置 / 总文本长度 * 100 pos = (e["start_pos"] / text_length) * 100 positions.append(pos) # 创建热力图数据:横轴为时间(0-100),纵轴为情感类型 unique_tags = sorted(list(set([e["tag"] for e in events]))) tag_to_idx = {tag: i for i, tag in enumerate(unique_tags)} # 初始化矩阵:100个时间点 × N种情感 heatmap_data = np.zeros((len(unique_tags), 100)) for pos in positions: time_bin = int(min(99, max(0, pos))) # 限制在0-99 # 找到对应情感的行 for e in events: if e["start_pos"] == int(pos * text_length / 100): tag_idx = tag_to_idx.get(e["tag"], 0) heatmap_data[tag_idx, time_bin] += 1 # 绘制热力图 plt.figure(figsize=(10, 4)) sns.heatmap( heatmap_data, xticklabels=10, yticklabels=unique_tags, cmap="YlOrRd", cbar_kws={'label': '事件频次'} ) plt.xlabel("模拟时间轴(0-100)") plt.ylabel("情感类型") plt.title(title, fontsize=14, pad=20) plt.tight_layout() plt.savefig("emotion_heatmap.png", dpi=150, bbox_inches='tight') plt.show() # 调用(传入原始文本长度,这里用示例文本长度) plot_emotion_heatmap(events, text_length=len(raw_output))

生成效果:

  • 横轴0–100代表文本从头到尾的相对位置
  • 纵轴列出所有检测到的情感类型
  • 红色块越深,表示该情感在该位置出现越密集
  • 可直观看出“HAPPY”集中在开头和结尾,“NEUTRAL”在中间

优势:无需音频解码,规避时间戳不一致问题;结果符合人类阅读直觉(文本从左到右,情绪也从早到晚)。

4.3 图表三:声音事件词云图(突出非语音信息)

适用场景:快速发现背景干扰(如BGM过长影响信息传达、掌声集中说明演示成功)

from wordcloud import WordCloud import matplotlib.pyplot as plt def plot_event_wordcloud(events, title="声音事件词云"): # 只提取事件类标签(排除HAPPY/SAD等情感,专注BGM/APPLAUSE等) event_tags = [e["tag"] for e in events if e["tag"] in ["BGM", "APPLAUSE", "LAUGHTER", "CRY", "NOISE", "SILENCE"]] if not event_tags: print("ℹ 未检测到声音事件标签(BGM/APPLAUSE/LAUGHTER等)") return # 统计频次 from collections import Counter counter = Counter(event_tags) # 生成词云 wc = WordCloud( width=800, height=400, background_color='white', colormap='tab10', font_path='/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf' # 镜像内置字体 ).generate_from_frequencies(counter) plt.figure(figsize=(10, 5)) plt.imshow(wc, interpolation='bilinear') plt.axis('off') plt.title(title, fontsize=16, pad=20) plt.tight_layout() plt.savefig("event_wordcloud.png", dpi=150, bbox_inches='tight') plt.show() # 调用 plot_event_wordcloud(events)

生成效果:

  • “APPLAUSE”字体最大(出现2次),“BGM”次之(1次)
  • 彩色区分不同事件类型
  • 一图覆盖所有非语音信号,比表格更直观

优势:聚焦“环境音”,帮内容创作者快速优化录音环境(如减少BGM时长、增加掌声引导)。

5. 进阶技巧:批量处理多段音频,生成对比报告

单次分析价值有限。真正落地需要横向对比——比如对比10段客服录音,看哪位员工的“愤怒响应率”最低。

5.1 批量解析脚本(支持文件夹一键处理)

创建batch_analyze.py

import os import json from parse_emotion import extract_emotions # 复用前面写的函数 def batch_process_audio_folder(folder_path, output_json="batch_report.json"): """ 批量处理文件夹内所有 .txt 文件(假设你已用WebUI导出结果为txt) """ report = {} for filename in os.listdir(folder_path): if not filename.endswith(".txt"): continue filepath = os.path.join(folder_path, filename) with open(filepath, "r", encoding="utf-8") as f: text = f.read().strip() events = extract_emotions(text) if not events: continue # 统计关键指标 tags = [e["tag"] for e in events] from collections import Counter counter = Counter(tags) report[filename] = { "total_events": len(events), "emotion_ratio": { tag: round(count / len(events), 3) for tag, count in counter.items() }, "dominant_tag": counter.most_common(1)[0][0] if counter else "NONE" } # 保存为JSON,方便后续导入Excel或BI工具 with open(output_json, "w", encoding="utf-8") as f: json.dump(report, f, ensure_ascii=False, indent=2) print(f" 批量分析完成!报告已保存至 {output_json}") return report # 使用示例:将WebUI导出的10个txt文件放入 ./transcripts/ 文件夹 # batch_process_audio_folder("./transcripts/")

运行后生成batch_report.json,内容类似:

{ "call_001.txt": { "total_events": 8, "emotion_ratio": {"HAPPY": 0.625, "NEUTRAL": 0.375}, "dominant_tag": "HAPPY" }, "call_002.txt": { "total_events": 12, "emotion_ratio": {"ANGRY": 0.583, "NEUTRAL": 0.417}, "dominant_tag": "ANGRY" } }

5.2 用Pandas快速生成对比柱状图

import pandas as pd import matplotlib.pyplot as plt def plot_batch_comparison(json_file="batch_report.json"): with open(json_file, "r", encoding="utf-8") as f: data = json.load(f) # 转为DataFrame df = pd.DataFrame.from_dict(data, orient="index") # 提取HAPPY/ANGRY占比(若不存在则为0) df["HAPPY"] = df["emotion_ratio"].apply(lambda x: x.get("HAPPY", 0)) df["ANGRY"] = df["emotion_ratio"].apply(lambda x: x.get("ANGRY", 0)) df["NEUTRAL"] = df["emotion_ratio"].apply(lambda x: x.get("NEUTRAL", 0)) # 绘制分组柱状图 ax = df[["HAPPY", "ANGRY", "NEUTRAL"]].plot( kind="bar", stacked=False, figsize=(10, 5), color=["#4CAF50", "#F44336", "#9E9E9E"] ) plt.title("各音频情感占比对比", fontsize=14, pad=20) plt.xlabel("音频文件") plt.ylabel("占比") plt.xticks(rotation=45) plt.legend(title="情感类型") plt.tight_layout() plt.savefig("batch_comparison.png", dpi=150, bbox_inches='tight') plt.show() # 调用 plot_batch_comparison()

生成效果:

  • 每个音频一个柱子,三种颜色代表三种情感占比
  • 一眼锁定“ANGRY占比最高”的异常录音(如 call_002)
  • 支持导出PNG/PDF,直接用于周报

优势:从“单次分析”升级为“质量监控”,真正赋能业务决策。

6. 总结:你的语音情感分析工作流已就绪

回顾一下,你现在已经掌握了一套免训练、免部署、纯前端驱动的语音情感可视化方案:

  • 第一步:用WebUI快速获取带标签文本
    → 无需写代码,5分钟上手,支持中英日韩粤五语种
  • 第二步:三行正则提取情感事件
    → 稳定、轻量、不依赖模型内部接口
  • 第三步:三类图表解决三类问题
    • 饼图:看整体情绪基调(适合汇报)
    • 热力图:找情绪转折点(适合深度分析)
    • 词云图:抓背景声音干扰(适合录音优化)
  • 第四步:批量处理生成对比报告
    → 从单次分析走向持续监控,支撑团队级改进

这套方法不追求“毫秒级精准时间戳”,而是用文本位置模拟时间、用标签频次代替强度值,在准确性和工程效率间取得绝佳平衡。它不替代专业声学分析工具,但足以让产品经理、运营、培训师——这些不写代码的人——第一次真正“看见”语音中的情绪。

下一步,你可以:
🔹 把parse_emotion.py封装成Gradio按钮,让同事一键生成图表
🔹 将batch_report.json导入Excel,用条件格式标红高愤怒录音
🔹 用热力图结果反推:在“NEUTRAL”密集区插入提示音,提升用户注意力

语音的情感,不该只留在耳朵里。现在,它已经在你的屏幕上,清晰可见。

7. 常见问题速查

Q:为什么我的结果里没有<|ANGRY|>标签?

A:SenseVoiceSmall 对愤怒的识别敏感度略低于开心/中性。建议:① 使用更强烈的愤怒语料(如“这根本不行!”);② 在WebUI中手动指定语言为zh而非auto;③ 检查音频是否有明显停顿——模型对连续激烈语句识别更准。

Q:热力图的“时间轴”不准,怎么和真实时间对齐?

A:本教程采用文本位置模拟,适合快速分析。如需真实时间轴,请用ffmpeg提取音频时长,再按比例缩放:

ffmpeg -i input.mp3 -show_entries format=duration -v quiet -of csv="p=0"

将输出秒数代入plot_emotion_heatmap(events, text_length=总秒数*10)即可。

Q:词云图显示乱码(方块)?

A:镜像已预装中文字体,但需确保font_path路径正确。如报错,替换为:

font_path="/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"

Q:想导出图表为PDF/PNG发邮件,怎么设置高清?

A:所有plt.savefig()中的dpi=150已满足打印需求。如需更高清,改为dpi=300,文件体积增大但清晰度跃升。


获取更多AI镜像

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

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

实战案例中整流二极管开关特性的体现

以下是对您提供的技术博文进行 深度润色与结构重构后的专业级技术文章 。整体遵循“去AI化、强工程感、重实测逻辑、口语化但不失严谨”的风格&#xff0c;彻底摒弃模板化表达和空洞术语堆砌&#xff0c;代之以一线工程师视角的思考脉络、真实调试经验、参数取舍权衡与可落地…

作者头像 李华
网站建设 2026/1/31 9:24:25

Live Avatar效率提升:并行任务调度部署方案

Live Avatar效率提升&#xff1a;并行任务调度部署方案 1. 模型背景与硬件现实 1.1 开源数字人模型的诞生 Live Avatar是由阿里联合高校团队开源的端到端数字人生成模型&#xff0c;它能将静态图像、文本提示和语音输入融合&#xff0c;实时生成高质量的说话视频。不同于传统…

作者头像 李华
网站建设 2026/1/31 9:30:21

3个步骤终结直播平台切换烦恼:这款聚合工具如何重塑观看体验

3个步骤终结直播平台切换烦恼&#xff1a;这款聚合工具如何重塑观看体验 【免费下载链接】dart_simple_live 简简单单的看直播 项目地址: https://gitcode.com/GitHub_Trending/da/dart_simple_live 在这个直播内容爆炸的时代&#xff0c;每个平台都在构建自己的内容壁垒…

作者头像 李华
网站建设 2026/1/30 15:00:15

USB Over Network配置详解:一文说清基本工作流程

以下是对您提供的博文《USB Over Network 配置详解&#xff1a;技术原理、实现机制与工程实践深度解析》的 全面润色与重构版本 。本次优化严格遵循您的要求&#xff1a; ✅ 彻底去除AI痕迹&#xff0c;语言自然、专业、有“人味”&#xff0c;像一位深耕嵌入式与远程硬件协…

作者头像 李华
网站建设 2026/1/30 12:47:52

Arduino Uno作品I2C设备连接技巧系统学习

以下是对您提供的博文内容进行深度润色与工程化重构后的版本。我以一位长期从事嵌入式教学、Arduino实战开发及硬件调试的一线工程师视角&#xff0c;将原文从“技术文档式说明”升级为真实项目中可复用、可验证、有温度的技术笔记。全文去除了AI腔调和模板化表达&#xff0c;强…

作者头像 李华
网站建设 2026/1/30 18:50:36

告别USB安全移除烦恼:USB Disk Ejector设备管理工具全解析

告别USB安全移除烦恼&#xff1a;USB Disk Ejector设备管理工具全解析 【免费下载链接】USB-Disk-Ejector A program that allows you to quickly remove drives in Windows. It can eject USB disks, Firewire disks and memory cards. It is a quick, flexible, portable alt…

作者头像 李华