(五)模型微调训练:基于 BERT 的中文评价情感分析[附源码]

上一章模型下载并在本地跑通。本章在此基础上做微调:用 Hugging Face 的 BERT,在 ChnSentiCorp 上训练中文评价情感分析。

流程:加载数据 → 做 Dataset → Tokenizer 编码 → 设计下游模型 → 训练保存权重;效果评估与测试下期。

1.Hugging Face模型微调训练

模型微调的基本概念

微调是指在预训练模型的基础上,通过进一步的训练来适应特定的需求。

BERT模型通过预训练来学习语言的通用模式,然后通过微调来适应特定的任务,如情感分析、命名实体识别等。

微调过程中,通常冻结BERT的预训练层,只训练与下游任务相关的层。

微调三个关键阶段

一个典型的 NLP 项目,可以按下面三个阶段来推进:

1. 数据

  • 对数据做格式转换,整理成模型可使用的数据集
  • 将文本转换为数字,叫做文本编码 (最终要变成词向量:用一个向量代表一个词)

2. 模型 / 训练

  • 判断模型的训练状态:欠拟合、拟合、过拟合

3. 模型测试

  • 测试指标

2.加载数据集

情感分析任务的数据通常包括文本 及其对应的情感标签

使用Hugging Face的datasets库可以轻松地加载和处理数据集。

  • 在线加载数据
python 复制代码
from datasets import load_dataset
# 加载数据集
dataset = load_dataset(path="lansinuote/ChnSentiCorp",cache_dir="D:/model/data/")
# 查看数据集
print(dataset)
  • 从缓存加载数据
python 复制代码
# 第一个示例
from datasets import load_from_disk
# 从缓存加载数据
datasets = load_from_disk(r"D:\model\data\ChnSentiCorp")
# 查看数据集
print(dataset)
  • 查看测试集的数据信息
python 复制代码
# # 查看测试集的数据信息 【train-训练数据,validation-验证数据,test-测试数据】
train_data = datasets["test"]
for data in train_data:
    print(data)
json 复制代码
[
{'text': '东西不错,不过有人不太喜欢镜面的,我个人比较喜欢,总之还算满意。', 'label': 1},
{'text': '非常一般的一本书,充满了假想的理想主义色彩,建议刚毕业的职场新人千万不要看。', 'label': 0}
]
  • dataset 转 csv
python 复制代码
#转为csv格式 
dataset.to_csv(path_or_buf=r"D:\\model\\data\\ChnSentiCorp.csv")
  • 加载 csv 文件
python 复制代码
from datasets import load_dataset
# 扩展:加载CSV格式数据
dataset = load_dataset(path="csv",data_files=r"D:\\model\\data\\ChnSentiCorp.csv")
print(dataset)

2.1数据集格式

Hugging Face的datasets库支持多种数据集格式,如CSV、JSON、TFRecord等。至少包含两列:一列是文本数据 ,另一列是情感标签

2.2数据集信息

加载数据集后,可以查看数据集的基本信息,如数据集大小、字段名称等。这有助于我们了解数据的分布情况,并在后续步骤中进行适当的处理。

css 复制代码
DatasetDict({
    train: Dataset({
        features: ['text', 'label'],
        num_rows: 9600
    })
    validation: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
    test: Dataset({
        features: ['text', 'label'],
        num_rows: 1200
    })
})

训练集-train:9600条数据;

验证集-validation:1200条数据;

测试集-test:1200条数据;

包含text和label两个字段。

3.制作DataSet

加载数据集后,需要对其进行处理以适应模型的输入格式。这包括数据清洗、格式转换等操作。

python 复制代码
# 自定义数据集
from torch.utils.data import Dataset
from datasets import load_from_disk

class MyDataset(Dataset):
    #初始化数据集
    def __init__(self,split):
        #从磁盘加载数据
        self.dataset = load_from_disk(r"D:\\model\\data\\ChnSentiCorp")
        if split == "train": # 训练集
            self.dataset = self.dataset["train"]
        elif split == "test": # 测试集
            self.dataset = self.dataset["test"]
        elif split == "validation":  # 验证集
            self.dataset = self.dataset["validation"]
        else:
            print("数据名错误!")

    #返回数据集长度
    def __len__(self):
        return len(self.dataset)

    #对每条数据单独做处理
    def __getitem__(self, item):
        text = self.dataset[item]["text"]
        label = self.dataset[item]["label"]

        return text,label

if __name__ == '__main__':
    dataset = MyDataset("train")
    for data in dataset:
      print(data)

输出结果:

