【LLM】第三章:BERT讲解+情感分析案例

【LLM】第三章:BERT讲解+情感分析案例

本篇开讲当年叱诧风云、风光无二的BERT大模型。

说明:本文是假定你已经非常了解Transformer了,也不会重复写和注意力机制、Transformer相关的内容了。想了解详情的自行查阅我的NLP专栏:https://blog.csdn.net/friday1203/category_12833594.html?spm=1001.2014.3001.5482

一、BERT概述

2018年10月,Google发布一篇论文《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》,BERT模型横空出世,并横扫NLP领域11项目任务的最佳成绩!在BERT出现之前,NLP领域占据统治地位的是LSTM和GRU模型,但是BERT出世后,基本就是NLP领域的标配了。其强大的自然语言表示能力,被广泛应用于文本分类、序列标注,比如命名实体识别、句子匹配等场景。

BERT论文地址是:https://arxiv.org/pdf/1810.04805.pdf 大家可以自行下载原论文阅读。

在BERT中发挥重要作用的结构就是Transformer,之后又出现XLNET,roBERT等模型击败了BERT,但是它们的核心没有变,仍然是Transformer。

二、BERT模型架构

BERT(Bidirectional Encoder Representation from Transformers),采用的是Transformer的编码器结构 ,而编码器是用来理解语义 的,生成词语的token表示 的。所以BERT其实就是一个词向量化模型 ,只是它因使用了注意力机制,而具有更加强大的自然语言表示能力。

同时BERT也是一个预训练模型 ,也就是谷歌已经用海量语料把模型预训练完毕了,发布了两个版本:BERT-base和BERT-large,具体参数规格如下右图:我们以BERT-base版本为例来讲解。

1、从HF上下载BERT-base预训练模型到本地

在前一个篇章:【LLM】第二章:HuggingFace入门学习-CSDN博客 如何配置环境变量、下载的文件及其地址都有详细的说明和示例。这里不再重复。我把cache中下载的文件复制粘贴到项目文件夹中,如下图所示:

2、从本地加载预训练模型BERT-base

python 复制代码
#这是加载本地BERT预训练模型+tokenizer的代码
from transformers import AutoModel, AutoTokenizer
model_name = "bert-base-chinese"
cache_dir = r"D:\2MyWork\MyFile\pytorch-deeplearning\大模型\hf-models\bert-model"    #带盘符的本地地址,也是1中的位置

model_bert = AutoModel.from_pretrained(model_name, cache_dir=cache_dir)
model_bert

tokenizer_bert = AutoTokenizer.from_pretrained(model_name, cache_dir=cache_dir)
tokenizer_bert

3、从2中看BERT的整体架构 可见,bert架构和transformer的编码器相差不大,只要对transformer非常熟悉,上面的架构就没啥新鲜的。

4、澄清:什么是"双向自注意力结构"

网上很多资料强调BERT的创新在于它是一个双向自注意力结构 :在训练每个token表示时,BERT是整合了左右两个方向上的上下文信息,从而获得更准确、更丰富的语义表示:了解注意力机制的同学就会秒懂,这本身就是注意力机制的固有特点,因为计算注意力分值时,计算的就是整个序列中样本与样本之间的分值。也所以它具有长期记忆能力!所以这个创新根本不是啥创新,没啥稀奇的。上图是BERT论文中的图,个人感觉有些不对! 下篇我们学的GPT,GPT是由12个transformer中的解码器串联而成!不是上面右图那样的神经元连接的结构!同理,BERT也是编码器串联而成的!不是像神经元链接的这种方式!如果是这种方式,那得要多少个编码器?!此外,一个TM就可以输出被喂入的整个序列中的所有token的结果!不是上图那种的一个TM就输出序列中对应的token的结果!

5、BERT的输入部分

BERT的每个输入token表示由三部分组成:

Token Embedding(词嵌入):词本身的embedding表示;

Segment Embedding (段落编码):用于区分句子对任务 中的两个句子,分别用一个可学习的向量表示。

Position Embedding(位置编码):每个词的位置编码;

