ARTICLE DETAIL

资讯详情

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

多模态情感识别大作业:从单模态基线到融合的完整方案

多模态情感识别大作业:从单模态基线到融合的完整方案 简介这份资源是国科大人机交互课程的大作业完整工程围绕多模态情感识别展开面向人机交互、情感计算方向的高校学生与自学者可用于课程设计、期末大作业、工程实训或毕业设计等场景。项目包含原始数据、预处理数据与模型代码涉及脑电、眼电等多模态信号的读取、特征处理与情感分类建模适合具备一定Python与机器学习基础的学习者复现与二次开发。压缩包共39个文件约548.29MB以mat与pkl数据文件为主辅以py与ipynb源码脚本、jpg与png实验图表及md说明文档覆盖数据、代码与结果展示的完整链路。目前已有119人学习关注。资源经过实际运行验证答辩评审平均分达96分读者可据此复现多模态情感识别流程借鉴实验设计与结果分析思路并在此基础上扩展新功能或迁移到相关课题中。1. 多模态情感识别大作业从「能跑」到「能过答辩」差在哪人机交互方向的大作业里多模态情感识别是出镜率最高的选题之一。它天然踩中三个得分点有明确的应用场景情感计算、智能座舱、在线教育状态监测有可量化的指标准确率、F1、混淆矩阵还有足够的工程复杂度撑起一份课设报告。但真正动手做过的人都清楚这个题目最大的坑不在模型本身而在「多模态」三个字——文本、语音、图像三路数据的时间对齐、特征融合、模态缺失处理每一步都能让一个看起来能跑的 demo 在答辩现场翻车。这篇笔记面向正在做课设、实训或大作业的同学也面向第一次接触多模态融合的工程师。我会按「数据怎么组织 → 单模态怎么跑通 → 融合怎么做 → 坑在哪 → 怎么把指标讲清楚」的顺序把一套可复现的最小方案拆开讲。不依赖任何特定数据集思路可以平移到你手头的语料上。读完你应该能判断自己的方案卡在哪一层以及答辩时老师最可能追问什么。2. 多模态情感识别的数据组织与单模态基线2.1 三路数据怎么对齐才不会错位多模态情感识别最常见的数据形态是「一段视频 对应音频 对应文本转写」。以对话级情感识别为例一条样本通常包含一段 310 秒的语音片段、这段语音的梅尔频谱、对应的文本转写、以及一个情感标签积极/中性/消极或更细的 Ekman 六类。麻烦在于三路数据的采样率、时间粒度和长度都不一致音频是 16kHz 波形文本是词序列视频是 25fps 的帧序列。我一般会先做一次「样本级对齐」而不是「帧级对齐」。也就是说以一条对话为单位把音频、文本、视频各自处理成定长特征向量再在特征层拼接。帧级对齐对课设来说性价比太低除非你的题目明确要求做连续情感维度回归valence-arousal。样本级对齐的流程是先按标注文件切分出每条样本的起止时间再用这个时间戳去裁音频、抽视频帧、截文本。import pandas as pd # 标注文件通常长这样id, wav_path, text, label, start, end df pd.read_csv(labels.csv) def build_sample(row): # 1. 按 start/end 裁音频重采样到 16k wav load_audio(row[wav_path], sr16000, offsetrow[start], durationrow[end] - row[start]) # 2. 文本直接用整句不做截断交给 tokenizer 处理 text row[text].strip() # 3. 视频帧按同样时间窗抽每秒抽 2 帧足够 frames extract_frames(row[wav_path].replace(.wav, .mp4), startrow[start], endrow[end], fps2) return {wav: wav, text: text, frames: frames, label: row[label]}这段代码的关键在offset和duration两个参数。很多同学直接读整段音频结果一条样本里混进了相邻对话的语音标签和内容对不上训练 loss 怎么都不降。fps2是经验值情感识别不需要高帧率抽太多帧只会让显存爆炸。文本不做截断是因为情感词往往在句尾截断会丢信息长度交给 tokenizer 的max_length统一处理。2.2 文本、语音、图像三路基线各自怎么跑在融合之前必须先把每一路单独跑通拿到单模态基线。这不是走流程而是为了后面判断融合到底有没有增益。如果文本单模态就有 85% 准确率融合后只有 86%那你的融合模块基本没起作用答辩时会被问穿。文本路最省事用预训练模型微调即可。中文场景用 bert-base-chinese英文用 roberta-base接一个分类头。语音路我一般用 openSMILE 抽 eGeMAPS 特征集88 维或者用 wav2vec2 抽深度特征。前者快、可解释后者效果好但吃显存。图像路用 ResNet18 抽每帧特征再平均或者直接用 OpenFace 抽 AU面部动作单元特征。from transformers import AutoTokenizer, AutoModelForSequenceClassification tok AutoTokenizer.from_pretrained(bert-base-chinese) model AutoModelForSequenceClassification.from_pretrained( bert-base-chinese, num_labels3) def text_baseline(texts, labels): enc tok(texts, paddingTrue, truncationTrue, max_length64, return_tensorspt) out model(**enc, labelslabels) return out.loss, out.logitsmax_length64对对话级情感足够超过 64 个 token 的样本很少。num_labels3对应三分类如果你的数据集是二分类就改成 2。这里不展开训练循环重点是先确认单模态能收敛、验证集指标合理。语音和图像基线同理各自跑一遍把准确率和 F1 记下来后面融合时做对比。提示单模态基线一定要在验证集上评估不要用训练集指标。课设报告里放训练集准确率是硬伤老师一眼就能看出来。3. 特征融合的三种做法与最小可跑实现3.1 早期融合、晚期融合、中期融合怎么选融合策略分三类选哪种直接决定你的代码复杂度和最终指标。早期融合early fusion是把三路特征在输入层拼接送进一个分类器。优点是实现简单缺点是不同模态的特征尺度差异大文本是 768 维、语音是 88 维、图像是 512 维直接拼容易被某一路主导。晚期融合late fusion是每路单独出预测再对预测分数做加权平均或投票。优点是鲁棒某一路缺失不影响其他路缺点是丢掉了模态间的交互信息。中期融合mid fusion是在中间层做交叉注意力让文本和语音互相「看」对方效果最好但代码量最大。对课设来说我的建议是先做晚期融合拿到一个能交差的基线再尝试中期融合冲指标。晚期融合的加权系数可以用验证集上的单模态准确率做归一化也可以直接学一个可训练的权重。import torch import torch.nn as nn class LateFusion(nn.Module): def __init__(self, n_cls3): super().__init__() # 三路各自映射到相同维度再出 logits self.text_head nn.Linear(768, n_cls) self.audio_head nn.Linear(88, n_cls) self.video_head nn.Linear(512, n_cls) # 可学习的融合权重初始化为等权 self.weights nn.Parameter(torch.ones(3) / 3) def forward(self, t, a, v): lt self.text_head(t) la self.audio_head(a) lv self.video_head(v) w torch.softmax(self.weights, dim0) return w[0] * lt w[1] * la w[2] * lvself.weights用 softmax 归一化保证三个权重和为 1训练时梯度会自动调整哪一路更重要。这个模块只有几百个参数训练极快适合作为融合的起点。如果验证集上某一路权重趋近于 0说明那一路特征质量太差需要回头检查单模态基线。3.2 用交叉注意力做中期融合的最小代码中期融合的核心是让不同模态的特征互相 attend。最简做法是拿文本特征做 query语音和图像特征做 key/value过一层多头注意力再把输出和原文本特征拼接分类。class CrossFusion(nn.Module): def __init__(self, d_text768, d_audio88, d_video512, n_cls3): super().__init__() # 把语音和图像投影到文本维度才能做注意力 self.proj_a nn.Linear(d_audio, d_text) self.proj_v nn.Linear(d_video, d_text) self.attn nn.MultiheadAttention(d_text, num_heads4, batch_firstTrue) self.cls nn.Linear(d_text * 2, n_cls) def forward(self, t, a, v): # t: [B, 768], a: [B, 88], v: [B, 512] t t.unsqueeze(1) # [B, 1, 768] kv torch.stack([self.proj_a(a), self.proj_v(v)], dim1) # [B, 2, 768] out, _ self.attn(t, kv, kv) # [B, 1, 768] fused torch.cat([t.squeeze(1), out.squeeze(1)], dim-1) return self.cls(fused)num_heads4是常见起点768 维除以 4 每头 192 维够用。batch_firstTrue避免维度顺序踩坑。torch.cat把注意力输出和原文本特征拼起来保留原始信息防止注意力层学偏。这个模块参数量不大但训练时学习率要调小否则注意力层容易震荡。注意中期融合对数据量敏感。如果你的课设数据集只有几百条样本交叉注意力很容易过拟合验证集指标反而不如晚期融合。这时候老老实实用晚期融合别硬上。4. 模态缺失、类别不平衡与训练不收敛的排查4.1 模态缺失现象是某一路 loss 不降现象训练时文本和语音的 loss 正常下降图像那一路 loss 一直卡在高位融合后指标比文本单模态还低。原因通常是视频帧抽取失败或对齐错位导致图像特征全是噪声。解决方法是先单独可视化几帧确认抽出来的图不是黑屏或错帧再检查时间戳单位标注文件里 start/end 是秒还是毫秒差 1000 倍结果完全不同。4.2 类别不平衡现象是模型全预测多数类现象验证集准确率看着有 70%但混淆矩阵里少数类召回率接近 0。原因就是类别不平衡模型学会了偷懒。解决办法有两个一是用加权交叉熵权重按类别频率的倒数设置二是用 focal loss让模型聚焦难样本。课设里加权交叉熵足够实现简单。# 假设类别频率为 [800, 150, 50] weights torch.tensor([1.0, 800/150, 800/50]) criterion nn.CrossEntropyLoss(weightweights)权重不用精确计算量级对就行。800/150≈5.3800/5016少数类权重给大模型才会重视。4.3 训练不收敛现象是 loss 震荡或变 NaN现象前几个 epoch loss 正常突然跳到 NaN。原因多半是学习率太大或者中期融合里注意力层梯度爆炸。解决办法是加梯度裁剪torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0)并把学习率降到 1e-5 量级。另一个常见原因是音频特征没做归一化eGeMAPS 某些维度数值范围很大送进网络直接炸。4.4 融合后指标反而下降现象是融合不如单模态现象晚期融合准确率 82%文本单模态 85%。原因是某一路特征质量太差融合时拖了后腿。解决办法是先看学到的融合权重如果差的那一路权重没降下去说明权重初始化或学习率有问题也可以手动设权重把差的那一路直接置零验证一下融合上限。4.5 验证集和测试集分布不一致现象是线下好线上崩现象自己切的验证集指标很好换一批数据就崩。原因是切分时没有按说话人或按场景划分同一个人的样本同时出现在训练和验证集里模型记住了人而不是情感。解决办法是按说话人 ID 做分组切分确保验证集里的人没在训练集出现过。5. 把指标讲清楚答辩时老师会追问的四个点5.1 混淆矩阵比准确率更能说明问题准确率是答辩里最容易被质疑的指标。老师会问「三分类里中性样本占 70%你全预测中性也有 70% 准确率你的模型到底学到了什么」这时候你需要混淆矩阵和每类的 precision/recall。把混淆矩阵画出来指出模型在积极和消极上的区分度中性类的误判主要来自哪里。如果中性类确实难分就老实说这是数据标注模糊导致的比硬吹准确率可信得多。5.2 消融实验怎么做才有说服力消融实验是证明「多模态有用」的关键。最少做三组纯文本、纯语音、文本语音融合。如果时间够再加纯图像和三者融合。每组跑三次取平均报告均值和标准差。只跑一次的结果没有说服力随机种子一换指标就变。表格里把单模态和融合的指标并排列出增益一目了然。配置准确率宏 F1纯文本0.830.79纯语音0.710.65文本语音晚期0.860.83文本语音中期0.880.85这张表不用编按你自己的实验结果填。关键是融合相对单模态要有稳定增益哪怕只有 2 个点也能说明融合模块起了作用。5.3 可视化注意力权重让融合可解释中期融合的交叉注意力权重可以直接可视化。把每条样本的注意力权重画成热力图看模型在判断「积极」时更关注语音的哪些片段、文本的哪些词。答辩时放一张这样的图比讲十句原理都管用。实现上nn.MultiheadAttention返回的 attention 矩阵就是权重取平均后画图即可。5.4 一个我踩过的坑别在答辩前夜改融合结构我做过一次多模态课设答辩前一天觉得晚期融合太简单连夜改成中期融合结果注意力层没调好指标从 86% 掉到 79%。第二天硬着头皮讲被老师追问「为什么融合比单模态还低」场面很难看。后来我的习惯是答辩前三天冻结模型结构只做推理和可视化不再动训练代码。想冲指标就提前一周做完消融留足回退时间。这套方案的核心不是某个模型多先进而是每一步都有对照、有回退、有解释。单模态基线保证你至少能交差融合模块决定你的上限消融实验和可视化决定你能不能把故事讲圆。希望帮到你。本文还有配套的精品资源点击获取
返回列表