使用Hugging Face中的BERT进行标题分类
前言
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv10训练自己的数据集(交通标志检测)
- YOLO11训练自己的数据集(吸烟、跌倒行为检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
相关介绍
BERT(Bidirectional Encoder Representations from Transformers)是Google在2018年提出的一种基于Transformer架构的预训练语言模型。
出处
BERT由Google AI研究院提出,《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》,旨在通过大规模无标注数据的预训练,学习丰富的语义表示,进而在各种自然语言处理(NLP)任务中进行微调,以取得优异的表现。BERT的提出标志着NLP领域的一个重要里程碑,刷新了多个NLP任务的记录。
基本原理
BERT的基本原理是基于Transformer架构的双向编码器,通过在大规模文本数据上的预训练来捕捉语言的深层双向表征。其训练过程分为两个阶段:预训练和微调。
-
预训练阶段:
- 任务 :BERT的预训练任务主要包括两个,即遮蔽语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)。
- MLM:类似于完形填空,模型被训练来预测输入句子中被遮蔽的词。这种任务促使模型学习上下文中的词汇关系。
- NSP:判断两个句子是否是连续的文本序列。这有助于模型理解句子间的关联。
- 模型结构:BERT模型由多层的Transformer编码器堆叠而成,每一层都包含自注意力机制(Self-Attention)和前馈神经网络。这种深层结构使得BERT能够捕捉从浅层语法特征到深层语义特征的不同级别的语言信息。
- 任务 :BERT的预训练任务主要包括两个,即遮蔽语言模型(Masked Language Model, MLM)和下一句预测(Next Sentence Prediction, NSP)。
-
微调阶段:
- 在预训练完成后,BERT模型可以通过添加任务特定的输出层来进行微调,以适应不同的NLP任务,如情感分析、问答、命名实体识别等。
- 微调过程利用了预训练阶段学到的语言表征,使得模型能够快速适应新的任务并取得优异的性能。
优点
- 上下文相关性:BERT采用双向Transformer编码器,可以有效地捕捉上下文之间的依赖关系,从而提高了NLP任务的准确性。这种双向编码的能力使得BERT在理解文本时能够更全面地考虑上下文信息。
- 预训练和微调:BERT通过在大规模无标注数据上进行预训练,学习到了通用的语言知识。这使得BERT可以在各种具体的NLP任务上进行微调,从而大大减少训练时间和数据需求。
- 多任务支持:BERT可以支持多种NLP任务,如文本分类、命名实体识别、问答系统、文本相似度计算等。这种多任务支持的能力使得BERT具有广泛的应用前景。
- 可迁移性:BERT具有很高的可迁移性,可以通过微调适应不同的NLP任务。这意味着在训练一个新的NLP任务时,可以利用BERT已经学到的通用语言知识,从而加速训练过程并提高性能。
- 处理速度和精度:BERT使用了Transformer结构,可以并行计算,速度较快。同时,BERT在处理各种NLP任务时都表现出了较高的精度。
缺点
- 计算资源需求高:BERT需要大量的计算资源和存储空间进行训练和部署。这使得在一些资源受限的环境下使用BERT变得困难。
- 训练数据需求大:BERT需要大量的训练数据才能发挥其优势。对于一些小型数据集,BERT的表现可能不如其他模型。这限制了BERT在一些特定领域的应用。
- 长文本处理效果有限:BERT对于长文本的处理效果不如一些传统模型(如CNN和RNN)。这可能是因为BERT采用的是固定长度的输入序列,对于过长的文本可能无法有效地捕捉全部信息。
- 微调成本:虽然BERT可以通过微调适应不同的NLP任务,但这需要一定的人工标注数据和时间成本。这增加了使用BERT的复杂性。
- 特定领域适应性:对于一些特定领域的文本,BERT可能需要进行特定的训练才能达到更好的效果。这限制了BERT在某些专业领域的应用。
综上所述,BERT作为一种基于Transformer架构的预训练语言模型,在自然语言处理领域具有广泛的应用前景和优势。然而,其计算资源需求高、训练数据需求大、长文本处理效果有限等缺点也需要注意。在实际应用中,需要根据具体任务和资源情况选择合适的模型和方法。
前提条件
- 熟悉Python
实验环境
bash
Package Version
----------------------------- ------------
matplotlib 3.3.4
numpy 1.19.5
Pillow 8.4.0
pip 21.2.2
protobuf 3.19.6
requests 2.27.1
scikit-learn 0.24.2
scipy 1.5.4
sentencepiece 0.1.91
setuptools 58.0.4
threadpoolctl 3.1.0
thulac 0.2.2
tokenizers 0.9.3
torch 1.9.1+cu111
torchaudio 0.9.1
torchvision 0.10.1+cu111
tornado 6.1
tqdm 4.64.1
traitlets 4.3.3
transformers 3.5.1
urllib3 1.26.20
BERT进行标题分类
准备数据集
- academy_titles.txt:该文件是关于学术的标题数据。
- job_titles.txt:该文件是关于招聘的标题数据。
读取数据集
python
# 定义两个list分别存放两个板块的帖子数据
academy_titles = []
job_titles = []
with open('academy_titles.txt', encoding='utf8') as f:
for l in f: # 按行读取文件
academy_titles.append(l.strip()) # strip 方法用于去掉行尾空格
with open('job_titles.txt', encoding='utf8') as f:
for l in f: # 按行读取文件
job_titles.append(l.strip()) # strip 方法用于去掉行尾空格
data_list = []
for title in academy_titles:
data_list.append([title, 0])
for title in job_titles:
data_list.append([title, 1])
max_length = 0
for case in data_list:
max_length = max(max_length, len(case[0])+2)
print(max_length)
划分数据集
python
from sklearn.model_selection import train_test_split
train_list, dev_list = train_test_split(data_list,test_size=0.3,random_state=15,shuffle=True)
设置相关参数
python
import os
import time
import random
import torch
import torch.nn.functional as F
from torch import nn
from tqdm import tqdm
import random
from transformers import get_linear_schedule_with_warmup, AdamW
# from transformers import BertTokenizer, BertForSequenceClassification
from transformers import BertTokenizer, BertForSequenceClassification, BertConfig, BertModel
if torch.cuda.is_available():
device = torch.device("cuda")
else:
device = torch.device("cpu")
max_train_epochs = 10
warmup_proportion = 0.05
gradient_accumulation_steps = 2
train_batch_size = 16
valid_batch_size = train_batch_size
test_batch_size = train_batch_size
# data_workers= 2
data_workers= 0
learning_rate=1e-6
weight_decay=0.01
max_grad_norm=1.0
cur_time = time.strftime("%Y-%m-%d_%H:%M:%S")
这些参数通常用于配置深度学习模型的训练过程,特别是在使用PyTorch这样的深度学习框架时。下面是对每个参数的解释:
-
batch_size = 128:
- 批大小(Batch Size)是指在模型训练过程中,一次迭代(iteration)所使用的数据样本数量。这里设置为128,意味着每次更新模型参数前,会使用128个样本来计算损失和梯度。较大的批大小可以加速训练,但也可能增加内存消耗并影响模型的泛化能力。
-
data_workers = 0:
- 数据加载工作线程数(Data Workers)是指用于并行加载数据的线程数量。设置为0意味着数据加载将在主线程上同步进行,这可能会降低数据加载的速度。通常,增加工作线程数可以加速数据加载过程,但过多的线程可能会增加系统开销。
-
learning_rate = 0.0001:
- 学习率(Learning Rate)是控制模型参数更新幅度的超参数。较小的学习率意味着参数更新的步长较小,训练过程可能更稳定但收敛速度较慢;较大的学习率可能导致训练过程不稳定甚至发散。这里设置为0.0001是一个相对较小的值,适用于一些精细调整的场景。
-
gradient_accumulation_steps = 1:
- 梯度累积步数(Gradient Accumulation Steps)是指在更新模型参数前,累积梯量的次数。设置为1意味着每次迭代都会立即更新模型参数。在内存有限但希望使用较大批大小进行训练时,可以通过增加梯度累积步数来模拟较大的批大小。
-
max_train_epochs = 30:
- 最大训练轮数(Max Training Epochs)是指整个训练数据集被遍历的次数。一个epoch等于整个数据集通过模型一次。这里设置为30,意味着整个数据集将被遍历30次。
-
warmup_proportion = 0.05:
- 预热比例(Warmup Proportion)是指在训练初期,学习率逐渐增加所占整个训练过程的比例。预热可以帮助模型在训练初期更稳定地更新参数,避免由于初始学习率过高而导致的训练不稳定。这里设置为0.05,意味着在前5%的训练轮数中,学习率会逐渐增加。
-
weight_decay = 0.01:
- 权重衰减(Weight Decay)是一种正则化技术,用于防止模型过拟合。它通过向损失函数添加一个与模型参数平方成正比的项来实现,鼓励模型参数保持较小值。这里设置为0.01。
-
max_grad_norm = 1.0:
- 最大梯度范数(Max Grad Norm)是梯度裁剪(Gradient Clipping)的一种形式,用于控制梯度的最大值。如果梯度的范数超过这个值,梯度将被缩放以确保其范数不超过这个值。这有助于防止梯度爆炸问题。这里设置为1.0。
-
cur_time = time.strftime("%Y-%m-%d_%H:%M:%S"):
- 这行代码用于获取当前时间,并将其格式化为字符串,通常用于生成具有时间戳的文件名或日志,以便记录训练过程。
-
device = torch.device('cuda'):
- 这行代码指定了模型和数据应该在哪种设备上运行。
'cuda'
表示使用NVIDIA的CUDA技术来加速计算,通常是在具有NVIDIA GPU的计算机上。如果系统中没有可用的CUDA设备,PyTorch将回退到CPU。
- 这行代码指定了模型和数据应该在哪种设备上运行。
这些参数共同决定了模型训练的具体配置,包括训练速度、模型性能以及训练过程中的稳定性等。
创建自己DataSet对象
python
# tokenizer = BertTokenizer.from_pretrained('bert-base-chinese') # 在线读取,可能会报网络错误
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese/') # 注意此处为本地文件夹
class MyDataSet(torch.utils.data.Dataset):
def __init__(self, examples):
self.examples = examples
def __len__(self):
return len(self.examples)
def __getitem__(self, index):
example = self.examples[index]
title = example[0]
label = example[1]
r = tokenizer.encode_plus(title, max_length=max_length, padding="max_length")
return title, label, index#, r['token_type_ids'], label, index
def the_collate_fn(batch):
r = tokenizer([b[0] for b in batch], padding=True)
input_ids = torch.LongTensor(r['input_ids'])
attention_mask = torch.LongTensor(r['attention_mask'])
label = torch.LongTensor([b[1] for b in batch])
indexs = [b[2] for b in batch]
return input_ids, attention_mask, label, indexs #, token_type_ids
train_dataset = MyDataSet(train_list)
train_data_loader = torch.utils.data.DataLoader(
train_dataset,
batch_size=train_batch_size,
shuffle = True,
num_workers=data_workers,
collate_fn=the_collate_fn,
)
dev_dataset = MyDataSet(dev_list)
dev_data_loader = torch.utils.data.DataLoader(
dev_dataset,
batch_size=train_batch_size,
shuffle = False,
num_workers=data_workers,
collate_fn=the_collate_fn,
)
计算准确率
python
def get_score():
y_true = []
y_pred = []
for step, batch in enumerate(tqdm(dev_data_loader)):
model.eval()
with torch.no_grad():
input_ids, attention_mask = (b.to(device) for b in batch[:2])
y_true += batch[2].numpy().tolist()
logist = model(input_ids, attention_mask)[0]
result = torch.argmax(logist, 1).cpu().numpy().tolist()
y_pred += result
correct = 0
for i in range(len(y_true)):
if y_true[i] == y_pred[i]:
correct += 1
accuracy = correct / len(y_pred)
return accuracy
定义预训练模型
python
# model = BertForSequenceClassification.from_pretrained('bert-base-chinese') # 在线读取预训练模型
# 本地读取预训练模型
config = BertConfig.from_json_file("bert-base-chinese/config.json")
model = BertForSequenceClassification.from_pretrained("bert-base-chinese/pytorch_model.bin", config=config)
model.to(device)
定义优化器
python
from transformers import AdamW, get_linear_schedule_with_warmup
t_total = len(train_data_loader) // gradient_accumulation_steps * max_train_epochs + 1
num_warmup_steps = int(warmup_proportion * t_total)
print('warmup steps : %d' % num_warmup_steps)
no_decay = ['bias', 'LayerNorm.weight'] # no_decay = ['bias', 'LayerNorm.bias', 'LayerNorm.weight']
param_optimizer = list(model.named_parameters())
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 = AdamW(optimizer_grouped_parameters, lr=learning_rate, correct_bias=False)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=num_warmup_steps, num_training_steps=t_total)
训练模型
python
def print_test(title):
r = tokenizer([title])
input_ids = torch.LongTensor(r['input_ids']).to(device)
attention_mask = torch.LongTensor(r['attention_mask']).to(device)
logist = model(input_ids, attention_mask)[0]
result = torch.argmax(logist, 1).cpu().numpy().tolist()[0]
result = ['考研考博', '招聘信息'][result]
print(title, result)
def print_cases():
print_test('考研心得')
print_test('北大实验室博士')
print_test('考外校博士')
print_test('北大实验室招博士')
print_test('工作or考研?')
print_test('急求自然语言处理工程师')
print_test('校招offer比较')
for epoch in range(max_train_epochs):
b_time = time.time()
model.train()
for step, batch in enumerate(tqdm(train_data_loader)):
input_ids, attention_mask, label = (b.to(device) for b in batch[:-1])
loss = model(input_ids, attention_mask, labels=label)
loss = loss[0]
loss.backward()
if (step + 1) % gradient_accumulation_steps == 0:
optimizer.step()
scheduler.step()
optimizer.zero_grad()
print('Epoch = %d Epoch Mean Loss %.4f Time %.2f min' % (epoch+1, loss.item(), (time.time() - b_time)/60))
print(get_score())
print_cases()
bash
0.9896858884200657
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:20<00:00, 14.83it/s]
Epoch = 2 Epoch Mean Loss 0.0172 Time 0.35 min
100%|██████████| 134/134 [00:02<00:00, 55.21it/s]
0.9967182372245663
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:19<00:00, 15.56it/s]
Epoch = 3 Epoch Mean Loss 0.0123 Time 0.33 min
100%|██████████| 134/134 [00:04<00:00, 31.16it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:22<00:00, 14.12it/s]
Epoch = 4 Epoch Mean Loss 0.0053 Time 0.37 min
100%|██████████| 134/134 [00:04<00:00, 31.56it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:21<00:00, 14.58it/s]
Epoch = 5 Epoch Mean Loss 0.0052 Time 0.36 min
100%|██████████| 134/134 [00:03<00:00, 33.80it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:20<00:00, 14.85it/s]
Epoch = 6 Epoch Mean Loss 0.0030 Time 0.35 min
100%|██████████| 134/134 [00:04<00:00, 33.33it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:21<00:00, 14.79it/s]
Epoch = 7 Epoch Mean Loss 0.0022 Time 0.35 min
100%|██████████| 134/134 [00:04<00:00, 32.78it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:21<00:00, 14.65it/s]
Epoch = 8 Epoch Mean Loss 0.0053 Time 0.35 min
100%|██████████| 134/134 [00:04<00:00, 33.41it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:21<00:00, 14.64it/s]
Epoch = 9 Epoch Mean Loss 0.0052 Time 0.35 min
100%|██████████| 134/134 [00:03<00:00, 33.91it/s]
0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
100%|██████████| 311/311 [00:20<00:00, 14.84it/s]
Epoch = 10 Epoch Mean Loss 0.0021 Time 0.35 min
100%|██████████| 134/134 [00:04<00:00, 32.88it/s]0.9995311767463666
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
保存模型
python
torch.save(model.state_dict(), 'bert_model_parameter.pkl')
torch.save(model, 'bert_model.pkl')
测试模型
python
model = torch.load('bert_model.pkl')
model.load_state_dict(torch.load('bert_model_parameter.pkl'))
print_cases()
bash
考研心得 考研考博
北大实验室博士 考研考博
考外校博士 考研考博
北大实验室招博士 招聘信息
工作or考研? 考研考博
急求自然语言处理工程师 招聘信息
校招offer比较 招聘信息
参考文献
[1] 论文地址:https://arxiv.org/abs/1810.04805
[2] 开源代码:https://paperswithcode.com/method/bert
[3] https://huggingface.co/docs/transformers/model_doc/bert
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv10训练自己的数据集(交通标志检测)
- YOLO11训练自己的数据集(吸烟、跌倒行为检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目