本篇博文继续参考:愤怒的可乐:【实战篇】是时候彻底弄懂BERT模型了(收藏,已修复)https://helloai.blog.csdn.net/article/details/120232707

目录

准备数据集

导入依赖

首先用pandas读取csv,'D:\\nlp_prj\\IMDB.csv'是我csv数据集的存储路径。

将文本标签转换为数字

把 Pandas DataFrame 转换成 HuggingFace Dataset

打印第一条数据,验证是否成功

将数据集拆分为训练和测试集

输出数据集

创建训练集和测试集

下载并加载预训练的BERT模型

下载并加载用于预训练bert-base-uncased模型的分词器

定义process函数预处理数据集

使用preprocess函数预处理训练和测试数据集

从 Hugging Face 的 Dataset 对象中 删除不需要的列(字段)

 训练模型

开始训练

在验证集上评估性能

查看评估结果

1. 使用 Trainer.evaluate()

2. 查看日志文件或 TensorBoard

3. 保存和加载评估结果

总结


本篇博文主要用来学习如何为一个文本分类任务微调BERT模型,本次具体要做的任务是情绪识别。在情绪识别任务领域,核心目标是精准地判断一个句子究竟蕴含正向情感还是负向情感。假设有一个句子:

I like reading.

重复上一节所讲过的步骤,大家可以和我一起过一遍:

首先对句子进行分词处理,并在分词结果的前后分别添加用于标识开始的 [CLS] 标记和用于分隔句子或表示结束的 [SEP] 标记

接着,将这些标记输入到 BERT 模型中,经过模型内部的多层结构计算,得到每个标记对应的嵌入表示向量。

由于 [CLS] 标记的独特作用,它所对应的嵌入表示向量 R [CLS] ​ 被认为是融合了整个句子语义信息的聚合表示。因此,我们仅选取这个 R [CLS] 嵌入向量,将其作为后续分类任务的输入特征喂给一个带有 softmax 激活函数的前馈神经网络分类器,通过训练该分类器,使其能够基于这个句子级的嵌入表示来精准地完成情绪识别任务。

在从预训练的BERT中抽取嵌入表示的文章中,我们知道在提取句子的嵌入后,我们将R [CLS] 
 提供给分类器并训练分类器执行分类。类似地,在微调期间,我们也将R [CLS] 喂给一个分类并训练分类器执行分类。那么这个过程【微调预训练的BERT模型】与使用预训练的BERT模型作为特征提取器有何不同?

不同点在于:

  • 微调预训练的 BERT 模型时,我们不仅会更新分类器的参数,同时也会对 BERT 模型本身的参数进行更新,使其能够更好地适应特定的任务。
  • 然而,当我们把预训练的 BERT 模型作为特征提取器时,我们仅更新分类器的参数,而保持预训练 BERT 模型的参数不变。在这种情况下,BERT 模型只是作为一个固定特征提取器,为分类器提供输入特征。

在对预训练的 BERT 模型进行微调时,我们有两种不同的策略来调整模型的参数:

第一种方式是将预训练的 BERT 模型与分类器视为一个整体,让它们的参数同时得到更新。这意味着在反向传播过程中,不仅分类器的参数会根据损失函数进行调整,BERT 模型内部的参数也会相应地进行更新,以适应当前的具体任务,这种方式可以使整个模型更好地协同工作,提升在特定任务上的表现。

第二种方式则只更新分类器的参数,而保持预训练 BERT 模型的参数不变。这种情况下,预训练的 BERT 模型就相当于一个固定不变的特征提取器,它提取出的特征向量被输入到分类器中,只有分类器的参数会根据任务进行优化调整。


了解完原理后,让我们正式开始探索如何使用 IMDB 数据集为情感分析任务微调预训练的 BERT 模型吧!!!

准备数据集

IMBD数据集下载链接在此:IMDB Dataset of 50K Movie Reviews,进入kaggle页面后下载数据集(需要先登录)。下载完成后是一个zip文件,需要先解压得到一个csv文件。该数据集包含 50,000 条电影评论,这些评论来自 IMDB 网站,每条评论都有一个表示情感倾向的标签,即正面或负面。数据内容如图所示,review是电影评论,sentiment是标签。

导入依赖

from transformers import BertForSequenceClassification, BertTokenizerFast, Trainer, TrainingArguments
from nlp import load_dataset
import torch
import numpy as np
import pandas as pd
from datasets import Dataset
from sklearn.preprocessing import LabelEncoder

