pytorch版的bert分类模型流程

pytorch版的bert分类模型流程,后加bert_BiLSTM融合对比

一.前言

版本详述

python 3.6
pytorch 1.7
transformers 4.3.3
一块GPU,2次EPOCH训练时间不超30min

数据预处理

数据分train,dev,test,三个txt文件
转换数据格式
设置分批训练
打乱数据顺序

定义模型参数

batch_size 64
max_len 30
device cuda
epochs 2 (自己可修改多轮训练尝试,2轮训练正确率结果94.9%)

预训练模型下载 放在bert_model 包含config.json,pytorch_model.bin,vocab_txt

git clone https://huggingface.co/bert-base-chinese

二.完整代码github链接

https://github.com/young-yhx/bert_classify

三.上代码

import

import pandas as pd
import numpy as np
import json,time
from  tqdm import tqdm
from sklearn.metrics import accuracy_score,classification_report
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import TensorDataset,DataLoader,RandomSampler,SequentialSampler
from transformers import BertModel,BertConfig,BertTokenizer,AdamW,get_cosine_schedule_with_warmup

#参数
bert_path = 'bert_model/'   #预训练模型的位置
tokenizer = BertTokenizer.from_pretrained(bert_path)   #初始化分词器
max_len = 30     #数据阻断长度
BATCH_SIZE = 64
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
EPOCHS = 2

1.预处理数据

1.1处理数据成input_ids,token_type_ids,attention_mask,label
def dataSet(data_path):
    input_ids,token_type_ids,attention_mask = [],[],[]
    labels = []
    with open(data_path,encoding='utf-8') as f:
        for i,line in tqdm(enumerate(f)):
            title,y = line.strip().split('\t')   #删除所有的空格,用\t分割数据集和标签
            #调用tokenizer转换成bert需要的数据格式
            encode_dict = tokenizer.encode_plus(text=title,max_length=max_len,padding='max_length',truncation=True)
            #分别获取三个值  目前值的类型为list
            input_ids.append(encode_dict['input_ids'])
            token_type_ids.append(encode_dict['token_type_ids'])
            attention_mask.append(encode_dict['attention_mask'])
            labels.append(int(y))
    #list转化成tensor格式
    input_ids,token_type_ids,attention_mask = torch.tensor(input_ids),torch.tensor(token_type_ids),torch.tensor(attention_mask)
    return input_ids,token_type_ids,attention_mask,labels

#1.2 dataloder批量处理
def dataLoader(input_ids,token_type_ids,attention_mask,labels):
    #tensor数据整合
    labels = torch.tensor(labels)
    data = TensorDataset(input_ids,token_type_ids,attention_mask,labels)
    loader = DataLoader(data,batch_size=BATCH_SIZE,shuffle=True)    #shuffle打乱每行数据的顺序
    return loader
#1.3实例化函数
#训练集带label
input_ids_train,token_type_ids_train,attention_mask_train,labels_train = dataSet('data/train.txt')
train_loader = dataLoader(input_ids_train,token_type_ids_train,attention_mask_train,labels_train)
#验证集带label
input_ids_dev,token_type_ids_dev,attention_mask_dev,labels_dev = dataSet('data/dev.txt')
dev_loader = dataLoader(input_ids_dev,token_type_ids_dev,attention_mask_dev,labels_dev)
#测试集
input_ids_test,token_type_ids_test,attention_mask_test,labels_test = dataSet('data/test.txt')
test_loader = dataLoader(input_ids_test,token_type_ids_test,attention_mask_test,labels_test)
#得到后续用的数据为train_loader,dev_loader,test_loader

2.定义bert模型

class Bert_Model(nn.Module):
    def __init__(self,bert_path,classes=10):
        super(Bert_Model,self).__init__()
        self.config = BertConfig.from_pretrained(bert_path)
        self.bert = BertModel.from_pretrained(bert_path)
        for param in self.bert.parameters():
            param.requires_grad=True
        self.fc = nn.Linear(self.config.hidden_size,classes)  #直接分类
    def forward(self,input_ids,token_type_ids,attention_mask):
        output = self.bert(input_ids,token_type_ids,attention_mask)[1]  #池化后的输出,是向量
        logit = self.fc(output)    #全连接层,概率矩阵
        return logit

#实例化bert模型
model = Bert_Model(bert_path).to(DEVICE)

3.定义优化器和线性学习率