(1)加入了段落编码(Segment Embedding),就是给序列也进行了位置编码。

(2)位置编码用的是可训练的位置编码(Trainable Positional Embedding)。

(3)BERT输入中一般会包含两个特殊标识:

CLS: 句首标识,一个序列的起始标志。而且这个标识的输出一般被视为其所在序列的整体输出 ,所以这个标识的输出经常被用于下游的文本分类任务。因为BERT是双向自注意力,所以将第一个token当成整个序列的输出也是可以的 ,也就是它可以被看作是专门用于汇总整个序列的语义信息的。

SEP: 句间分隔符,出现在每个句子的末尾。因为有这个标识,所以BERT既可以处理单句任务,也可以处理多句任务,非常灵活。

(4)BERT-base支持的最大序列长度是512。也就是我们一次最多只能喂入模型512个token,再长就不支持了,需要截断,短了没事,加padding直到512即可。

(5)BERT-base的词表是21128个token。

6、BERT的输出部分

BERT的输出有2部分:

一部分是编码层最后的输出层的输出 。是所有输入的tokens(512个)的输出(768维)。

另一部分是pooler层的输出。pooler层是承接CLStoken的,就是处理CLStoken的输出的。

所以,你可以根据你自己的具体下游任务类型,给BERT接入不同的任务输出头

(1) Token-Level任务,(如命名实体识别、序列标注任务):需要使用每个位置的输出表示,所以每个位置的输出都要接入任务输出头。而任务输出头一般就是一个线性层,该回归该分类视你的具体下游任务而定。

(2) Seqence-Level任务(如文本分类、句子对分类):可以使用特殊tokenCLS的输出表示,来接入任务输出头。此时CLS就被视作是汇总整个序列语义信息的token。

小结:通过BERT预训练模型,我们可以得到每个token的语义表示,微调时,我们可以通过添加简单的任务特定层,适配不同类型的下游任务。

三、BERT的预训练和微调过程

BERT是谷歌的一个开源项目,我们从HF上下载下来的BERT,是人家谷歌,已经使用大量自然语言语料,训练好了的一个自然语言领域的预训练模型。所以,BERT是自然语言领域 的一个通用模型、预训练模型,或者说BERT就是一个自然语言处理的通用解决方案、通用的解决框架,因为它可以解决大部分的NLP任务。当你拿着BERT让它学比如法律、金融等子领域的专业知识,你只需整理好你的法律或者金融领域的特定语料,然后训练很少的参数,就是fine-tune(微调)一下,这个模型就是一个专业领域的专业语言模型了。

所以下面我们详细看看Bert是如何进行预训练的。当我们拿到预训练的bert后又是如何进行微调的。

(一)BERT是如何进行预训练的

1、BERT预训练阶段有两个任务

任务1:掩码语言模型 (MLM, Masked Language Modeling):就是把训练句子随机遮住 15%的token,然后让模型根据上下文去预测这15%的token。这个任务是为了让模型学习词级语义

说明:被遮盖的这15%的token是:

80%的被替换成特殊tokenMASK

10%被替换成随机词;

10%保持原词不变。

为什么不全部替换成MASK?这么设计是为了增强模型的泛化性,就是今后模型遇到的不是MASK这个特殊token,即使是其他随机词token,模型依然可以预测出这个位置的词。

任务2:下一句预测 (Next Sentence Prediction,NSP)

为了提升模型理解句子和句子之间的逻辑关系 ,BERT在预训练时,模型被喂入的是两个句子,让模型判断第二句是否是第一句的真实后续句子。其中:

50%的训练样本是上下文中真实相邻的句子(正例); 50%是从语料中随机采样的非相邻句子(反例)。

2、在预训练时,BERT是同时优化MLM和 NSP两个目标 ,具体操作如下:

其实,NSP其实后面接的就是pooler层,pooler层是一个线性层+tanh激活层,用来承接CLStoken的输出的,而CLStoken的输出可以看作是对所有输入的总结信息。所以可以用这个头来承接Seqence-Level任务。

