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

Transformer实战(15)——使用PyTorch微调Transformer语言模型

Transformer实战(15)——使用PyTorch微调Transformer语言模型

    • 0. 前言
    • 1. 从零开始微调预训练模型
    • 2. 单步前向传播和反向传播
    • 3. 训练循环
    • 小结
    • 系列链接

0. 前言

在本节中,我们将全面剖析预训练 Transformer 模型的微调过程。相比于依赖高级 API (如 Trainer )的便捷封装,本节聚焦于使用 PyTorch 手动构建训练管道——包括模型加载、优化器配置、前向传播、反向传播、损失计算以及自定义数据集与数据加载器的实现。通过对单步前向与反向传播的演示,再到完整的 epoch 循环与验证流程,将深入理解 AdamW 优化器在 Transformer 微调中的优势,以及如何将批数据高效地送入模型进行训练和评估。

1. 从零开始微调预训练模型

接下来,我们将从零开始微调预训练模型,以了解其背后的运行机制。

(1) 首先,加载模型进行微调。本节我们选择 DistilBert,它是 BERT 的一个轻量、快速的版本:

from transformers import DistilBertForSequenceClassification
model = DistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased')

(2) 要对模型进行微调,需要将其设置为训练模式:

model.train()

(3) 接下来,加载分词器:

from transformers import DistilBertTokenizerFast
tokenizer = DistilBertTokenizerFast.from_pretrained('bert-base-uncased')

(4) 由于 Trainer 类为我们组织了整个过程,因此在之前的 IMDb 情感分类模型训练中,我们并未涉及优化和其他训练设置。在本节中,我们需要自己实例化优化器,选择 AdamW 优化器,它是 Adam 算法的一种改进实现,解决了权重衰减问题。研究表明,AdamW 比使用 Adam 训练的模型能够产生更好的训练损失和验证损失优化表现,因此在 Transformer 训练过程中被广泛使用:

from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=1e-3)

2. 单步前向传播和反向传播

为了从零开始设计微调过程,我们必须理解如何实现单步前向传播和反向传播。将单个批次的数据通过 Transformer 层并获取输出,即前向传播。然后,使用输出和真实标签计算损失,并根据损失更新模型权重,即反向传播。

(1) 接收一个批次中的三个句子及其对应的标签,并执行前向传播。最后,模型会自动计算损失:

import torch
texts= ["this is a good example","this is a bad example","this is a good one"]
labels= [1,0,1]
labels = torch.tensor(labels).unsqueeze(0)
encoding = tokenizer(texts, return_tensors='pt', padding=True, truncation=True, max_length=512)
input_ids = encoding['input_ids']
attention_mask = encoding['attention_mask']
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
outputs
# SequenceClassifierOutput(loss=tensor(0.6841, grad_fn=<NllLossBackward0>), logits=tensor([[ 0.0428,  0.1283],
#        [ 0.0652,  0.2260],
#        [-0.0192,  0.1239]], grad_fn=<AddmmBackward0>), hidden_states=None, attentions=None)

模型接受由分词器生成的 input_idsattention_mask 作为输入,并使用真实标签计算损失。输出包含损失和 logitsloss.backward() 会通过使用输入和标签对模型进行评估来计算张量的梯度。optimizer.step() 执行一次优化步骤,并使用已计算的梯度更新权重,这就是反向传播。将这些代码放入一个循环中时,还会添加 optimizer.zero_grad() 清除所有参数的梯度。在循环开始时调用这一操作非常重要,否则可能会积累多个步骤的梯度。输出的第二个张量是 logits,在深度学习中,logits(logistic units) 是神经网络结构的最后一层,由实数预测值组成。在分类问题中,logits 需要通过 softmax 函数转换为概率;而对于回归任务,则只需进行简单的归一化处理。

(2) 模型只会输出 logits,而不会计算损失。以下示例展示了如何手动计算交叉熵损失:

from torch.nn import functional
labels = torch.tensor([1,0,1])
outputs = model(input_ids, attention_mask=attention_mask)
loss = functional.cross_entropy(outputs.logits, labels)
loss.backward()
optimizer.step()
loss
# tensor(0.5494, grad_fn=<NllLossBackward0>)

3. 训练循环

通过以上内容,我们了解了如何将批数据通过网络进行单步前向传播。接下来,设计循环以批次形式遍历整个数据集,进行多个 epoch 的训练。

(1) 首先实现 Dataset,它是 torch.Dataset 的子类,继承了成员变量和函数,并实现了 __init__()__getitem__() 这两个抽象函数:

from torch.utils.data import Dataset
class MyDataset(Dataset):def __init__(self, encodings, labels):self.encodings = encodingsself.labels = labelsdef __getitem__(self, idx):item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}item['labels'] = torch.tensor(self.labels[idx])return itemdef __len__(self):return len(self.labels)

(2) 接下来,使用 sst2 (Stanford Sentiment Treebank v2) 数据集,来微调模型进行情感分析。我们还将加载与 sst2 对应的评估指标:

import datasets
from datasets import load_dataset
sst2= load_dataset("glue","sst2")
from evaluate import load
metric = load("glue", "sst2")

(3) 提取句子和对应的标签:

texts=sst2['train']['sentence']
labels=sst2['train']['label']
val_texts=sst2['validation']['sentence']
val_labels=sst2['validation']['label']

