Bert的使用

一、Data.py

python 复制代码
#  data负责产生两个dataloader
from torch.utils.data import DataLoader, Dataset
from sklearn.model_selection import train_test_split   #给X,Y 和分割比例, 分割出来一个训练集和验证机的X, Y
import torch


def read_file(path):
    data = []
    label = []

    with open(path, "r", encoding="utf-8") as f:
        for i, line in enumerate(f):
            if i == 0:
                continue
            if i > 200 and i< 7500:
                continue
            line = line.strip("\n")
            line = line.split(",", 1)  #把这句话,按照,分割, 1表示分割次数
            data.append(line[1])
            label.append(line[0])
    print("读了%d的数据"%len(data))
    return data, label


# file = "../jiudian.txt"
# read_file(file)
class jdDataset(Dataset):
    def __init__(self, data, label):
        self.X = data
        self.Y = torch.LongTensor([int(i) for i in label])

    def __getitem__(self, item):
        return self.X[item], self.Y[item]

    def __len__(self):
        return len(self.Y)





def get_data_loader(path, batchsize, val_size=0.2):          #读入数据,分割数据。
    data, label = read_file(path)
    train_x, val_x, train_y, val_y = train_test_split(data, label, test_size=val_size, shuffle=True, stratify=label)
    train_set = jdDataset(train_x, train_y)
    val_set = jdDataset(val_x, val_y)
    train_loader = DataLoader(train_set, batchsize, shuffle=True)
    val_loader = DataLoader(val_set, batchsize, shuffle=True)
    return train_loader, val_loader

if __name__ == "__main__":
    get_data_loader("../jiudian.txt", 2)

代码逐行解释

导入必要的库
python 复制代码
from torch.utils.data import DataLoader, Dataset
from sklearn.model_selection import train_test_split   # 给X, Y 和分割比例, 分割出来一个训练集和验证集的X, Y
import torch
  • DataLoaderDataset :来自 torch.utils.data,用于创建数据加载器和自定义数据集。
  • train_test_split :来自 sklearn.model_selection,用于将数据集划分为训练集和验证集。
  • torch:PyTorch 的核心库,用于深度学习模型的构建和训练。
定义读取文件的函数
python 复制代码
def read_file(path):
    data = []
    label = []

    with open(path, "r", encoding="utf-8") as f:
        for i, line in enumerate(f):
            if i == 0:
                continue
            if i > 200 and i < 7500:
                continue
            line = line.strip("\n")
            line = line.split(",", 1)  # 把这句话,按照逗号分割, 1表示分割次数
            data.append(line[1])
            label.append(line[0])
    print("读了%d的数据" % len(data))
    return data, label
  • read_file 函数
    • 打开指定路径的文件并读取每一行。
    • 跳过第一行(假设是表头)。
    • 忽略第201到7499行的数据。
    • 使用 strip("\n") 去除每行末尾的换行符。
    • 使用 split(",", 1) 按照第一个逗号分割每行数据,生成包含两个元素的列表。
    • 将分割后的第二个元素(即文本数据)添加到 data 列表中,第一个元素(即标签)添加到 label 列表中。
    • 打印读取的数据条数,并返回 datalabel
自定义数据集类
python 复制代码
class jdDataset(Dataset):
    def __init__(self, data, label):
        self.X = data
        self.Y = torch.LongTensor([int(i) for i in label])

    def __getitem__(self, item):
        return self.X[item], self.Y[item]

    def __len__(self):
        return len(self.Y)
  • jdDataset
    • 继承自 torch.utils.data.Dataset
    • __init__ 方法中初始化 self.Xself.Y,其中 self.Y 是将标签转换为 torch.LongTensor 类型。
    • __getitem__ 方法用于根据索引获取数据项,返回对应的文本数据和标签。
    • __len__ 方法返回数据集的长度。
