Appearance
微调预训练模型
从零训练 NanoGPT 让你吃透了原理;但真实工程里,我们几乎总是在已有预训练模型上微调,用少量数据「激活」它已有的语言能力。这一页给你两条最常用的实战路径。
1. 为什么要微调,而不是从头训
从头预训练: 需要海量语料 + 大量算力(动辄数周/数卡), 学的是"通用语言能力"
微调: 站在巨人肩膀上, 几千~几万条数据, 让模型适配你的领域/风格/任务 通用预训练模型(GPT-2 / BERT / Qwen ...)
│ 用你的小数据继续训练(改权重)
▼
你的领域模型(医疗问答 / 法律摘要 / 客服口吻 ...)2. 路线 A:全参数微调一个 GPT-2(续写你的风格)
把 GPT架构 的预训练目标原封不动用在你的语料上——继续预测下一词,只是数据换成你的。
python
import torch
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer, DataCollatorForLanguageModeling
name = "gpt2"
tok = AutoTokenizer.from_pretrained(name)
tok.pad_token = tok.eos_token # GPT-2 没有 PAD, 用 EOS 顶替
model = AutoModelForCausalLM.from_pretrained(name)
# 你的语料(这里用一条演示, 实际换成公司文档/小说/对话记录)
ds = load_dataset("text", data_files={"train": "my_corpus.txt"})
def encode(b):
return tok(b["text"], truncation=True, max_length=128)
ds = ds["train"].map(encode, batched=True).train_test_split(test_size=0.05)
# 关键: 语言建模 collator 会自动把 labels 设为 input(并把 padding 处设为 -100 忽略)
collator = DataCollatorForLanguageModeling(tokenizer=tok, mlm=False)
args = TrainingArguments(
output_dir="gpt2-finetune",
learning_rate=5e-5, # 微调用比从零更小的学习率
per_device_train_batch_size=8,
num_train_epochs=3,
eval_strategy="epoch",
save_strategy="epoch",
fp16=torch.cuda.is_available(),
logging_steps=20,
)
trainer = Trainer(model=model, args=args,
train_dataset=ds["train"], eval_dataset=ds["test"],
data_collator=collator)
trainer.train()
trainer.save_model("gpt2-finetune/final")mlm=False→ 因果语言建模(GPT 式),labels=input_ids,padding 位被置-100(交叉熵忽略)。- 训练完的
final/直接可用AutoModelForCausalLM.from_pretrained("gpt2-finetune/final")加载续写,风格已向你的语料靠拢。
3. 路线 B:LoRA 参数高效微调(大模型的主流做法)
全参数微调 7B 模型显存爆炸。LoRA 冻结原权重,只在旁边加一对极小的「低秩适配器」训练,参数量常不到 1%,效果却接近全量微调。
原始权重 W (冻结, 不训练)
x ───────────────► W·x
│
└─► A·B (可训练, 秩 r 很小) ──► (A·B)·x
输出 = W·x + (A·B)·x 只训练 A,Bpython
from peft import LoraConfig, get_peft_model, TaskType
from transformers import AutoModelForCausalLM
base = AutoModelForCausalLM.from_pretrained("gpt2") # 演示用小模型
lora = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, lora_alpha=16, lora_dropout=0.05,
target_modules=["c_attn"], # 挂到注意力投影上
)
model = get_peft_model(base, lora)
model.print_trainable_parameters() # trainable: xxx (≈ 总量的百分之零点几)
# 之后照常交给 Trainer 训练, 只更新 LoRA 权重- r(秩) 越小越省、越大表达越强,常见 4/8/16/64。
- 训练产物只有几十 MB,可热插拔式加载到不同基座。
- QLoRA = 4-bit 量化基座 + LoRA,让单张消费级显卡也能微调大模型。
4. Encoder 模型的微调(分类)
理解类任务的微调,就是 训练流程Trainer 里的例子:换 ForSequenceClassification 头、给标签、小学习率微调。此处不再重复。
5. 全量微调 vs LoRA vs 提示词:怎么选
能力够用? ──► 直接用 Prompt(零训练, 最省) ← 先试这个!
│不够
▼
领域/风格适配? ──► LoRA(省显存, 多任务热切换) ← 大模型默认
│数据多、要深度改造
▼
全参数微调 ← 数据充足且有算力
│连语言都不是、要从零建模
▼
从头预训练(NanoGPT 那种) ← 极少需要工程第一原则:能靠 Prompt 解决的,别微调;能靠 LoRA 解决的,别全量。 微调是最后的、最贵的手段。
6. 微调避坑清单
- 学习率要小(全量常 1e-5~5e-5;LoRA 可大些如 1e-4~2e-4),太大会「灾难性遗忘」。
- 别把 padding 当词学:确保 labels 里 padding 位是
-100。 - 数据格式对齐预训练:分类/生成要用与基座一致的模板(对话模型尤其注意 ChatML 模板)。
- 留验证集:监控是否过拟合到小数据、是否遗忘了通用能力。
- 存 adapter 而非全模型(LoRA),便于版本管理与复用。
小结
- 微调 = 用少量数据在预训练模型上「继续学」,远比从头训划算。
- 全参数微调简单直接但吃资源;LoRA/QLoRA 是当今大模型微调主流。
- 选型顺序:Prompt → LoRA → 全量 → 从头预训练,从便宜到昂贵。
- 小心学习率过大导致的灾难性遗忘与 padding 污染。
恭喜!到这里你已经完成了从「理解原理」到「从零训练」再到「工程微调」的完整闭环。接下来看几篇进阶主题,把你的知识与现代大模型接轨 → 进阶主题·高效注意力。