大模型微调与强化学习训练实战操作手册

在这里插入图片描述


第一部分:PEFT微调核心技术与参数详解

本部分将详细拆解参数高效微调(PEFT)的核心流程,重点解析LoRA/QLoRA的关键参数设置及其背后的数学与工程意义。

1.1 LoRA核心参数详解

在使用LoRA(Low-Rank Adaptation)进行微调时,以下参数直接决定了模型的训练效果和显存占用。

1. lora_rank ®
  • 定义:低秩矩阵的秩(Rank)。它决定了LoRA适配器中可训练参数的数量。LoRA将一个大的权重更新矩阵 ΔW\Delta WΔW 分解为两个小矩阵 AAABBB 的乘积,即 ΔW=B×A\Delta W = B \times AΔW=B×A。其中 BBB 的维度是 d×rd \times rd×rAAA 的维度是 r×dr \times dr×d
  • 意义r 越大,模型的可塑性越强,能学习到的特征越复杂,但显存占用和训练时间也会增加,且过大容易导致过拟合。
  • 推荐范围
    • 通用任务816。对于大多数指令微调任务,这个范围已经足够捕捉所需的模式。
    • 复杂推理/数学/代码任务3264 甚至 128。当任务需要模型学习全新的逻辑或大量新知识时,需要更高的秩。
    • 极简任务(如简单分类):48
2. lora_alpha
  • 定义:LoRA的缩放系数(Scaling Factor)。在训练过程中,LoRA更新的权重会乘以一个缩放因子 αr\frac{\alpha}{r}rα 加到原始权重上。
  • 意义:它控制了LoRA新增权重对原始模型权重的影响程度。alpha 越大,LoRA学到的特征在最终推理中占的比重就越大。
  • 推荐设置
    • 黄金法则:通常设置为 lora_rank2倍。例如,如果 r=16,则 alpha=32;如果 r=64,则 alpha=128
    • 原理:这种设置(α=2r\alpha = 2rα=2r)在实践中被证明能提供最稳定的梯度更新,类似于一种自适应的学习率调整。
3. lora_dropout
  • 定义:在LoRA层中应用的Dropout概率。
  • 意义:防止过拟合。在训练过程中随机“丢弃”一部分神经元,迫使模型学习更鲁棒的特征。
  • 推荐范围
    • 数据量较少(<1万条):0.10.05
    • 数据量充足(>10万条):0(可以关闭以加快收敛)或 0.05