重点:损失函数 是上图的两个任务头的损失加和,通过优化加和损失来训练模型的。

(二)BERT是如何进行微调的

在模型预训练完成后,BERT就可以通过微调来适配多种下游任务了,比如文本分类、句子匹配、问答系统、序列标注等。

老套路,微调时,模型主体结构保持不变,仅在顶部添加一个特定任务的输出头 ,使用少量的、带标注的、专业领域的任务数据,对整个模型进行微调训练 。不同下游任务的差异主要体现在输出层的设计 、以及从模型输出中提取哪些信息来标识预测结果

此外,微调时的格式依旧要和BERT的要求保持一致,比如也是要以CLS为序列头标识,以SEP为句子的结尾标识。这都是基本常识。

1、句子对儿分类任务

比如判断两个句子之间是否存在重复、蕴含、矛盾等关系。要实现上面的任务,我们可以到GLUE网站上下载对应的数据集,可以让你的模型在这些数据集上跑跑效果。

2、单句分类任务

3、问答任务 上图是抽取式问答,还有生成式问答,后面讲T5是生成式问答。我们平时和AI助手聊天的应用,就是生成式问答。

4、序列标注任务

比如命名实体识别任务:

总之:bert模型本质上是一个分类任务的模型,然而由于各种花式分类,让bert不仅可以分类,还可以填空。

四、示例:下载bert到本地-->加载bert-->实现分类示例

由于我们想实现分类任务,而且不想写模型架构了,所以这里展示一下如何直接调用带有任务头的bert-base模型:

python 复制代码
#这是下载的带任务头的BERT-base预训练模型+tokenizer的代码
from transformers import BertTokenizer, BertForSequenceClassification
model_name='bert-base-chinese'
model = BertForSequenceClassification.from_pretrained(model_name)
tokenizer = BertTokenizer.from_pretrained(model_name)
model
tokenizer

其实不用再下载一遍,其实这里下载的文件和地址都和上面的一样,都在cache路径下,都是一样的文件,只是BertForSequenceClassification这个类会帮我们自动添加一个二分类的任务头:

python 复制代码
#把从本地加载BertForSequenceClassification
from transformers import BertTokenizer, BertForSequenceClassification
model_name = 'bert-base-chinese'
model_dir = r"D:\2MyWork\MyFile\pytorch-deeplearning\大模型\hf-models\bert-model\models--bert-base-chinese\snapshots\8f23c25b06e129b6c986331a13d8d025a92cf0ea"
model = BertForSequenceClassification.from_pretrained(model_dir)
tokenizer = BertTokenizer.from_pretrained(model_dir)
model
tokenizer

意思就是预训练模型:bert主体架构,是已经被训练好了的模型,但是最后的线性层(任务头)是没有被训练的,需要训练才能推理。

python 复制代码
#pipeline是在线访问,我们用pipoline推理一下试试,访问成功与否取决于你的网络状况
from transformers import pipeline
classifier = pipeline('text-classification', model=model, tokenizer=tokenizer)
result = classifier('这是一首歌')
result

只是一个简单的在线实现的任务,其实很多时候我们是需要本地部署、本地推理预测的,此时我们不仅得把模型下载下来,还得用我们自己的数据集对模型进行训练,才能完成我们自己设置的任务,比如写诗啥的。下面是用我们自己数据训练的情感分类任务:

五、实战:基于本地的bert、本地的数据,实现评论的情感分析任务

说明:本例用的数据是 【NLP】第八章:项目实操案例:文本情感分析-CSDN博客 这个例子中的数据。

(一)只要BERT主体架构,我们自己手动添加适配具体任务的线性层

1、代码架构是:

2、各个模块的代码及运行结果

python 复制代码
#config.py模块的代码   
from pathlib import Path

ROOT_DIR = Path(__file__).parent #d:\2MyWork\MyFile\pytorch-deeplearning\大模型\BERT实战-情感分析

