Appearance
大模型的预训练与微调
预训练和微调是大模型开发的两个核心阶段。预训练使模型从海量数据中学习通用知识,微调则使模型适应特定任务。本章节详细介绍这两个阶段的关键技术。
1. 预训练基础
1.1 预训练的概念与目的
核心概念:
- 预训练是在大规模未标注数据上训练模型的过程
- 目的是让模型学习语言的通用表示和知识
- 为后续的任务特定微调奠定基础
主要优势:
- 充分利用海量未标注数据
- 提高模型的泛化能力
- 减少下游任务的标注数据需求
- 加速微调过程
1.2 常见预训练目标
掩码语言模型 (MLM)
BERT采用的预训练目标:
原始文本:这是一个[MASK]的例子
输入:这是一个[MASK]的例子
目标:预测[MASK]位置的原始词("简单")实现代码:
python
def create_mlm_training_data(text, mask_prob=0.15):
tokens = tokenizer.tokenize(text)
labels = tokens.copy()
# 随机掩码token
for i in range(len(tokens)):
if random.random() < mask_prob:
p = random.random()
if p < 0.8:
tokens[i] = "[MASK]" # 80%的概率替换为[MASK]
elif p < 0.9:
tokens[i] = random.choice(tokenizer.vocab.keys()) # 10%的概率替换为随机词
# 10%的概率保持不变
return tokens, labels因果语言建模 (CLM)
GPT系列采用的预训练目标:
输入序列:这是一个
目标:预测下一个词 "简单"实现逻辑:
- 自回归方式生成
- 每次预测序列中的下一个token
- 注意力机制包含掩码,防止访问未来token
置换语言模型 (PLM)
XLNet采用的预训练目标:
原始序列:这 是 一 个 简 单 的 例 子
置换顺序:这 简 个 单 是 的 例 子 一
目标:在置换后的序列中预测被掩盖的词对比学习目标
SimCSE等模型采用:
输入:同一个句子的两个不同表示(如不同dropout掩码)
目标:使相似表示距离更近,不同表示距离更远1.3 预训练数据处理
数据收集与清洗
关键步骤:
- 多源数据收集(网页、书籍、文章等)
- 去重处理
- 质量过滤(低质量、重复、有毒内容)
- 格式标准化
常见数据集:
- Common Crawl:网页数据集
- BookCorpus:图书语料
- Wikipedia:维基百科
- 行业特定语料库
分词策略
主要分词方法:
- 字符级分词:简单但上下文信息少
- 词级分词:需要词典,未登录词问题
- 子词分词:
- BPE (Byte Pair Encoding):GPT系列使用
- WordPiece:BERT系列使用
- Unigram LM:T5使用
- SentencePiece:多语言模型常用
实现示例:
python
from tokenizers import ByteLevelBPETokenizer
# 训练BPE分词器
tokenizer = ByteLevelBPETokenizer()
tokenizer.train(
files=["corpus.txt"],
vocab_size=50257,
min_frequency=2,
special_tokens=["<s>", "<pad>", "</s>", "<unk>", "<mask>"]
)
# 保存分词器
tokenizer.save_model("./bpe_tokenizer")2. 大模型预训练技术
2.1 大规模预训练方法
数据并行训练
方法原理:
- 将数据分割到多个设备上
- 每个设备处理不同的数据批次
- 通过梯度同步保持模型参数一致
实现方式:
- DataParallel (DP):单进程多GPU
- DistributedDataParallel (DDP):多进程多GPU
代码示例:
python
import torch
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
# 初始化分布式环境
dist.init_process_group("nccl")
local_rank = dist.get_rank()
# 移动模型到指定设备
model = model.to(local_rank)
# 包装为DDP模型
ddp_model = DDP(model, device_ids=[local_rank], output_device=local_rank)模型并行训练
方法原理:
- 按层分割模型到不同设备
- 流水线并行 (Pipeline Parallelism):按层划分
- 张量并行 (Tensor Parallelism):按张量维度划分
- 专家并行 (MoE, Mixture of Experts):将模型分为多个专家网络
主流框架:
- DeepSpeed
- Megatron-LM
- Colossal-AI
- GShard
2.2 高效预训练优化
混合精度训练
python
from torch.cuda.amp import autocast, GradScaler
# 创建梯度缩放器
scaler = GradScaler()
# 前向传播使用混合精度
with autocast():
outputs = model(input_ids)
loss = criterion(outputs, labels)
# 反向传播使用梯度缩放
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()梯度累积
python
gradient_accumulation_steps = 4
for i, batch in enumerate(dataloader):
with autocast():
outputs = model(**batch)
loss = outputs.loss / gradient_accumulation_steps
scaler.scale(loss).backward()
if (i + 1) % gradient_accumulation_steps == 0:
scaler.step(optimizer)
scaler.update()
optimizer.zero_grad()学习率调度
python
from transformers import get_linear_schedule_with_warmup
# 总训练步数
total_steps = len(dataloader) * epochs // gradient_accumulation_steps
# 学习率调度器
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=0.1 * total_steps, # 10%步数用于预热
num_training_steps=total_steps
)
# 每个优化步更新学习率
scheduler.step()3. 微调技术
3.1 监督微调 (SFT)
核心概念:
- 在标注数据集上对预训练模型进行微调
- 调整模型参数以适应特定任务
- 监督微调是最基础的微调方式
微调流程:
- 加载预训练模型
- 准备任务特定的标注数据
- 设置适当的训练参数
- 执行微调过程
- 评估并保存微调后的模型
实现代码:
python
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainer
# 加载预训练模型
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=2 # 二分类任务
)
# 设置训练参数
training_args = TrainingArguments(
output_dir="./results",
learning_rate=2e-5,
per_device_train_batch_size=16,
per_device_eval_batch_size=16,
num_train_epochs=3,
evaluation_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
)
# 创建训练器
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
compute_metrics=compute_metrics,
)
# 执行微调
trainer.train()
# 保存微调后的模型
trainer.save_model("./fine-tuned-model")3.2 参数高效微调 (PEFT)
动机:
- 全参数微调需要大量计算资源
- 存储多个全微调模型成本高
- 过度微调可能导致过拟合
主要PEFT方法:
LoRA (Low-Rank Adaptation)
工作原理:
- 冻结预训练模型权重
- 在关键层添加低秩适应矩阵
- 只训练这些低秩矩阵
实现代码:
python
from peft import get_peft_model, LoraConfig, TaskType
# 配置LoRA
peft_config = LoraConfig(
task_type=TaskType.SEQ_CLS,
inference_mode=False,
r=8, # 秩
lora_alpha=32,
lora_dropout=0.1,
target_modules=["query", "key", "value"] # 目标层
)
# 应用LoRA到模型
model = AutoModelForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=2)
peft_model = get_peft_model(model, peft_config)
# 显示可训练参数比例
peft_model.print_trainable_parameters()QLoRA (Quantized LoRA)
工作原理:
- 先对预训练模型进行4-bit或8-bit量化
- 再应用LoRA技术
- 大幅降低内存需求
实现示例:
python
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import get_peft_model, LoraConfig
import bitsandbytes as bnb
# 加载量化模型
model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-v0.1",
load_in_4bit=True,
device_map="auto",
quantization_config={"bnb_4bit_compute_dtype": torch.float16}
)
# 配置QLoRA
peft_config = LoraConfig(
r=64,
lora_alpha=16,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM"
)
# 应用QLoRA
model = get_peft_model(model, peft_config)Prefix Tuning
工作原理:
- 在输入序列前添加可训练的前缀向量
- 冻结模型其余参数
- 适合生成式任务
Prompt Tuning
工作原理:
- 学习任务特定的软提示嵌入
- 通过注意力机制引导模型输出
- 计算效率高
3.3 指令微调 (Instruction Tuning)
核心概念:
- 使用格式化的指令数据进行微调
- 指令包含任务描述和期望输出
- 增强模型的泛化能力和零样本性能
指令格式示例:
{"instruction": "总结以下文章", "input": "[文章内容]", "output": "[摘要内容]"}实现代码:
python
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
from datasets import load_dataset
# 加载模型和分词器
model = AutoModelForSeq2SeqLM.from_pretrained("t5-base")
tokenizer = AutoTokenizer.from_pretrained("t5-base")
# 准备指令数据
def preprocess_function(examples):
inputs = ["指令: " + ex["instruction"] + "\n输入: " + ex["input"] for ex in examples]
targets = [ex["output"] for ex in examples]
model_inputs = tokenizer(inputs, max_length=512, truncation=True)
with tokenizer.as_target_tokenizer():
labels = tokenizer(targets, max_length=128, truncation=True)
model_inputs["labels"] = labels["input_ids"]
return model_inputs
# 应用预处理
dataset = load_dataset("instruction_dataset")
processed_datasets = dataset.map(preprocess_function, batched=True)
# 执行指令微调(与标准微调类似)3.4 人类反馈强化学习 (RLHF)
核心流程:
- 监督微调:在高质量数据集上微调模型
- 奖励模型训练:收集人类偏好数据,训练奖励模型
- 强化学习微调:使用奖励模型作为反馈,通过PPO等算法微调模型
实现框架:
python
# 简化的RLHF流程示例
from trl import PPOTrainer, PPOConfig
# 配置PPO
config = PPOConfig(
model_name="gpt2",
learning_rate=1.41e-5,
log_with="wandb"
)
# 创建PPO训练器
ppo_trainer = PPOTrainer(
model=model,
ref_model=ref_model, # 参考模型
tokenizer=tokenizer,
args=config,
)
# 执行RL训练
for batch in dataset:
# 生成响应
response = ppo_trainer.generate(batch["query"])
# 计算奖励(使用奖励模型)
rewards = reward_model.compute_rewards(batch["query"], response)
# 更新模型
train_stats = ppo_trainer.step(batch["query"], response, rewards)4. 微调最佳实践
4.1 超参数选择
关键超参数:
| 超参数 | 推荐范围 | 影响 | 调整策略 |
|---|---|---|---|
| 学习率 | 1e-5 ~ 5e-5 | 模型更新步长 | 小数据集用小学习率 |
| 批次大小 | 8 ~ 64 | 训练稳定性 | 受GPU内存限制 |
| 训练轮数 | 2 ~ 10 | 过拟合风险 | 使用早停机制 |
| 权重衰减 | 0.01 | 正则化强度 | 防止过拟合 |
| 学习率调度 | 线性预热 | 训练稳定性 | 预热10%的步数 |
最佳实践:
- 从较小的学习率开始尝试
- 使用验证集监控性能
- 采用早停避免过拟合
- 对不同任务进行超参数搜索
4.2 数据增强技术
常用数据增强方法:
- 回译:通过翻译生成变体
- 同义词替换:替换部分词为同义词
- 随机插入/删除:修改句子结构
- 噪声注入:添加适当的噪声
实现示例:
python
def synonym_replacement(sentence, n=1):
words = sentence.split()
new_words = words.copy()
random_word_list = list(set([word for word in words if word in wordnet.words()]))
random.shuffle(random_word_list)
num_replaced = 0
for random_word in random_word_list:
synonyms = get_synonyms(random_word)
if len(synonyms) >= 1:
synonym = random.choice(list(synonyms))
new_words = [synonym if word == random_word else word for word in new_words]
num_replaced += 1
if num_replaced >= n:
break
return ' '.join(new_words)4.3 防止过拟合的策略
有效方法:
- Dropout:在微调中保持dropout层激活
- 数据洗牌:每轮打乱训练数据顺序
- 早停机制:监控验证性能,停止过拟合
- 正则化:使用L2正则化限制参数规模
- 梯度裁剪:防止梯度爆炸
python
# 早停实现示例
from transformers import EarlyStoppingCallback
callbacks = [
EarlyStoppingCallback(
early_stopping_patience=3, # 连续3轮无改进则停止
early_stopping_threshold=0.01 # 改进阈值
)
]
trainer = Trainer(
callbacks=callbacks,
# 其他参数...
)5. 高级微调技术
5.1 多任务微调
工作原理:
- 同时在多个相关任务上微调模型
- 利用任务间的知识迁移
- 提高模型的泛化能力
实现方式:
- 任务标识前缀
- 共享底层表示,任务特定头部
- 联合损失函数
5.2 持续学习微调
核心挑战:
- 解决灾难性遗忘问题
- 在保留旧知识的同时学习新知识
主要方法:
- 参数隔离:新任务使用专用参数
- 知识蒸馏:保留旧模型的知识
- 弹性权重整合 (EWC):保护重要参数
5.3 领域适应微调
应用场景:
- 将通用模型适应特定领域(如医疗、法律、金融)
- 提高模型在特定领域的性能
实现策略:
- 收集领域特定数据
- 进行领域特定的预训练(继续预训练)
- 在领域特定任务上进行微调
代码示例:
python
# 领域适应继续预训练
from transformers import AutoModel, AutoTokenizer, TrainingArguments
# 加载模型
model = AutoModel.from_pretrained("bert-base-uncased")
# 设置继续预训练的参数
training_args = TrainingArguments(
output_dir="./domain-adapted-model",
learning_rate=5e-5,
per_device_train_batch_size=32,
num_train_epochs=2,
gradient_accumulation_steps=4,
)
# 使用领域特定数据进行继续预训练
trainer.train()6. 微调评估与分析
6.1 评估指标
通用评估指标:
- 准确率/精确率/召回率/F1:分类任务
- BLEU/ROUGE/METEOR:生成任务
- 困惑度 (PPL):语言建模评估
- 人类评估:主观质量评价
6.2 错误分析
分析方法:
- 错误案例聚类
- 混淆矩阵分析
- 模型行为可视化
- 对抗性测试
工具推荐:
- SHAP/LIME:模型解释工具
- Weights & Biases:实验跟踪
- TensorBoard:训练可视化
6.3 模型解释
解释方法:
- 特征重要性分析:识别影响预测的关键特征
- 注意力可视化:展示模型关注的文本部分
- 逐层分析:理解不同层的信息处理
- 反事实解释:分析输入变化对输出的影响
7. 实践建议
7.1 微调资源需求
计算资源规划:
- 全参数微调:需要多GPU或高性能GPU
- 参数高效微调:单个GPU可能足够
- 存储需求:预训练模型通常1-几十GB
- 内存优化:梯度检查点、混合精度训练
7.2 常见问题与解决方案
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 过拟合 | 数据量小、模型大 | 早停、正则化、数据增强 |
| 欠拟合 | 学习率低、训练轮数少 | 增加学习率、增加训练轮数 |
| 训练不稳定 | 批次大小小、学习率高 | 梯度裁剪、学习率预热 |
| OOM错误 | GPU内存不足 | 梯度累积、混合精度、量化 |
| 效果不佳 | 数据质量差、超参数不合适 | 数据清洗、超参数搜索 |
7.3 微调工作流
推荐工作流:
- 准备数据:收集、清洗、格式化
- 基线评估:用预训练模型评估初始性能
- 选择微调策略:全参数、PEFT等
- 超参数调优:在小数据集上测试
- 执行微调:监控训练过程
- 全面评估:在测试集上评估性能
- 部署优化:模型量化、加速等
8. 前沿研究方向
8.1 无监督微调
- 不需要标注数据的微调方法
- 利用自监督学习目标
- 降低标注成本
8.2 联邦微调
- 在用户设备上进行隐私保护的微调
- 聚合本地更新而不共享原始数据
- 提高模型在边缘设备上的性能
8.3 终身学习
- 模型持续学习新任务而不遗忘旧知识
- 动态适应新的数据分布
- 实现模型的长期演进
预训练和微调技术是大模型开发的核心。理解这些技术不仅有助于使用现有模型,也为未来开发更高效、更强大的模型奠定基础。