4. target_modules
  • 定义:指定模型中哪些层需要应用LoRA适配器。
  • 意义:决定了微调的“深度”和“广度”。
  • 推荐设置
    • 全量微调效果(推荐)["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]。即对Attention层(Q, K, V, O)和MLP层(Gate, Up, Down)全部添加LoRA。这被称为“全模块LoRA”,效果最接近全参数微调。
    • 节省显存:仅对 ["q_proj", "v_proj"] 添加。这是最早LoRA论文中的默认设置,显存占用最小,但效果不如全模块。

1.2 训练超参数详解

无论使用哪种工具,以下训练参数都是通用的,且至关重要。

1. learning_rate (学习率)
  • 意义:控制模型参数更新的步长。
  • 推荐范围
    • LoRA/QLoRA1e-42e-4。LoRA通常需要比全参数微调(通常 1e-5)更大的学习率。
    • 全参数微调1e-55e-5
2. num_train_epochs (训练轮数)
  • 意义:数据集被完整遍历的次数。
  • 推荐范围
    • 指令微调(SFT)13 个Epoch。大模型很容易过拟合,通常1-2个Epoch即可,超过3个Epoch往往会导致模型丧失通用能力(Catastrophic Forgetting)。
3. per_device_train_batch_size & gradient_accumulation_steps
  • 定义
    • batch_size:单次前向传播的数据条数。
    • accumulation_steps:梯度累积步数。
  • 实际Batch Size = batch_size * accumulation_steps * GPU数量
  • 推荐设置
    • 目标:使实际Batch Size达到 64128
    • 显存受限时:设小 batch_size(如1或2),调大 accumulation_steps(如16或32)。
4. warmup_ratio (预热比例)
  • 意义:在训练初期,学习率从0线性增加到设定的最大值,以防止训练初期梯度过大导致模型崩溃。
  • 推荐范围0.03 (3%) 或 0.05 (5%)。

第二部分:LLaMA-Factory 实战操作指南

LLaMA-Factory 是目前最适合新手的“一站式”微调工具,其WebUI界面覆盖了从数据处理到模型导出的全流程。

2.1 环境准备与启动

  1. 克隆仓库
    git clone https://github.com/hiyouga/LLaMA-Factory.git
    cd LLaMA-Factory
    pip install -e .[metrics]
    
  2. 启动WebUI
    llamafactory-cli webui
    
    启动后,浏览器访问 http://localhost:7860

2.2 WebUI 操作流程详解

步骤一:模型选择 (Model Selection)
  • Model Name: 选择基础模型系列,如 LLaMA-3-8B
  • Model Path: 填写模型在本地的绝对路径,或者Hugging Face的模型ID(如 meta-llama/Meta-Llama-3-8B)。
  • Finishing Method: 选择 LoRA。如果显存极度受限(如<16GB),勾选 Use QLoRA(这将自动启用4-bit量化加载)。
步骤二:数据集配置 (Dataset)
  • Dataset: 在下拉菜单中选择内置数据集(如 identity, alpaca_en),或选择自定义数据集。
    • 自定义数据格式:在 data/dataset_info.json 中注册你的数据文件。标准格式为JSON列表:
      [
        {"instruction": "用户指令", "input": "可选输入", "output": "期望回答"},
        ...
      ]
      
  • Cutoff Length: 截断长度。推荐 10242048。如果显存允许且任务涉及长文本,可设为 4096
步骤三:训练参数设置 (Train)

这是最关键的配置面板,请严格按照以下推荐设置:

  • Stage: 选择 Supervised Finetuning (SFT)。
  • Learning Rate: 输入 2e-4
  • Epochs: 输入 3.0
  • Batch Size: 根据显存调整。24GB显存推荐 48
  • Gradient Accumulation: 配合Batch Size,使乘积接近 64。例如Batch Size为4,则此项设为 16
  • LR Scheduler: 选择 cosine(余弦退火),这是目前最主流的学习率调度策略。
  • Max Samples: 用于调试,设为 1000 可快速跑通流程;正式训练留空(使用全部数据)。
步骤四:LoRA参数配置 (LoRA)

点击 “Advanced Configuration” 展开:

  • LoRA Rank: 16
  • LoRA Alpha: 32
  • LoRA Dropout: 0.05
  • Target Modules: 默认通常是全选(All),或者手动输入 q_proj,v_proj,k_proj,o_proj,gate_proj,up_proj,down_proj
步骤五:启动训练
  1. 点击 Preview Command:查看生成的命令行脚本,确认参数无误。
  2. 点击 Start:开始训练。
  3. 监控:右侧会实时显示 Loss 曲线。正常的曲线应呈现平滑下降趋势。如果Loss震荡剧烈,尝试减小学习率;如果Loss下降极慢,尝试增大学习率。
步骤六:模型导出 (Export)

训练完成后,切换到 Export 选项卡:

  • Model Path: 自动填充为刚才训练的基础模型。
  • Adapter Path: 选择刚才训练保存的Checkpoint路径。
  • Export Dir: 设置合并后模型的保存路径。
  • Max Shard Size: 设置分块大小,推荐 2GB5GB
  • 点击 Export,工具会将LoRA权重与基础模型合并,生成一个完整的、可直接加载的模型文件。

第三部分:强化学习微调(DPO/RLHF)详解

在完成SFT(有监督微调)后,模型虽然学会了说话,但可能还不够“听话”或“安全”。强化学习微调(尤其是DPO)是让模型对齐人类偏好的关键步骤。

3.1 DPO (Direct Preference Optimization) 核心参数

DPO不需要训练奖励模型(Reward Model),直接使用偏好数据(Chosen vs Rejected)进行优化。其核心参数如下:

1. beta (DPO Beta)
  • 定义:KL散度惩罚系数。它控制了微调后的模型与原始参考模型(Reference Model)之间的差异程度。
  • 意义
    • beta 越大,模型越保守,越倾向于保持原始模型的行为,生成的文本多样性越低。
    • beta 越小,模型越激进,越倾向于优化偏好数据的差异,但也更容易过拟合或产生“胡言乱语”。
  • 推荐范围
    • 标准设置0.1。这是绝大多数DPO论文和实践中的默认值,是一个稳健的起点。
    • 激进优化0.05。如果你希望模型非常强烈地偏向“Chosen”的回答风格,可以尝试减小此值。
    • 保守优化0.2 - 0.5。如果发现模型训练后开始输出乱码或重复内容,增大此值以加强约束。
2. learning_rate (DPO学习率)
  • 特别注意:DPO的学习率必须比SFT阶段 低一个数量级
  • 推荐范围5e-61e-6。如果SFT用的是 2e-4,DPO千万不要用这么大,否则模型会瞬间崩溃。
3. 数据格式 (Preference Dataset)

DPO需要特定的“三元组”数据格式:

  • Prompt: 提示词。
  • Chosen: 人类倾向的回答(胜者)。
  • Rejected: 人类拒绝的回答(败者)。
  • 关键点:Chosen和Rejected的回答必须是针对同一个Prompt的,且两者要有明显的质量差异。

第四部分:Unsloth 代码实战指南

Unsloth 是追求极致效率的开发者的首选。以下是一个完整的Python脚本实战,涵盖从加载模型到DPO训练的全流程。

4.1 环境安装

pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps "xformers<0.0.26" "trl<0.9.0" peft accelerate bitsandbytes

4.2 SFT微调实战代码

from unsloth import FastLanguageModel
import torch
from trl import SFTTrainer
from transformers import TrainingArguments

# 1. 加载模型与Tokenizer
# max_seq_length: 设置为2048或4096,根据显存决定
# load_in_4bit: True 开启4bit量化,显存占用极低
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/llama-3-8b-bnb-4bit",
    max_seq_length = 2048,
    dtype = None, # 自动检测硬件支持的数据类型(float16或bfloat16)
    load_in_4bit = True,
)