RAW_DATA = ROOT_DIR/'data'/'raw'
PROCESSED_DATA = ROOT_DIR/'data'/'processed'
PRETRAINED_DIR = ROOT_DIR/'pretrained'
MODELS_DIR = ROOT_DIR/'models'

SEQ_LEN = 128
BATCH_SIZE=16

LEARNING_RATE=1e-6    #预训练模型的微调,由于只调最后一个层,所以尽量把学习率设置小一点,避免梯度不稳定。
EPOCHS=1
python 复制代码
#process.py模块的代码
from datasets import load_dataset, ClassLabel
from transformers import AutoTokenizer
import config
from tqdm import tqdm

def process():
    print('开始处理数据。。。。')

    #读取原始数据文件
    raw_data = load_dataset('csv', data_files=str(config.RAW_DATA/'online_shopping_10_cats.csv'))
    dataset1 = raw_data['train']  #raw_data是个字典,所以要用key把文件索引出来

    #数据清洗
    dataset2 = dataset1.remove_columns('cat') #删除列
    dataset3 = dataset2.filter(lambda x:x['review'] is not None)  #删除空行

    #划分数据集
    dataset4 = dataset3.cast_column('label', ClassLabel(names=['negative','positive'])) #转字段类型
    dataset_train_test = dataset4.train_test_split(test_size=0.2, stratify_by_column='label') 
    print(dataset_train_test)

    #创建Tokenizer
    tokenizer = AutoTokenizer.from_pretrained(config.PRETRAINED_DIR)
    
    #构建可以直接喂入模型的训练集和测试集
    def batch_encode(batch):
        inputs = tokenizer(batch['review'], padding='max_length', truncation=True,max_length=config.SEQ_LEN)
        inputs['labels'] = batch['label']
        return inputs
    dataset_ids = dataset_train_test.map(batch_encode, batched=True, remove_columns=['review', 'label'])
    
    #保存训练集和测试集
    dataset_ids.save_to_disk(config.PROCESSED_DATA)    
    
    print('数据处理完成。。。。')

if __name__ == '__main__':
    process()
python 复制代码
#dataset.py模块的代码
import config
from datasets import load_from_disk
from torch.utils.data import Dataset, DataLoader

def get_dataloader(train=True):
    path = str(config.PROCESSED_DATA/('train' if train else 'test'))
    dataset = load_from_disk(path)
    dataset.set_format(type='torch')
    return DataLoader(dataset, batch_size=config.BATCH_SIZE, shuffle=True)
python 复制代码
#model.py模块的代码
import config
from transformers import AutoModel
import torch

class Bert_Main(torch.nn.Module):   #只用主模型,下游任务的线性层自己写
    def __init__(self, ):
        super().__init__()
        self.bert = AutoModel.from_pretrained(config.PRETRAINED_DIR)
        self.linear = torch.nn.Linear(self.bert.config.hidden_size, 1)

    def forward(self, input_ids, attention_mask, token_type_ids):
        output = self.bert(input_ids, attention_mask, token_type_ids)
        last_hidden_state = output.last_hidden_state
        cls_hidden_state = last_hidden_state[:,0,:]
        yhat = self.linear(cls_hidden_state).squeeze(-1)
        return yhat
python 复制代码
#train.py模块的代码
import torch
from transformers import AutoTokenizer
import config, model, dataset
from tqdm import tqdm


