论文复现-2:ConSERT: A Contrastive Framework for Self-Supervised Sentence Representation Transfer


在这里插入图片描述

代码实现部分

1.数据集

Chinese dataset:ATEC-CCKS
数据集中是sentence pair形式存储的数据集,每行数据包括两个内容,一个是original sentence,一个是reference sentence。
然后,数据的最后一行是相似度判断一个sentence pair是不是相似的。
0表示不相似,1表示相似。

数据集示例如下:

我提前还款之后,还可以再借么 如果提前还清款项。可以在借款 还款后能否在借钱 1
蚂蚁花呗怎么申请临时额度 我的花呗固定额度不张临时额度老张,为什么不给涨固定的 0
为什么我还完钱借呗可以使用,过了一两天就不能用用了 为什么我的蚂蚁借呗用不了了?一直正常还款 0
没信用卡可以吗 换卡好了 1
花呗有额度,信用住自动扣款扣不了花呗 花呗没有信用住了嘛 0

2.模型部分

在论文中,作者将整个模型分为了三个部分,分别是数据增强、encoder和对比学习模块。
数据增强是使用cut off 和 token shuffle 和 dropout做数据增强(是在embedding matricx上的数据增强工作)。
encoder是使用bert的encoder,使用的average pooling做的constractive loss计算。
然后是对比学习模块,将增强后的samples作为正例,batch中的其余samples作为负例。

模型的总体框架
data_utils:是做的数据加载,中文数据和英文数据的加载
main:主函数运行

2.1 main 主文件

模型部分

模型是SentenceTransformer
在之后的model.fit以及其他过程中,都是使用的sentence transformer中的文件

在bert embedding中,是通过models.Transformer函数得到的Word 的embedding。
word_embedding_model = models.Transformer(args.model_name_or_path)
然后,通过models.Pooling得到的pooling model

数据加载部分

在对中文数据做相似度判断时,一般使用的是hfl/chinese-roberta-wwm-ext
model 的config 文件:

model 的加载
{
  "architectures": [
    "BertForMaskedLM"
  ],
  "attention_probs_dropout_prob": 0.1,
  "bos_token_id": 0,
  "directionality": "bidi",
  "eos_token_id": 2,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "output_past": true,
  "pad_token_id": 0,
  "pooler_fc_size": 768,
  "pooler_num_attention_heads": 12,
  "pooler_num_fc_layers": 3,
  "pooler_size_per_head": 128,
  "pooler_type": "first_token_transform",
  "type_vocab_size": 2,
  "vocab_size": 21128
}

在train setting设置时,是将config中的attention_probs_dropout_probhidden_dropout_prob的参数设置为0.0
然后,做了重写工作。

sentence embedding的得到

在获得sentence embedding时,使用的是SentenceTransformer模型。SentenceTransformer模型是为了将sentences或者text映射到对应的embedding。

train 数据

train_samples加载的函数使用的是load_chinese_tsv_data

在加载数据时,还使用到一个工具包是 InputExample。InputExample 是一个class,可以以字典形式存储相关的信息。
数据形式如下:

self.guid = guid
self.texts = [text.strip() for text in texts] if texts is not None else texts
self.texts_tokenized = texts_tokenized
self.label = label

最终的数据是以列表形式存储的。
数据的最终存储是:

sent1, sent2, label = line.strip().split(“\t”)
all_samples.append(InputExample(texts=[sent1]))

train_dataset加载的函数使用的是SentencesDataset
train_dataloader 加载的函数使用的是Dataloader

损失函数使用的是loss.AdvCLSoftmaxLoss

dev 数据

dev数据加载使用的是load_chinese_tsv_data
dev数据相似度的测评 EmbeddingSimilarityEvaluator
在使用EmbeddingSimilarityEvaluator时,是Evaluate a model based on the similarity of the embeddings by calculating the Spearman and Pearson rank correlation
选择的similarity function是COSINE函数

在得到句子的embedding上,使用的model.encode函数。

几种不同的相似度函数下的distance测量:

cosine_scores = 1 - (paired_cosine_distances(embeddings1, embeddings2))
manhattan_distances = -paired_manhattan_distances(embeddings1, embeddings2)
euclidean_distances = -paired_euclidean_distances(embeddings1, embeddings2)
dot_products = [np.dot(emb1, emb2) for emb1, emb2 in zip(embeddings1, embeddings2)]

几种相关系数下的计算(pearsonr/spearmanr)

eval_pearson_cosine, _ = pearsonr(labels, cosine_scores)
eval_spearman_cosine, _ = spearmanr(labels, cosine_scores)

eval_pearson_manhattan, _ = pearsonr(labels, manhattan_distances)
eval_spearman_manhattan, _ = spearmanr(labels, manhattan_distances)

eval_pearson_euclidean, _ = pearsonr(labels, euclidean_distances)
eval_spearman_euclidean, _ = spearmanr(labels, euclidean_distances)

eval_pearson_dot, _ = pearsonr(labels, dot_products)
eval_spearman_dot, _ = spearmanr(labels, dot_products)

以上这些函数是从已经预定好的库中引入的,是从已经定义好的python tool中导入的。

from sklearn.metrics.pairwise import paired_cosine_distances, paired_euclidean_distances, paired_manhattan_distances
from scipy.stats import pearsonr, spearmanr
model 的train

使用的是函数:model.fit()
在model.fit中,模型的参数有:

train_objectives: Iterable[Tuple[DataLoader, nn.Module]],
            evaluator: SentenceEvaluator = None,
            epochs: int = 1,
            steps_per_epoch = None,
            scheduler: str = 'WarmupLinear',
            warmup_steps: int = 10000,
            optimizer_class: Type[Optimizer] = transformers.AdamW,
            optimizer_params : Dict[str, object]= {'lr': 2e-5, 'eps': 1e-6, 'correct_bias': False},
            weight_decay: float = 0.01,
            evaluation_steps: int = 0,
            output_path: str = None,
            save_best_model: bool = True,
            max_grad_norm: float = 1,
            use_amp: bool = False,
            use_apex_amp: bool = False,
            apex_amp_opt_level: str = None,
            callback: Callable[[float, int, int], None] = None,
            output_path_ignore_not_empty: bool = False,
            early_stop_patience: Optional[int] = None

train_objectives,有两项,一项是dataloader,一项是loss。
在optimizer和schedule的参数更新设定,模型中做了如下定义:

更新参数
param_optimizer = list(loss_model.named_parameters())

            no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']
            optimizer_grouped_parameters = [
                {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay': weight_decay},
                {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay': 0.0}
            ]

            optimizer = optimizer_class(optimizer_grouped_parameters, **optimizer_params)
            scheduler_obj = self._get_scheduler(optimizer, scheduler=scheduler, warmup_steps=warmup_steps, t_total=num_train_steps)

            optimizers.append(optimizer)
            schedulers.append(scheduler_obj)
train process
        for epoch in trange(epochs, desc="Epoch"):
            training_steps = 0

            for loss_model in loss_models:
                loss_model.zero_grad()
                loss_model.train()

            for _ in trange(steps_per_epoch, desc="Iteration", smoothing=0.05):
                for train_idx in range(num_train_objectives):
                    loss_model = loss_models[train_idx]
                    optimizer = optimizers[train_idx]
                    scheduler = schedulers[train_idx]
                    data_iterator = data_iterators[train_idx]

                    try:
                        data = next(data_iterator)
                    except StopIteration:
                        #logging.info("Restart data_iterator")
                        data_iterator = iter(dataloaders[train_idx])
                        data_iterators[train_idx] = data_iterator
                        data = next(data_iterator)

                    features, labels = batch_to_device(data, self._target_device)

                    if use_amp:
                        with autocast():
                            loss_value = loss_model(features, labels)

                        scale_before_step = scaler.get_scale()
                        scaler.scale(loss_value).backward()
                        scaler.unscale_(optimizer)
                        torch.nn.utils.clip_grad_norm_(loss_model.parameters(), max_grad_norm)
                        scaler.step(optimizer)
                        scaler.update()

                        skip_scheduler = scaler.get_scale() != scale_before_step
                    else:
                        loss_value = loss_model(features, labels)
                        self.tensorboard_writer.add_scalar(f"train_loss_{train_idx}", loss_value.item(), global_step=self.global_step)
                        if use_apex_amp:
                            with amp.scale_loss(loss_value, optimizer) as scaled_loss_value:
                                scaled_loss_value.backward()
                            torch.nn.utils.clip_grad_norm_(amp.master_params(optimizer), max_grad_norm)
                        else:
                            loss_value.backward()
                            torch.nn.utils.clip_grad_norm_(loss_model.parameters(), max_grad_norm)
                        optimizer.step()

                    optimizer.zero_grad()

                    if not skip_scheduler:
                        scheduler.step()

test数据集的完成类似于dev数据集。使用的是相同的函数
在进行evaluation过程时,使用的函数是eval_chinese_unsup(model_save_path, dataset_name, batch_size=16, main_similarity=SimilarityFunction.COSINE)

3.损失函数与优化器

模型的损失函数的定义
在这里插入图片描述

4.模型训练与推理

在模型的train阶段,是执行的model.fit函数
在模型的evaluation阶段,是执行的EmbeddingSimilarityEvaluator 类

5.代码运行示例

代码存放在本电脑的地址是:

E:\23年研文件\W3-5\对比学习\论文源码执行结果.pdf

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【资源介绍】 Python基于BERT的中文文本相似度识别模型源码+项目说明.zipPython基于BERT的中文文本相似度识别模型源码+项目说明.zip 无监督学习:向量白化、对比学习 bertwhitening:bert输出向量白化 论文:Whitening Sentence Representations for Better Semantics and Faster Retrieval 训练数据:lcqmc随机选取10000语句,抛弃标签。 SimCSE_unsupervised:采用与论文相同的损失函数 论文:SimCSE: Simple Contrastive Learning of Sentence Embeddings 训练数据:lcqmc随机选取10000语句,抛弃标签。 SimCSE_unsupervised_sp:采用与苏剑林相同的损失函数 训练数据:同上 SimCSE_unsupervised_sp_simplified:采用与苏剑林相同的损失函数,从transformers加载bert 训练数据:同上 SimCSE_unsupervised_simplified:采用与论文相同的损失函数,从transformers加载bert 训练数据:同上 ConSERT_unsupervised_shuffle:对posids进行shuffle 论文:ConSERT: A Contrastive Framework for Self-Supervised Sentence Representation Transfer 训练数据:同上 ESimCSE_unsupervised_endpoints: 采用与论文相同的损失函数 论文:ESimCSE: Enhanced Sample Building Method for Contrastive Learning of Unsupervised Sentence Embedding 训练数据:同上 ## 监督学习:双塔模型、对比学习 SBERT:SentenceBERT 论文Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks 训练数据:lcqmc训练集 SBERT:SentenceBERT_simplified, 从transformers加载bert 论文:同上 训练数据:同上 SimCSE_supervised:采用与论文相同的损失函数 训练数据:snli随机选取10000条数据,数据格式[sentence,sentence_entailment,sentence_contradiction] 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,也适用于小白学习入门进阶。当然也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或者热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载,沟通交流,互相学习,共同进步!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YingJingh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值