text 复制代码
('虽是观景房,不过我住的楼层太低(19楼)看不到江景,但地点很好,离轻轨临江门站和较场口站(起点)很近,解放碑就在 
附近(大约100多公尺吧)!', 1)
('性价比不错,交通方便。行政楼层感觉很好,只是早上8点楼上装修,好吵。 中餐厅档次太低,虽然便宜,但是和酒 
店档次不相配。', 1)
('跟心灵鸡汤没什么本质区别嘛,至少我不喜欢这样读经典,把经典都解读成这样有点去中国化的味道了', 0)   

3.1 数据集字段

在制作 Dataset时,需定义数据集的字段。本案例中,text(文本)和 label(情感标签)。每个字段都需要与模型的输入和输出匹配。

3.2 数据集信息

制作 Dataset 后,通过 dataset.info 等方法查看其大小、字段名称等信息,以确保数据集的正确性和完整性。

4.vocab字典制作

在微调 BERT 模型之前,需要将模型的词汇表(vocab)与数据集中的文本匹配。这一步骤确保输入的文本能够被正确转换为模型的输入格式。

python 复制代码
from transformers import BertTokenizer

#加载字典和分词器
token = BertTokenizer.from_pretrained(r"D:\model\bert-base-chinese\models--bert-base-chinese\snapshots\c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f")
# print(token)
python 复制代码
"""
本节小结核心介绍AI模型是如何处理字符数据的
"""
#准备要编码的文本数据
sents = ["床前明月光",
         "跟心灵鸡汤没什么本质区别嘛,至少我不喜欢这样读经典"]

#批量编码句子(如何将编码成数字)
out = token(
    [sents[0], sents[1]], # 编码的文本段
    add_special_tokens=True, # 是否增加 special tokens ,必须要添加
    #当句子长度大于max_length(上限是model_max_length)时,截断
    truncation=True,
    max_length=15, # 设置最大编码长度
    #一律补0到max_length (凑一个矩阵出来,长了需要载掉,短了要补0 )
    padding="max_length",
    #数据返回样式,可取值为tf,pt,np,默认为list   
    # pt	PyTorch Tensor	使用 PyTorch 框架时
    return_tensors=None,
    return_attention_mask=True,
    return_token_type_ids=True,
    return_special_tokens_mask=True,
    #返回序列长度
    return_length=True
)

for k,v in out.items():
    print(k,":",v)

#解码文本数据
print(token.decode(out["input_ids"][0]),token.decode(out["input_ids"][1]))

上面 token(...) 函数,常见字段含义如下:

  • input_ids:编码后的词 ID 序列,即模型真正读入的数字。
  • token_type_ids :区分两个句子------第一句与特殊符号为 0 ,第二句为 1 ;仅用于句对任务(如问答、下一句预测 NSP)。
  • special_tokens_mask :特殊符号位置为 1 ,普通词为 0
  • length:本条样本编码后的实际长度(可不含右侧 padding)。
  • attention_mask :有效 token 为 1 ,padding 为 0;模型会忽略 mask 为 0 的位置。

本案例是单句情感分析,一般只需 input_idsattention_masktoken_type_ids 在句对任务里才用得上。

4.1词汇表(vocab)

BERT 模型使用词汇表(vocab)将文本转换为模型可以理解的输入格式。 词汇表包含所有模型已知的单词及其对应的索引。 确保数据集中的所有文本都能找到对应的词汇索引是至关重要的。

4.2文本转换

使用 tokenizer 将文本分割成词汇表中的单词,并转换为相应的索引。此步骤需要确保文本长度、特殊字符处理等都与 BERT 模型的预训练设置相一致。

5.下游任务模型设计

在微调 BERT 模型之前,需要设计一个适应情感分析任务的下游模型结构。通常包括一个或多个全连接层,用于将 BERT 输出的特征向量转换为分类结果。

python 复制代码
# 模型
import torch
from transformers import BertModel

#定义设备信息
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(DEVICE)

#加载预训练模型
pretrained = BertModel.from_pretrained(r"D:\model\bert-base-chinese\models--bert-base-chinese\snapshots\c30a6ed22ab4564dc1e3b2ecbf6e766b0611a33f").to(DEVICE)
print(pretrained)
# word_embedding 词向量编码。例:"床"在vocab.txt中的索引为2415,再将2415转化为向量,即为词向量
#定义下游任务(增量模型)
class Model(torch.nn.Module):
    def __init__(self):
        super().__init__()
        #设计全连接网络,实现二分类任务,十分类就设置成10
        self.fc = torch.nn.Linear(768,2)
    #使用模型处理数据(执行前向计算)
    def forward(self,input_ids,attention_mask,token_type_ids):
        #冻结Bert模型的参数,让其不参与训练(Bert模型不参与训练的原因:没有必要,没有数据,没有设备)
        with torch.no_grad():
            out = pretrained(input_ids=input_ids,attention_mask=attention_mask,token_type_ids=token_type_ids)
        #增量模型参与训练
        out = self.fc(out.last_hidden_state[:,0])
        return out

5.1 模型结构

下游任务模型通常包括以下几个部分:

  • BERT模型 用于生成文本的上下文特征向量。
  • Dropout层 用于防止过拟合,通过随机丢弃一部分神经元来提高模型的泛化能力。
  • 全连接层 用于将BERT的输出特征向量映射到具体的分类任务上。

5.2 模型初始化

使用 BertModel.from_pretrained() 方法加载预训练的 BERT 模型,同时也可以初始化自定义的全连接层。初始化时,需要根据下游任务的需求,定义合适的输出维度。

6.自定义模型训练

模型设计完成后,进入训练阶段。通过数据加载器(DataLoader)高效地批处理数据,并使用优化器更新模型参数。

python 复制代码
from torch.utils.data import DataLoader
from torch.optim import AdamW
from net import Model # 自定义类

#定义设备信息
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

#实例化 dataLoader
train_loader = DataLoader(
    dataset=train_dataset,
    #训练批次
    batch_size=50,
    #打乱数据集
    shuffle=True,
    #舍弃最后一个批次的数据,防止形状出错
    drop_last=True,
    #对加载的数据进行编码 (下面是内部方法调用,不要写括号)
    collate_fn=collate_fn
)

model = Model().to(DEVICE)
#定义优化器
optimizer = AdamW(model.parameters())

#定义损失函数
loss_func = torch.nn.CrossEntropyLoss()

for epoch in range(EPOCH): # 整体训练的轮次
	for i,(input_ids,attention_mask,token_type_ids,label) in enumerate(train_loader):
		#将数据放到DEVICE上面
		input_ids, attention_mask, token_type_ids, label = input_ids.to(DEVICE),attention_mask.to(DEVICE),token_type_ids.to(DEVICE),label.to(DEVICE)
		#前向计算(将数据输入模型得到输出)
		out = model(input_ids,attention_mask,token_type_ids)
		#根据输出计算损失
		loss = loss_func(out,label)
		#根据误差优化参数
		optimizer.zero_grad() #优化器的梯度归0
		loss.backward()
		optimizer.step() 

6.1 数据加载

使用 DataLoader 实现批量数据加载。DataLoader 自动处理数据的批处理和随机打乱,确保训练的高效性和数据的多样性。

6.2 优化器

Adamw 是一种适用于 BERT 模型的优化器,结合 Adam 和权重衰减的特点,能够有效地防止过拟合。

6.3 训练循环

训练循环包含前向传播(forward pass)、损失计算(loss calculation)、反向传播(backward pass)、参数更新(parameter update)等步骤。每个epoch 都会对整个数据集进行一次遍历,更新模型参数。通常训练过程中会跟踪损失值的变化,以判断模型的收敛情况。

python 复制代码
#验证模型(判断模型是否过拟合)
#设置为评估模型
model.eval()
#不需要模型参与训练
with torch.no_grad():
    val_acc = 0.0 #定义验证的精度
    val_loss = 0.0 #定义验证的loss
    for i, (input_ids, attention_mask, token_type_ids, label) in enumerate(val_loader):
        # 将数据放到DVEVICE上面
        input_ids, attention_mask, token_type_ids, label = input_ids.to(DEVICE), attention_mask.to(
            DEVICE), token_type_ids.to(DEVICE), label.to(DEVICE)
        # 前向计算(将数据输入模型得到输出)
        out = model(input_ids, attention_mask, token_type_ids)
        # 根据输出计算损失
        val_loss += loss_func(out, label)
        #根据数据,计算验证精度
        out = out.argmax(dim=1)
        val_acc+=(out==label).sum().item()
    val_loss/=len(val_loader)
    val_acc/=len(val_loader)
    print(f"验证集:loss:{val_loss},acc:{val_acc}")


    #根据验证准确率保存最优参数
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        torch.save(model.state_dict(),"params/best_bert.pth")  # 目录必须手动创建 或 python 代码创建
        print(f"EPOCH:{epoch}:保存最优参数:acc{best_val_acc}")
#保存最后一轮参数
torch.save(model.state_dict(), "params/last_bert.pth")
print(f"EPOCH:{epoch}:最后一轮参数保存成功!")

7.下期预告:最终效果评估与测试

在模型训练完成后,需要评估其在测试集上的性能。通常使用准确率、精确率、召回率和F1分数等指标来衡量模型的效果。

8.小结

介绍了如何使用Hugging Face的 BERT模型进行中文评价情感分析的微调训练。

我们从加载数据集、制作Datasets、词汇表操作、模型设计、自定义训练,下期介绍效果评估与测试,逐步讲解了整个微调过程。

相关推荐
song50111 小时前
昇腾 910 的硬件架构:为什么它适合跑大模型
图像处理·人工智能·分布式·flutter·硬件架构·交互
ACP广源盛1392462567311 小时前
OpenAI 推出的 GPT-5.5 大模型,倒逼接口芯片升级迭代@ACP#IX8024应用迭代
网络·人工智能·嵌入式硬件·电脑·音视频
南屹川11 小时前
【服务网格】Istio入门:从部署到流量管理实战
人工智能
掘根11 小时前
【openCV】cv::Mat的创建和赋值,图像像素的读写,算术操作
人工智能·opencv·计算机视觉
救救孩子把11 小时前
65-机器学习与大模型开发数学教程-6-1 浮点数精度与数值稳定性
人工智能·机器学习
南屹川11 小时前
【云计算】Kubernetes入门与实践:从部署到运维
人工智能
MediaTea11 小时前
AI 术语通俗词典:GRU
人工智能·rnn·深度学习·gru
IT_陈寒11 小时前
Vite踩坑实录:静态资源加载把我搞懵了
前端·人工智能·后端
RSTJ_162511 小时前
PYTHON+AI LLM DAY FIFITY-FIVE
人工智能