基于Bert-base-chinese训练多分类文本模型(代码详解)

目录

一、简介

二、模型训练

三、模型推理


一、简介

BERT(Bidirectional Encoder Representations from Transformers)是基于深度学习在自然语言处理(NLP)领域近几年出现的、影响深远的创新模型之一。在BERT之前,已经有许多预训练语言模型,如ELMO和GPT,它们展示了预训练模型在NLP任务中的强大性能。然而,这些模型通常基于单向的上下文信息,即只考虑文本中的前向或后向信息,这限制了它们对文本的全局理解BERT旨在通过引入双向上下文信息来解决这一问题,从而更准确地表示文本中的语义信息

与传统的单向语言模型相比,BERT 的核心优势在于:

  • 双向性:BERT通过使用Transformer的编码器结构,能够同时从文本的左右两个方向学习上下文信息,使模型能够更好地理解句子中的每个词的语义。
  • 预训练与微调:通过预训练任务,BERT 可以在多种下游任务上进行快速微调。

其中,Bert-base-chinese模型是一个在简体和繁体中文文本上训练得到的预训练模型。

二、模型训练

数据示例如下,现在有一个data.csv文件,包含两列分别是特征(feature)和标签(label)。其中,标签可能是多个分类。

第一步:读取数据并提取出特征和标签
data = pd.read_csv('./data/data.csv', encoding='utf-8')   # 如果表格数据是gbk格式,则修改encoding='gbk'
X = data['feature']   # 特征列
y = data['label'].values   # 标签列
第二步:对标签数据进行编码转换
label_encoder = LabelEncoder()  # 初始化
y_encoded = label_encoder.fit_transform(y)
joblib.dump(label_encoder, './data/encoder.joblib') # 保存 label_encoder 以便以后使用
print(f'分类数:{len(label_encoder.classes_)} \n')  # 标签的类别数量
第三步:划分训练数据集和测试数据集
X_train, X_val, y_train, y_val = train_test_split(X, y_encoded, test_size=0.1,random_state=42)  # test_size=0.1表示训练集和测试集划分比例是9:1 
# random_state=42表示固定随机种子为42,保证每一次分割数据集都是一样的结果
第四步:加载BERT分词器
local_model_path = './bert-base-chinese'   # 模型地址
tokenizer = BertTokenizer.from_pretrained(local_model_path)
tokenizer.save_pretrained(best_model_path)
第五步:将文本数据转换成BERT模型能够理解的格式
def preprocess_for_bert(data, labels):input_ids = []attention_masks = []for sent in data:  # 对每个句子(sent)进行编码处理encoded_sent = tokenizer.encode_plus(text=sent,  # 要处理的句子add_special_tokens=True,  # 添加特殊标记,如句子的起始标记和结束标记max_length=256,  # 句子的最大长度为256个标记,超出部分将被截断,不足部分将被填充padding='max_length',  # 将句子填充到固定长度(256),不足部分会用0补齐return_attention_mask=True,  # 返回注意力掩码,用于标记哪些位置是填充部分,哪些位置是实际的句子内容truncation=True  # 如果句子超过了最大长度,进行截断)input_ids.append(encoded_sent.get('input_ids'))attention_masks.append(encoded_sent.get('attention_mask'))# 转换为PyTorch张量(tensor),以便后续可以输入到模型中进行训练或推理input_ids = torch.tensor(input_ids)attention_masks = torch.tensor(attention_masks)labels = torch.tensor(labels)return input_ids, attention_masks, labelstrain_inputs, train_masks, train_labels = preprocess_for_bert(X_train, y_train)
val_inputs, val_masks, val_labels = preprocess_for_bert(X_val, y_val)
第六步:创建训练集DataLoader和测试集DataLoader
train_data = TensorDataset(train_inputs, train_masks, train_labels)
train_sampler = RandomSampler(train_data)
train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=8)validation_data = TensorDataset(val_inputs, val_masks, val_labels)
validation_sampler = SequentialSampler(validation_data)
validation_dataloader = DataLoader(validation_data, sampler=validation_sampler, batch_size=8)
第七步:加载BERT模型
model = BertForSequenceClassification.from_pretrained(local_model_path, num_labels=len(label_encoder.classes_),ignore_mismatched_sizes=True)
model.cuda()  # 默认使用第一张显卡,如果没有显卡,则可以注释改行代码
第八步:设置优化器和调度器
EPOCHS = 5   # 训练次数,可以自定义修改
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)   # 优化器
total_steps = len(train_dataloader) * EPOCHS   # 训练步数
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=total_steps)  # 调度器
第九步:设置精确度的计算方式
def flat_accuracy(preds, labels):pred_flat = np.argmax(preds, axis=1).flatten()labels_flat = labels.flatten()return np.sum(pred_flat == labels_flat) / len(labels_flat)  # 通过比较预测类别和实际标签的相同之处,计算出预测正确的比例
第十步:训练和评估
best_model_path = './model' # 最优模型训练结果的保存路径
best_val_accuracy = 0  # 初始化最优精确度
for epoch in range(EPOCHS):model.train()  # 第一步:将模型设置为训练模式total_train_loss = 0  # 初始化训练总损失为0for step, batch in enumerate(train_dataloader):  # 第二步:加载训练集DataLoaderb_input_ids = batch[0].cuda()  # 如果没有显卡,则可以将.cuda给删除了b_input_mask = batch[1].cuda()  # 如果没有显卡,则可以将.cuda给删除了b_labels = batch[2].cuda().long()  # 如果没有显卡,则可以将.cuda给删除了model.zero_grad() # 清除模型的梯度outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels)  # 第三步:将输入数据传递给模型,得到模型的输出loss = outputs.loss # 第四步:提取出损失值,用于后续的反向传播total_train_loss += loss.item()loss.backward() # 第五步:进行反向传播,计算梯度torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)optimizer.step()scheduler.step()avg_train_loss = total_train_loss / len(train_dataloader)  # 第六步:更新学习率torch.cuda.empty_cache()  # 训练一轮就清空一次显卡缓存,如果没有显卡,则注释# 第七步:模型测试,计算准确度,处理逻辑和训练差不多model.eval()total_eval_accuracy = 0total_eval_loss = 0for batch in validation_dataloader:  # 加载测试集DataLoaderb_input_ids = batch[0].cuda()b_input_mask = batch[1].cuda()b_labels = batch[2].cuda().long()with torch.no_grad():outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels)loss = outputs.losstotal_eval_loss += loss.item()logits = outputs.logitslogits = logits.detach().cpu().numpy()label_ids = b_labels.to('cpu').numpy()total_eval_accuracy += flat_accuracy(logits, label_ids)avg_val_accuracy = total_eval_accuracy / len(validation_dataloader)avg_val_loss = total_eval_loss / len(validation_dataloader)torch.cuda.empty_cache() # 验证一轮就清空一次显卡缓存,如果没有显卡,则注释print(f'Training loss: {avg_train_loss}')print(f'Validation loss: {avg_val_loss}')print(f'Validation Accuracy: {avg_val_accuracy}')   # 主要看这个精度,一般准确率90%以上就可以投入实际生产环境中# 在验证集上计算准确率if avg_val_accuracy > best_val_accuracy:best_val_accuracy = avg_val_accuracy# 保存模型model.save_pretrained(best_model_path)   # 根据训练次数,保存最优的一个模型结果
完整代码如下:
import pandas as pd
import numpy as np
import joblib
import torch
import timefrom transformers import BertTokenizer, BertForSequenceClassification
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from torch.utils.data import DataLoader, RandomSampler, SequentialSampler, TensorDataset, random_split
from torch.optim import AdamW
from transformers import get_linear_schedule_with_warmup# 读取数据
data = pd.read_csv('./data/data.csv', encoding='utf-8')   # 如果表格数据是gbk,则修改encoding='gbk'# 最优模型训练结果的保存路径
best_model_path = './model'X = data['feature']   # 特征列
y = data['label'].values   # 标签列# 对标签数据进行编码转换
print("1、开始编码转换啦~")
label_encoder = LabelEncoder()  # 初始化
#label_encoder = joblib.load('./data/encoder.joblib')   # 当你使用同样的data第二次运行脚本时,就可以直接加载上一次保存的编码结果,而不需要重复编码(除非两次加载的数据有变动)
y_encoded = label_encoder.fit_transform(y)
print(f'分类数:{len(label_encoder.classes_)} \n')  # 标签的类别数量# 保存 label_encoder 以便以后使用
joblib.dump(label_encoder, './data/encoder.joblib')# 分割数据集
X_train, X_val, y_train, y_val = train_test_split(X, y_encoded, test_size=0.1,random_state=42)  # 这里训练和测试数据集比例是9:1,test_size=0.2或者0.3  固定随机种子42,保证每一次分割数据集都是一样的# 加载BERT分词器
local_model_path = './bert-base-chinese'
tokenizer = BertTokenizer.from_pretrained(local_model_path)
tokenizer.save_pretrained(best_model_path)# BERT预处理 -- 将文本数据转换成BERT模型能够理解的格式
def preprocess_for_bert(data, labels):input_ids = []attention_masks = []for sent in data:  # 对每个句子(sent)进行编码处理encoded_sent = tokenizer.encode_plus(text=sent,  # 要处理的句子add_special_tokens=True,  # 添加特殊标记,如句子的起始标记和结束标记max_length=256,  # 句子的最大长度为256个标记,超出部分将被截断,不足部分将被填充padding='max_length',  # 将句子填充到固定长度(256),不足部分会用0补齐return_attention_mask=True,  # 返回注意力掩码,用于标记哪些位置是填充部分,哪些位置是实际的句子内容truncation=True  # 如果句子超过了最大长度,进行截断)input_ids.append(encoded_sent.get('input_ids'))attention_masks.append(encoded_sent.get('attention_mask'))# 转换为PyTorch张量(tensor),以便后续可以输入到模型中进行训练或推理input_ids = torch.tensor(input_ids)attention_masks = torch.tensor(attention_masks)labels = torch.tensor(labels)return input_ids, attention_masks, labels# 预处理数据
print("2、开始预处理数据啦~")
train_inputs, train_masks, train_labels = preprocess_for_bert(X_train, y_train)
val_inputs, val_masks, val_labels = preprocess_for_bert(X_val, y_val)# 创建DataLoader
train_data = TensorDataset(train_inputs, train_masks, train_labels)
train_sampler = RandomSampler(train_data)
train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=8)validation_data = TensorDataset(val_inputs, val_masks, val_labels)
validation_sampler = SequentialSampler(validation_data)
validation_dataloader = DataLoader(validation_data, sampler=validation_sampler, batch_size=8)# 加载BERT模型
print("3、开始预加载模型啦~")
model = BertForSequenceClassification.from_pretrained(local_model_path, num_labels=len(label_encoder.classes_),ignore_mismatched_sizes=True)
model.cuda()  # 默认使用第一张显卡# 设置优化器和调度器
EPOCHS = 5   # 训练次数,可以先训练5次看看效果,可以自定义修改
optimizer = AdamW(model.parameters(), lr=2e-5, eps=1e-8)   # 优化器
total_steps = len(train_dataloader) * EPOCHS
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0, num_training_steps=total_steps)# 计算精确度 -- 通过比较预测类别和实际标签的相同之处,计算出预测正确的比例
def flat_accuracy(preds, labels):pred_flat = np.argmax(preds, axis=1).flatten()labels_flat = labels.flatten()return np.sum(pred_flat == labels_flat) / len(labels_flat)# 训练和评估
print("4、开始训练啦~")
best_val_accuracy = 0
for epoch in range(EPOCHS):print(f'Epoch {epoch + 1}')now_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())   # 记录每一轮的训练开始时间和结束时间print("start time:", now_time)model.train()  # 模型设置为训练模式total_train_loss = 0  # 初始化训练总损失为0for step, batch in enumerate(train_dataloader):b_input_ids = batch[0].cuda()b_input_mask = batch[1].cuda()b_labels = batch[2].cuda().long()model.zero_grad() # 清除模型的梯度outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels)  # 将输入数据传递给模型,得到模型的输出loss = outputs.loss # 提取出损失值,用于后续的反向传播total_train_loss += loss.item()loss.backward() # 进行反向传播,计算梯度torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)optimizer.step()scheduler.step()avg_train_loss = total_train_loss / len(train_dataloader)  # 更新学习率torch.cuda.empty_cache()  # 训练一轮就清空一次显卡缓存# 模型测试,计算准确度model.eval()total_eval_accuracy = 0total_eval_loss = 0for batch in validation_dataloader:b_input_ids = batch[0].cuda()b_input_mask = batch[1].cuda()b_labels = batch[2].cuda().long()with torch.no_grad():outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels)loss = outputs.losstotal_eval_loss += loss.item()logits = outputs.logitslogits = logits.detach().cpu().numpy()label_ids = b_labels.to('cpu').numpy()total_eval_accuracy += flat_accuracy(logits, label_ids)avg_val_accuracy = total_eval_accuracy / len(validation_dataloader)avg_val_loss = total_eval_loss / len(validation_dataloader)torch.cuda.empty_cache() # 验证一轮就清空一次显卡缓存print(f'Training loss: {avg_train_loss}')print(f'Validation loss: {avg_val_loss}')print(f'Validation Accuracy: {avg_val_accuracy}')   # 主要看这个精度,一般准确率90%以上就可以投入实际生产环境中# 在验证集上计算准确率if avg_val_accuracy > best_val_accuracy:best_val_accuracy = avg_val_accuracy# 保存模型model.save_pretrained(best_model_path)   # 根据训练次数,保存最优的一个模型结果now_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())print("end time:",now_time)print("-------------------")

