NLP,对比学习SimCSE和Rdrop的区别

参考文章1:https://blog.csdn.net/c9Yv2cf9I06K2A9E/article/details/118447050
参考文章2:https://www.modb.pro/db/379565

SimCSE,用Dropout构造正负样本,本质是数据增广,存在训练和预测不一致的问题:

NLP,对比学习SimCSE和Rdrop的区别_第1张图片

对比学习的思想在于:将样本和与它语义相似的样本(正样例)和与它语义不相似的样本(负样例)进行对比,希望通过设计模型结构和对比损失,使语义相近的样本对应的表示在表示空间更接近,语义不相近的样本的距离更远,以达到类似聚类的效果。

对于无监督来说,作者使用了Droupout来构建正例,将一个样本经过encoder两次,就得到了一个正例对,负例则是同一个batch里的其它句子。

其实,本质上是在用Dropout来添加噪音。传统的方法有词替,裁剪以及回译,但是作者发现这些方法都没有简单的dropout效果好。

R-Drop: 模型层直接操作,Regularized Dropout for Neural Networks对于每个训练样本,R-Drop最小化通过dropout采样的两个子模型的输出分布之间的KL散度,进行约束,缓解预测和训练不一致的问题。

NLP,对比学习SimCSE和Rdrop的区别_第2张图片
同一个step里面,对于同一个输入x,模型通过次"Dropout"操作,前向传播两次,由于Dropout的存在,会得到两个不同但差异很小的概率分布,通过在原来的交叉熵损失中加入这两个分布的KL散度损失,来共同进行反向传播,参数更新。
在这里插入图片描述
NLP,对比学习SimCSE和Rdrop的区别_第3张图片

首先,在训练时,因为Dropout每次随机丢弃部分神经元,导致每次丢弃后产生的子模型都不一样,所以 Dropout 的操作一定程度上使得训练后的模型是一种多个子模型的组合。

其次,Dropout在训练是开启,在测试时关闭,因此训练和测试存在不一致性。而R-Drop 在训练过程中通过刻意对子模型之间的输出进行约束,让两次的输出保持一致,从而降低了训练和测试的不一致性。

特别的,作者在附录中,通过数学证明,R-Drop通过引入同一个样本,经过同一个模型的不同Dropout,输出的概率要尽可能相等的优化目标,等价于令模型的所有参数尽可能相等的正则化约束。

总结

SimCSE、ESimCSE通过Dropout实现样本和与它语义相似的样本(正样例)和与它语义不相似的样本(负样例)进行对比,希望通过设计模型结构和对比损失,使语义相近的样本对应的表示在表示空间更接近,语义不相近的样本的距离更远,适合用于聚类,向量检索等。

R-Drop利用Dropout的随机性,缓解了训练和测试的不一致性,在有监督的场景下适用于所有带Dropout的模型,简单粗暴,但效果出众。

代码展示

simcse通过一个batch内自己个自己构造正样本,和其他构造负样本,通过人工的阈值来调节对应的输出,标签直接自定义0,1,2,3…

Rdrop通过自己和自己的约束,来避免训练和预测的不一致

    def forward(self,
                query_input_ids,
                title_input_ids,
                query_token_type_ids=None,
                query_position_ids=None,
                query_attention_mask=None,
                title_token_type_ids=None,
                title_position_ids=None,
                title_attention_mask=None):

        query_cls_embedding = self.get_pooled_embedding(
            query_input_ids, query_token_type_ids, query_position_ids,
            query_attention_mask)

        title_cls_embedding = self.get_pooled_embedding(
            title_input_ids, title_token_type_ids, title_position_ids,
            title_attention_mask)

        logits1 = self.classifier(query_cls_embedding)
        logits2 = self.classifier(title_cls_embedding)
        kl_loss = self.rdrop_loss(logits1, logits2)

        cosine_sim = paddle.matmul(
            query_cls_embedding, title_cls_embedding, transpose_y=True)

        # substract margin from all positive samples cosine_sim()
        margin_diag = paddle.full(
            shape=[query_cls_embedding.shape[0]],
            fill_value=self.margin,
            dtype=paddle.get_default_dtype())

        cosine_sim = cosine_sim - paddle.diag(margin_diag)

        # scale cosine to ease training converge
        cosine_sim *= self.sacle

        labels = paddle.arange(0, query_cls_embedding.shape[0], dtype='int64')
        labels = paddle.reshape(labels, shape=[-1, 1])

        loss = F.cross_entropy(input=cosine_sim, label=labels)

        return loss, kl_loss

你可能感兴趣的:(NLP实战项目,笔记,术语,自然语言处理,学习,聚类,人工智能)