生成数据加载器
python 复制代码
def get_data_loader(path, batchsize, val_size=0.2):  # 读入数据,分割数据
    data, label = read_file(path)
    train_x, val_x, train_y, val_y = train_test_split(data, label, test_size=val_size, shuffle=True, stratify=label)
    train_set = jdDataset(train_x, train_y)
    val_set = jdDataset(val_x, val_y)
    train_loader = DataLoader(train_set, batchsize, shuffle=True)
    val_loader = DataLoader(val_set, batchsize, shuffle=True)
    return train_loader, val_loader
  • get_data_loader 函数
    • 调用 read_file 函数读取数据并获得 datalabel
    • 使用 train_test_split 函数将数据集划分为训练集和验证集。参数包括:
      • test_size: 验证集的比例,默认为0.2。
      • shuffle: 是否在分割前打乱数据,默认为 True
      • stratify: 根据标签进行分层抽样,确保训练集和验证集中的标签分布一致。
    • 创建 jdDataset 实例 train_setval_set
    • 使用 DataLoader 创建训练集和验证集的数据加载器。训练集的数据加载器设置为 shuffle=True,以便每次迭代时打乱数据顺序;验证集的数据加载器也设置为 shuffle=True,但在实际应用中通常不需要打乱验证集的数据顺序。
    • 返回训练集和验证集的数据加载器。
主程序入口
python 复制代码
if __name__ == "__main__":
    get_data_loader("../jiudian.txt", 2)
  • 主程序入口
    • 当脚本作为主程序运行时,调用 get_data_loader 函数读取路径为 "../jiudian.txt" 的文件,并生成批量大小为2的数据加载器。

详细步骤说明

  1. 读取文件

    • read_file 函数负责从指定路径读取文件内容,并跳过某些特定行(如第一行和第201到7499行),然后将剩余的有效行按逗号分割为标签和文本数据,分别存储在 datalabel 列表中。
  2. 创建自定义数据集

    • jdDataset 类继承自 Dataset,实现了 __getitem____len__ 方法。它将原始数据和标签封装成一个可被 DataLoader 使用的对象。
  3. 数据集划分

    • get_data_loader 函数使用 train_test_split 将数据集划分为训练集和验证集,并创建相应的 jdDataset 实例。
  4. 创建数据加载器

    • 使用 DataLoader 创建训练集和验证集的数据加载器。训练集的数据加载器设置了 shuffle=True,以便每次迭代时打乱数据顺序;验证集的数据加载器也设置了 shuffle=True,但通常不需要打乱验证集的数据顺序。

示例输出

假设 "../jiudian.txt" 文件的内容如下:

id,label,text
1,0,这是一家不错的酒店。
2,1,房间太小了。
...

执行 get_data_loader("../jiudian.txt", 2) 后,程序将读取文件内容,忽略第一行和第201到7499行的数据,然后将剩余的数据按逗号分割为标签和文本数据,并将其划分为训练集和验证集,最后创建对应的数据加载器。

总结

这段代码的主要功能是从文件中读取数据,将其划分为训练集和验证集,并创建相应的数据加载器。通过这种方式,可以方便地将数据提供给深度学习模型进行训练和验证。


二、Model.py

python 复制代码
import torch
import torch.nn as nn
from transformers import BertModel, BertTokenizer, BertConfig


class myBertModel(nn.Module):
    def __init__(self, bert_path, num_class, device):
        super(myBertModel, self).__init__()

        self.bert = BertModel.from_pretrained(bert_path)
        # config = BertConfig.from_pretrained(bert_path)
        # self.bert = BertModel(config)



        self.device = device
        self.cls_head = nn.Linear(768, num_class)
        self.tokenizer = BertTokenizer.from_pretrained(bert_path)

    def forward(self, text):
        input = self.tokenizer(text, return_tensors="pt", truncation=True, padding="max_length", max_length=128)
        input_ids = input["input_ids"].to(self.device)
        token_type_ids = input['token_type_ids'].to(self.device)
        attention_mask = input['attention_mask'].to(self.device)

        sequence_out, pooler_out = self.bert(input_ids=input_ids,
                        token_type_ids=token_type_ids,
                        attention_mask=attention_mask,
                        return_dict=False)      #return_dict

        pred = self.cls_head(pooler_out)
        return pred

if __name__ == "__main__":
    model = myBertModel("../bert-base-chinese", 2)
    pred = model("今天天气真好")

代码逐行解释

导入必要的库
python 复制代码
import torch
import torch.nn as nn
from transformers import BertModel, BertTokenizer, BertConfig
  • torchtorch.nn:PyTorch的核心库,用于构建神经网络。
  • BertModel, BertTokenizer, BertConfig :来自 transformers 库,分别用于加载预训练的BERT模型、BERT分词器和BERT配置。
