Transformers 预训练模型微调实战:从 Trainer 到原生 PyTorch 训练循环
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
导读
本文基于 🤗 Transformers 官方日语文档 docs/source/ja/training.md 编写,系统讲解如何对预训练模型进行微调(Fine-tuning):先走一遍使用Trainer的高层训练流程,再深入原生 PyTorch 手写训练循环的底层实现。读完本文,你将掌握从数据集加载与分词、训练超参数配置、评估指标接入,到优化器与学习率调度器创建、完整训练循环与评估的全链路能力,并能将这些技能直接复用到文本分类、生成、序列标注等各类任务上。
为什么需要微调
使用预训练模型可以显著降低计算成本与碳排放,无需从零开始训练即可获得 SOTA 模型。🤗 Transformers 提供了数千个面向不同任务的预训练模型,而将预训练模型迁移到自己的特定任务数据集上继续训练,这一过程称为微调(Fine-tuning)——它是当今 NLP 领域最强大的训练技术之一。
微调的本质是:模型不再从随机权重起步,而是在一个已经学到大语言规律的权重基础上,用较小的领域数据继续优化。因此它所需的算力、数据和时间都远小于从零预训练。在动手之前,请确保已按官方安装指南准备好环境,并安装了datasets、evaluate、accelerate等配套库。
准备数据集(Prepare a dataset)
微调的第一步是下载并预处理数据集。本教程使用 Yelp Reviews 数据集(包含 5 个情感标签的评论数据)进行序列分类任务的演示。
加载数据集
通过datasets库的load_dataset一行代码即可加载:
>>> from datasets import load_dataset >>> dataset = load_dataset("yelp_review_full") >>> dataset["train"][100] {'label': 0, 'text': 'My expectations for McDonalds are t rarely high. ...'}每个样本包含两个字段:text(评论文本)与label(情感标签 0~4),共 5 个类别。
分词与批处理
分词器负责把文本转换为模型可理解的 token id,同时处理可变序列长度问题(padding 与 truncation)。利用datasets的map方法,可以将预处理函数一次性应用到整个数据集:
>>> from transformers import AutoTokenizer >>> tokenizer = AutoTokenizer.from_pretrained("google-bert/bert-base-cased") >>> def tokenize_function(examples): ... return tokenizer(examples["text"], padding="max_length", truncation=True) >>> tokenized_datasets = dataset.map(tokenize_function, batched=True)padding="max_length"将每条样本填充到固定最大长度,便于组成 batch;truncation=True截断超长序列;batched=True让预处理函数对一批样本(而非单条)批量执行,充分利用 GPU/CPU 向量化能力,显著缩短预处理时间。
构建小子集加速实验
为缩短运行时间,可以创建完整数据集的小型子集用于快速验证流程:
>>> small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000)) >>> small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))固定seed=42保证每次实验数据划分一致、结果可复现。
使用 PyTorch Trainer 微调
🤗 Transformers 的Trainer类专门针对 Transformers 模型训练做了优化,免去手动编写训练循环的繁琐,同时支持日志记录、梯度累积、混合精度等一系列训练选项与特性(对应实现见 src/transformers/trainer.py)。
加载模型并指定标签数
>>> from transformers import AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)关于"部分权重未使用"警告:加载时你可能会看到"某些预训练权重未被使用、某些权重被随机初始化"的警告,这完全正常。BERT 预训练的分类头会被丢弃,替换为随机初始化的新分类头;微调的过程正是让这个新分类头在序列分类任务上学习,同时把预训练模型学到的知识迁移过来。
配置训练超参数(TrainingArguments)
TrainingArguments类承载所有训练选项与可调超参数。其字段定义可在 src/transformers/training_args.py 中查看,这里仅指定输出目录即可开始:
>>> from transformers import TrainingArguments >>> training_args = TrainingArguments(output_dir="test_trainer")常用超参数及其源码默认值一览(均为TrainingArguments的默认配置):
| 参数 | 默认值 | 说明 |
|---|---|---|
output_dir | None(必填) | 模型预测与 checkpoint 的输出目录 |
per_device_train_batch_size | 8 | 每张设备(GPU/TPU core/CPU)上的训练 batch 大小 |
num_train_epochs | 3.0 | 总训练轮数 |
max_steps | -1 | 若为正数则覆盖num_train_epochs,适用于流式数据集 |
learning_rate | 5e-5 | 优化器初始学习率 |
lr_scheduler_type | "linear" | 学习率调度器类型,见SchedulerType枚举 |
warmup_steps | 0 | 从 0 线性预热到learning_rate的步数 |
optim | "adamw_torch"(PyTorch 2.8+ 为"adamw_torch_fused") | 优化器类型 |
weight_decay | 0.0 | 权重衰减系数(自动排除 bias 与 LayerNorm 参数) |
gradient_accumulation_steps | 1 | 梯度累积步数,有效 batch =per_device_train_batch_size × 设备数 × 累积步数 |
max_grad_norm | 1.0 | 梯度裁剪范数,设 0 关闭 |
bf16/fp16 | False | 混合精度开关,硬件支持时优先bf16 |
eval_strategy | "no" | 何时评估:"no"/"steps"/"epoch" |
eval_steps | None | eval_strategy="steps"时的评估间隔,默认回退到logging_steps |
save_strategy | "steps" | 何时保存 checkpoint |
load_best_model_at_end | False | 训练结束时加载最优 checkpoint(需设置eval_strategy) |
logging_steps | 500 | 日志输出间隔 |
接入评估指标(Evaluate)
Trainer在训练过程中不会自动评估模型性能,需要你把计算指标的函数传给它。evaluate库提供了简洁的accuracy指标加载方式:
>>> import numpy as np >>> import evaluate >>> metric = evaluate.load("accuracy")由于所有 🤗 Transformers 模型返回的是logits,在调用metric.compute前必须先用argmax把 logits 转换为预测类别:
>>> def compute_metrics(eval_pred): ... logits, labels = eval_pred ... predictions = np.argmax(logits, axis=-1) ... return metric.compute(predictions=predictions, references=labels)若希望在微调过程中监控指标,可在训练参数中通过eval_strategy指定评估时机——例如每个 epoch 结束时报告一次:
>>> from transformers import TrainingArguments, Trainer >>> training_args = TrainingArguments(output_dir="test_trainer", eval_strategy="epoch")组装 Trainer 并开始训练
把模型、训练参数、训练/测试数据集与评估函数组装为Trainer对象,随后调用train():
>>> trainer = Trainer( ... model=model, ... args=training_args, ... train_dataset=small_train_dataset, ... eval_dataset=small_eval_dataset, ... compute_metrics=compute_metrics, ... )>>> trainer.train()Trainer内部会自动完成:optimizer 与 scheduler 的创建(见 src/transformers/trainer.py 的create_optimizer/create_scheduler)、梯度累积、梯度裁剪、混合精度、日志记录与 checkpoint 保存。训练结束后还可调用trainer.save_model()保存微调后的模型与分词器。
使用原生 PyTorch 微调
Trainer一行代码即可完成微调;但如果你想完全掌控训练过程,也可以使用原生 PyTorch 手写训练循环。此部分对应文档中的 "Train in native Pytorch" 章节。
环境清理与数据格式调整
若此前已运行过Trainer流程,建议释放显存:
>>> del model >>> del trainer >>> torch.cuda.empty_cache()随后调整数据集格式,使其适配模型输入:
# 1. 模型不接受原始文本输入,删除 text 列 >>> tokenized_datasets = tokenized_datasets.remove_columns(["text"]) # 2. 将 label 列重命名为 labels(模型期望的参数名) >>> tokenized_datasets = tokenized_datasets.rename_column("label", "labels") # 3. 设置数据集格式为 PyTorch 张量 >>> tokenized_datasets.set_format("torch")同样构建小子集加速验证:
>>> small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000)) >>> small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))创建 DataLoader
用 PyTorch 的DataLoader包装数据集以迭代 batch:
>>> from torch.utils.data import DataLoader >>> train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=8) >>> eval_dataloader = DataLoader(small_eval_dataset, batch_size=8)加载序列分类模型(同样指定 5 个标签):
>>> from transformers import AutoModelForSequenceClassification >>> model = AutoModelForSequenceClassification.from_pretrained("google-bert/bert-base-cased", num_labels=5)优化器与学习率调度器
使用 PyTorch 的AdamW优化器:
>>> from torch.optim import AdamW >>> optimizer = AdamW(model.parameters(), lr=5e-5)学习率调度器可以使用transformers的get_scheduler统一创建,其实现位于 src/transformers/optimization.py。该函数通过TYPE_TO_SCHEDULER_FUNCTION映射表(见同文件 L944-L957)把调度器名称解析为具体实现:
>>> from transformers import get_scheduler >>> num_epochs = 3 >>> num_training_steps = num_epochs * len(train_dataloader) >>> lr_scheduler = get_scheduler( ... name="linear", optimizer=optimizer, num_warmup_steps=0, num_training_steps=num_training_steps ... )get_scheduler支持的name取值(即SchedulerType枚举)包括:linear、cosine、cosine_with_restarts、polynomial、constant、constant_with_warmup、inverse_sqrt、reduce_on_plateau、cosine_with_min_lr、cosine_warmup_with_min_lr、warmup_stable_decay、greedy。注意:除constant等少数类型外,大多数调度器要求提供num_warmup_steps,否则会抛出ValueError。
指定运行设备
尽可能使用 GPU,否则 CPU 上训练可能耗时数小时:
>>> import torch >>> device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") >>> model.to(device)如果没有云 GPU,可以使用 Colaboratory 或 SageMaker StudioLab 等托管笔记本免费获得 GPU 资源。
手写训练循环
使用tqdm显示训练进度,标准的三轮循环如下:
>>> from tqdm.auto import tqdm >>> progress_bar = tqdm(range(num_training_steps)) >>> model.train() >>> for epoch in range(num_epochs): ... for batch in train_dataloader: ... batch = {k: v.to(device) for k, v in batch.items()} ... outputs = model(**batch) ... loss = outputs.loss ... loss.backward() ... optimizer.step() ... lr_scheduler.step() ... optimizer.zero_grad() ... progress_bar.update(1)训练循环的关键步骤:
- 将每个 batch 的张量搬运到
device; - 前向传播得到
loss,调用loss.backward()计算梯度; optimizer.step()更新权重;lr_scheduler.step()推进学习率调度;optimizer.zero_grad()清零梯度,避免累积。
手写评估循环
与Trainer的compute_metrics不同,原生循环中我们用evaluate.add_batch累积所有 batch 的预测,最后统一计算指标:
>>> import evaluate >>> metric = evaluate.load("accuracy") >>> model.eval() >>> for batch in eval_dataloader: ... batch = {k: v.to(device) for k, v in batch.items()} ... with torch.no_grad(): ... outputs = model(**batch) ... logits = outputs.logits ... predictions = torch.argmax(logits, dim=-1) ... metric.add_batch(predictions=predictions, references=batch["labels"]) >>> metric.compute()评估时务必:
- 调用
model.eval()切换为推理模式(关闭 dropout 等); - 使用
torch.no_grad()禁用梯度计算,节省显存与时间; - 对 logits 取
argmax得到类别预测后送入metric.add_batch累积。
从源码看 Trainer 的底层实现
为进一步理解Trainer做了什么,可以对照源码确认关键环节:
- 优化器与调度器:
Trainer.create_optimizer()(src/transformers/trainer.py#L1227)根据TrainingArguments.optim创建优化器;create_scheduler()(src/transformers/trainer.py#L1303)内部同样调用get_scheduler按lr_scheduler_type生成调度器——与原生 PyTorch 路径完全一致; - 训练主循环:
Trainer.train()(src/transformers/trainer.py#L1406)封装了数据迭代、梯度累积、梯度裁剪、混合精度、日志与 checkpoint 逻辑; - 评估:
Trainer.evaluate()(src/transformers/trainer.py#L2635)负责在eval_strategy指定的时机执行评估并调用compute_metrics。
TrainingArguments的字段校验逻辑(src/transformers/training_args.py#L1526-L1550)还做了若干约束:例如设置eval_strategy="steps"时必须提供eval_steps或logging_steps;load_best_model_at_end=True时save_strategy与eval_strategy必须匹配(除非save_strategy="best")。了解这些约束可避免运行时参数报错。
进阶参考与示例脚本
- 仓库的 examples/pytorch/text-classification 目录提供了可直接运行的文本分类微调脚本:
run_glue.py(基于Trainer的 GLUE 基准微调)、run_glue_no_trainer.py(基于Accelerate与原生训练循环)、run_classification.py与run_xnli.py,可作为实战模板; - 仓库 notebooks 目录收录了针对不同任务微调模型的各类 notebook;
- 更多
Trainer特性(自定义损失函数、内存高效评估、checkpoint 等)与回调机制可参阅 docs/source/en/trainer_recipes.md、docs/source/en/trainer_callbacks.md 等英文文档。
总结
本文完整复现了官方日语教程的核心脉络:从 Yelp Reviews 数据集的加载与分词预处理,到Trainer高层 API 的微调(含TrainingArguments超参数、compute_metrics评估接入、eval_strategy评估时机),再到原生 PyTorch 手写训练循环(含DataLoader、AdamW、get_scheduler调度器、完整训练与评估代码),并结合仓库源码说明了Trainer内部与原生路径的一致性。无论你偏好开箱即用的Trainer,还是希望完全掌控每一步的原生 PyTorch,本文给出的代码均可直接复制运行,帮助你快速上手预训练模型的微调实践。
【免费下载链接】transformers🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考