def train():
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    train_data = dataset.get_dataloader()   #数据
    bert_main_model = model.Bert_Main().to(device)   #模型
    criterion = torch.nn.BCEWithLogitsLoss()    #损失函数
    optimizer = torch.optim.Adam(bert_main_model.parameters(), lr=config.LEARNING_RATE)   #优化器

    #开始训练
    bert_main_model.train()
    epoch_loss = []
    best_loss = float('inf')

    for epoch in range(config.EPOCHS):
        print('='*10, f'Epoch: {epoch+1}', '='*10)
        batch_loss=0
        for batch in tqdm(train_data, desc='模型训练中。。。'):
            inputs = {k : v.to(device) for k,v in batch.items()}  #把张量都迁移到GPU上
            labels = inputs.pop('labels').to(dtype=torch.float)   #把标签拿出来
            outputs = bert_main_model(**inputs)  #正向传播   把除标签外的数据,以解引用的方式传给预训练模型
            loss = criterion(outputs, labels)  #求损失
            loss.backward()   #反向传播
            optimizer.step()   #更新网络参数
            optimizer.zero_grad()   #情况梯度
            batch_loss += loss.item()
        epoch_loss_mean = batch_loss/len(train_data)
        print(f'本轮epoch的平均损失是: {epoch_loss_mean}')
        epoch_loss.append(epoch_loss_mean)

        if epoch_loss_mean < best_loss:
            best_loss = epoch_loss_mean
            torch.save(bert_main_model.state_dict(),config.MODELS_DIR/'best_model.pt')
            print('模型保存成功。。。')


if __name__=='__main__':
    train()      
python 复制代码
#predict.py模块的代码
import torch
from transformers import AutoTokenizer
import config, model

def predict(text, tokenizer, Model, device):
    tokens = tokenizer(text,padding='max_length',truncation=True,max_length=config.SEQ_LEN,return_tensors='pt')
    inputs = {k : v.to(device) for k,v in tokens.items()}  #把张量都迁移到GPU上
    Model.eval()
    with torch.no_grad():        
        output = Model(**inputs)
        yhat = torch.sigmoid(output)
        predict_re = f'正向评价(置信度{yhat})' if yhat>=0.5 else f'负向评价(置信度{1-yhat})'
    return predict_re


def run_predict():
    print('欢迎使用BERT模型进行情感分析(输入q或者quit退出)')

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    my_tokenizer = AutoTokenizer.from_pretrained(config.PRETRAINED_DIR)  #tokenizer
    bert_main_model = model.Bert_Main().to(device)   #加载模型
    bert_main_model.load_state_dict(torch.load(config.MODELS_DIR/'best_model.pt')) #加载参数
    
    while True:
        user_input= input('请输入>>> ')
        if user_input in ['q', 'quit']:
            print('欢迎下次再来!')
            break
        if user_input.strip() == '':
            print('请输入内容')
            continue
        predict_re = predict(user_input, my_tokenizer, bert_main_model, device)
        print(predict_re)
 
 
if __name__=='__main__':
    run_predict()
python 复制代码
#evaluate.py模块的代码
import torch
import config, model, dataset
from tqdm import tqdm
 
def run_evaluate():
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    Model = model.Bert_Main().to(device)       #加载模型
    Model.load_state_dict(torch.load(config.MODELS_DIR/'best_model.pt'))  #加载参数
    print('模型加载成功!!!')
 
    test_dataloader = dataset.get_dataloader(train=False)   #测试数据
    total_sample=0
    predict_correct = 0
    Model.eval()
    with torch.no_grad():
        for batch in tqdm(test_dataloader, desc='模型测试中。。。'):
            inputs = {k : v.to(device) for k,v in batch.items()}  #把张量都迁移到GPU上
            labels = inputs.pop("labels").tolist()
            output = Model(**inputs) #正向传播
            output = torch.sigmoid(output)
            output = output.tolist()
            result = [0 if i<0.5 else 1 for i in output]
            total_sample += len(output)
            for yhat, y_true in zip(result, labels):
                if yhat == y_true:
                    predict_correct += 1
   
    print(f'模型在测试集的效果:准确率{predict_correct/total_sample:.4f}')

if __name__ == '__main__':
    run_evaluate()

(二)直接调用带任务头的BERT

1、项目架构:

2、各个模块的代码及运行结果展示

python 复制代码
#config.py模块的代码   
from pathlib import Path

ROOT_DIR = Path(__file__).parent #d:\2MyWork\MyFile\pytorch-deeplearning\大模型\BERT实战-情感分析

RAW_DATA = ROOT_DIR/'data'/'raw'
PROCESSED_DATA = ROOT_DIR/'data'/'processed'
PRETRAINED_DIR = ROOT_DIR/'pretrained'
MODELS_DIR = ROOT_DIR/'models'