定义自定义BERT模型类
python 复制代码
class myBertModel(nn.Module):
    def __init__(self, bert_path, num_class, device):
        super(myBertModel, self).__init__()

        self.bert = BertModel.from_pretrained(bert_path)
        self.device = device
        self.cls_head = nn.Linear(768, num_class)
        self.tokenizer = BertTokenizer.from_pretrained(bert_path)
  • __init__ 方法
    • 加载预训练的BERT模型。
    • 初始化设备(device),可以是CPU或GPU。
    • 定义分类头(cls_head),将BERT输出的768维向量映射到指定的类别数。
    • 加载BERT的分词器(tokenizer),用于将输入文本转换为BERT模型所需的输入格式。
python 复制代码
    def forward(self, text):
        input = self.tokenizer(text, return_tensors="pt", truncation=True, padding="max_length", max_length=128)
        input_ids = input["input_ids"].to(self.device)
        token_type_ids = input['token_type_ids'].to(self.device)
        attention_mask = input['attention_mask'].to(self.device)

        sequence_out, pooler_out = self.bert(input_ids=input_ids,
                                             token_type_ids=token_type_ids,
                                             attention_mask=attention_mask,
                                             return_dict=False)      #return_dict

        pred = self.cls_head(pooler_out)
        return pred
  • forward 方法
    • 使用 tokenizer 将输入文本转换为BERT模型所需的输入张量(input_ids, token_type_ids, attention_mask)。
    • 将这些张量移动到指定的设备(CPU或GPU)上。
    • 调用 self.bert 进行前向传播,得到序列输出(sequence_out)和池化输出(pooler_out)。
    • 使用分类头(cls_head)对池化输出进行线性变换,得到最终的预测结果(pred)。
主程序入口
python 复制代码
if __name__ == "__main__":
    model = myBertModel("../bert-base-chinese", 2, "cpu")
    pred = model("今天天气真好")
    print(pred)
  • 主程序入口
    • 创建 myBertModel 实例,指定预训练BERT模型路径、类别数和设备。
    • 调用模型进行前向传播,传入一条测试文本 "今天天气真好",并打印预测结果。

三、train.py

python 复制代码
import torch
import time
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm



def train_val(para):

########################################################
    model = para['model']
    train_loader =para['train_loader']
    val_loader = para['val_loader']
    scheduler = para['scheduler']
    optimizer = para['optimizer']
    loss = para['loss']
    epoch = para['epoch']
    device = para['device']
    save_path = para['save_path']
    max_acc = para['max_acc']
    val_epoch = para['val_epoch']


#################################################
    plt_train_loss = []
    plt_train_acc = []
    plt_val_loss = []
    plt_val_acc = []
    val_rel = []

    for i in range(epoch):
        start_time = time.time()
        model.train()
        train_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        val_loss = 0.0
        for batch in tqdm(train_loader):
            model.zero_grad()
            text, labels = batch[0], batch[1].to(device)
            pred = model(text)
            bat_loss = loss(pred, labels)
            bat_loss.backward()
            optimizer.step()
            scheduler.step()              #scheduler     调整学习率
            optimizer.zero_grad()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)       #梯度裁切
            train_loss += bat_loss.item()    #.detach 表示去掉梯度
            train_acc += np.sum(np.argmax(pred.cpu().data.numpy(),axis=1)== labels.cpu().numpy())
        plt_train_loss . append(train_loss/train_loader.dataset.__len__())
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())
        if i % val_epoch == 0:
            model.eval()
            with torch.no_grad():
                for batch in tqdm(val_loader):
                    val_text, val_labels = batch[0], batch[1].to(device)
                    val_pred = model(val_text)
                    val_bat_loss = loss(val_pred, val_labels)
                    val_loss += val_bat_loss.cpu().item()

                    val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_labels.cpu().numpy())
                    val_rel.append(val_pred)

            if val_acc > max_acc:
                torch.save(model, save_path+str(epoch)+"ckpt")
                max_acc = val_acc
            plt_val_loss.append(val_loss/val_loader.dataset.__len__())
            plt_val_acc.append(val_acc/val_loader.dataset.__len__())
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f | valAcc: %3.6f valLoss: %3.6f  ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1], plt_val_acc[-1], plt_val_loss[-1])
                  )
            if i % 50 == 0:
                torch.save(model, save_path+'-epoch:'+str(i)+ '-%.2f'%plt_val_acc[-1])
        else:
            plt_val_loss.append(plt_val_loss[-1])
            plt_val_acc.append(plt_val_acc[-1])
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f   ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1])
                  )
    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title('loss')
    plt.legend(['train', 'val'])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title('Accuracy')
    plt.legend(['train', 'val'])
    plt.savefig('acc.png')
    plt.show()