# 2. 配置LoRA适配器
# 这里是Unsloth特有的API,比官方PEFT库更简洁
model = FastLanguageModel.get_peft_model(
    model,
    r = 16,              # LoRA Rank
    target_modules = ["q_proj", "k_proj", "v_proj", "o_proj",
                      "gate_proj", "up_proj", "down_proj"], # 全模块微调
    lora_alpha = 32,     # 建议为Rank的2倍
    lora_dropout = 0,    # Unsloth建议设为0以优化速度
    bias = "none",       # 不训练偏置项
    use_gradient_checkpointing = True, # 开启梯度检查点,节省显存
    random_state = 3407,
)

# 3. 准备数据集 (Alpaca格式)
# 假设你已经加载了dataset对象
alpaca_prompt = """Below is an instruction that describes a task...
### Instruction:
{}
### Input:
{}
### Response:
{}"""

def formatting_prompts_func(examples):
    instructions = examples["instruction"]
    inputs       = examples["input"]
    outputs      = examples["output"]
    texts = []
    for instruction, input, output in zip(instructions, inputs, outputs):
        text = alpaca_prompt.format(instruction, input, output) + tokenizer.eos_token
        texts.append(text)
    return { "text" : texts, }

# 4. 配置训练参数
training_args = TrainingArguments(
    per_device_train_batch_size = 2,
    gradient_accumulation_steps = 4,
    warmup_steps = 5,
    max_steps = 60, # 测试用,正式训练建议用num_train_epochs = 1
    learning_rate = 2e-4,
    fp16 = not torch.cuda.is_bf16_supported(),
    bf16 = torch.cuda.is_bf16_supported(),
    logging_steps = 1,
    optim = "adamw_8bit", # 使用8bit优化器进一步节省显存
    weight_decay = 0.01,
    lr_scheduler_type = "linear",
    seed = 3407,
    output_dir = "outputs",
)

# 5. 开始训练
trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = dataset,
    dataset_text_field = "text",
    max_seq_length = 2048,
    dataset_num_proc = 2,
    packing = False, # 设为True可以将短数据拼接,加速训练
    args = training_args,
)

trainer.train()

4.3 DPO微调实战代码

在SFT训练完成后,我们可以继续进行DPO训练。注意,DPO需要加载两个模型:一个是待训练的模型(Policy Model),一个是参考模型(Reference Model)。Unsloth通过一种巧妙的方式(Patching)避免了显式加载两个模型,从而节省了一半显存。

from unsloth import PatchDPOTrainer
from trl import DPOTrainer

# 1. 应用DPO补丁
PatchDPOTrainer()

# 2. 加载刚刚SFT微调好的模型
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "outputs/checkpoint-60", # SFT的产物
    max_seq_length = 2048,
    load_in_4bit = True,
)

# 3. 配置DPO训练参数
dpo_trainer = DPOTrainer(
    model = model,
    ref_model = None, # Unsloth会自动处理Reference Model,无需手动加载
    tokenizer = tokenizer,
    beta = 0.1,       # DPO核心参数:KL惩罚系数
    train_dataset = dpo_dataset, # 必须包含 prompt, chosen, rejected 三列
    max_length = 1024,
    max_prompt_length = 512,
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 8,
        learning_rate = 5e-6, # 注意:比SFT低很多
        max_steps = 100,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_first_step = True,
        optim = "adamw_8bit",
        output_dir = "dpo_outputs",
    ),
)

dpo_trainer.train()

第五部分:常见问题与避坑指南

5.1 Loss不下降怎么办?

  • 检查学习率:如果是SFT,尝试从 2e-4 降到 1e-45e-5。如果是DPO,确保学习率在 1e-6 级别。
  • 检查数据质量:数据中是否存在大量重复、空白或格式错误的样本。
  • 检查Prompt格式:确保训练时的Prompt模板与推理时完全一致,包括换行符和特殊token。

5.2 显存爆了(OOM)怎么办?

  1. 开启4-bit量化:这是最立竿见影的方法(load_in_4bit=True)。
  2. 减小Batch Size:将 per_device_train_batch_size 设为1,同时成倍增加 gradient_accumulation_steps
  3. 开启梯度检查点gradient_checkpointing=True。这会以时间换空间,显著降低显存,但训练速度会慢20%左右。
  4. 减少LoRA Rank:将 r 从64降到16或8。

5.3 训练后的模型只会复读怎么办?

  • 原因:通常是过拟合,或者EOS Token(结束符)没有正确学习。
  • 解决
    • 检查数据集中每条样本的末尾是否都添加了 <|end_of_text|> 或对应的EOS Token。
    • 减少Epoch数。
    • 增大 lora_dropout

结语:大模型微调是一门实验科学。本文档提供的参数范围是基于广泛实践的“经验值”,在实际操作中,建议您先使用小批量数据(如500条)进行快速实验,观察Loss曲线和生成效果,再确定最终的全量训练方案。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