三、模型推理

模型训练完成后,现在有一批新数据,你想要使用训练好的模型预测该文本数据的分类结果,则可以使用下面的推理代码,详解看注释。

import pandas as pd
import time
import torch
import joblib
from transformers import BertTokenizer, BertForSequenceClassification
import torch.nn.functional as F# 第一步:加载数据
file_path = './data/detect.csv'  # 要推理的数据路径
df = pd.read_csv(file_path, encoding='utf-8')# 第二步:加载训练好的模型
best_model_path = './model'
model = BertForSequenceClassification.from_pretrained(best_model_path)
tokenizer = BertTokenizer.from_pretrained(best_model_path)# 第三步:加载编码(训练时保存的结果)
label_encoder = joblib.load('./data/encoder.joblib')predictions = []  # 预测值
confidence_scores = []  # 可信度,一般可信度大于0.9说明效果比较准确# 第四步:遍历推理数据
for row in df.iterrows():content = row[1]['feature']  # 特征列(推理样本)inputs = tokenizer(content, return_tensors="pt", padding=True, truncation=True, max_length=256)outputs = model(**inputs)probs = F.softmax(outputs.logits, dim=1)pred = torch.argmax(probs, dim=1)confidence = torch.max(probs, dim=1)   # 获取置信度的值predictions.append(pred.item())confidence_scores.append(confidence.values.item())# 第五步:将预测结果解码为类别标签
decoded_categories = label_encoder.inverse_transform(predictions)# 第六步:创建一个空的DataFrame来存储推理结果
df['pred'] = decoded_categories
df['confidence_score'] = confidence_scores# 将结果保存到本地
output_file_path = './data/detect_pred.csv'  # 保存推理结果的路径
df.to_csv(output_file_path, index=False)