这段代码实现了一个完整的训练和验证循环,用于训练一个深度学习模型。它包括训练过程中的损失和准确率记录、模型保存、学习率调整以及结果的可视化。

代码逐行解释

导入必要的库
python 复制代码
import torch
import time
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm
  • torch:PyTorch的核心库,用于构建和训练神经网络。
  • time:用于记录训练时间。
  • matplotlib.pyplot:用于绘制训练和验证的损失和准确率曲线。
  • numpy:用于数值计算,特别是处理预测结果和标签的比较。
  • tqdm:用于显示进度条,方便监控训练过程。
定义训练和验证函数
python 复制代码
def train_val(para):
  • train_val 函数 :接受一个包含所有必要参数的字典 para,并在其中进行训练和验证。
参数解析
python 复制代码
    model = para['model']
    train_loader = para['train_loader']
    val_loader = para['val_loader']
    scheduler = para['scheduler']
    optimizer = para['optimizer']
    loss = para['loss']
    epoch = para['epoch']
    device = para['device']
    save_path = para['save_path']
    max_acc = para['max_acc']
    val_epoch = para['val_epoch']
  • model:要训练的模型。
  • train_loaderval_loader:训练集和验证集的数据加载器。
  • scheduler:学习率调度器。
  • optimizer:优化器(如Adam、SGD等)。
  • loss:损失函数(如交叉熵损失)。
  • epoch:训练的总轮数。
  • device:设备(CPU或GPU)。
  • save_path:模型保存路径。
  • max_acc:当前最高的验证准确率。
  • val_epoch:每隔多少个epoch进行一次验证。
初始化记录变量
python 复制代码
    plt_train_loss = []
    plt_train_acc = []
    plt_val_loss = []
    plt_val_acc = []
    val_rel = []
  • plt_train_lossplt_train_acc:记录每轮训练的损失和准确率。
  • plt_val_lossplt_val_acc:记录每轮验证的损失和准确率。
  • val_rel:用于存储验证集的预测结果(未在后续代码中使用)。
训练和验证循环
python 复制代码
    for i in range(epoch):
        start_time = time.time()
        model.train()
        train_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        val_loss = 0.0
  • 初始化每轮的损失和准确率:在每个epoch开始时,重置这些变量。
训练阶段
python 复制代码
        for batch in tqdm(train_loader):
            model.zero_grad()
            text, labels = batch[0], batch[1].to(device)
            pred = model(text)
            bat_loss = loss(pred, labels)
            bat_loss.backward()
            optimizer.step()
            scheduler.step()  # 调整学习率
            optimizer.zero_grad()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)  # 梯度裁切
            train_loss += bat_loss.item()
            train_acc += np.sum(np.argmax(pred.cpu().data.numpy(), axis=1) == labels.cpu().numpy())
  • tqdm(train_loader):显示训练进度条。
  • model.zero_grad():清空之前的梯度。
  • text, labels = batch[0], batch[1].to(device):将数据和标签移动到指定设备。
  • pred = model(text):前向传播得到预测结果。
  • bat_loss = loss(pred, labels):计算损失。
  • bat_loss.backward():反向传播计算梯度。
  • optimizer.step():更新模型参数。
  • scheduler.step():调整学习率(根据调度器)。
  • optimizer.zero_grad() :清空梯度(通常在 zero_grad() 之后不需要再次调用)。
  • torch.nn.utils.clip_grad_norm_:梯度裁切,防止梯度过大导致训练不稳定。
  • train_loss += bat_loss.item():累加当前批次的损失。
  • train_acc += np.sum(np.argmax(pred.cpu().data.numpy(), axis=1) == labels.cpu().numpy()):计算当前批次的准确率。
计算平均损失和准确率并记录
python 复制代码
        plt_train_loss.append(train_loss / len(train_loader.dataset))
        plt_train_acc.append(train_acc / len(train_loader.dataset))
  • plt_train_loss.append(...):记录每轮训练的平均损失。
  • plt_train_acc.append(...):记录每轮训练的平均准确率。