SEQ_LEN = 128
BATCH_SIZE=16

LEARNING_RATE=1e-6    #预训练模型的微调,由于只调最后一个层,所以尽量把学习率设置小一点,避免梯度不稳定。
EPOCHS=1
python 复制代码
#process.py模块的代码
from datasets import load_dataset, ClassLabel
from transformers import AutoTokenizer
import config
from tqdm import tqdm

def process():
    print('开始处理数据。。。。')

    #读取原始数据文件
    raw_data = load_dataset('csv', data_files=str(config.RAW_DATA/'online_shopping_10_cats.csv'))
    dataset1 = raw_data['train']  #raw_data是个字典,所以要用key把文件索引出来

    #数据清洗
    dataset2 = dataset1.remove_columns('cat') #删除列
    dataset3 = dataset2.filter(lambda x:x['review'] is not None)  #删除空行

    #划分数据集
    dataset4 = dataset3.cast_column('label', ClassLabel(names=['negative','positive'])) #转字段类型
    dataset_train_test = dataset4.train_test_split(test_size=0.2, stratify_by_column='label') 
    print(dataset_train_test)

    #创建Tokenizer
    tokenizer = AutoTokenizer.from_pretrained(config.PRETRAINED_DIR)
    
    #构建可以直接喂入模型的训练集和测试集
    def batch_encode(batch):
        inputs = tokenizer(batch['review'], padding='max_length', truncation=True,max_length=config.SEQ_LEN)
        inputs['labels'] = batch['label']
        return inputs
    dataset_ids = dataset_train_test.map(batch_encode, batched=True, remove_columns=['review', 'label'])
    
    #保存训练集和测试集
    dataset_ids.save_to_disk(config.PROCESSED_DATA)    
    
    print('数据处理完成。。。。')

if __name__ == '__main__':
    process()
python 复制代码
#dataset.py模块的代码
import config
from datasets import load_from_disk
from torch.utils.data import Dataset, DataLoader

def get_dataloader(train=True):
    path = str(config.PROCESSED_DATA/('train' if train else 'test'))
    dataset = load_from_disk(path)
    dataset.set_format(type='torch')
    return DataLoader(dataset, batch_size=config.BATCH_SIZE, shuffle=True)
python 复制代码
#model.py模块的代码---只要加载一下预训练模型即可
from transformers import AutoModelForSequenceClassification
import config
import torch

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
model_full = AutoModelForSequenceClassification.from_pretrained(config.PRETRAINED_DIR).to(device) 
print(model_full)
python 复制代码
#train.py模块的代码
import torch
from transformers import AutoModelForSequenceClassification
import config, dataset
from tqdm import tqdm


def train():
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    train_data = dataset.get_dataloader()   #数据
    model_full = AutoModelForSequenceClassification.from_pretrained(config.PRETRAINED_DIR).to(device)   #模型
    optimizer = torch.optim.Adam(model_full.parameters(), lr=config.LEARNING_RATE)   #优化器

    #开始训练
    model_full.train()
    epoch_loss = []
    best_loss = float('inf')

    for epoch in range(config.EPOCHS):
        print('='*10, f'Epoch: {epoch+1}', '='*10)
        batch_loss=0
        for batch in tqdm(train_data, desc='模型训练中。。。'):
            inputs = {k : v.to(device) for k,v in batch.items()}  #把张量都迁移到GPU上
            outputs = model_full(**inputs)  #正向传播   把除标签外的数据,以解引用的方式传给预训练模型
            loss = outputs.loss
            loss.backward()   #反向传播
            optimizer.step()   #更新网络参数
            optimizer.zero_grad()   #情况梯度
            batch_loss += outputs.loss.item()
        epoch_loss_mean = batch_loss/len(train_data)
        print(f'本轮epoch的平均损失是: {epoch_loss_mean}')
        epoch_loss.append(epoch_loss_mean)

        if epoch_loss_mean < best_loss:
            best_loss = epoch_loss_mean
            model_full.save_pretrained(config.MODELS_DIR)  #####保存模型的api#####
            print('模型保存成功。。。')