首先用pandas读取csv,'D:\\nlp_prj\\IMDB.csv'是我csv数据集的存储路径。

df = pd.read_csv('D:\\nlp_prj\\IMDB.csv')

将文本标签转换为数字

le = LabelEncoder() #创建了一个 LabelEncoder 对象
df['labels'] = le.fit_transform(df['sentiment'])#fit_transform将值替换成对应的数字,存入新列labels

原始数据中的情感标签是字符串,例如:"positive", "negative", "positive", ...模型无法直接理解这些字符串,所以需要把它们转换成数字。

把 Pandas DataFrame 转换成 HuggingFace Dataset

dataset = Dataset.from_pandas(df)

把 Pandas DataFrame 转换成 HuggingFace Dataset,是为了让数据更高效、更规范地参与模型训练,特别是配合 Trainer 使用时必不可少的一步。Hugging Face 的 Trainer 类要求输入的数据是一个 Dataset 对象,而不是普通的 Pandas DataFrame 或 NumPy 数组。

打印第一条数据,验证是否成功

print(dataset[0])

将数据集拆分为训练和测试集

dataset = dataset.train_test_split(test_size=0.3)

输出数据集

print(dataset)
#输出为
DatasetDict({
    train: Dataset({
        features: ['review', 'sentiment', 'labels'],
        num_rows: 35000
    })
    test: Dataset({
        features: ['review', 'sentiment', 'labels'],
        num_rows: 15000
    })
})

创建训练集和测试集

train_set = dataset['train']
test_set = dataset['test']

下载并加载预训练的BERT模型

使用预训练的bert-base-uncased模型进行微调,因为我们做的是序列分类任务,所以可以使用BertForSequenceClassification类:

model = BertForSequenceClassification.from_pretrained('./uncased_L-12_H-768_A-12',num_labels=2)

google/bert_uncased_L-12_H-128_A-2 at main,进入该网址下载所有文件,下载后全部放入文件夹,取名为uncased_L-12_H-768_A-12,将该文件夹放入同py文件的同一个文件夹下。

 

下载并加载用于预训练bert-base-uncased模型的分词器

使用BertTokenizerFast类创建分词器而不是BertTokenizer。因为BertTokenizerFast相比BertTokenizer具有很多优势。

tokenizer = BertTokenizerFast.from_pretrained('./uncased_L-12_H-768_A-12')

定义process函数预处理数据集

将输入数据中的 'review' 文本字段用 BERT 分词器进行编码,并统一长度为 128,以便输入模型训练。这里,padding='max_length':把所有句子填充到指定的最大长度(这里是 128)。
truncation=True:如果句子超过最大长度,就截断。

def preprocess(data):
  return tokenizer(data['review'], padding='max_length', truncation=True, max_length=128)

那么这个函数的返回值是什么呢?

假设有如下评论数据:

data = {
    'review': [
        "I love this movie!",
        "This is the worst film ever.",
        "It was an amazing experience."
    ]
}

经过函数的返回值是:

{
    'input_ids': tensor([
        [  101,   1045,   2345,   2019,   2828,    102,     0,     0,     0,     0],
        [  101,   2019,   2073,   2046,   2213,   2026,   1012,    102,     0,     0],
        [  101,   2070,   3043,   2014,  16132,    102,     0,     0,     0,     0]
    ]),
    
    'attention_mask': tensor([
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 0, 0],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
    ])
}

input_ids是每个词被分词后对应的 ID。比如 "I" → 1045,"love" → 2345 等;attention_mask    表示哪些位置是真实内容(1),哪些是填充的(0)。

使用preprocess函数预处理训练和测试数据集

使用 Hugging Face 的 datasets.Dataset.map() 方法对训练集和测试集进行预处理,对每一条评论文本调用 preprocess 函数(也就是 tokenizer),把原始文本转换为模型能接受的输入格式(如 input_ids, attention_mask 等)。

train_set = train_set.map(preprocess, batched=True, batch_size=16)
test_set = test_set.map(preprocess, batched=True, batch_size=16)

batched,类型是布尔值,默认值为False,作用是是否按批次处理(推荐设为 True)
batch_size,类型是整数,默认值是1000,作用是每个批次包含多少条数据,这里设置为16。

从 Hugging Face 的 Dataset 对象中 删除不需要的列(字段)