优化器
optimizer = AdamW(model.parameters(),lr=2e-5,weight_decay=1e-4)  #使用Adam优化器
#设置学习率
schedule = get_cosine_schedule_with_warmup(optimizer,num_warmup_steps=len(train_loader),num_training_steps=EPOCHS*len(test_loader))

4.定义训练函数和验证测试函数

#在验证集上评估模型性能的函数
def evaluate(model,data_loader,device):
    model.eval()   #防止模型训练改变权值
    val_true,val_pred = [],[]
    with torch.no_grad():     #计算的结构在计算图中,可以进行梯度反转等操作
        for idx,(ids,tpe,att,y) in enumerate(data_loader): #得到的y要转换一下数据格式
            y_pred = model(ids.to(device),tpe.to(device),att.to(device))  #此时得到的是概率矩阵
            y_pred = torch.argmax(y_pred,dim=1).detach().cpu().numpy().tolist()  #将概率矩阵转换成标签并变成list类型
            val_pred.extend(y_pred)   #将标签值放入列表
            val_true.extend(y.squeeze().cpu().numpy().tolist())   #将真实标签转换成list放在列表中
    
    return accuracy_score(val_true,val_pred)
#如果是比赛没有labels_test,那么这个函数for里面没有y,输出没有test_true,处理数据的时候没有labels_test放到dataloader里
def predict(model,data_loader,device):
    model.eval()
    test_pred,test_true = [],[]
    with torch.no_grad():
        for idx,(ids,tpe,att,y) in enumerate(data_loader):
            y_pred = model(ids.to(device),tpe.to(device),att.to(device))   #得到概率矩阵
            y_pred = torch.argmax(y_pred,dim=1).detach().cpu().numpy().tolist()  #将概率矩阵转化成标签值
            test_pred.extend(y_pred)
            test_true.extend(y.squeeze().cpu().numpy().tolist())
    return test_pred,test_true
#训练函数
def train_and_eval(model,train_loader,valid_loader,optimizer,schedule,device,epoch):
    best_acc = 0.0
    patience = 0
    criterion = nn.CrossEntropyLoss()       #损失函数
    for i in range(epoch):
        start = time.time()
        model.train()   #开始训练
        print("***************Running training epoch{}************".format(i+1))
        train_loss_sum = 0.0
        for idx,(ids,tpe,att,y) in enumerate(train_loader):
            ids,tpe,att,y = ids.to(device),tpe.to(device),att.to(device),y.to(device)
            y_pred = model(ids,tpe,att)   #加载模型获得概率矩阵
            loss = criterion(y_pred,y)    #计算损失
            optimizer.zero_grad()         #梯度清零
            loss.backward()               #反向传播
            optimizer.step()              #更新优化参数
            schedule.step()               #更新学习率
            train_loss_sum += loss.item()
            #只打印五次结果
            if(idx+1)%(len(train_loader)//5)==0:
                print("Epoch {:04d} | Step {:04d}/{:04d} | Loss {:.4f} | Time {:.4f}".format(
                i+1,idx+1,len(train_loader),train_loss_sum/(idx+1),time.time()-start))
        #每一次epoch输出一个准确率
        model.eval()
        acc = evaluate(model,valid_loader,device)     #验证模型的性能
        if acc > best_acc :
            best_acc = acc
            torch.save(model.state_dict(),"best_bert_model.pth")    #保存最好的模型
        print("current acc is {:.4f},best acc is {:.4f}".format(acc,best_acc))
        print("time costed = {}s \n".format(round(time.time()-start,5)))

5.开始训练

train_and_eval(model,train_loader,dev_loader,optimizer,schedule,DEVICE,EPOCHS)

结果
在这里插入图片描述

6.加载最优模型进行测试

model.load_state_dict(torch.load("best_bert_model.pth"))
#得到预测标签和真实标签
test_pred,test_true= predict(model,test_loader,DEVICE)
#输出测试机的准确率
print("\n Test Accuracy = {} \n ".format(accuracy_score(test_true,test_pred)))
#打印各项验证指标
print(classification_report(test_true,test_pred,digits=4))
print(test_pred[:10])
print('------------------')
print(test_true[:10])

结果
在这里插入图片描述

完整代码Github链接

https://github.com/young-yhx/bert_classify
我的数据在git的链接中data/
bert与LSTM的融合代码也在我的GitHub中


本文参考了很多大佬的代码,在此致谢
就当是做个笔记吧,学习到了很多,希望能对友友有帮助!!!


参考链接
https://zhuanlan.zhihu.com/p/112655246
https://github.com/649453932/Bert-Chinese-Text-Classification-Pytorch

  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值