当前位置: 首页 > news >正文

Transformer实战(13)——微调Transformer语言模型用于文本分类

Transformer实战(13)——微调Transformer语言模型用于文本分类

    • 0. 前言
    • 1. 文本分类介绍
    • 2. 微调 BERT 模型进行情感分析
    • 3. 模型推理
    • 小结
    • 系列链接

0. 前言

文本分类 (Text Classification) 广泛应用于舆情监测、邮件过滤、新闻聚类、产品评价等场景。传统方法通常难以充分挖掘文本的深层语义信息,基于 Transformer 的预训练模型(如 BERTDistilBERT 等)凭借强大的语义表示能力,已在各类文本分类任务上取得了显著提升。本文从文本分类的基本概念出发,学习如何利用 transformers 库对预训练 BERT 模型进行微调,实现高效的文本分类。

1. 文本分类介绍

文本分类 (Text Classification) 是将一个文档(如句子、评论、书籍章节、电子邮件内容等)映射到一个预定义类别列表中的一种方法。在只有两个类别的情况下,分别为正面和负面标签,使用二分类,例如情感分析;对于超过两个类别的情况,称之为多类别分类,其中类别是互斥的;而在多标签分类中类别不是互斥的,这意味着一个文档可以有多个标签。例如,一篇新闻文章的内容可能同时涉及体育和娱乐。此外,我们可能还需要对文档进行评分(范围在 [-1,1] 之间)或对其进行排名(范围在 [1-5] 之间),可以通过回归模型来解决这类问题,因为输出类型是连续的数值,而不是离散的类别。
Transformer 架构能够高效地解决这些问题。对于句子对任务,如文档相似性或文本蕴含,输入不是单个句子,而是两个句子,如下图所示。我们可以评估两个句子在语义上的相似度,或者预测它们是否在语义上相似。另一类句子对任务是文本蕴含,可以将其定义为多类别分类,两个序列映射为在 GLUE 基准测试中所定义的蕴含/矛盾/中立。

文本分类

接下来,通过微调预训练的 BERT 模型开始训练过程。微调需要对现有的 BERT 模型的权重进行细微调整,以适应不同的自然语言处理任务。本节中,我们使用一个非常常见的任务进行微调:情感分析。

2. 微调 BERT 模型进行情感分析

在本节中,我们将学习如何通过使用 IMDb 情感数据集来微调预训练的 BERT 模型进行情感分析。

(1) 首先,检查并保存当前的设备信息:

from torch import cuda
device = 'cuda' if cuda.is_available() else 'cpu'

(2) 使用 DistilBertForSequenceClassification 类,它继承自 DistilBert 类,并在顶部添加了一个特殊的序列分类头。我们利用这个分类头来训练分类模型,默认情况下类别数为 2

from transformers import DistilBertTokenizerFast, DistilBertForSequenceClassification
model_path= 'distilbert-base-uncased'
tokenizer = DistilBertTokenizerFast.from_pretrained(model_path)
model = DistilBertForSequenceClassification.from_pretrained(model_path, id2label={0:"NEG", 1:"POS"}, label2id={"NEG":0, "POS":1})

需要注意的是,在推理过程中,需要将参数 id2labellabel2id 传递给模型。或者,可以实例化一个特定的 config 对象并将其传递给模型:

config = AutoConfig.from_pre-trained(....)
SequenceClassification.from_pre-trained(.... config=config)

(3) 本节中,我们使用流行的情感分类数据集——IMDb 数据集。原始数据集包含两组数据:25000 个训练样本和 25000 个测试样本,我们将把测试数据集进一步拆分为测试集和验证集。需要注意的是,数据集的前半部分样本为正面情感,后半部分样本为负面情感。我们可以按如下方式拆分数据样本:

from datasets import load_dataset# to take entire dataset from original train 25 K AND TEST 25K
imdb_train= load_dataset('imdb', split="train")
imdb_test= load_dataset('imdb', split="test[:6250]+test[-6250:]")
imdb_val= load_dataset('imdb', split="test[6250:12500]+test[-12500:-6250]")

(4) 检查数据集的形状:

imdb_train.shape, imdb_test.shape, imdb_val.shape
# ((25000, 2), (12500, 2), (12500, 2))

(5) 为了加速训练过程,我们也可以选择数据集的其中一部分。例如,选择 4000 个训练样本、1000 个测试样本和 1000 个验证样本:

imdb_train= load_dataset('imdb', split="train[:2000]+train[-2000:]")
imdb_test= load_dataset('imdb', split="test[:500]+test[-500:]")
imdb_val= load_dataset('imdb', split="test[500:1000]+test[-1000:-500]")

(6) 将数据集通过分词器模型进行处理,以便为训练做准备:

enc_train = imdb_train.map(lambda e: tokenizer( e['text'], padding=True, truncation=True), batched=True, batch_size=1000) 
enc_test =  imdb_test.map(lambda e: tokenizer( e['text'], padding=True, truncation=True), batched=True, batch_size=1000) 
enc_val =   imdb_val.map(lambda e: tokenizer( e['text'], padding=True, truncation=True), batched=True, batch_size=1000) 

