基于transformers框架实践Bert系列4-文本相似度

本系列用于Bert模型实践实际场景,分别包括分类器、命名实体识别、选择题、文本摘要等等。(关于Bert的结构和详细这里就不做讲解,但了解Bert的基本结构是做实践的基础,因此看本系列之前,最好了解一下transformers和Bert等)
本篇主要讲解文本相似度应用场景。本系列代码和数据集都上传到GitHub上:https://github.com/forever1986/bert_task

目录

  • 1 环境说明
  • 2 前期准备
    • 2.1 了解Bert的输入输出
    • 2.1 了解Bert的输入输出
    • 2.2 数据集与模型
    • 2.3 任务说明
    • 2.4 实现关键
  • 3 关键代码
    • 3.1 数据集处理
    • 3.2 模型加载
    • 3.3 评估函数
  • 4 整体代码
  • 5 运行效果

1 环境说明

1)本次实践的框架采用torch-2.1+transformer-4.37
2)另外还采用或依赖其它一些库,如:evaluate、pandas、datasets、accelerate等

2 前期准备

2.1 了解Bert的输入输出

Bert模型是一个只包含transformer的encoder部分,并采用双向上下文和预测下一句训练而成的预训练模型。可以基于该模型做很多下游任务。

2.1 了解Bert的输入输出

Bert的输入:input_ids(使用tokenizer将句子向量化),attention_mask,token_type_ids(句子序号)、labels(结果)
Bert的输出:
last_hidden_state:最后一层encoder的输出;大小是(batch_size, sequence_length, hidden_size)
pooler_output:这是序列的第一个token(classification token)的最后一层的隐藏状态,输出的大小是(batch_size, hidden_size),它是由线性层和Tanh激活函数进一步处理的。(通常用于句子分类,至于是使用这个表示,还是使用整个输入序列的隐藏状态序列的平均化或池化,视情况而定)。(注意:这是关键输出,本次任务就需要获取该值,并进行相似度计算
hidden_states: 这是输出的一个可选项,如果输出,需要指定config.output_hidden_states=True,它也是一个元组,它的第一个元素是embedding,其余元素是各层的输出,每个元素的形状是(batch_size, sequence_length, hidden_size)
attentions:这是输出的一个可选项,如果输出,需要指定config.output_attentions=True,它也是一个元组,它的元素是每一层的注意力权重,用于计算self-attention heads的加权平均值。

2.2 数据集与模型

1)数据集来自:Chinese_Text_Similarity
2)模型权重使用:bert-base-chinese

2.3 任务说明

1)文本相似度任务就是判断2段文本的相似程度,可以理解为是否表达相同的意思。这时可能想到的最简单的方法就是将2段文本作为输入,label是0或1这样一个分类方法,可以采用系列1(情感分类)的方式。但是如果找是一段文本对应多个文本之间的相似度呢?或许你会想到系列3(选择题)的方式。但是如果是一段文本对应几十万的文本之间的相似度呢?虽然系列3(选择题)也能解决问题,但是会很慢,因为你要一一匹配。这里我们可以采用一个特征提取方式,先将文本输入到模型做特征,最后在通过相似度比较函数对2段文本的特征进行比较即可,虽然也是需要每段文本都做比较,但是好处是先将文本做好特征。
在这里插入图片描述
2)这时候我们需要做的是分别将数据放入同一个BERT模型进行特征提取,然后通过相似度和余弦相似度损失计算进行模型训练即可

2.4 实现关键

1)将数据处理成对放入模型中
2)自定义模型,在forward中对2个句子分别通过bert做特征提取,然后计算相似度和余弦相似度损失

3 关键代码

3.1 数据集处理

Chinese_Text_Similarity数据集是一个txt文件,每一行分别存储“句子1”、“句子2”、“相似度”。下面代码就是读取数据并处理为模型想要的类型

