作者 | 上杉翔二 悠闲会 · 信息检索 整理 | NewBeeNLP
前已经有博文整理过了对比学习的概念,比较重要且流行的文章,和一些已经有的应用:
本篇博文继续整理几篇代表性的对比学习在句子表征上的文章们。
EMNLP2021,简单方法大能量,即仅将标准dropout用作噪声在对比目标中进行预测。
如上图,有两种形式:
可以来看看对比学习部分的代码实现:
def cl_forward(cls,...): #对比学习的部分代码
return_dict = return_dict if return_dict is not None else cls.config.use_return_dict
ori_input_ids = input_ids
batch_size = input_ids.size(0)
# Number of sentences in one instance
# 2: pair instance; 3: pair instance with a hard negative
num_sent = input_ids.size(1)
mlm_outputs = None
# Flatten input for encoding
input_ids = input_ids.view((-1, input_ids.size(-1))) # (bs * num_sent, len)
attention_mask = attention_mask.view((-1, attention_mask.size(-1))) # (bs * num_sent len)
if token_type_ids is not None:
token_type_ids = token_type_ids.view((-1, token_type_ids.size(-1))) # (bs * num_sent, len)
# Get raw embeddings,得到原句子特征
outputs = encoder(
input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
position_ids=position_ids,
head_mask=head_mask,
inputs_embeds=inputs_embeds,
output_attentions=output_attentions,
output_hidden_states=True if cls.model_args.pooler_type in ['avg_top2', 'avg_first_last'] else False,
return_dict=True,
)
# MLM auxiliary objective,执行MLM任务
if mlm_input_ids is not None:
mlm_input_ids = mlm_input_ids.view((-1, mlm_input_ids.size(-1)))
mlm_outputs = encoder( #得到特征
mlm_input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids,
position_ids=position_ids,
head_mask=head_mask,
inputs_embeds=inputs_embeds,
output_attentions=output_attentions,
output_hidden_states=True if cls.model_args.pooler_type in ['avg_top2', 'avg_first_last'] else False,
return_dict=True,
)
# Pooling,池化
pooler_output = cls.pooler(attention_mask, outputs)
pooler_output = pooler_output.view((batch_size, num_sent, pooler_output.size(-1))) # (bs, num_sent, hidden)
# If using "cls", we add an extra MLP layer
# (same as BERT's original implementation) over the representation.
if cls.pooler_type == "cls":
pooler_output = cls.mlp(pooler_output)
# Separate representation,分别得到两个表示z1,z2
z1, z2 = pooler_output[:,0], pooler_output[:,1]
cos_sim = cls.sim(z1.unsqueeze(1), z2.unsqueeze(0)) #计算对比loss
labels = torch.arange(cos_sim.size(0)).long().to(cls.device)
loss_fct = nn.CrossEntropyLoss()
loss = loss_fct(cos_sim, labels)
句子级别特征的抽取任务。CLEAR的模型结构和SimCLR类似。因此这篇文章主要是提出了四种数据增强构建负例句子的方法,词汇删除(Word deletion)、词段删除(Span deletion)、词序重排(Reordering)、同义词替换(Synonym Substitution)。如下图所示。
其实构建的负例的方法会更多咯,可以参考一些自监督文章。
ACL2021,无监督句子级别的特征提取。其实也是探讨如何构建负例,这篇文章的架构如上图,使用对比学习的方法拉近相同文章中句子embedding之间的距离,拉远不同文章之间句子embedding之间的距离。
具体的做法是通过从同一文档中的其他部分采样文本段,并通过对抗loss来最大化上下文段落span的相似性,以学习句子的上下表示。
如下图,将三种类型的正例:部分与锚点重叠,与锚点相邻,以及包含于锚点中。两种类型的负例:来自于其他文档的易负例,来自于同一文档的难负例。
来自NAACL2022,主要基于dropout masks作为数据增强策略,作为不敏感转换学习对比学习损失和基于MLM语言模型进行词语替换的方法作为敏感转换学习,即原始句子与编辑句子之间的差异,共同优化句向量表征。
模型架构图如上,左侧为一个标准的SimCSE模型,右侧为一个带条件的句子差异预测模型。左侧不再赘述,右侧包含生成器和判别器。