(4) 将数据集通过分词器处理,并实例化 MyDataset 对象,使 DistiBert 模型能够处理这些数据:

train_dataset= MyDataset(tokenizer(texts, truncation=True, padding=True), labels)
val_dataset=  MyDataset(tokenizer(val_texts, truncation=True, padding=True), val_labels)

(5) 接下来,实例化一个 DataLoader 类,它提供了按加载顺序遍历数据样本的接口,也有助于批处理和内存固定:

from torch.utils.data import DataLoadertrain_loader = DataLoader(train_dataset, batch_size=16, shuffle=True)
val_loader =  DataLoader(val_dataset, batch_size=16, shuffle=True)

(6) 检测设备并定义 AdamW 优化器:

from transformers import  AdamWdevice = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
model.to(device)
optimizer = AdamW(model.parameters(), lr=1e-5)

(7) 我们已经了解了如何实现前向传播,在这个过程中,批数据通过神经网络通过神经网络的每一层,从第一个层到最后一个层,依次处理,并经过激活函数后传递到下一个层。为了在多个 epoch 中遍历整个数据集,需要嵌套循环:外循环用于 epoch,而内循环则用于处理每个批次。每个 epoch 由两个模块组成;一个用于训练,另一个用于评估。在训练循环中调用了 model.train(),而在评估循环中调用了 model.eval()。通过相应的指标对象来跟踪模型的表现:

for epoch in range(3):model.train()for batch in train_loader:optimizer.zero_grad()input_ids = batch['input_ids'].to(device)attention_mask = batch['attention_mask'].to(device)labels = batch['labels'].to(device)outputs = model(input_ids, attention_mask=attention_mask, labels=labels)loss = outputs[0]loss.backward()optimizer.step()model.eval()for batch in val_loader:input_ids = batch['input_ids'].to(device)attention_mask = batch['attention_mask'].to(device)labels = batch['labels'].to(device)outputs = model(input_ids, attention_mask=attention_mask, labels=labels)predictions=outputs.logits.argmax(dim=-1)  metric.add_batch(predictions=predictions,references=batch["labels"],)eval_metric = metric.compute()print(f"epoch {epoch}: {eval_metric}")

微调后的模型,达到了大约 90.94% 的准确率:

epoch 0: {'accuracy': 0.9048165137614679}
epoch 1: {'accuracy': 0.8944954128440367}
epoch 2: {'accuracy': 0.9094036697247706}

小结

本节通过 DistilBert 的微调,介绍了使用 PyToch 从加载预训练模型、手动构建优化器,到实现前向与反向传播、管理梯度,展示了使用 PyTorch 微调 Transformer 语言模型的完整流程。我们演示了如何定义 DatasetDataLoader,并利用 SST-2 数据集完成多轮训练与评估,最终取得约 90.94% 的验证准确率。

系列链接

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的文本到文本模型
Transformer实战(13)——从零开始训练GPT-2语言模型
Transformer实战(14)——微调Transformer语言模型用于文本分类

http://www.xdnf.cn/news/18998.html

相关文章:

  • 了解迁移学习吗?大模型中是怎么运用迁移学习的?
  • 达梦数据库配置文件-COMPATIBLE_MODE
  • 数据结构青铜到王者第七话---队列(Queue)
  • 《websocketpp使用指北》
  • ModuleNotFoundError: No module named ‘dbgpt_app‘
  • Python音频分析与线性回归:探索声音中的数学之美
  • 学习游戏制作记录(存档点和丢失货币的保存以及敌人的货币掉落)8.27
  • 计算机网络——DNS,ARP,RARP,DHCP,ICMP
  • Marin说PCB之包地间距对GMSL2信号阻抗的影响分析--01
  • 【图像算法 - 25】基于深度学习 YOLOv11 与 OpenCV 实现人员跌倒识别系统(人体姿态估计版本)
  • 学习 Android (十七) 学习 OpenCV (二)
  • string::erase
  • Prometheus+Grafana监控安装及配置
  • Python 并行计算进阶:ProcessPoolExecutor 处理 CPU 密集型任务
  • 从“找不到”到“秒上手”:金仓文档系统重构记
  • 《电商库存系统超卖事故的技术复盘与数据防护体系重构》
  • 推荐系统王树森(四)特征交叉+行为序列
  • java基础(十六)操作系统(上)
  • 基于单片机光照强度检测(光敏电阻)系统Proteus仿真(含全部资料)
  • 【Qt开发】常用控件(七)-> styleSheet
  • 深度学习(鱼书)day12--卷积神经网络(后四节)
  • Java项目-苍穹外卖_Day3-Day4
  • 深度解析Structured Outputs:基于JSON Schema的结构化输出实践与最佳方案
  • 8月26日
  • 开发避坑指南(37):Vue3 标签页实现攻略
  • iPhone 17 Pro 全新配色确定,首款折叠屏 iPhone 将配备 Touch ID 及四颗镜头
  • 二、JVM 入门 —— (四)堆以及 GC
  • MATLAB中函数的详细使用
  • Slice-100K:推动AI驱动的CAD与3D打印创新的多模态数据集
  • 『专利好药用力心脑血管健康』——爱上古中医(28)(健康生活是coder抒写优质代码的前提条件——《黄帝内经》伴读学习纪要)