在 NLP 模型训练过程中,原始文本数据(如 'review')和原始标签(如 'sentiment',值为 "positive" 或 "negative")已经通过预处理函数(如 preprocess)被转换成了模型可以理解的数字格式(如 input_ids, attention_mask, labels 等)。因此,原始的文本字段(如 'review')不再需要。原始字符串形式的标签(如 'sentiment')也已经被编码成数字(如 0 和 1),所以也可以删除。

train_set = train_set.remove_columns(['review', 'sentiment'])
test_set = test_set.remove_columns(['review', 'sentiment'])

 训练模型

设置超参数

# 设置超参数
batch_size = 8                  # 每个设备上的 batch size(用于训练和评估)
epochs = 2                      # 总共训练的轮数(epoch 数量)
warmup_steps = 500              # 学习率预热步数(前几步学习率逐渐增加)
weight_decay = 0.01             # 权重衰减系数(L2 正则化强度)

 创建 TrainingArguments 对象,配置训练过程的各种参数

training_args = TrainingArguments(
    output_dir='./results',                 # 输出目录,保存模型检查点等文件
    num_train_epochs=epochs,                # 总共训练多少轮
    per_device_train_batch_size=batch_size, # 每个设备上用于训练的 batch size
    per_device_eval_batch_size=batch_size,  # 每个设备上用于评估的 batch size
    warmup_steps=warmup_steps,              # 学习率预热步数
    weight_decay=weight_decay,              # 权重衰减系数,防止过拟合
    logging_dir='./logs',                   # 日志输出目录(例如 TensorBoard 使用)
    optim="adamw_torch",                    # 使用 PyTorch 实现的 AdamW 优化器(修复警告)
)

创建 Trainer 对象,负责整个训练流程

trainer = Trainer(
    model=model,               # 要训练的模型(通常是 BertForSequenceClassification)
    args=training_args,        # 训练参数配置
    train_dataset=train_set,   # 训练数据集(HuggingFace Dataset 格式)
    eval_dataset=test_set      # 验证/测试数据集
)

开始训练

trainer.train()

在验证集上评估性能

trainer.evaluate()

查看评估结果

那么训练完毕后,如果想再次查看模型的评估结果,怎么实现呢?

1. 使用 Trainer.evaluate()

如果在训练结束后没有关闭Python 环境,并且 trainer 对象仍然存在,可以直接调用 evaluate() 方法来获取最新的评估结果。

results = trainer.evaluate()
print(results)

这将输出类似以下的结果(具体的指标取决于在 compute_metrics 函数中定义的内容):

{'eval_loss': 0.34, 'eval_accuracy': 0.89, 'eval_f1': 0.88}
2. 查看日志文件或 TensorBoard

如果在训练时配置了日志记录,比如设置了 logging_dir='./logs',那么可以使用 TensorBoard 来可视化训练和评估过程中的各种指标。

首先,确保已经安装了 TensorBoard:

pip install tensorboard

然后,在终端运行以下命令启动 TensorBoard:

tensorboard --logdir=./logs

打开浏览器并访问 http://localhost:6006,就可以看到训练和评估过程中的损失值、准确率等指标的变化趋势。

3. 保存和加载评估结果


如果希望保存评估结果以便日后查看,可以在评估之后将结果保存到文件中:

import json

results = trainer.evaluate()

# 将结果保存到 JSON 文件
with open('evaluation_results.json', 'w') as f:
    json.dump(results, f)

# 以后可以从文件中读取结果
with open('evaluation_results.json', 'r') as f:
    loaded_results = json.load(f)
    print(loaded_results)

总结

通过这一章我们实现了一个完整的 BERT 文本分类模型训练流程,包括数据加载、预处理、模型训练和评估,适用于二分类情感分析任务。这也属于典型的 迁移学习中的“微调”(fine-tuning) 方法,即:使用 Hugging Face 提供的预训练 BERT 模型,在具体任务(如情感分析)数据上继续训练,从而让模型适应目标任务。

整体流程如下:
阶段           功能
数据准备    使用 Pandas 加载 CSV 数据并转换为 HuggingFace Dataset
标签编码    将文本标签(如 "positive"、"negative")转换为数字(0/1)
数据集划分    划分为训练集和测试集
分词器加载    加载 BERT 分词器(如 bert-base-uncased)
数据预处理    对文本字段进行分词、填充、截断等操作
删除原始列    删除不需要的原始文本和标签列
模型构建    加载预训练的 BERT 分类模型
训练配置    设置超参数和训练选项(batch size、epochs、优化器等)
模型训练    使用 Trainer API 进行训练
模型评估    在测试集上评估模型性能

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