微博作为国内主流的社交媒体平台,其评论数据蕴含着丰富的用户情感倾向。本文将详细介绍如何基于 BERT 预训练模型构建微博评论情感分析模型,实现 8 类情感(喜欢、厌恶、开心、悲伤、愤怒、惊讶、恐惧、无情感)的分类任务,并完整拆解从数据加载、模型构建到训练推理的全流程。
一、项目背景与数据集说明
1.1 任务目标
本次任务聚焦微博评论的多分类情感分析,需将评论文本映射到 8 个情感标签,分别为:like(喜欢)、disgust(厌恶)、happiness(开心)、sadness(悲伤)、anger(愤怒)、surprise(惊讶)、fear(恐惧)、none(无情感)。
1.2 数据集结构
数据集分为训练集、验证集和测试集三个子集,具体规模如下:
- 训练集:50000 条样本,数据量约 4.57MB;
- 验证集:5000 条样本,数据量约 455KB;
- 测试集:4571 条样本,数据量约 415KB;
数据集以 CSV 格式存储,每条样本包含text(评论文本)和label(情感标签)两个核心字段,数据集元信息可参考dataset_info.json配置文件。
二、技术选型
本次模型训练基于以下核心技术栈,兼顾效果与工程落地性:
- 预训练模型:BERT-base-chinese(中文场景适配的基础版 BERT,输出维度 768);
- 深度学习框架:PyTorch(灵活的模型构建与训练流程);
- 数据处理:Hugging Face Datasets(高效加载数据集)、BertTokenizer(BERT 专用分词器);
- 优化器:AdamW(针对 Transformer 模型优化的自适应学习率优化器);
- 损失函数:CrossEntropyLoss(适配多分类任务,后续可优化为加权损失处理样本不均衡)。
三、核心代码实现
3.1 数据集加载(MyData.py)
自定义Dataset类加载 CSV 格式的微博数据集,兼容训练 / 验证 / 测试集的灵活切换:
# 导入PyTorch的Dataset基类,用于自定义数据集
from torch.utils.data import Dataset
# 导入Hugging Face的datasets库中的load_dataset函数,用于便捷加载各类数据集
from datasets import load_dataset
# 自定义数据集类,继承自PyTorch的Dataset基类
# 该类用于加载微博(Weibo)的CSV格式数据集,适配PyTorch的数据加载流程
class MyDataset(Dataset):
def __init__(self, split):
"""
初始化函数:加载指定划分的CSV数据集
Args:
split (str): 数据集划分标识,如"train"(训练集)、"test"(测试集)、"val"(验证集)
对应磁盘上的文件路径为 data/Weibo/{split}.csv
"""
# 使用load_dataset加载CSV格式数据
# path="csv" 指定加载CSV类型数据
# data_files 指定数据文件路径,根据传入的split参数拼接出对应文件(如test.csv)
# split="train" 这里是datasets库的参数,由于是单文件,统一标记为train分区
self.dataset = load_dataset(
path="csv",
data_files=f"data/Weibo/{split}.csv",
split="train"
)
def __len__(self):
"""
重写Dataset基类的__len__方法,返回数据集的总样本数
Returns:
int: 数据集样本总数
"""
return len(self.dataset)
def __getitem__(self, item):
"""
重写Dataset基类的__getitem__方法,根据索引获取单个样本
Args:
item (int): 样本索引,从0开始到__len__()-1
Returns:
tuple: (text, label) 元组,包含样本的文本内容和对应的标签
"""
# 根据索引获取样本的text字段(文本内容)
text = self.dataset[item]["text"]
# 根据索引获取样本的label字段(标签)
label = self.dataset[item]["label"]
# 返回文本和标签组成的元组
return text, label
# 主函数:测试自定义数据集类是否能正常工作
if __name__ == '__main__':
# 实例化测试集(split="test" 对应加载 data/Weibo/test.csv)
dataset = MyDataset("test")
# 遍历数据集,打印每个样本(文本+标签),验证数据加载是否正常
for data in dataset:
print(data)
3.2 模型构建(net.py)
基于 BERT-base-chinese 构建下游分类模型,提取 CLS token 的特征并映射到 8 类情感:
# 导入Hugging Face transformers库中的BertModel,用于加载预训练BERT模型
from transformers import BertModel
# 注:如需安装指定版本的PyTorch,可执行该行pip命令(CPU版本)
# pip install torch==2.4.1 torchvision==0.19.1 torchaudio==2.4.1 --index-url https://download.pytorch.org/whl/cpu
import torch
# 以下为备选的DirectML设备配置(适配AMD显卡),当前已注释
# import torch_directml as dml
# DEVICE = dml.device() if dml.is_available() else torch.device("cpu")
# 配置计算设备:优先使用CUDA(NVIDIA显卡),无则使用CPU
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# ===================== 1. 加载本地预训练BERT模型 =====================
# 本地中文BERT模型的存放路径(bert-base-chinese适配中文文本处理)
model_dir = "D:\\本地模型\\google-bert\\bert-base-chinese"
# 从本地路径加载预训练BERT模型,并将模型移至指定计算设备(CUDA/CPU)
# BertModel是BERT的主干网络,仅负责提取文本特征,不包含分类层
pretrained = BertModel.from_pretrained(model_dir).to(DEVICE)
# 打印模型结构,可查看BERT的网络层组成(如嵌入层、Transformer层等)
print(pretrained)
# ===================== 2. 定义下游分类任务模型 =====================
# 自定义模型类,继承自PyTorch的nn.Module,用于BERT特征的分类任务
# 核心逻辑:利用BERT提取的768维特征,通过全连接层映射到8分类任务
class Model(torch.nn.Module):
def __init__(self):
"""初始化函数:定义分类头(全连接层)"""
super().__init__() # 继承父类的初始化方法
# 定义全连接层(fc: fully connected):
# 输入维度768(BERT-base-chinese的输出特征维度),输出维度8(8分类任务)
self.fc = torch.nn.Linear(768, 8)
def forward(self, input_ids, attention_mask, token_type_ids):
"""
前向传播函数:完成文本特征提取 + 分类预测
Args:
input_ids: 文本token化后的索引序列(BERT输入)
attention_mask: 注意力掩码,标记有效token(1)和填充token(0)
token_type_ids: 句子类型标识,区分单/双句中的不同句子(此处单句均为0)
Returns:
out: 8类别的概率分布(经softmax归一化)
"""
# 禁用梯度计算:BERT预训练模型作为特征提取器,不参与训练(冻结主干)
with torch.no_grad():
# 将输入传入BERT模型,获取特征输出
out = pretrained(
input_ids=input_ids,
attention_mask=attention_mask,
token_type_ids=token_type_ids
)
# 提取CLS token的特征:BERT输出的last_hidden_state是[batch_size, seq_len, 768]
# 其中[:,0]取每个样本序列的第一个token(CLS),该token通常作为整句的聚合特征
cls_feature = out.last_hidden_state[:, 0]
# 将CLS特征传入全连接层,映射到8维的分类得分
out = self.fc(cls_feature)
# 对分类得分做softmax归一化(dim=1表示按样本维度归一化),得到各类别的概率
out = out.softmax(dim=1)
return out
3.3 数据预处理(collate_fn)
批量处理文本数据,完成分词、截断、padding 等操作,适配 BERT 输入格式:
from transformers import BertTokenizer
model_dir = "D:\\本地模型\\google-bert\\bert-base-chinese"
token = BertTokenizer.from_pretrained(model_dir)
def collate_fn(data):
# 分离文本和标签
sentes = [i[0] for i in data]
label = [i[1] for i in data]
# 批量编码文本
data = token.batch_encode_plus(
batch_text_or_text_pairs=sentes,
truncation=True, # 截断过长文本
padding="max_length", # 补齐到固定长度
max_length=500, # 文本最大长度(可根据数据分布调整)
return_tensors="pt", # 返回PyTorch张量
return_length=True
)
# 提取BERT输入特征
input_ids = data["input_ids"]
attention_mask = data["attention_mask"]
token_type_ids = data["token_type_ids"]
labels = torch.LongTensor(label)
return input_ids,attention_mask,token_type_ids,labels
3.4 训练流程(trainer.py)
实现模型训练与验证的全流程,包含设备适配、数据加载、训练循环、模型保存:
# 导入PyTorch核心库
import torch
# 导入自定义的数据集类(对应之前编写的MyDataset,加载CSV格式的微博数据)
from MyData import MyDataset
# 导入PyTorch的DataLoader,用于批量加载数据
from torch.utils.data import DataLoader
# 导入自定义的下游任务模型(BERT+全连接层的8分类模型)
from net import Model
# 导入Hugging Face的BertTokenizer,用于BERT的文本编码
from transformers import BertTokenizer
# 导入AdamW优化器(BERT训练常用的优化器,带权重衰减)
from torch.optim import AdamW
# 备选的DirectML设备配置(适配AMD显卡),当前注释未启用
# import torch_directml as dml
# DEVICE = dml.device() if dml.is_available() else torch.device("cpu")
# 配置计算设备:优先使用CUDA(NVIDIA显卡),无则降级为CPU
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# ===================== 超参数与路径配置 =====================
# 训练轮数(注意:30000轮对于文本分类来说过大,实际建议根据验证集效果早停)
EPOCH = 30000
# 本地中文BERT模型/分词器的存放路径
model_dir = "D:\\本地模型\\google-bert\\bert-base-chinese"
# 加载BERT分词器(与预训练模型配套,用于文本转token索引)
token = BertTokenizer.from_pretrained(model_dir)
# ===================== 数据预处理函数(collate_fn) =====================
# 自定义DataLoader的批处理函数,用于将原始样本转换为BERT可接收的输入格式
# 注:代码注释提到"样本不均衡的分类损失",但此处仅为数据预处理,未实际处理样本不均衡问题
def collate_fn(data):
"""
批处理数据转换函数:将原始(文本, 标签)样本转换为BERT的标准输入格式
Args:
data (list): 一个batch的原始样本,每个元素是(text, label)元组
Returns:
tuple: (input_ids, attention_mask, token_type_ids, labels)
均为torch张量,适配BERT模型输入
"""
# 提取一个batch中的所有文本和标签
sentes = [i[0] for i in data] # 所有样本的文本列表
label = [i[1] for i in data] # 所有样本的标签列表
# 使用BERT分词器对批量文本进行编码
data = token.batch_encode_plus(
batch_text_or_text_pairs=sentes, # 待编码的批量文本
truncation=True, # 超过max_length时截断
padding="max_length", # 不足max_length时填充到指定长度
max_length=500, # 文本最大长度(BERT-base建议不超过512)
return_tensors="pt", # 返回PyTorch张量
return_length=True # 返回每个文本的原始长度(此处未使用)
)
# 提取BERT的核心输入张量
input_ids = data["input_ids"] # token的索引序列(核心输入)
attention_mask = data["attention_mask"]# 注意力掩码:1表示有效token,0表示填充token
token_type_ids = data["token_type_ids"]# 句子类型标识:单句任务下全为0
labels = torch.LongTensor(label) # 标签转换为长整型张量(适配CrossEntropyLoss)
return input_ids, attention_mask, token_type_ids, labels
# ===================== 数据集与数据加载器初始化 =====================
# 创建训练集和验证集实例(加载对应CSV文件)
train_dataset = MyDataset("train") # 加载训练集:data/Weibo/train.csv
val_dataset = MyDataset("validation") # 加载验证集:data/Weibo/validation.csv
# 创建训练集DataLoader(批量加载数据)
# 注:若显存不足,可减小batch_size;shuffle=True打乱数据提升泛化性
train_loader = DataLoader(
dataset=train_dataset,
batch_size=10, # 每个批次的样本数
shuffle=True, # 每个epoch打乱训练数据
drop_last=True, # 丢弃最后一个不足batch_size的批次
collate_fn=collate_fn # 自定义批处理函数
)
# 创建验证集DataLoader
val_loader = DataLoader(
dataset=val_dataset,
batch_size=5, # 验证集批次可更小,减少显存占用
shuffle=True, # 验证集打乱不影响结果,仅为随机查看
drop_last=True,
collate_fn=collate_fn
)
# ===================== 主训练流程 =====================
if __name__ == '__main__':
# 打印当前使用的计算设备(确认是否使用GPU)
print(f"当前使用计算设备:{DEVICE}")
# 初始化下游任务模型,并移至指定设备
model = Model().to(DEVICE)
# 初始化AdamW优化器(BERT训练专用,带权重衰减防止过拟合)
# lr=5e-4:学习率(BERT微调通常建议5e-5~2e-5,此处5e-4偏大,需注意)
optimizer = AdamW(model.parameters(), lr=5e-4)
# 定义损失函数:交叉熵损失(适配多分类任务)
# 注:若样本不均衡,可添加weight参数设置类别权重
loss_func = torch.nn.CrossEntropyLoss()
# 将模型设为训练模式(启用Dropout、BatchNorm等训练层)
model.train()
# 遍历训练轮数
for epoch in range(EPOCH):
# 初始化验证集累计损失和准确率
sum_val_acc = 0 # 验证集总准确率
sum_val_loss = 0 # 验证集总损失
# ---------------------- 训练阶段 ----------------------
for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(train_loader):
# 将所有输入张量移至指定计算设备(GPU/CPU)
input_ids = input_ids.to(DEVICE)
attention_mask = attention_mask.to(DEVICE)
token_type_ids = token_type_ids.to(DEVICE)
labels = labels.to(DEVICE)
# 前向传播:模型输出8类别的概率分布
out = model(input_ids, attention_mask, token_type_ids)
# 计算损失:交叉熵损失(out为概率分布,labels为真实标签)
loss = loss_func(out, labels)
# 反向传播与优化
optimizer.zero_grad() # 清空梯度(避免梯度累积)
loss.backward() # 反向传播计算梯度
optimizer.step() # 优化器更新模型参数
# 每5个批次打印一次训练状态
if i % 5 == 0:
out = out.argmax(dim=1) # 取概率最大的类别作为预测结果
acc = (out == labels).sum().item() / len(labels) # 计算批次准确率
print(f"训练轮数:{epoch},批次:{i},损失:{loss.item():.4f},准确率:{acc:.4f}")
# ---------------------- 验证阶段 ----------------------
# 将模型设为评估模式(禁用Dropout、BatchNorm等训练层)
model.eval()
# 验证阶段禁用梯度计算,节省显存并加速计算
with torch.no_grad():
for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(val_loader):
# 数据移至计算设备
input_ids = input_ids.to(DEVICE)
attention_mask = attention_mask.to(DEVICE)
token_type_ids = token_type_ids.to(DEVICE)
labels = labels.to(DEVICE)
# 前向传播
out = model(input_ids, attention_mask, token_type_ids)
# 计算验证损失
loss = loss_func(out, labels)
# 计算验证准确率
out = out.argmax(dim=1)
accuracy = (out == labels).sum().item() / len(labels)
# 累计验证损失和准确率
sum_val_loss += loss.item()
sum_val_acc += accuracy
# 计算验证集平均损失和平均准确率
avg_val_loss = sum_val_loss / len(val_loader)
avg_val_acc = sum_val_acc / len(val_loader)
print(f"验证==>轮数:{epoch},平均损失:{avg_val_loss:.4f},平均准确率:{avg_val_acc:.4f}")
# 注:代码注释提到"验证集精度升高、损失降低说明模型稳定,可保存参数"
# 保存当前轮数的模型参数(仅保存权重,不保存模型结构)
torch.save(model.state_dict(), f"params/{epoch}bert-weibo.pth")
print(f"第{epoch}轮训练完成,参数已保存至 params/{epoch}bert-weibo.pth!")
# 验证完成后切回训练模式,准备下一轮训练
model.train()
3.5 推理与测试
3.5.1 交互式推理(run.py)
加载训练好的模型,支持实时输入文本并输出情感类别:
# 导入PyTorch核心库,用于张量操作和模型推理
import torch
# 导入自定义的下游任务模型(BERT+全连接层的8分类情感模型)
from net import Model
# 导入Hugging Face的BertTokenizer,用于BERT的文本编码
from transformers import BertTokenizer
# ===================== 计算设备配置 =====================
# 备选配置1:优先使用CUDA(NVIDIA显卡),无则使用CPU
# DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 备选配置2:优先使用DirectML(适配AMD显卡),无则使用CPU
# 注:代码中此处重复定义了DEVICE,最终生效的是这一行
import torch_directml as dml
DEVICE = dml.device() if dml.is_available() else torch.device("cpu")
# ===================== 情感类别配置 =====================
# 定义8类情感标签的名称,与模型输出维度(8维)一一对应
# 顺序需与训练时的标签编码完全一致,否则预测结果会错位
names = [
"like", # 喜欢
"disgust", # 厌恶
"happiness", # 开心
"sadness", # 悲伤
"anger", # 愤怒
"surprise", # 惊讶
"fear", # 恐惧
"none" # 无情绪/中性
]
# 打印当前使用的计算设备,便于确认硬件是否生效
print(f"当前使用计算设备:{DEVICE}")
# ===================== 模型与分词器初始化 =====================
# 初始化自定义情感分类模型,并移至指定计算设备
model = Model().to(DEVICE)
# 本地中文BERT模型/分词器的存放路径(需与训练时一致)
model_dir = "D:\\本地模型\\google-bert\\bert-base-chinese"
# 加载与预训练BERT配套的分词器,用于将输入文本转为模型可识别的token索引
token = BertTokenizer.from_pretrained(model_dir)
# ===================== 文本编码函数 =====================
def collate_fn(data):
"""
单条文本编码函数:将输入的原始文本转换为BERT模型可接收的标准输入格式
Args:
data (str): 待预测的原始中文文本
Returns:
tuple: (input_ids, attention_mask, token_type_ids)
均为PyTorch张量,适配BERT模型输入
"""
# 将单条文本封装为列表(适配token.batch_encode_plus的批量输入格式)
sentes = []
sentes.append(data)
# 使用BERT分词器对文本进行编码
data = token.batch_encode_plus(
batch_text_or_text_pairs=sentes, # 待编码的文本列表(单条)
truncation=True, # 文本长度超过max_length时截断
padding="max_length", # 文本长度不足时填充到max_length
max_length=500, # 文本最大长度(需与训练时一致)
return_tensors="pt", # 返回PyTorch张量格式
return_length=True # 返回文本原始长度(此处未使用)
)
# 提取BERT模型的核心输入张量
input_ids = data["input_ids"] # token的索引序列(核心输入)
attention_mask = data["attention_mask"]# 注意力掩码:1=有效token,0=填充token
token_type_ids = data["token_type_ids"]# 句子类型标识:单句任务下全为0
return input_ids, attention_mask, token_type_ids
# ===================== 交互式测试函数 =====================
def test():
"""
交互式预测函数:加载训练好的模型权重,循环接收用户输入文本并预测情感类别
"""
# 加载训练好的模型权重文件(此处加载第2轮训练的参数)
# 注:需确保权重文件路径正确,且与模型结构匹配
model.load_state_dict(torch.load("params/2bert-weibo.pth"))
# 将模型设为评估模式:禁用Dropout、BatchNorm等训练层,保证推理稳定
model.eval()
# 循环接收用户输入,实现交互式测试
while True:
# 提示用户输入测试文本,或输入'q'退出
data = input("请输入测试数据(输入'q'退出):")
# 退出条件:用户输入'q'时终止循环
if data == "q":
print("测试结束")
break
# 步骤1:将输入文本编码为BERT的标准输入格式
input_ids, attention_mask, token_type_ids = collate_fn(data)
# 步骤2:将输入张量移至指定计算设备(GPU/CPU/DirectML)
input_ids = input_ids.to(DEVICE)
attention_mask = attention_mask.to(DEVICE)
token_type_ids = token_type_ids.to(DEVICE)
# 步骤3:模型推理(禁用梯度计算,节省显存并加速)
with torch.no_grad():
# 前向传播:模型输出8类别的概率分布
out = model(input_ids, attention_mask, token_type_ids)
# 取概率最大的类别索引作为预测结果(dim=1表示按样本维度取最大值)
out = out.argmax(dim=1)
# 根据索引映射到对应的情感类别名称并输出
print("模型判定:", names[out.item()], "\n")
# ===================== 主函数入口 =====================
if __name__ == '__main__':
# 执行交互式测试函数
test()
3.5.2 测试集批量评估(test.py)
基于测试集评估模型整体准确率:
# 导入PyTorch核心库,用于张量操作和模型推理
import torch
# 导入自定义的数据集类(加载CSV格式的微博测试数据)
from MyData import MyDataset
# 导入PyTorch的DataLoader,用于批量加载测试集数据
from torch.utils.data import DataLoader
# 导入自定义的下游任务模型(BERT+全连接层的8分类模型)
from net import Model
# 导入Hugging Face的BertTokenizer,用于BERT的文本编码
from transformers import BertTokenizer
# 备选的DirectML设备配置(适配AMD显卡),当前注释未启用
# import torch_directml as dml
# DEVICE = dml.device() if dml.is_available() else torch.device("cpu")
# 配置计算设备:优先使用CUDA(NVIDIA显卡)加速推理,无则降级为CPU
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# ===================== 模型与分词器配置 =====================
# 本地中文BERT模型/分词器的存放路径(需与训练时一致)
model_dir = "D:\\本地模型\\google-bert\\bert-base-chinese"
# 加载与预训练BERT配套的分词器,用于将测试文本转为模型可识别的token索引
token = BertTokenizer.from_pretrained(model_dir)
# ===================== 数据预处理函数(collate_fn) =====================
def collate_fn(data):
"""
批处理数据转换函数:将测试集的原始(文本, 标签)样本转换为BERT的标准输入格式
Args:
data (list): 一个batch的原始测试样本,每个元素是(text, label)元组
Returns:
tuple: (input_ids, attention_mask, token_type_ids, labels)
均为torch张量,适配BERT模型输入和标签计算
"""
# 提取一个batch中的所有文本和标签
sentes = [i[0] for i in data] # 所有测试样本的文本列表
label = [i[1] for i in data] # 所有测试样本的真实标签列表
# 使用BERT分词器对批量文本进行编码(参数需与训练时完全一致)
data = token.batch_encode_plus(
batch_text_or_text_pairs=sentes, # 待编码的批量文本
truncation=True, # 超过max_length时截断
padding="max_length", # 不足max_length时填充到指定长度
max_length=500, # 文本最大长度(需与训练时一致)
return_tensors="pt", # 返回PyTorch张量
return_length=True # 返回每个文本的原始长度(此处未使用)
)
# 提取BERT的核心输入张量
input_ids = data["input_ids"] # token的索引序列(核心输入)
attention_mask = data["attention_mask"]# 注意力掩码:1表示有效token,0表示填充token
token_type_ids = data["token_type_ids"]# 句子类型标识:单句任务下全为0
labels = torch.LongTensor(label) # 真实标签转换为长整型张量
return input_ids, attention_mask, token_type_ids, labels
# ===================== 测试集与数据加载器初始化 =====================
# 创建测试集实例(加载 data/Weibo/test.csv 文件)
test_dataset = MyDataset("test")
# 创建测试集DataLoader(批量加载测试数据)
# 注:测试阶段batch_size可设置更大(如100),提升推理效率;shuffle=True不影响准确率计算
test_loader = DataLoader(
dataset=test_dataset,
batch_size=100, # 测试批次大小(显存充足时可增大)
shuffle=True, # 打乱测试数据(仅为随机遍历,不影响最终准确率)
drop_last=True, # 丢弃最后一个不足batch_size的批次
collate_fn=collate_fn # 自定义批处理函数,转换为BERT输入格式
)
# ===================== 主测试流程 =====================
if __name__ == '__main__':
# 初始化准确率统计变量
acc = 0 # 累计预测正确的样本数
total = 0 # 累计参与测试的总样本数
# 打印当前使用的计算设备,便于确认硬件是否生效
print(f"当前使用计算设备:{DEVICE}")
# 初始化自定义模型,并移至指定计算设备
model = Model().to(DEVICE)
# 加载训练好的模型权重文件(此处加载第0轮训练的bert-large权重)
# 注:需确保权重文件路径正确,且模型结构与训练时一致
model.load_state_dict(torch.load("params/0bert-large-weibo.pth"))
# 将模型设为评估模式:禁用Dropout、BatchNorm等训练层,保证推理结果稳定
model.eval()
# 禁用梯度计算:测试阶段无需反向传播,节省显存并加速推理
with torch.no_grad():
# 遍历测试集的所有批次
for i, (input_ids, attention_mask, token_type_ids, labels) in enumerate(test_loader):
# 将所有输入张量和标签移至指定计算设备(GPU/CPU)
input_ids = input_ids.to(DEVICE)
attention_mask = attention_mask.to(DEVICE)
token_type_ids = token_type_ids.to(DEVICE)
labels = labels.to(DEVICE)
# 前向传播:模型输出8类别的概率分布
out = model(input_ids, attention_mask, token_type_ids)
# 取概率最大的类别索引作为预测结果(dim=1表示按样本维度取最大值)
out = out.argmax(dim=1)
# 统计当前批次预测正确的样本数,并累加到总正确数
batch_correct = (out == labels).sum().item()
acc += batch_correct
# 打印当前批次索引和正确样本数,便于实时查看测试进度
print(f"测试批次:{i},当前批次正确数:{batch_correct}")
# 累计总测试样本数
total += len(labels)
# 计算并打印测试集的整体准确率
overall_accuracy = acc / total
print(f"\n测试集整体准确率:{overall_accuracy:.4f}")
四、训练调优与优化方向
4.1 现有问题与调优点
- 样本不均衡:代码注释中提到样本不均衡问题,可通过加权 CrossEntropyLoss(根据类别占比设置权重)、过采样 / 欠采样、SMOTE 等方法优化;
- 超参数调整:当前 EPOCH 设置为 30000(偏多),可增加早停策略(验证集损失连续多轮不下降则停止训练);学习率 5e-4 可下调(如 2e-5),适配 BERT 微调的最佳实践;
- 文本长度:max_length=500 可能过长(微博评论通常较短),可统计数据分布后调整为 128/256,减少计算量;
- 模型冻结策略:当前完全冻结 BERT 层,可尝试解冻最后 2-4 层联合训练,提升特征适配性。
4.2 进阶优化方向
- 模型轻量化:使用 DistilBERT、ALBERT 等轻量化模型替代 BERT-base,降低部署成本;
- 数据增强:通过同义词替换、回译等方式扩充训练数据,提升模型泛化能力;
- 多尺度特征融合:除 CLS token 外,融合 BERT 最后几层的平均特征,提升分类效果;
- 部署优化:将模型导出为 ONNX 格式,结合 TensorRT 加速推理,适配线上服务。
五、总结
本文基于 BERT 预训练模型实现了微博评论 8 类情感的分类任务,完整覆盖了数据加载、模型构建、训练验证、推理测试的全流程。现有代码具备清晰的模块化结构,可快速复现并适配不同的情感分析场景。后续可针对样本不均衡、超参数、模型结构等方向进一步优化,提升模型的准确率和工程落地性。# 基于 BERT 的微博评论情感分析模型训练实践微博作为国内主流的社交媒体平台,其评论数据蕴含着丰富的用户情感倾向。本文将详细介绍如何基于 BERT 预训练模型构建微博评论情感分析模型,实现 8 类情感(喜欢、厌恶、开心、悲伤、愤怒、惊讶、恐惧、无情感)的分类任务,并完整拆解从数据加载、模型构建到训练推理的全流程。