验证阶段
python 复制代码
        if i % val_epoch == 0:
            model.eval()
            with torch.no_grad():
                for batch in tqdm(val_loader):
                    val_text, val_labels = batch[0], batch[1].to(device)
                    val_pred = model(val_text)
                    val_bat_loss = loss(val_pred, val_labels)
                    val_loss += val_bat_loss.cpu().item()

                    val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_labels.cpu().numpy())
                    val_rel.append(val_pred)

            if val_acc > max_acc:
                torch.save(model, save_path + str(epoch) + "ckpt")
                max_acc = val_acc
            plt_val_loss.append(val_loss / len(val_loader.dataset))
            plt_val_acc.append(val_acc / len(val_loader.dataset))
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f | valAcc: %3.6f valLoss: %3.6f' % \
                  (i, epoch, time.time() - start_time, plt_train_acc[-1], plt_train_loss[-1], plt_val_acc[-1], plt_val_loss[-1])
                  )
            if i % 50 == 0:
                torch.save(model, save_path + '-epoch:' + str(i) + '-%.2f' % plt_val_acc[-1])
        else:
            plt_val_loss.append(plt_val_loss[-1])
            plt_val_acc.append(plt_val_acc[-1])
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f' % \
                  (i, epoch, time.time() - start_time, plt_train_acc[-1], plt_train_loss[-1])
                  )
  • if i % val_epoch == 0: :每隔 val_epoch 轮进行一次验证。
  • model.eval():设置模型为评估模式,关闭dropout等训练特定操作。
  • with torch.no_grad()::禁用梯度计算,节省内存并加速推理。
  • val_text, val_labels = batch[0], batch[1].to(device):将验证数据和标签移动到指定设备。
  • val_pred = model(val_text):前向传播得到验证预测结果。
  • val_bat_loss = loss(val_pred, val_labels):计算验证损失。
  • val_loss += val_bat_loss.cpu().item():累加验证损失。
  • val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_labels.cpu().numpy()):计算验证准确率。
  • if val_acc > max_acc::如果当前验证准确率超过历史最高值,则保存模型。
  • plt_val_loss.append(...)plt_val_acc.append(...):记录每轮验证的平均损失和准确率。
  • print(...):打印当前轮次的训练和验证信息。
  • if i % 50 == 0::每隔50轮保存一次模型。
绘制训练和验证曲线
python 复制代码
    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title('loss')
    plt.legend(['train', 'val'])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title('Accuracy')
    plt.legend(['train', 'val'])
    plt.savefig('acc.png')
    plt.show()
  • plt.plot(...):绘制训练和验证的损失和准确率曲线。
  • plt.title(...):设置图表标题。
  • plt.legend(...):添加图例。
  • plt.show():显示图表。
  • plt.savefig('acc.png'):保存准确率图表为PNG文件。
相关推荐
龚大龙6 分钟前
机器学习(李宏毅)——Domain Adaptation
人工智能·机器学习
源码姑娘13 分钟前
基于DeepSeek的智慧医药系统(源码+部署教程)
java·人工智能·程序人生·毕业设计·springboot·健康医疗·课程设计
AIGC_ZY13 分钟前
扩散模型中三种加入条件的方式:Vanilla Guidance,Classifier Guidance 以及 Classifier-Free Guidance
深度学习·机器学习·计算机视觉
☞黑心萝卜三条杠☜1 小时前
后门攻击仓库 backdoor attack
论文阅读·人工智能
三三木木七1 小时前
BERT、T5、GPTs,Llama
人工智能·深度学习·bert
problc2 小时前
Manus AI 全球首款通用型 Agent,中国制造
大数据·人工智能·制造
xiangzhihong82 小时前
GitHub神秘组织3小时极速复刻Manus
人工智能·深度学习·机器学习
博云技术社区2 小时前
DeepSeek×博云AIOS:突破算力桎梏,开启AI普惠新纪元
人工智能·博云·deepseek
ZHOU_WUYI2 小时前
Process-based Self-Rewarding Language Models 论文简介
人工智能·深度学习
优维科技EasyOps2 小时前
优维眼中的Manus:AI工程化思维重构Agent的运维端启示
运维·人工智能·重构