ARTICLE DETAIL

资讯详情

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

多模态情感分析实战:从MOSI数据集到跨模态注意力融合

多模态情感分析实战:从MOSI数据集到跨模态注意力融合 简介这是一份面向多模态情感分析研究与毕业设计场景的模型资源包聚焦多模态表示学习、多模态融合及情感分类等下游任务。包内集成了Multimodal-Infomax、MISA、BBFN、Hfusion等多种深度学习模型实现并配有MELD、MUStARD、M2H2等标准数据集覆盖文本、音频、视觉三种模态的特征提取与融合流程可直接用于训练、对比和实验评估。资源总计185个文件压缩包大小约285.97MB。文件类型以Python脚本41个py、数据集与标注文件csv、arff、pickle、h5、模型权重p、Markdown文档md等为主py脚本负责模型构建与训练csv、arff等承载多模态数据样本p、h5保存预训练权重md与pdf则提供说明与实验记录。整体目录结构清晰便于按模型或任务模块检索。目前已有1492人学习下载。对于正在做多模态情感分析课设、毕设或相关课题的开发者可直接复用其中的模型代码、数据预处理与评估脚本节省搭建时间也可借助附带数据集快速验证改进思路是兼顾学习与项目落地的实用资源。1. 多模态情感分析是什么为什么文本不够用视频和语音也得跟上情感分析做到后面你会发现一个反直觉的现象纯文本模型在影评、商品评论上已经能跑出不错的准确率但一换到视频、直播、客服对话场景就明显掉点。原因是真实交流里情绪不只靠词语调上扬、表情变化、停顿长短都在传递态度。只拿文本等于扔掉大半信息这就是多模态情感分析要解决的问题——把文本、音频、视觉三种模态的特征联合起来训练一个能综合判断情感极性和强度的模型。这个方向现在已经是多模态领域最有落地价值的场景之一。你拿到手的这份资源包覆盖的正是这条完整链路数据集的预处理与特征提取、多模态表示学习与融合策略、以及实验评估的指标和对比方法。适合谁用做毕业设计、论文复现的研究生或者公司里要给视频内容做舆情分析、客服质检算法的工程师。它能帮你省掉最痛苦的部分——从零攒数据、对齐模态、踩融合的坑。2. 特征提取与多模态表示学习先解决怎么把三种模态变成能喂给模型的向量2.1 标准数据集选型MOSI、MOSEI 和它们为什么是事实标准多模态情感分析绕不开 CMU 的 MOSI 和 MOSEI 两个数据集。MOSI 是 2199 段 YouTube 影评视频每段都带文本转录、音频和视频帧标注值是 [-3, 3] 的连续情感分数MOSEI 是它的升级版样本量到 23416 段涉及更多说话人和主题。凡是做多模态情感分析的论文对比实验表里几乎全是这两个用它们你才能和已有结果对齐。某些资源包里可能还附带 DEAP 这类生理信号数据集那偏情绪计算方向和 MOSI/MOSEI 的任务设定不太一样注意区分。拿到数据后第一件事不是建模而是确认特征是否已经对齐。MOSI 和 MOSEI 官方发布时就有两个版本ALIGNED 和 UNALIGNED。ALIGNED 版本把文本、音频、视频按词级别强制对齐到同一个时间步适合早期用 LSTM 拼接特征的老方案UNALIGNED 版本保留原始时间分辨率文本最快、音频次之、视频最慢适合现在主流的跨模态注意力方法。刚开始跑实验建议先用 ALIGNED 版本跑通再做 UNALIGNED因为后者的时序对齐本身就是研究重点直接上手会被各种维度不匹配的报错淹没。2.2 特征提取的落地实操Text、Audio、Visual 三路分开处理如果你拿到的是原始视频而不是预提取特征第一步就得做三路提取。文本分支常见做法是用 BERT 或 GloVe音频分支用 COVAREP 或 openSMILE 提取 74 维声学特征包含 F0、MFCC、响度等视频分支用 OpenFace 提取面部动作单元和姿态特征通常 47 维或 35 维。关键点在于三个分支的采样率天然不同——文本一个词可能对应好几帧视频和好几段音频怎么让它们能对齐直接决定后续融合的质量。import torch import torch.nn as nn class MultiModalFeatureExtractor(nn.Module): def __init__(self, text_dim768, audio_dim74, video_dim47): super().__init__() # 文本分支BERT输出维度是768如果不做微调就固定用它 self.text_proj nn.Linear(text_dim, 128) # 音频分支COVAREP特征通常是74维 self.audio_proj nn.Linear(audio_dim, 64) # 视频分支OpenFace输出一般是47维左右 self.video_proj nn.Linear(video_dim, 64) self.dropout nn.Dropout(0.2) def forward(self, text_feat, audio_feat, video_feat): # text_feat: [batch, seq_len, 768] # audio_feat: [batch, audio_len, 74] # video_feat: [batch, video_len, 47] t torch.relu(self.text_proj(text_feat)) a torch.relu(self.audio_proj(audio_feat)) v torch.relu(self.video_proj(video_feat)) return self.dropout(t), self.dropout(a), self.dropout(v)这里每个分支先独立过一层线性映射把原始维度压缩到统一的低维空间。这一步很关键因为原始特征里存在大量冗余——视频的 47 维里有不少是说话人 ID 造成的身份信息和情感无关音频的 74 维里同样有环境噪声成分。投影到 64 或 128 维后后续融合的计算量会小很多而且模型不容易被高维稀疏特征带偏。如果不做压缩直接拼接你会在训练时发现 loss 要很久才降下去尤其是 BERT 特征这种 768 维的大块头直接拼接会让音频和视频的特征被淹没。提取完成后保存成.pkl或.npy文件缓存起来。训练时直接用缓存的特征而不是每次重跑 BERT 和 OpenFace整个实验周期能缩短一个量级。3. 多模态融合把特征拼起来不是答案时序对齐才是3.1 三类融合路线Early Fusion、Late Fusion、中间融合的取舍多模态融合的经典划分是早融合、晚融合和中间融合。早融合就是把三路特征沿着特征维度直接拼接然后喂给一个分类器。优点是无脑简单缺点是完全不管三路特征的采样率差异文本 20 个词、音频 400 帧、视频 100 帧拼接出来的向量内部严重错位模型学到的是噪声对齐。晚融合是各模态独立训练一个分类器最后把预测分数加权平均或投票这样做各模态互不干扰但丢失了模态之间的交互信息——比如一个人嘴上说很棒表情却是嘲讽这种矛盾信号只有跨模态交互才能捕捉到。中间融合是目前的主流路线核心思路是先给每个模态做局部时序编码再通过跨模态注意力让不同模态在时间步上互相 attend。MulT 就是这么做的它给每对模态都建一个 Transformer 编码器文本去 attend 音频特征音频去 attend 视频特征然后把交互后的序列再过融合层。这套设计的理论基础是情感信号在不同模态之间有滞后和提前的对应关系比如语调先于表情变化直接拼接学不到这种时间偏移规律但跨模态注意力可以学会去关注对方模态的某个时间窗口。3.2 融合模块的代码实现一个能直接改的最小跨模态注意力层import torch import torch.nn as nn import math class CrossModalAttention(nn.Module): def __init__(self, embed_dim128, num_heads4): super().__init__() self.embed_dim embed_dim self.num_heads num_heads self.head_dim embed_dim // num_heads assert self.head_dim * num_heads embed_dim self.q_proj nn.Linear(embed_dim, embed_dim) self.k_proj nn.Linear(embed_dim, embed_dim) self.v_proj nn.Linear(embed_dim, embed_dim) self.out_proj nn.Linear(embed_dim, embed_dim) self.scale math.sqrt(self.head_dim) def forward(self, query, key, value): # query: [batch, t_len, embed_dim] (源模态比如文本) # key/value: [batch, s_len, embed_dim] (目标模态比如视频) batch, t_len, _ query.shape _, s_len, _ key.shape Q self.q_proj(query).view(batch, t_len, self.num_heads, self.head_dim).transpose(1, 2) K self.k_proj(key).view(batch, s_len, self.num_heads, self.head_dim).transpose(1, 2) V self.v_proj(value).view(batch, s_len, self.num_heads, self.head_dim).transpose(1, 2) attn_weights torch.matmul(Q, K.transpose(-2, -1)) / self.scale attn_weights torch.softmax(attn_weights, dim-1) out torch.matmul(attn_weights, V) out out.transpose(1, 2).contiguous().view(batch, t_len, self.embed_dim) return self.out_proj(out)这段实现了标准的缩放点积跨模态注意力。Query 来自关心源模态Key 和 Value 来自被交互的目标模态这样模型可以学到文本中的这个词应该对齐视频中的哪几帧。参数上要注意num_heads一般取 4 或 8embed_dim建议 128如果数据量大可以升到 256但训练时间会显著增加。scale用1 / sqrt(head_dim)是防止 softmax 输入过大导致梯度消失这个细节新手经常漏掉不除的话训练前期 loss 很容易落到 0.69log2附近不动。完整看一个 MulT 风格的融合流程文本序列先过一层 self-attention 做局部编码然后和音频、视频分别做跨模态注意力再把交互后的三路输出拼接过一个两层全连接网络输出到分类头。整个模块的输入是第 2 章特征提取器的输出所以两个模块能直接对接成一条流水线。跑通基线之后再考虑加门控机制比如动态调节各模态的贡献权重一开始不要做太复杂的结构。3.3 表示学习的进阶从静态融合到动态调整静态融合在一段视频内权重固定但真实场景里不同片段的主导模态在变——前两秒画面抖动语音是主要线索中间说话人转过头去表情信息丢失文本和语调权重就该上来。动态融合的代表做法是基于注意力给每个时间步分配模态权重比如 Self-MM 用标签平滑和对比学习约束单模态和融合模态的表示一致性另外一些工作用图神经网络建模模态之间的语义交互和二阶关系。这些进阶方案做论文对比实验时很有价值但落地时要冷静动态权重的收益通常只有 1~2 个点的 ACC 提升代价是训练不稳定、hyperparameter 空间变大了很多。我的建议是先把静态融合作为基线跑稳确认你的复现效果和原论文的差距在合理范围内再往上加动态机制。如果基线就比论文差 3 个点以上先不要怀疑模型回去检查数据处理。4. 实验评估用指标对齐论文用对比实验检验模块价值4.1 MOSI 和 MOSEI 上的评估协议与三组硬指标多模态情感分析论文的标准评估协议是在 MOSI 和 MOSEI 上分别做 7 分类和 2 分类评估。7 分类把 [-3, 3] 的情感分数映射成 7 档-3 强烈消极到 3 强烈积极指标用 Acc-7 衡量2 分类按分数正负分成积极/消极两档有些工作把 0 归入某一边或剔除指标用 Acc-2 和 F1。另外还有回归指标 MAE 和 Corr——MAE 是预测分数和真实分数的绝对误差Corr 是预测值和真实值的皮尔逊相关系数。单独看 Acc-2 容易被骗。因为 MOSI 里积极样本占比略高一个全部预测积极的桩模型也能到 55% 左右的准确率。所以做实验时三个指标都要报F1 能反映少数类的召回情况MAE 能暴露你模型是不是只会在 1 和 -1 附近打转。一个经验值MOSI 上 Acc-2 到 82% 以上、MAE 到 0.95 以下才算得上可发表的基线水平MOSEI 因为数据量更大Acc-2 一般能到 85% 左右。4.2 评估脚本统一指标计算与结果输出import numpy as np from sklearn.metrics import accuracy_score, f1_score, mean_absolute_error from scipy.stats import pearsonr def evaluate(preds, labels, preds_regNone, labels_regNone): # 7分类评估原始分数直接作为类别标签 acc7 accuracy_score(labels, preds) # 2分类评估正负二分等于0的样本可以剔除或归入积极 pos_mask labels ! 0 labels_bin (labels[pos_mask] 0).astype(int) preds_bin (preds[pos_mask] 0).astype(int) acc2 accuracy_score(labels_bin, preds_bin) f1 f1_score(labels_bin, preds_bin, averagebinary) # 回归评估如果用的是回归输出需要额外预测连续分数 mae mean_absolute_error(labels_reg, preds_reg) corr, _ pearsonr(labels_reg, preds_reg) print(fAcc-7: {acc7:.4f} | Acc-2: {acc2:.4f} | F1: {f1:.4f}) print(fMAE: {mae:.4f} | Corr: {corr:.4f}) return {acc7: acc7, acc2: acc2, f1: f1, mae: mae, corr: corr}注意 2 分类评估里的pos_mask——我把标签等于 0 的样本剔除了。不同论文对这个边界样本的处理方式不一样有的归积极有的归消极有的直接丢掉这会导致 Acc-2 差一两个点。跑对比时务必查清楚对方论文怎么处理 0 标签否则你以为的差距可能是协议差异造成的。4.3 消融实验怎么设计三个必须做的对照组一份能说服人的实验评估至少要有三组消融单模态基线只用 text、只用 audio、只用 video、成对模态组合textaudio、textvideo、audiovideo、以及完整三模态融合。单模态基线告诉你每个模态在任务里的上限在哪——如果 video 单模态 ACC 只有 40%说明视觉特征提取质量本身有问题后续融合效果再好也心虚。成对组合能看出哪些模态之间存在强互补关系一般来说 textaudio 已经能到三模态的 95% 水平video 的增量在 MOSI 上相对有限。另一个重要对比是融合策略对比把中间融合换成早融合直接拼接和晚融合分数平均做对照这比对比多个 SOTA 论文更有说服力因为所有模态特征预处理完全一致差量完全来自融合模块本身。论文审稿人很喜欢看到这样的对比因为它干净、能说明问题。5. 避坑指南多模态情感分析里最容易翻车的 5 个环节5.1 特征不对齐导致维度爆炸现象训练时模型很大、loss 在 3~4 个 epoch 后开始震荡GPU 显存占用异常。原因文本序列长度、音频帧数、视频帧数分别走各自投影层输出序列长度不一致时直接拼接后注意力矩阵的形状就变成了[batch, 文本_len 音频_len 视频_len]的平方复杂度显存直接爆掉。解决在融合前对每个模态做池化mean pooling或设固定长度截断。我一般先统计训练集各模态序列长度的分位数取 90 分位作为裁剪阈值然后用 padding mask 对齐到同一长度。不要用全局 max pooling会丢失大量时序位置信息。5.2 跨模态注意力训练不收敛现象训练早期 loss 一直停在 0.69 附近log2即二分类随机猜测过了 10 个 epoch 也不下降。原因cross-modal attention 的 softmax 输入值过大梯度消失常见诱因是没做 scale或者 Q/K 的初始化方差太大。解决确认scale 1 / sqrt(head_dim)加上了把nn.Linear的初始化改成正交初始化或 xavier_uniform再不行就先把所有投影层的 bias 置零。另外一个常见问题是学习率——融合层和 BERT 微调层的合理学习率差异很大一般 BERT 用 2e-5融合层用 1e-3 起步用 AdamW 分组设置参数。5.3 训练集和验证集数据泄露现象验证集指标异常高Acc-2 直接到 100%但换到另一批数据泛化效果很差。原因数据预处理脚本里把同一个视频的多个片段切分时没有先按视频 ID 分组再划分 train/valid/test导致同一个人的不同片段同时在训练集和验证集里。MOSI 的原始划分对视频 ID 做了隔离但你自己扩展数据集或用别的数据时容易忽略。解决数据划分前先按视频 ID 排序去重用group_kfold或直接按 ID 哈希分割保证同一视频的所有片段永远在同一折里。这是多模态任务最隐蔽也最常见的翻车点。5.4 BERT 微调导致内存爆炸现象加入 BERT 后训练显存直接从 6G 飙升到 20G小卡根本跑不动。原因BERT 的 12 层 Transformer 权重 梯度占了绝大部分显存而多模态任务里文本只是其中一路这种开销不划算。解决第一种方案是 BERT 不微调离线提取特征后冻结文本分支只训练一层 MLP显存能控制在 4G 以内第二种方案是 BERT 后段微调只让后 4 层参与梯度计算前 8 层冻结。做实验对比时先用方案一跑通确定需要微调 BERT 能带来显著提升后再上方案二。5.5 视频帧采样率不统一导致结果不可复现现象同一条代码在一台机器跑出 ACC 82%换台机器变 79%再跑一次变 83%。原因视频帧提取时用 OpenCV 的VideoCapture按帧索引读但视频的原始帧率不统一有的 24fps 有的 30fps采样步长单位不一致造成特征错位。解决统一用cap.get(cv2.CAP_PROP_FPS)读取实际帧率按时间戳采样而不是按帧序号采样提取完成后把特征文件的时间戳信息保存下来训练前检查各模态时长是否匹配。这是一个血泪经验多模态数据的可复现性 80% 取决于预处理阶段的确定性而不是模型随机种子。6. 进阶验证怎么判断你的融合模块真的学到了东西融合模型最怕的是表面融合实际只用了文本分支其他两个模态的投影层学会了直接输出常量。要识别这种退化除了对比单模态基线还有一个简单的验证手段把音频和视频特征的顺序随机打乱重新跑一遍预测。如果模型在打乱后得分变化很小说明它根本没有用音频和视频的时序信息——它在用文本硬撑。另一个实用的验证维度是回归预测的误差分布。好的多模态模型误差应该集中在零附近且误差大小和情感极性无关。如果发现正极性样本的 MAE 明显大于负极性说明模型对积极情绪的表达模式学习不足这往往是训练集中消极样本过多造成的类不平衡可以在损失函数里按极性加权。做论文或工程交付时额外建议在 MOSEI 上做一次交叉验证而不是只跑官方划分。MOSEI 的说话人分布不均匀官方划分可能让一部分说话人的片段垄断了验证集交叉验证比单一划分更可靠。代价是 MOSEI 规模大全量训练一轮可能要大半天可以先做 5 折中的 2 折来估计方差。最后说一下我给自己的验收习惯只跑出一个好数字不算完我会强迫自己检查三件事——融合层的注意力权重是否呈现有意义的模态间对齐模式用可视化 heatmap 查看各模态投影层的输出方差是否显著大于零方差为零说明退化了以及去掉任何一个模态后 ACC 是否明显下降下降幅度低于 0.5 个点说明这个模态没起作用。这套检查跑完模型才真正算数。多模态工作的价值不在最后那个 float 指标在于融合过程是不是真的让每个模态都参与了决策。希望帮到你。本文还有配套的精品资源点击获取
返回列表