# 读取数据
df = pd.read_csv(data_path, sep='\s+')
df = df.sample(n=5000)  # 取其中5000条
datasets = Dataset.from_pandas(df)
# 划分训练集和测试集
datasets = datasets.train_test_split(test_size=0.1, shuffle=True, seed=42)
# 划分训练集和验证集
train_datasets = datasets["train"].train_test_split(test_size=0.05, shuffle=True, seed=42)
datasets["train"] = train_datasets["train"]
datasets["validation"] = train_datasets["test"]
tokenizer = BertTokenizerFast.from_pretrained(model_path)# 数据处理函数
def process_function(datas):sentences = []labels = []for sentence1, sentence2, label in zip(datas["句子1"], datas["句子2"], datas["相似度"]):sentences.append(sentence1)sentences.append(sentence2)labels.append(1 if int(label) == 1 else -1)tokenized_datas = tokenizer(sentences, max_length=256, truncation=True, padding="max_length")# 关键点:这里将2条数据合并为一组,也就是reshape,从(2倍datas数量 * max_length),变成(datas数量 * 2 * max_length)tokenized_datas = {k: [v[i: i + 2] for i in range(0, len(v), 2)] for k, v in tokenized_datas.items()}tokenized_datas["labels"] = labelsreturn tokenized_datasnew_datasets = datasets.map(process_function, batched=True)

3.2 模型加载

自定义模型,模仿transformers中的其它BERT模型,继承BertPreTrainedModel(为了方便使用XXX.from_pretrained()获取模型),参照其它BERT模型写法,重新init和forward方法

class SimilarityModel(BertPreTrainedModel):# 不需要增加其它层def __init__(self, config: PretrainedConfig, *inputs, **kwargs):super().__init__(config, *inputs, **kwargs)self.bert = BertModel(config)self.post_init()# 在forward中对2个句子分别通过bert做特征提取,然后计算相似度和余弦相似度损失def forward(self,input_ids: Optional[torch.Tensor] = None,attention_mask: Optional[torch.Tensor] = None,token_type_ids: Optional[torch.Tensor] = None,position_ids: Optional[torch.Tensor] = None,head_mask: Optional[torch.Tensor] = None,inputs_embeds: Optional[torch.Tensor] = None,labels: Optional[torch.Tensor] = None,output_attentions: Optional[bool] = None,output_hidden_states: Optional[bool] = None,return_dict: Optional[bool] = None,):return_dict = return_dict if return_dict is not None else self.config.use_return_dict# 分别获取sentenceA 和 sentenceB的输入senA_input_ids, senB_input_ids = input_ids[:, 0], input_ids[:, 1]senA_attention_mask, senB_attention_mask = attention_mask[:, 0], attention_mask[:, 1]senA_token_type_ids, senB_token_type_ids = token_type_ids[:, 0], token_type_ids[:, 1]# 分别获取sentenceA 和 sentenceB的向量表示senA_outputs = self.bert(senA_input_ids,attention_mask=senA_attention_mask,token_type_ids=senA_token_type_ids,position_ids=position_ids,head_mask=head_mask,inputs_embeds=inputs_embeds,output_attentions=output_attentions,output_hidden_states=output_hidden_states,return_dict=return_dict,)# 获得pooler_outputsenA_pooled_output = senA_outputs[1]senB_outputs = self.bert(senB_input_ids,attention_mask=senB_attention_mask,token_type_ids=senB_token_type_ids,position_ids=position_ids,head_mask=head_mask,inputs_embeds=inputs_embeds,output_attentions=output_attentions,output_hidden_states=output_hidden_states,return_dict=return_dict,)senB_pooled_output = senB_outputs[1]  # [batch, hidden]# 计算相似度cos = CosineSimilarity()(senA_pooled_output, senB_pooled_output)# 计算lossloss = Noneif labels is not None:loss_fct = CosineEmbeddingLoss(0.3)loss = loss_fct(senA_pooled_output, senB_pooled_output, labels)output = (cos,)return ((loss,) + output) if loss is not None else outputmodel = SimilarityModel.from_pretrained(model_path)

3.3 评估函数

这里采用evaluate库加载accuracy准确度计算方式来做评估,本次实验将accuracy和f1的计算py文件下载下来,因此也是本地加载

# 评估函数:此处的评估函数可以从https://github.com/huggingface/evaluate下载到本地
acc_metric = evaluate.load("./evaluate/metric_accuracy.py")
f1_metric = evaluate.load("./evaluate/metric_f1.py")def evaluate_function(eval_predict):predictions, labels = eval_predictpredictions = [int(p > 0.7) for p in predictions]labels = [int(l > 0) for l in labels]acc = acc_metric.compute(predictions=predictions, references=labels)f1 = f1_metric.compute(predictions=predictions, references=labels)acc.update(f1)return acc

4 整体代码