参考文章链接:https://blog.csdn.net/yihong23/article/details/138543746

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

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

相关文章

Allure报告下载不同格式的文件

支持类型: class AttachmentType(Enum):def __init__(self, mime_type, extension):self.mime_type mime_typeself.extension extensionTEXT ("text/plain", "txt")CSV ("text/csv", "csv")TSV ("text/tab-sep…

搭建 xxl-job 调度中心

文章目录 1、初始化“调度数据库”2、修改“调度中心”配置3、打包运行”调度中心“ 1、初始化“调度数据库” 请下载项目源码并解压,获取 “调度数据库初始化SQL脚本” 并执行即可。 “调度数据库初始化SQL脚本” 位置为:/xxl-job/doc/db/tables_xxl_j…

【Qt】QLCDNumber | QProgressBar | QCalendarWidget

文章目录 QLCDNumber —— 显示数字QLCDNumber 的属性QLCDNumber 的使用 QProgressBar —— 进度条QProgressBar 的属性创建一个进度条修改为 红色的进度条 QCalendarWidget —— 日历QCalendarWidget 的属性QCalendarWidget 的使用 QLCDNumber —— 显示数字 QLCDNumber 的属…

C++基础多态

目录 学习内容: 1. 多态 1.1 多态的实现 1.2 函数重写(override) 1.3 虚函数 1.4 使用多态实现的实例 1.5 虚函数的底层实现 1.6 重载(voerload)、重写(override)和隐藏(h…

DeepWalk【图神经网络论文精读】笔记

链接: DeepWalk【图神经网络论文精读】_哔哩哔哩_bilibili [内容总结::] - deep walk 解决图嵌入问题:将结点压缩成低维向量随机游走(类似NLP生成句子) 优点与缺点: 相关学习资料 - word2vec 开山必读论文 - DeepWalk 论文阅读 - 代…

若楠带你初识OpenCV(1)-- 视频、图片基础处理,颜色获取

文章目录 OpenCV一、核心功能二、初识OpenCV1. 安装OpenCV2. 读取图片3. 读取灰度图4. 图片保存5. 视频文件读取6. 区域截取7. 提取RGB通道颜色8. 合并颜色通道 总结 OpenCV OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学…

MES 系统中工艺管理--工艺指令管理

一.生产系统柔性,高复用性需求。 工艺路线---------生产线 工序------工位 工艺指令-----具体工步 二.工艺指令分类 1、工艺要求支持配置指令、站点后在过点时,自动调用指令执行。物料、工步、人员作为预留设置; 2、指令主要分ABCD四类&…

Git基础教程:掌握版本控制的秘密

🌈 个人主页:Zfox_ 🔥 系列专栏:C从入门到精通 目录 🚀 什么是Git 🚀 在本系列博客中所实现的目标 一: 🔥 Git 初识 二: 🔥 Git 安装 🍊 Linux-c…

GAMES202——作业4 Kulla-Conty BRDF(BRDF的预计算、重要性采样)

目录 任务 实现 预计算E() 预计算Eavg Bonus1:重要性采样 在实时渲染中使用预计算数据 结果 任务 完成 Kulla-Conty BRDF 模型,关键在于计算 BRDF 的补偿项 f ms ,而 f ms 的计算需要 E ( ) 和 E avg 两个前置变量。 1.预计算E() …

【pgAdmin4】创建/删除:数据库Database和数据库表Table

目录 0.环境 1.简介 2.详细步骤 1)创建数据库 法一:UI界面创建 法二:sql语句创建数据库 2)创建数据库表 查看数据库表 查看数据库表内容 法一:UI界面创建数据库表 法二:sql语句创建数据库表 3&…

