联合模态融合+图对比学习


✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨

🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。

我是Srlua小谢,在这里我会分享我的知识和经验。🎥

希望在这里,我们能一起探索IT世界的奥妙,提升我们的技能。🔮

记得先点赞👍后阅读哦~ 👏👏

📘📚 所属专栏:传知代码论文复现

欢迎访问我的主页:Srlua小谢 获取更多信息和资源。✨✨🌙🌙

​​

​​

目录

文章摘要

模型整体框架

特征提取模块

特征融合模块

图对比学习模块

图数据增强

图卷积对比学习

演示效果

核心逻辑

使用&部署方式

温馨提示


  本文所有资源均可在该地址处获取。

文章摘要

多模态情感识别旨在识别多种模态中每个话语的情感,这在人机交互应用中越来越受到关注。当前基于图的方法未能同时描述对话中的全局上下文特征和局部多样的单模态特征。此外,随着图层数量的增加,它们很容易陷入过度平滑的情况。本文提出了一种用于多模态情感识别的联合模态融合和图对比学习方法(JOYFUL),其中多模态融合、对比学习和情感识别被联合优化。具体来说,我们首先设计了一种新的多模态融合机制,可以提供全局上下文和单模态特定特征之间的深度交互和融合。然后,我们引入了一个图对比学习框架,包括视图间和视图内对比损失,以学习更可区分的表示,适用于具有不同情绪的样本。对三个基准数据集的大量实验证明,JOYFUL相对于所有基线方法取得了最先进的性能。

模型整体框架


JOYFUL架构整体;首先提取单模态特征,然后使用多模态融合模块将它们融合起来,并将其作为基于 GCL 的框架的输入,以学习更好的情感识别表示。

特征提取模块

JOYFUL模型对于特征提取并有做很多的创新,基本上都是使用预训练模型进行特征的提取

对于 IEMOCAP 数据集(Busso 等人,2008),视频特征 xv∈R512xv​∈R512,音频特征 xa∈R100xa​∈R100,文本特征 xt∈R768xt​∈R768 分别从 OpenFace(Baltrusaitis 等人,2018)、OpenSmile(Eyben 等人,2010)和 SBERT(Reimers 和 Gurevych,2019)中获得。对于 MELD 数据集(Poria 等人,2019a),xv∈R16053xv​∈R16053,xa∈R300xa​∈R300,xt∈R768xt​∈R768 分别从 DenseNet(Huang 等人,2017)、OpenSmile 和 TextCNN(Kim,2014)中获得。对于 MOSEI 数据集(Zadeh 等人,2018),xv∈R35xv​∈R35,xa∈R80xa​∈R80,xt∈R768xt​∈R768 分别从 TBJE(Delbrouck 等人,2020)、LibROSA(Raguraman 等人,2019)和 SBERT 中获得。文本特征是句子级别的静态特征。音频和视觉模态是通过对所有令牌特征进行平均得到的话语级别特征。

特征融合模块

特征融合模块一共包含两部分,一部分是上下文特征融合模块,一部分是特定区域的特征表示

上下文学习模块
通过简单的全链接网络调整了特征维度,堆叠两层transformer得到最终的融合之后的特征;模型在这里做了两次重构损失,对特征在做变换的时候做了一定的限制。
(1)特征在经过transformer之前与之后二范数损失重构:


(2)文章定义了一个全局情感状态,用最终的特征与该全局情感状态做重构损失:

个人理解:模型在进行训练的时候,是把一个向量空间的向量映射到另外一个向量空间上的向量,因为深度学习的这个向量映射的方式是不可控的,加入一定的限制之后,训练的方向会尽量的向着希望的方向进行。以上两种损失重构,就是把限制加到损失函数里面,进行限制。(1)中的重构方式是不希望特征在经过两层的transformer之后太过发散,与原来的特征求二范数加入损失函数,达到最终不会太过偏离原是数据的效果(2)定义了一个全局的情感信息,希望上下文学习的时候,不太偏离主题;

上下文主题的定义方式是:

局部特征学习

定义一个向量空间,用三个模态的向量分别与该空间的所有基向量求余弦相似度,然后使用该空间的所有基向量乘以相应的余弦相似度后累加,获得新的向量表示:

中间增加了一步归一化处理,防止某些值过大:

最后还是有重构损失的步骤:

图对比学习模块

首先是对图的定义,如图所示,图的定义很简单,一个结点表示从上一个模块得到的特征信息,紫色表示外部联系,红色是内部联系,箭头表示先后顺序

补充对比学习的理论知识:
 


先简单的理解正样本就是和目标比较相似但又不完全一样的样本, 负样本就是完全不一样的样本. 那么优化InfoNEC实际就是:
1.拉近正样本之间的距离
2.推远负样本之间的距离

数据增强(Data Augmentation, DA). 由于CL是一种自监督学习,并不依赖标签(label)来定义正负样本. 因此CL的正负样本往往依靠数据增强来产生, 在CL中来自同一样本的数据增强认为是正样本, 不同样本之间的数据增强是负样本,(见图 2). 那么这也就意味数据增强就会很大程度的影响对比学习的性能.

下面将是本论文数据增强的方式

图数据增强

图增强(Graph Augmentation)是指通过添加、修改或删除图中的节点或边来改善图数据的质量或增加图的多样性。这种技术通常用于图数据挖掘和图神经网络等领域,旨在提高模型的性能和鲁棒性。

特征屏蔽(FM):给定初始化的话语的表示,我们随机选择初始化表示语句的p个维度,并用0掩盖它们的元素

边扰动(EP):给定图G,我们随机删除并添加p%的边

全局拉近(GP):给定图G,我们首先计算说话人之间的相似性,并在说话人之间随机添加p%边