"""
基于BERT做文本相似度
1)数据集来自:Chinese_Text_Similarity
2)模型权重使用:bert-base-chinese
"""# step 1 引入数据库
import torch
from torch.nn import CosineSimilarity, CosineEmbeddingLossimport evaluate
import pandas as pd
from typing import Optional
from datasets import Dataset
from transformers import TrainingArguments, Trainer, BertTokenizerFast, BertPreTrainedModel, PretrainedConfig, BertModelmodel_path = "./model/tiansz/bert-base-chinese"
data_path = "./data/Chinese_Text_Similarity.txt"# step 2 数据集处理
df = pd.read_csv(data_path, sep='\s+')
df = df.sample(n=5000)  # 取其中5000条
datasets = Dataset.from_pandas(df)
# 划分训练集和测试集
datasets = datasets.train_test_split(test_size=0.1, shuffle=True, seed=42)
# 划分训练集和验证集
train_datasets = datasets["train"].train_test_split(test_size=0.05, shuffle=True, seed=42)
datasets["train"] = train_datasets["train"]
datasets["validation"] = train_datasets["test"]
tokenizer = BertTokenizerFast.from_pretrained(model_path)def process_function(datas):sentences = []labels = []for sentence1, sentence2, label in zip(datas["句子1"], datas["句子2"], datas["相似度"]):sentences.append(sentence1)sentences.append(sentence2)labels.append(1 if int(label) == 1 else -1)tokenized_datas = tokenizer(sentences, max_length=256, truncation=True, padding="max_length")# 这里将2条数据合并为一组,也就是reshape,从(2倍datas数量 * max_length),变成(datas数量 * 2 * max_length)tokenized_datas = {k: [v[i: i + 2] for i in range(0, len(v), 2)] for k, v in tokenized_datas.items()}tokenized_datas["labels"] = labelsreturn tokenized_datasnew_datasets = datasets.map(process_function, batched=True)# step 3 加载模型
class SimilarityModel(BertPreTrainedModel):def __init__(self, config: PretrainedConfig, *inputs, **kwargs):super().__init__(config, *inputs, **kwargs)self.bert = BertModel(config)self.post_init()def forward(self,input_ids: Optional[torch.Tensor] = None,attention_mask: Optional[torch.Tensor] = None,token_type_ids: Optional[torch.Tensor] = None,position_ids: Optional[torch.Tensor] = None,head_mask: Optional[torch.Tensor] = None,inputs_embeds: Optional[torch.Tensor] = None,labels: Optional[torch.Tensor] = None,output_attentions: Optional[bool] = None,output_hidden_states: Optional[bool] = None,return_dict: Optional[bool] = None,):return_dict = return_dict if return_dict is not None else self.config.use_return_dict# Step1 分别获取sentenceA 和 sentenceB的输入senA_input_ids, senB_input_ids = input_ids[:, 0], input_ids[:, 1]senA_attention_mask, senB_attention_mask = attention_mask[:, 0], attention_mask[:, 1]senA_token_type_ids, senB_token_type_ids = token_type_ids[:, 0], token_type_ids[:, 1]# Step2 分别获取sentenceA 和 sentenceB的向量表示senA_outputs = self.bert(senA_input_ids,attention_mask=senA_attention_mask,token_type_ids=senA_token_type_ids,position_ids=position_ids,head_mask=head_mask,inputs_embeds=inputs_embeds,output_attentions=output_attentions,output_hidden_states=output_hidden_states,return_dict=return_dict,)senA_pooled_output = senA_outputs[1]  # [batch, hidden]senB_outputs = self.bert(senB_input_ids,attention_mask=senB_attention_mask,token_type_ids=senB_token_type_ids,position_ids=position_ids,head_mask=head_mask,inputs_embeds=inputs_embeds,output_attentions=output_attentions,output_hidden_states=output_hidden_states,return_dict=return_dict,)senB_pooled_output = senB_outputs[1]  # [batch, hidden]# step3 计算相似度cos = CosineSimilarity()(senA_pooled_output, senB_pooled_output)  # [batch, ]# step4 计算lossloss = Noneif labels is not None:loss_fct = CosineEmbeddingLoss(0.3)loss = loss_fct(senA_pooled_output, senB_pooled_output, labels)output = (cos,)return ((loss,) + output) if loss is not None else outputmodel = SimilarityModel.from_pretrained(model_path)# step 4 评估函数:此处的评估函数可以从https://github.com/huggingface/evaluate下载到本地
acc_metric = evaluate.load("./evaluate/metric_accuracy.py")
f1_metric = evaluate.load("./evaluate/metric_f1.py")def evaluate_function(eval_predict):predictions, labels = eval_predictpredictions = [int(p > 0.7) for p in predictions]labels = [int(l > 0) for l in labels]acc = acc_metric.compute(predictions=predictions, references=labels)f1 = f1_metric.compute(predictions=predictions, references=labels)acc.update(f1)return acc# step 5 创建TrainingArguments
# train是4275条数据,batch_size=32,因此每个epoch的step=134,总step=402
train_args = TrainingArguments(output_dir="./checkpoints",      # 输出文件夹per_device_train_batch_size=32,  # 训练时的batch_sizeper_device_eval_batch_size=32,    # 验证时的batch_sizenum_train_epochs=3,              # 训练轮数logging_steps=50,                # log 打印的频率evaluation_strategy="epoch",     # 评估策略save_strategy="epoch",           # 保存策略save_total_limit=3,              # 最大保存数load_best_model_at_end=True      # 训练完成后加载最优模型)# step 6 创建Trainer
trainer = Trainer(model=model,args=train_args,train_dataset=new_datasets["train"],eval_dataset=new_datasets["validation"],compute_metrics=evaluate_function,)# step 7 训练
trainer.train()# step 8 模型评估
evaluate_result = trainer.evaluate(new_datasets["test"])
print(evaluate_result)# step 9 模型预测
class SentenceSimilarityPipeline:def __init__(self, model, tokenizer) -> None:self.model = model.bertself.tokenizer = tokenizerself.device = model.devicedef preprocess(self, senA, senB):return self.tokenizer([senA, senB], max_length=128, truncation=True, return_tensors="pt", padding=True)def predict(self, inputs):inputs = {k: v.to(self.device) for k, v in inputs.items()}return self.model(**inputs)[1]  # [2, 768]def postprocess(self, logits):cos = CosineSimilarity()(logits[None, 0, :], logits[None,1, :]).squeeze().cpu().item()return cosdef __call__(self, senA, senB):inputs = self.preprocess(senA, senB)logits = self.predict(inputs)result = self.postprocess(logits)if result >= 0.7:return "相似"return "不相似"pipe = SentenceSimilarityPipeline(model, tokenizer)
print(pipe("广东哪里最好玩啊?", "广东最好玩的地方在哪?"))

5 运行效果

在这里插入图片描述

注:本文参考来自大神:https://github.com/zyds/transformers-code

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32入门笔记(02):USART串口通信注意事项笔记(SPL库函数版)

这是通过串口通信发送过来的数据,里面包括了故障码,电压,电流,频率等信息,请你用STM32f103系列单片机的串口1读取该数据并解析出电压和电流是多少? 要用STM32F103系列单片机的串口1读取并解析发电机上的逆…

【Django项目】 音乐网站spotify复刻

代码:https://github.com/tomitokko/spotify-clone 注:该项目不是自己提供mp3文件,而是使用spotify 的api接口获取。

MySQL主从复制故障:“ Slave_SQL_Running:No“ 两种解决办法

问题 今天搭建MySQL的主从复制,查看从机状态时show slave status\G,发现这个参数为NO,导致主从复制失败。 Slave_SQL_Running: No 后面上网查阅了一下资料,大概就是因为在连接支持数据库后,也就是这个命令后&#xff…

c-lodop 打印面单 内容串页

场景:使用c-lodop程序调取打印机连续打印多张快递单时,上页内容,打到了下一页了 问题原因: 由于是将所有面单内容放到了一个页面,c-lodop 在打印时,发现一页放不下,会自动分割成多页 页面元素…

【在Postman中,如果后端返回的是String类型的数据但不是JSON格式,报错】

在Postman中,如果后端返回的是String类型的数据但不是JSON格式 问题描述解决办法 postman后端返回的String数据,不是json,怎么设置结果的接收? 问题描述 在postman中测试接口,报错Error:Abort,或者显示返回数据校验失…

coveralls使用pytest进行本地测试时报错SyntaxError: invalid escape sequence \S

## 错误复现: git clone gitgithub.com:TheKevJames/coveralls-python.git cd coveralls-python poetry install poetry run pytest## 错误内容: ## 完整的打印信息 test session starts platform darwin -- Python 3.8.18, pytest-8.2.1, pluggy-1.5.…

使用 LlamaParse 进行 PDF 解析并创建知识图谱

此 Python 笔记本提供了有关利用 LlamaParse 从 PDF 文档中提取信息并随后将提取的内容存储到 Neo4j 图形数据库中的综合指南。本教程在设计时考虑到了实用性,适合对文档处理、信息提取和图形数据库技术感兴趣的开发人员、数据科学家和技术爱好者。 该笔记本电脑的主…

可视化大屏,不搞点3D效果,感觉有点对不起观众呢。

使用3D模型可以为可视化展现增加更多的维度和真实感,提供更直观、生动的视觉效果。以下是一些3D模型在可视化展现中的作用: 增强沉浸感:通过使用3D模型,可以让观众感受到更真实的场景和物体,增强他们的沉浸感。这有助…

【数组】Leetcode 452. 用最少数量的箭引爆气球【中等】

用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直 地…

Freeswitch-Python3开发

文章目录 一、Freeswitch如何使用mod_python31.1 Freeswitch和python1.2 Freeswitch版本选择1.3 Freeswitch编译mod_python31.3.1 debian安装python31.3.2 Freeswitch编译mod_python31.3.3 加载 二、如何编写脚本2.1 函数的基本框架2.2 基本使用2.2.1 触发条件2.2.2 默认脚本位…

pycharm更改远程编辑器设置

pycharm的远程编辑器可以分为两个部分:SFTP服务器(用于传输文件)和命令行(用于控制linux)系统。那么当创建完成后,如何才能更改其设置呢? 一、远程SFTP服务器设置 找到导航栏依次点击tools-deployment-co…

运行vue2项目基本过程

目录 步骤1 步骤2 步骤3 补充: 解决方法: node-scss安装失败解决办法 步骤1 安装npm 步骤2 切换淘宝镜像 #最新地址 淘宝 NPM 镜像站喊你切换新域名啦! npm config set registry https://registry.npmmirror.com 步骤3 安装vue-cli npm install…

采用伪代码及C代码演示如何解决脱机最小值问题

采用伪代码及C代码演示如何解决脱机最小值问题 问题背景算法设计伪代码实现C代码实现证明数组正确性使用不相交集合数据结构最坏情况运行时间的紧确界 问题背景 脱机最小值问题涉及到一个动态集合 ( T ) (T) (T&…

并网逆变器学习笔记9---VSG控制

参考文献:《新型电力系统主动构网机理与技术路径》 “构网技术一般包含下垂控制,功率同步控制,虚拟同步机控制,直接功率控制,虚拟振荡器控制 等。其中,虚拟同步机技术,即 VSG,因其物…

蓝牙(2):BR/EDR的连接过程;查询(发现)=》寻呼(连接)=》安全建立=》认证=》pair成功;类比WiFi连接过程。

4.2.1 BR/EDR 流程: 查询(发现)》寻呼(连接)》安全建立》认证》pair成功 4.2.1.1 查询(发现)流程Inquiry (discovering) 类比WiFi的probe request/response 蓝牙设备使用查询流程来发现附近的…

Github 2024-05-24 Java开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-24统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目10Java设计模式:提高开发效率的正规化实践 创建周期:3572 天开发语言:Java协议类型:OtherStar数量:86766 个Fork数量:25959 次关…

探数API统计分享-1949年-2021年中国历年夏粮产量统计报告

​​​​​​​​中国历年夏粮产量​,为1949年到2021年我国每年的夏粮产量数据。2021年,我国夏粮产量为14596万吨,比上年增长2.2%。 数据统计单位为:万吨 。 我国夏粮产量有多少? 2021年,我国夏粮产量为1…

202206青少年软件编程(Python)等级考试试卷(三级)

第 1 题 【单选题】 下图所示, 有一个名为"书目.csv"的文件。 小明针对这个文件编写了 5 行代码,请问, 代码运行到最后打印在屏幕上的结果是? ( ) with open(书目.csv, r, encoding=utf-8) as f:for line in f.readlines

利用Axure模板快速设计,可视化大屏信息大屏,含近200例资源和各类部件

模板类别: **通用模板:**提供基础的布局和设计元素,适用于各种场景。 **行业特定模板:**如农业、医院、销售、能源、物流、政府机关等,针对不同行业提供专业模板。 **数据展示模板:**包括大数据驾驶舱、统…