if __name__=='__main__':
    train()      
python 复制代码
#predict.py模块的代码
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer
import config

def predict(text, tokenizer, Model, device):
    tokens = tokenizer(text,padding='max_length',truncation=True,max_length=config.SEQ_LEN,return_tensors='pt')
    inputs = {k : v.to(device) for k,v in tokens.items()}  #把张量都迁移到GPU上
    Model.eval()
    with torch.no_grad():    
        output = Model(**inputs)
        yhat = torch.sigmoid(output.logits)   ####用.logits取出结果
        predict_re = f'负向评价(置信度{yhat[0][0]})' if yhat[0][0]>yhat[0][1] else f'正向评价(置信度{yhat[0][1]})'
    return predict_re


def run_predict():
    print('欢迎使用BERT模型进行情感分析(输入q或者quit退出)')

    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    my_tokenizer = AutoTokenizer.from_pretrained(config.PRETRAINED_DIR)  #tokenizer
    model_full = AutoModelForSequenceClassification.from_pretrained(config.MODELS_DIR).to(device) #加载模型
    
    
    while True:
        user_input= input('请输入>>> ')
        if user_input in ['q', 'quit']:
            print('欢迎下次再来!')
            break
        if user_input.strip() == '':
            print('请输入内容')
            continue
        predict_re = predict(user_input, my_tokenizer, model_full, device)
        print(predict_re)
 
 
if __name__=='__main__':
    run_predict()
python 复制代码
#evaluate.py模块的代码
import torch
import config, dataset
from transformers import AutoModelForSequenceClassification
from tqdm import tqdm
 
def run_evaluate():
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   #设备
    model_full = AutoModelForSequenceClassification.from_pretrained(config.MODELS_DIR).to(device) #加载模型
    print('模型加载成功!!!')
 
    test_dataloader = dataset.get_dataloader(train=False)   #测试数据
    total_sample=0
    predict_correct = 0
    model_full.eval()
    with torch.no_grad():
        for batch in tqdm(test_dataloader, desc='模型测试中。。。'):
            inputs = {k : v.to(device) for k,v in batch.items()}  #把张量都迁移到GPU上
            labels = inputs.pop("labels").tolist()
            output = model_full(**inputs) #正向传播
            output = torch.sigmoid(output.logits)
            output = output.tolist()
            result = [0 if i[0]>i[1] else 1 for i in output]
            total_sample += len(output)
            for yhat, y_true in zip(result, labels):
                if yhat == y_true:
                    predict_correct += 1
   
    print(f'模型在测试集的效果:准确率{predict_correct/total_sample:.4f}')

if __name__ == '__main__':
    run_evaluate()

这个结果和我们自己手动写最后线性层的结果一模一样。

相关推荐
z小猫不吃鱼1 小时前
06 权重幅值剪枝、剪枝后微调与稀疏网络训练:模型剪枝中的三个基本问题
网络·算法·剪枝
-cywen-1 小时前
BLIP:Bootstrapping Language-Image Pre-training
人工智能
2301_780356701 小时前
全视通智慧医院解决方案:构建数智化医疗新生态
大数据·网络·人工智能
sunywz1 小时前
【AI RAG知识库】09.【检索】【节点7】
人工智能
国服第二切图仔1 小时前
HarmonyOS APP《画伴梦工厂》开发第54篇-鸿蒙AI开放能力——语音与自然语言处理
人工智能·自然语言处理·harmonyos
AC赳赳老秦1 小时前
采购专员自动化:OpenClaw 自动比价、生成询价单、跟踪供应商报价进度实战指南
开发语言·数据库·人工智能·python·自动化·github·openclaw
QiLinkOS1 小时前
QiLink OS的失败数据共享平台的运作模式是否适用于所有行业?
android·开发语言·人工智能·算法·重构·开源
秦歌6661 小时前
agno-9-Culture
人工智能
AI小白Lin1 小时前
我用三个 Python 脚本,让 AI 学会了"记得自己是谁"
人工智能