FM+GP、FM+EP两种方式,产生两个子图,分别用于对比学习。

图卷积对比学习

题外话:这篇文章其实本质还是在做图卷积,说一下我个人的理解,图卷积其实还是从一个向量映射的到另外一个向量,这篇文章其实就是在做图卷积的时候,增加了一种损失函数,来要求,或者说是限制了,从一个向量映射到另外一个向量的这个映射方式。因为深度学习不可控,增加了一种限制方式,让这种映射往一个想要的方向去靠近。

图增强之后变成3个图,分别进行图卷积,然后中间的图是没有进行变化的,用来和上下两个图进行图对比,简单理解就是做了损失函数,加在最后的损失函数里面。


 

演示效果

核心逻辑

在这里可以粘贴您的核心代码逻辑:

# start
class JOYFUL(nn.Module):
    def __init__(self, args):
        super(JOYFUL, self).__init__()
        u_dim = 100
        if args.rnn == "transformer":
            g_dim = args.hidden_size
        else:
            g_dim = 200
        h1_dim = args.hidden_size
        h2_dim = args.hidden_size
        hc_dim = args.hidden_size
        dataset_label_dict = {
            "iemocap": {"hap": 0, "sad": 1, "neu": 2, "ang": 3, "exc": 4, "fru": 5},
            "iemocap_4": {"hap": 0, "sad": 1, "neu": 2, "ang": 3},
            "mosei": {"Negative": 0, "Positive": 1},
            "meld": {"Neutral": 0, "Surprise": 1, "Fear": 2, "Sadness": 3, "Joy": 4, "Disgust": 5, "Angry": 6}
        }

        dataset_speaker_dict = {
            "iemocap": 2,
            "iemocap_4": 2,
            "mosei": 1,
            "meld": 9
        }

        if args.dataset and args.emotion == "multilabel":
            dataset_label_dict["mosei"] = {
                "happiness": 0,
                "sadness": 1,
                "anger": 2,
                "surprise": 3,
                "disgust": 4,
                "fear": 5,
            }


        tag_size = len(dataset_label_dict[args.dataset])
        args.n_speakers = dataset_speaker_dict[args.dataset]
        self.concat_gin_gout = args.concat_gin_gout
        self.cl_loss_weight = args.cl_loss_weight

        self.wp = args.wp
        self.wf = args.wf
        self.device = args.device

        self.rnn = SeqContext(u_dim, g_dim, args)
        self.gcl = GNN(g_dim, h1_dim, h2_dim, args)
        if args.concat_gin_gout:
            self.clf = Classifier(
                g_dim + h2_dim * args.gnn_nheads, hc_dim, tag_size, args
            )
        else:
            self.clf = Classifier(h2_dim * args.gnn_nheads, hc_dim, tag_size, args)

        edge_type_to_idx = {}
        for j in range(args.n_speakers):
            for k in range(args.n_speakers):
                edge_type_to_idx[str(j) + str(k) + "0"] = len(edge_type_to_idx)
                edge_type_to_idx[str(j) + str(k) + "1"] = len(edge_type_to_idx)
        self.edge_type_to_idx = edge_type_to_idx
        log.debug(self.edge_type_to_idx)

    def get_rep(self, data=None, whetherT=None):
        node_features = self.rnn(data["text_len_tensor"], data["input_tensor"])


        features, edge_index, edge_type, edge_index_lengths = batch_graphify(
            node_features,
            data["text_len_tensor"],
            data["speaker_tensor"],
            self.wp,
            self.wf,
            self.edge_type_to_idx,
            self.device,
        )

        graph_out, cl_loss = self.gcl(features, edge_index, edge_type, whetherT)
        return graph_out, features, cl_loss

    def forward(self, data, whetherT):
        graph_out, features, cl_loss = self.get_rep(data,whetherT)
        if self.concat_gin_gout:
            out = self.clf(
                torch.cat([features, graph_out], dim=-1), data["text_len_tensor"]
            )
        else:
            out = self.clf(graph_out, data["text_len_tensor"])

        return out

    def get_loss(self, data, whetherT):
        graph_out, features, cl_loss = self.get_rep(data, whetherT)
        if self.concat_gin_gout:
            loss = self.clf.get_loss(
                torch.cat([features, graph_out], dim=-1),
                data["label_tensor"],
                data["text_len_tensor"],
            )
        else:
            loss = self.clf.get_loss(
                graph_out, data["label_tensor"], data["text_len_tensor"]
            )
        return loss + self.cl_loss_weight*cl_loss

使用&部署方式

模型训练
python train.py --dataset="iemocap_4" --modalities="atv" --from_begin --epochs=50
python train.py --dataset="iemocap" --modalities="atv" --from_begin --epochs=50

模型评价
python eval.py --dataset="iemocap_4" --modalities="atv"

单模态训练模型
python eval.py --dataset="iemocap_4" --modalities="t"
python eval.py --dataset="iemocap_4" --modalities="v"
python eval.py --dataset="iemocap_4" --modalities="a"

安装下面几个包

终端进入目录:
执行README中的命令即可

温馨提示

1.数据集和已训练好的模型都在.md文件中有百度网盘链接,直接下载放到指定文件夹即可

2.注意,训练出来的模型是有硬件要求的,我是用cpu进行训练的,模型只能在cpu跑,如果想在gpu上跑,请进行重新训练

3.如果有朋友希望用苹果的gpu进行训练,虽然现在pytorch框架已经支持mps(mac版本的cuda可以这么理解)训练,但是很遗憾,图神经网络的包还不支持,不过不用担心,这个模型的训练量很小,我全程都是苹果笔记本完成训练的。

​​

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值