Skip to content

大模型的预训练与微调

预训练和微调是大模型开发的两个核心阶段。预训练使模型从海量数据中学习通用知识,微调则使模型适应特定任务。本章节详细介绍这两个阶段的关键技术。

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)

核心概念

  • 在标注数据集上对预训练模型进行微调
  • 调整模型参数以适应特定任务
  • 监督微调是最基础的微调方式

微调流程

  1. 加载预训练模型
  2. 准备任务特定的标注数据
  3. 设置适当的训练参数
  4. 执行微调过程
  5. 评估并保存微调后的模型

实现代码

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)

核心流程

  1. 监督微调:在高质量数据集上微调模型
  2. 奖励模型训练:收集人类偏好数据,训练奖励模型
  3. 强化学习微调:使用奖励模型作为反馈,通过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 领域适应微调

应用场景

  • 将通用模型适应特定领域(如医疗、法律、金融)
  • 提高模型在特定领域的性能

实现策略

  1. 收集领域特定数据
  2. 进行领域特定的预训练(继续预训练)
  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 微调工作流

推荐工作流

  1. 准备数据:收集、清洗、格式化
  2. 基线评估:用预训练模型评估初始性能
  3. 选择微调策略:全参数、PEFT等
  4. 超参数调优:在小数据集上测试
  5. 执行微调:监控训练过程
  6. 全面评估:在测试集上评估性能
  7. 部署优化:模型量化、加速等

8. 前沿研究方向

8.1 无监督微调

  • 不需要标注数据的微调方法
  • 利用自监督学习目标
  • 降低标注成本

8.2 联邦微调

  • 在用户设备上进行隐私保护的微调
  • 聚合本地更新而不共享原始数据
  • 提高模型在边缘设备上的性能

8.3 终身学习

  • 模型持续学习新任务而不遗忘旧知识
  • 动态适应新的数据分布
  • 实现模型的长期演进

预训练和微调技术是大模型开发的核心。理解这些技术不仅有助于使用现有模型,也为未来开发更高效、更强大的模型奠定基础。