快专利与慢专利:速度与质量的天平

在当今快速发展的科技时代,专利成为了创新成果的重要保护手段。然而,不同的创新有着不同的节奏,由此也产生了“快专利”与“慢专利”之分。快专利以其迅速的申请和应用,为创新者抢占市场先机;慢专利则凭借深度的研发和…

【Redis之一:下载安装Redis】

Redis下载与安装 一、下载 Redis 安装包1、 Windows 安装包下载 二、安装Redis1、 Windows 安装Redis 三、配置 Redis1、 Windows 中配置 Redis(1)配置访问密码(2)重启 Redis 服务 三、访问 Redis1、命令行访问 Redis(…

【福利】最新可用!谷歌搜索和谷歌学术的镜像网站

转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 内容来自:https://www.80srz.com/posts/1633.html 谷歌搜索镜像 Google搜索镜像1:https://g.savalone.com/ Google搜索镜像2&…

Python基础笔记

一、python基础1.1 基础知识1.1.1 注释 注释:在程序中对程序代码进行解释说明的文字。 作用:注释不是程序,不能被执行,只是对程序代码进行解释说明,让别人可以看懂程序代码的作用,能够大大增强程序的可读性…

LabVIEW电机多次调用

在LabVIEW中,为实现对多个电机的独立控制,工程师可以采用可重入VI、动态VI调用、多任务结构或面向对象编程等方法。每种方法都有其优点和适用场景,选择合适的方法能有效提升系统的性能和可维护性。 在LabVIEW中,如果需要多次调用…

遥控器显示分别对应的无人机状态详解!!

1. 电量显示 遥控器电量:遥控器上通常会显示自身的电池电量,以提醒用户及时充电。 无人机电量:部分高端遥控器还会显示无人机的电池电量,以进度条或百分比的形式表示,帮助用户了解无人机的续航能力。 2. 飞行模式与…

Mybatis框架——缓存(一级缓存,二级缓存)

本章将简单介绍Mybatis框架中的缓存,欢迎大家点赞➕收藏,蟹蟹!!!💕 🌈个人主页:404_NOT_FOUND 🌈MyBatis环境搭建步骤(超全解析!!&am…

hackme靶机攻略

1.通过nmap扫描靶场ip 2.目录扫描 3.找出文件存储位置,看看哪里可以上传文件 4.注册账号登录一下 点击search 5.输入1 and 11 -- 1 and 12 --看看有无SQL注入 6.判断字段数 1 order by 3 -- 说明字段数是3 7.查看数据库 -1 union select database(),2,3 # 8.查…

【Linux】使用Linux实现小程序 - 进度条

目录 一、缓冲区二、回车换行的概念三、进度条的设计3.1 版本1(没有配合场景)3.2 版本2(配合场景)3.3 版本3(美化进度条) 结尾 一、缓冲区 C/C语言,会针对标准输出,给我们提供默认的…

Python数据分析实战,兰州市二手房市场深度分析

作为购房者,除了关注地段与价格外,房屋的总价与面积的关系,以及房屋朝向的选择,同样是决策过程中的关键因素。那么,兰州市的二手房市场中,房屋总价与面积之间究竟存在怎样的关系?各个朝向的房源…