(7) 查看处理后的训练集。分词器已经将注意力掩码和输入 ID 添加到数据集中,以便 BERT 模型能够进行处理:

import pandas as pd
pd.DataFrame(enc_train)

输出如下所示,可以看到数据集已经准备完毕,可以进行训练和测试:

输出结果

Trainer 类(对于 TensorFlowTFTrainer )和 TrainingArguments 类(对于 TensorFlowTFTrainingArguments )能够简化大部分训练的复杂性。在 TrainingArguments 类中定义参数集,然后将其传递给 Trainer 对象。每个训练参数的作用如下:

参数描述
outpu_dir模型权重以及预测结果最终保存的位置
do_train, do_eval用于在训练期间监控模型性能
logging_strategy可选值包括 no, epoch, steps (默认值)
logging_steps两次保存日志到 logging_dir 目录之间的步数间隔,默认值为 500
save_strategy用于保存模型权重,可选值包括 no, epoch, steps (默认值)
save_steps两次保存模型权重之间的步数间隔,默认值为 500
fp16用于混合精度训练,同时使用 16-bit32-bit 浮点类型,可加速模型训练并减少内存占用
load_best_model_at_end该选项会在训练结束时自动加载验证损失最优的模型权重
logging_dirTensorBoard 日志目录

(8) 长短期记忆 (Long Short Term Memory, LSTM) 网络之类的深度学习架构需要多个 epoch 进行微调,但是对于基于 Transformer 的模型,由于迁移学习的存在,通常 3epoch 就足够了。大多数情况下,3epoch 足以完成微调,因为预训练模型在预训练阶段已经学习了足够多的语言知识。为了确定合适的训练 epoch,我们需要检测训练过程中模型的损失变化。对于多数下游任务问题来说,3epoch 已经足够了。在训练过程中,每隔 200 步保存一次模型检查点,保存在 ./MyIMDBModel 文件夹中:

from transformers import TrainingArguments, Trainer
training_args = TrainingArguments(output_dir='./MyIMDBModel', do_train=True,do_eval=True,num_train_epochs=3,              per_device_train_batch_size=32,  per_device_eval_batch_size=64,warmup_steps=100,                weight_decay=0.01,logging_strategy='steps', logging_dir='./logs',            logging_steps=50,evaluation_strategy="steps",save_strategy="steps",fp16=cuda.is_available(),load_best_model_at_end=True
)

(9) 在实例化 Trainer 对象之前,定义 compute_metrics() 方法,帮助我们根据特定的指标(如精准度、RMSEPearson 相关系数、BLEU 等)监控训练进展。文本分类问题(例如情感分类和多类别分类问题)通常使用微平均( micro-averaging )或宏平均( macro-averaging )的 F1 分数进行评估。宏平均方法对每个类别赋予相等的权重,而微平均方法则对每个文本或每个词元的分类决策赋予相等的权重。微平均等于模型正确决策次数与总决策次数之比。而宏平均方法计算每个类别的精准度、召回率和 F1 分数的平均值。对于分类问题,更适合使用宏平均进行评估,因为我们希望对每个标签赋予相同的权重:

from sklearn.metrics import accuracy_score, precision_recall_fscore_support
def compute_metrics(pred):labels = pred.label_idspreds = pred.predictions.argmax(-1)precision, recall, f1, _ = precision_recall_fscore_support(labels, preds, average='macro')acc = accuracy_score(labels, preds)return {'Accuracy': acc,'F1': f1,'Precision': precision,'Recall': recall}

(10) 实例化 Trainer 对象并启动。得益于 transformers 库,Trainer 类是一个非常强大的工具,能够为 PyTorchTensorFlow (TensorFlow 使用 TFTrainer) 组织复杂的训练和评估流程:

trainer = Trainer(model=model,args=training_args,train_dataset=enc_train,         eval_dataset=enc_val,            compute_metrics= compute_metrics
)

(11) 最后,启动训练过程:

results=trainer.train()

以上调用会记录模型指标。整个 IMDb 数据集包含 25000 个训练样本,如果批大小为 32,则有 25K/32 ≈ 782 步,3epoch 总共需要 2346 步 (782 x 3):

输出结果

(12) Trainer 对象会保留验证损失最小的模型权重,在三个(训练/测试/验证)数据集上评估最佳模型权重:

q=[trainer.evaluate(eval_dataset=data) for data in [enc_train, enc_val, enc_test]]
pd.DataFrame(q, index=["train","val","test"]).iloc[:,:5]

输出结果如下所示:

输出结果

完成训练/测试阶段后,可以看到模型具有 91.40 的准确率和 91.29 的宏平均 F1 分数。为了更详细地监控训练过程,可以调用如 TensorBoard 等高级工具。这些工具会解析日志并能够跟踪各种指标进行全面分析。将性能和其他指标记录在 ./logs 文件夹中。只需运行 tensorboard 函数,就能查看日志:

%reload_ext tensorboard
%tensorboard --logdir logs

3. 模型推理

(1) 接下来,使用模型进行推理,检查它是否正常工作。定义一个预测函数来简化预测步骤:

def get_prediction(text):inputs = tokenizer(text, padding=True, truncation=True, max_length=250, return_tensors="pt").to(device)outputs = model(inputs["input_ids"].to(device),inputs["attention_mask"].to(device))probs = outputs[0].softmax(1)return probs, probs.argmax()

(2) 运行模型进行推理:

model.to(device)
text = "I didn't like the movie since it bored me "
get_prediction(text)[1].item()

(3) 输出结果为 0,表示负面情感。我们已经定义了每个 ID 对应的标签,可以使用这个映射关系来获取标签。或者,我们可以将这些繁琐的步骤交给专门的 API,即 Pipeline。在实例化之前,先保存最佳模型,以便以后进行推理:

model_save_path = "MyBestIMDBModel"
trainer.save_model(model_save_path)
tokenizer.save_pretrained(model_save_path)

Pipeline API 是一个便捷的方式,用来使用预训练模型进行推理。从保存的路径加载模型,然后将其传递给 Pipeline API,后者会完成其余的操作。我们也可以跳过保存模型的步骤,直接将模型和 tokenizer 对象传递给 Pipeline API

(4) 当进行二分类时,我们需要在 pipeline 中指定任务名称参数为 sentiment-analysis

from transformers import pipeline, DistilBertForSequenceClassification, DistilBertTokenizerFast
model = DistilBertForSequenceClassification.from_pretrained("MyBestIMDBModel")
tokenizer= DistilBertTokenizerFast.from_pretrained("MyBestIMDBModel")
nlp= pipeline("sentiment-analysis", model=model, tokenizer=tokenizer)
nlp("the movie was very impressive")
# [{'label': 'POS', 'score': 0.9461892247200012}]
nlp("the script of the picture was very poor")
# [{'label': 'NEG', 'score': 0.957322359085083}]

Pipeline API 处理输入,并能够自动学习哪个 ID 对应哪个标签 (POSNEG),还会输出类别的概率。

小结

本文介绍了基于 Transformer 架构的文本分类技术,重点介绍了如何使用 Hugging Facetransformers 库进行情感分析任务,使用 Trainer 类对 IMDb 数据集进行了情感预测模型的微调。训练后保存最佳模型权重,通过 Pipeline API 实现便捷的预测功能,自动输出标签及置信度。

系列链接

Transformer实战(1)——词嵌入技术详解
Transformer实战(2)——循环神经网络详解
Transformer实战(3)——从词袋模型到Transformer:NLP技术演进
Transformer实战(4)——从零开始构建Transformer
Transformer实战(5)——Hugging Face环境配置与应用详解
Transformer实战(6)——Transformer模型性能评估
Transformer实战(7)——datasets库核心功能解析
Transformer实战(8)——BERT模型详解与实现
Transformer实战(9)——Transformer分词算法详解
Transformer实战(10)——生成式语言模型 (Generative Language Model, GLM)
Transformer实战(11)——从零开始构建GPT模型
Transformer实战(12)——基于Transformer的文本到文本模型

http://www.dtcms.com/a/339342.html

相关文章:

  • 操作系统:多线程、进程管理、内存分配、任务调度等
  • Gemini CLI 详细操作手册
  • ECC升级S4,AVL自定义GUI 状态无法显示全选和取消全选按钮
  • Matplotlib数据可视化实战:Matplotlib基础与实践-快速上手数据可视化
  • 学习嵌入式的第二十一天——数据结构——链表
  • 08.19总结
  • 豆包1.5轻量版 vs Gemini 2.5闪存版:生成5000字深度文章,哪个模型更合适?
  • 玳瑁的嵌入式日记D20-08019(数据结构)
  • audio接口的技术发展
  • Java技术总监的成长之路(技术干货分享)
  • 昆仑万维SkyWork AI技术发布周:多模态模型的全面突破
  • 记一次 .NET 某自动化智能制造软件 卡死分析
  • 一条 SQL 语句在 MySQL中的执行过程。
  • Python网络爬虫全栈教程 – 从基础到实战
  • 【INOUT端口】
  • HarmonyOS 中的 setInterval的基本使用
  • openssl生成自签名证书的方法
  • 飞算JavaAI颠覆传统:SpringBoot项目开发效率革命实录
  • 基于uni-app的成人继续教育教务管理系统设计与实现
  • 0.开篇简介
  • 微信小程序连接到阿里云物联网平台
  • LeetCode 135.分发糖果:双向遍历下的贪心策略应用
  • Kubernetes Pod 控制器
  • Effective C++ 条款50:了解new和delete的合理替换时机
  • 实践项目-1
  • jenkins自动化部署
  • 七十二、【Linux数据库】MySQL数据库MHA集群概述 、 部署MHA集群
  • 当MySQL的int不够用了
  • GTSAM中实现多机器人位姿图优化(multi-robot pose graph optimization)示例
  • 权限管理系统