大模型全参数训练与微调
大模型全参数训练
常见的大模型全参数训练方法
| 方法 | 阶段 | 需要什么模型 | 数据需求 | 显存 | 稳定性 | 代表 |
|---|---|---|---|---|---|---|
| Pre-training | 基础 | 1 个(基座) | 万亿 token 文本 | 极高 | 稳定 | 所有 LLM |
| SFT | 对齐 | 1 个(基座) | 1~100 万条 QA 对 | 高 | 稳定 | 所有 LLM |
|
RLHF (PPO、GRPO) |
对齐 | 4 个 | 数万偏好对 + 在线采样 | 极高 | ⚠️ 难调 | ChatGPT, Claude |
| DPO | 对齐 | 2 个 | 数万偏好对(离线) | 中等 | ✅ 稳定 | Llama 2/3, Qwen |
| ORPO | 对齐 | 1 个 | 偏好对(和 DPO 一样) | ≈ SFT | ✅ 稳定 | 学术研究 |
| GRPO | 对齐 | 1 个 | 规则打分 + 组采样 | 中高 | ✅ 较稳定 | DeepSeek-R1 |
| SimPO | 对齐 | 2 个 | 偏好对 | 中等 | ✅ 稳定 | 学术研究 |
| KTO | 对齐 | 2 个 | 单条偏好(不需要成对!) | 中等 | ✅ 稳定 | 学术研究 |
TRL
TRL(Transformer Reinforcement Learning)是 HuggingFace 官方推出的训练库。它把 SFT、DPO、RLHF、GRPO 等训练方法封装成统一的 Trainer 接口,和 HuggingFace 的 Trainer API 一模一样——你会用 HuggingFace 的 Trainer,就会用 TRL。在 TRL 出现之前,做 RLHF 需要自己写 Reward Model 训练、PPO 采样循环、KL 惩罚计算……代码量几千行起。TRL 把这些全部封装好,只需定义配置 → 传入模型和数据 → 一行 trainer.train()。
TRL 提供的五个核心 Trainer,对应五种训练范式:
| TRL Trainer | 对应方法 | 一句话描述 | 数据需求 |
|---|---|---|---|
| SFTTrainer | SFT 指令微调 | 用 QA 对话数据训练模型学会"问答"格式 | 指令→回答 对 |
| RewardTrainer | Reward Model 训练 | 训练一个打分模型(RLHF 第二步需要) | 两个回答的偏好标注 |
| PPOTrainer | RLHF (PPO) | 用强化学习让模型输出更符合人类偏好 | prompt 列表 + 已训好的 Reward Model |
| DPOTrainer | DPO 直接偏好优化 | 无需 Reward Model,直接用偏好数据优化 | (prompt, win, lose) 三元组 |
| GRPOTrainer | GRPO 组内相对优化 | 用规则打分,组内比较,自进化(DeepSeek-R1) | prompt 列表 + 打分函数 |
大模型训练 = 预训练(认字)→ SFT(学说话)→ 偏好对齐(学品位)。每阶段都有统一的 TRL Trainer 接口,本文重点介绍后两个阶段。
- 阶段 1「预训练」→ 成本占 99%+,用 Next Token Prediction,不在本文范围
- 阶段 2「SFT」→ SFTTrainer,几万条数据,几小时
- 阶段 3「偏好对齐」→ DPOTrainer / PPOTrainer / GRPOTrainer,几千~几万条偏好数据
完整训练链路(从 0 到 ChatGPT 级别的对话模型):
Pre-training
目标函数:Next Token Prediction
Loss = -log P( tokent | token0, token1, ..., tokent-1 )
模型看到的永远是"前文",要预测的永远是"下一个词"。这个目标极其简单但极度高效——互联网上的每一段文字天然就是这个任务的训练数据,不需要人工标注。
SFT
SFT 用高质量的"问题→答案"对训练模型,让它学会:当用户给出某种格式的输入时,应该以某种格式输出。
SFT的数据格式
基于TRL的SFT训练脚本:
RLHF
RLHF 让模型学会"什么回答更让人满意"。三步走:SFT 打底 → 训练 Reward Model 打分 → PPO 强化学习。TRL 对应:RewardTrainer + PPOTrainer。后来 DeepSeek-R1 提出了 GRPO,省掉 Value Model(Critic),用组内归一化替代 V(s) 估计。reward 来源可以是 RM 或规则函数,TRL 对应:GRPOTrainer。
RLHF 三步走(PPO 路线)
四模型在代码中的对应关系
| 模型 | 代码中的变量名 | 加载方式 | 冻结/训练 | 做什么 | 输入→输出 |
|---|---|---|---|---|---|
| Policy Model | model |
AutoModelForCausalLMWithValueHead |
✅ 训练(唯一被 optimizer 更新的) | 生成回答 + 更新策略 | prompt → 回答文本 |
| Reference Model | ref_model |
AutoModelForCausalLMWithValueHead |
❌ 冻结 | 计算 KL 惩罚的基准线。PPO 目标是"偏离 ref 但别太远" | 回答 → log 概率 |
| Reward Model | reward_model |
AutoModelForSequenceClassification |
❌ 冻结(Step 1 已训好) | 给回答打分——模拟人类偏好 | (prompt, 回答) → 1 个标量分数 |
| Value Model (Critic) | model.v_head |
藏在 Policy Model 内部 | ✅ 训练(和 Policy 共享 backbone) | 估计 V(s)——"走到这一步,最终能拿多少 reward" | hidden state → 1 个标量 |
一个训练步的完整数据流
Step 1: 训练 Reward Model(培养裁判)
Reward Model 的任务:给定一个回答,预测人类会给它打多少分。训练数据格式是 (prompt, 好的回答, 差的回答)——人类标注员只需要说"A 比 B 好",不需要给出具体分数。
Step 2: PPO 强化学习(用裁判训练选手)
有了 Reward Model 作为裁判,现在可以用 PPO 来优化模型:多生成 Reward 高的回答,但同时用 KL 惩罚限制模型不要偏离 SFT 太远。
GRPO路线
GRPO 省掉的是 Value Model(Critic),不是 Reward Model。奖励信号仍然需要——可以来自 RM 模型、Rule-based 函数、或两者混合。核心创新是:同一个 prompt 生成 N 个回答,用组内均值和标准差做 baseline 来计算 advantage,从而不再需要训练一个单独的 Critic 网络来估计 value。
- PPO 的 advantage: reward - V(s),其中 V(s) 需要 Value Model 来估计
- GRPO 的 advantage: (reward - group_mean) / group_std,组内归一化替代 Value Model
PPO 最大的痛点是需要同时维护 4 个模型,其中 Value Model 就是专门用来估计"当前状态有多好"的 Critic 网络。GRPO 用一个巧妙的方法绕过了它:
🔑 关键澄清
GRPO 的 reward 可以来自多种来源:
① RM 模型 — 用 Stage 2 训练的 Reward Model 打分(和 PPO 一样!)
② Rule-based 函数 — 用 Python 写的规则打分(长度、格式、答案对错等)
③ 混合 — RM + 规则的加权组合
不管 reward 来自哪里,GRPO 都不需要 Value Model——这是它和 PPO 的唯一但关键的区别。DeepSeek-R1 论文的原始 GRPO 用法是 Rule-based reward(数学题用答案对错,代码题用测试通过率),但实际工程中 RM+规则混合也非常常见。
DPO
DPO 是 RLHF 的简化版——不需要 Reward Model,直接用偏好数据训练。数学上把 RLHF 的目标改写成了分类问题。TRL 对应:DPOTrainer。这是工业界当前性价比最高的对齐方案。
- 输入: (prompt, 好回答, 差回答) 三元组
- 目标: 让模型对"好回答"的概率 > "差回答"的概率
- 优势: 只需 2 个模型(Policy + Ref),显存是 PPO 的一半
输入案例:
DPO 的核心数学发现是:RLHF 的 PPO 目标函数中,Reward Model 是可以被"消掉"的。"好回答 vs 差回答"的相对关系,可以直接从模型对两个回答的概率比中推断出来。
LossDPO = -log σ( β · (log[πθ(ywin)/πref(ywin)] - log[πθ(ylose)/πref(ylose)]) )
🔑 DPO 的 beta 参数怎么调?
beta 控制模型可以偏离 Reference Model 多远。
· beta=0.01 → 几乎不偏离 → 训练后效果和 SFT 差不多 → 太小了
· beta=0.1 → 适中,工业界推荐起步值 → 大多数场景的最佳选择
· beta=1.0 → 大幅偏离 → 可能过拟合偏好数据中的噪声 → 太大了
调试方法:从 0.1 开始,观察训练 loss。loss 平稳降到 0.5~0.7 是理想区间。如果 loss 急剧降到 0.3 以下,说明 beta 太小。
四种训练范式对比
| 维度 | SFT | DPO | PPO (RLHF) | GRPO |
|---|---|---|---|---|
| TRL 工具 | SFTTrainer | DPOTrainer | RewardTrainer + PPOTrainer | GRPOTrainer |
| 需要几个模型 | 1 个 | 2 个(Policy+Ref) | 4 个(+Reward+Value) | 3 个(+Ref+RM可选) |
| 显存 (7B, BF16) | ~40GB | ~60GB | ~200GB | ~120GB |
| 数据需求 | QA 对话对 | 偏好对(离线) | 偏好对 + 在线采样 | prompt + 打分函数(RM/规则) |
| 需要人工标注 | ✅ 需要 | ✅ 需要 | ✅ 需要 | RM-based 需要 / 规则不需要 |
| 训练稳定性 | ✅ 极稳定 | ✅ 稳定 | ⚠️ 难调 | ⚠️ 中等 |
| 效果上限 | 基础 | 接近 PPO | 最高 | 推理任务最强 |
| 最佳场景 | 指令遵循 | 通用对齐 | 极致性能 | 推理/通用(灵活打分) |
方案选择思路
显存计算
SFT 全参数微调
| 显存占用项 | 计算方式 | 大小 |
|---|---|---|
| 模型参数 | 1.5B params × 2 bytes (bf16) | 3.0 GB |
| 梯度 | 同参数 | 3.0 GB |
| AdamW 动量 (m) | 1.5B × 4 bytes (fp32) | 6.0 GB |
| AdamW 方差 (v) | 1.5B × 4 bytes (fp32) | 6.0 GB |
| 中间激活 | batch=2×4=8, seq=512, 28层, d=1536 ≈ 8 × 512 × 1536 × 28 × 2 / 1e9 |
~3.5 GB |
| CUDA context | cuBLAS workspace, NCCL buffer 等 | ~1.0 GB |
| 合计 | ~22.5 GB |
梯度检查点
上面激活的 3.5 GB 是开了 gradient_checkpointing 之后的。不开的话,每层都要存完整的中间激活——28 层的 Transformer 堆下来大约 12~15 GB。开了之后只存 checkpoint 节点的激活,其余的反向传播时重新算——激活降到 3~4 GB,代价是多跑一遍前向(大约慢 20%~30%)。
| 无梯度检查点 | 有梯度检查点 | |
|---|---|---|
| 激活显存 | 12~15 GB | ~3.5 GB |
| 总显存 | 31~34 GB | ~22.5 GB |
| 训练速度 | 基准 | 慢 20%~30% |
RM 奖励模型
RM 训练和 SFT 结构几乎一样——都是全参数,只是输出层从 LM head 变成了一个标量头(1 个 label)。模型参数多加了 1×d_model ≈ 1536 个参数而已,可以忽略不计。
显存和 SFT 几乎一样:~22~25 GB。但 RM 用的是 TRL 的 RewardTrainer,它和 HuggingFace 原生 Trainer 有几个关键区别:
| 维度 | 普通 Trainer | RewardTrainer |
|---|---|---|
| 模型输出 | LM head → logits (vocab_size) | 标量头 → 1 个值(整个序列的总分) |
| loss 函数 | 交叉熵(逐 token 对比 label) | 对比 loss:−log[σ(r_chosen − r_rejected)] |
| 数据格式 | {input_ids, labels} | {input_ids_chosen, input_ids_rejected} |
| 前向传播 | 1 次(一条序列→loss) | 2 次(chosen→r_chosen, rejected→r_rejected→对比 loss) |
| 输入 padding 方向 | 右侧 padding | 无特殊要求,两个序列独立编码 |
| eval 指标 | accuracy / loss | accuracy(chosen 分 > rejected 分的占比)、score margin |
最核心的区别——loss 函数完全不同:普通 Trainer 用的是逐 token 交叉熵(每个 token 预测对不对),RewardTrainer 用的是 Bradley-Terry 对比 loss(整个 chosen 序列的得分是否高于整个 rejected 序列)。前者是"教学"——教模型每个位置该输出什么;后者是"裁判"——教模型学会哪个回答整体更好。
GRPO 强化学习
这个阶段显存消耗猛增——因为要同时加载两个模型:
- 策略模型(Policy):Qwen2.5-1.5B SFT 版,全参数训练状态,~22 GB
- 奖励模型(RM):Stage 2 训出来的,推理模式(不需要梯度/优化器),~3 GB
| 组件 | 模式 | 计算公式 | 大小 |
|---|---|---|---|
| Policy 模型参数 | 训练(bf16) | 1.5B × 2 bytes |
3.0 GB |
| Policy 梯度 | 训练 | 1.5B × 2 bytes |
3.0 GB |
| Policy 优化器状态 | 训练(AdamW fp32) | 1.5B × 8 bytes(m 4B + v 4B) |
12.0 GB |
| Policy 激活 | 含生成阶段(n_gen=4, max_len=256) | batch × seq × d_model × layers × n_gen |
~6.0 GB |
| RM 模型 | 推理(eval, no grad) | 1.5B × 2 bytes(仅权重,无梯度/优化器) |
3.0 GB |
| RM KV cache | 推理时缓存 | 2 × layers × seq × d_model × n_gen |
~1.0 GB |
| 合计 | P×12 + RM_P×2 + KV + activations |
~28.0 GB |
为什么激活比 SFT 大?GRPO 每步不只前向一次——num_generations=4 意味着每个 prompt 要生成 4 个回答。生成的 token 都要存 KV cache 和中间状态。虽然 RM 评分时不回传梯度到 policy,但生成阶段的中间结果要吃显存。
GRPO 的特殊性:
GRPO 不像 SFT 那样"一个 batch 训完就完"——它每个 step 是:
- 生成阶段:Policy 模型生成 num_generations=4 个回答(自回归解码,吃 KV cache)
- 评分阶段:RM 模型对每个回答打分(forward only,不存梯度)
- 训练阶段:计算 GRPO loss,反向传播更新 policy
PPO
GRPO 省掉了 Value Model(Critic),只需要 2 个模型(Policy + Ref)(或在三个Policy + Reference + Reward)。如果用经典 PPO,需要同时加载 4 个模型:Policy + Reference + Reward + Value。下面精确算一下差距。
| 模型 | 精度 | 计算 | 显存 |
|---|---|---|---|
| ① Policy Model(训练中) | bf16 + fp32 opt | 1.5B×2 + 1.5B×2 + 1.5B×8 | ~22 GB |
| ② Reference Model(冻结) | bf16 only | 1.5B×2 | ~3 GB |
| ③ Reward Model(冻结) | bf16 only | 1.5B×2 | ~3 GB |
| ④ Value Model / Critic(训练中) | bf16 + fp32 opt | 和 Policy 共享 backbone,额外 ~0.5 GB | ~0.5 GB |
| 激活值(PPO 在线采样) | — | 每步生成回答 + 四模型前向 | ~8~12 GB |
计算公式
VRAMPPO ≈ P×2 + P×2 + P×8 + (3 × P×2) + activations
= P×16 + activations
其中前三项是 Policy Model 的自身开销(参数 + 梯度 + 优化器),3×P×2 是 Ref、RM、Value 三个冻结/轻量模型的 BF16 权重。
PPO和GRPO对比
PPO 比 GRPO 多吃了 ~12~15 GB,主要来自三个地方:
- RM 模型(~3 GB)— GRPO 的 reward 来自打分函数(Python 代码),不需要加载一个 1.5B 参数的神经网络当裁判
- Value 模型(~0.5 GB + 优化器)— PPO 需要 Critic 网络估计 V(s),而 GRPO 用组内归一化替代:advantage = (reward - group_mean) / group_std
- 更大的激活值(~+4 GB)— PPO 每步要跑 4 个模型的前向,中间结果更多
大模型微调
微调(Fine-Tuning)= 在预训练大模型基础上,用少量领域数据继续训练,让模型学会特定技能。全参数微调需要更新所有参数(费 GPU),PEFT 方法(LoRA/QLoRA)只需训练 0.1%~1% 的参数(单张消费级显卡即可)。LlamaFactory 是目前最流行的开源微调工具。
微调方法分两大类:全参数微调(更新所有参数,土豪专用)和 PEFT 参数高效微调(只更新极少参数,性价比首选)。PEFT 家族中 LoRA 是绝对主流,QLoRA 是其 4-bit 量化升级版。
全参数 SFT 的内存分解(7B 模型,BF16):
| 组件 | 大小 | 说明 |
|---|---|---|
| 模型参数 | 7B × 2 bytes = 14 GB | BF16 存储 |
| 梯度 | 7B × 2 bytes = 14 GB | 每个参数一个梯度 |
| Adam 优化器 (m+v) | 7B × 8 bytes = 56 GB | FP32 存储,最大的内存消耗者 |
| 激活值 (batch=1, seq=2048) | ~10 GB | 开启 gradient checkpointing 可降到 ~2 GB |
| 总计 | ~94 GB | 需要 4+A100-80G |
PEFT — 参数高效微调家族
| 方法 | 核心思路 | 可训练参数比率 | 效果 | 主流程度 |
|---|---|---|---|---|
| LoRA | 在注意力层旁加两个小矩阵 B·A,只训练这两个矩阵 | 0.1%~1% | 接近全参数 | ⭐⭐⭐⭐⭐ |
| QLoRA | LoRA + 基座 4-bit 量化,显存再砍一半 | 0.1%~1% | 接近 LoRA | ⭐⭐⭐⭐⭐ |
| Adapter | 在每层 Transformer 后插入小型神经网络 | 1%~5% | 中等 | ⭐⭐ |
| Prefix Tuning | 在输入前加可训练的"前缀 token" | <0.1% | 中下 | ⭐ |
| P-Tuning v2 | 在每层都加可训练 prompt | 0.1%~3% | 中等 | ⭐ |
| IA³ | 只训练三个缩放向量 | <0.01% | 中等 | ⭐ |
LoRA 原理速览
LoRA 的核心假设:微调时模型权重的变化量 ΔW 可以分解为两个低秩矩阵的乘积:
ΔW = B · A (B ∈ Rd×r, A ∈ Rr×d)
前向传播时:h = W₀·x + (α/r)·B·A·x。W₀ 冻结,只训练 B 和 A。r 通常取 8~64,α 通常取 2×r。推理时可以把 B·A 合并回 W₀,零额外开销。
QLoRA 在上面基础上,把 W₀ 用 4-bit NormalFloat 量化存储,前向时反量化为 BF16 计算。基座模型显存从 14GB(BF16)降到 ~4GB(4-bit)。
LlamaFactory + LoRA 实例
LlamaFactory — 一站式微调利器
LlamaFactory 是 GitHub 上最火的大模型微调工具(30K+ Stars),支持 100+ 种模型和十几种微调方法。提供 WebUI(点鼠标)和 CLI(命令行)两种方式。安装简单,一行 pip install llamafactory 即可。
安装 LlamaFactory
LlamaFactory 的两种使用方式
| 方式 | 命令 | 适合谁 |
|---|---|---|
| WebUI | llamafactory-cli webui |
新手、快速实验、可视化调参 |
| CLI 命令行 | llamafactory-cli train config.yaml |
批量实验、脚本化、服务器后台运行 |
LoRA 微调
LoRA 微调 = 冻结基座模型 + 在注意力层加 adapter。LlamaFactory 预配置了最优参数,你只需要调整 rank、alpha、学习率三个值即可。训练产物只有 adapter 文件(几十 MB),可以挂载到任意同架构的基座模型上。
LoRA 的核心配置参数
| 参数 | 含义 | 推荐值 | 说明 |
|---|---|---|---|
| r (rank) | 低秩分解的秩 | 8~16 | r 越大容量越大但参数量也越大。r=8 适合 1 万条以内,r=16 适合更大数据量 |
| lora_alpha | 缩放系数 | 2×r (16~32) | 控制 LoRA 对输出的影响强度。通常设为 2×r |
| lora_dropout | adapter 上的 dropout | 0.05~0.1 | 防止过拟合小数据集。数据少于 1000 条时建议 0.1 |
| target_modules | 在哪些层加 adapter | q_proj, v_proj | 只给 Q/V 加最省参数;全部加(q/k/v/o/gate/up/down)效果更好但参数多 |
| learning_rate | 学习率 | 1e-4 ~ 5e-4 | LoRA 学习率可以比全参数 SFT 高 5~10 倍 |
WebUI 操作步骤
① 选模型→② 选数据→③ 选方法→④ 调参数→⑤ 点训练
CLI 命令行方式
配置YAML文件
执行
导出的 Adapter 怎么用?—— 挂载与推理全流程
adapter 不能独立运行——它只是 ΔW 的权重(即 B·A 两个矩阵),必须挂载到一个同架构的基座模型上才能工作。挂载后,模型的前向传播变成:
h = W基座·x + (α/r)·B·A·x
下面给出三种挂载和使用方式:
方式一:LlamaFactory WebUI 直接对话(最简单)
WebUI Chat 标签页操作:
① 选择模型:Qwen2.5-7B-Instruct(和训练时相同的基座)
② 勾选 "Use adapter" → 选择文件夹 ./output/lora_qwen
③ 点击 "Load model" → 等待加载 (1~2 分钟)
④ 在聊天框输入问题 → 模型就会用微调后的能力回答
工作原理:LlamaFactory 在后台调用 PEFT 库,动态地把 adapter 权重"挂"到基座模型上,不修改基座文件。
方式二:Python 代码动态加载(最灵活)
方式三:合并后部署到 vLLM/Ollama(生产环境)
动态加载有一个微小缺点:每次推理都要额外计算 B·A·x 这一项。在生产环境中,通常提前把 adapter 合并进基座权重,得到一个和普通模型完全一样的文件,然后扔给高性能推理框架:
基座 (14GB)+Adapter (30MB)→ 合并 →完整模型 (14GB)→vLLM / Ollama 部署
💡 最佳实践
实验阶段用 PeftModel 动态加载(省磁盘,一个基座配多个 adapter),确认效果后合并导出为完整模型,交给 vLLM/Ollama 做生产部署。不要在生产环境中用动态加载——那 5% 的额外开销在高并发下会被放大。
QLoRA微调
QLoRA = LoRA + 基座 4-bit 量化。基座模型从 14GB(BF16)压到 ~4GB(NF4),显存再砍一半。单张 RTX 3090/4090(24GB)就能训练 Llama-7B,甚至可以挑战 13B。
QLoRA 用了三个关键技巧来在极致压缩显存的同时保持训练质量:
| 技术 | 做什么 | 省多少 |
|---|---|---|
| 4-bit NormalFloat (NF4) | 一种专门为神经网络权重分布设计的 4-bit 量化格式。比传统的线性量化更好地保留了权重的信息分布 | 从 16bit 降到 4bit → 省 75% 模型显存 |
| 双重量化 (Double Quant) | 对量化常数本身也做一次量化。量化时每个 64 个参数共享一个量化常数,把这些常数再量化 | 再省 ~0.4 bit/参数 |
| 分页优化器 (Paged Optimizer) | 当 GPU 显存不足时,把优化器状态自动换出到 CPU 内存,需要时再换回来 | 避免 OOM,显存峰值可控 |
QLoRA 前向传播时发生了什么:
1. 4-bit 量化权重 (存于显存) → 反量化为 BF16 → 正常做矩阵乘法
2. 反向传播时,梯度只流向 LoRA adapter(基座权重不更新)
3. 优化器状态 只保存 LoRA 参数的 m/v(基座的不需要)
关键:虽然存储是 4-bit,但计算是 BF16 → 训练精度几乎不受影响
既然反量化了,显存占用不会很高吗?
关键:虽然计算精度是 BF16,但不是全量解压。bitsandbytes 逐块解压——每次只把当前要算的一小块权重从 4-bit → BF16,算完立刻丢弃。全量 BF16 模型永远不会同时存在于显存中,所以训练精度几乎不受影响,显存也不爆炸。
WebUI 操作步骤
QLoRA 的 YAML 配置文件
⚠️ QLoRA 的常见坑
① 不要同时开 fp16/bf16 和量化——量化已经处理了精度,再开混合精度会冲突甚至 OOM。
② QLoRA 训练速度可能比 LoRA 慢 10%~20%——因为每次前向都要做 4-bit→BF16 反量化。
③ NF4 是首选——不要用 int4,NF4 专门为神经网络权重设计,效果明显更好。
④ batch_size 可以比 LoRA 大——因为显存更充足,可以考虑把 batch 从 2 提到 4。
显存计算
通用显存公式
VRAM = Mparams + Mgrads + Moptim + Mactivations
| 组件 | 全参数 SFT | LoRA | QLoRA |
|---|---|---|---|
| 模型参数 | P × bytes_per_param | P × bytes_per_param | P × 0.5 bytes(含量化开销) |
| 梯度 | P × 2 bytes | Plora × 2 bytes(≈ 0.1% P) | Plora × 2 bytes |
| 优化器状态 | P × 8 bytes | Plora × 8 bytes | Plora × 8 bytes |
| 激活值 | ~10 GB | ~15 GB | ~10 GB |
关键洞察:LoRA/QLoRA 省显存的核心在于优化器状态。Adam 的 momentum 和 variance 各占 4 bytes(FP32),两个加起来 = P×8 bytes,是模型参数(P×2 bytes BF16)的 4 倍。全参数 SFT 的优化器状态(7B→56GB)比模型本身(14GB)还大。LoRA 的优化器只需要保存那 0.1% 的 adapter 参数,直接从 56GB 降到 ~100MB。
显存计算速查
| 模型 | 参数量 | 全参数 SFT | LoRA (rank=16) | QLoRA (NF4, rank=16) |
|---|---|---|---|---|
| Qwen2.5-0.5B | 0.5B | ~16 GB | ~8 GB | ~4 GB |
| Qwen2.5-1.5B | 1.5B | ~28 GB | ~10 GB | ~5 GB |
| Qwen2.5-3B | 3B | ~46 GB | ~14 GB | ~7 GB |
| Llama-3-8B | 8B | ~106 GB | ~24 GB | ~14 GB |
| Qwen2.5-7B | 7B | ~94 GB | ~22 GB | ~13 GB |
| Qwen2.5-14B | 14B | ~178 GB | ~36 GB | ~20 GB |
| Llama-3-70B | 70B | ~850 GB | ~160 GB | ~65 GB |
| Qwen2.5-72B | 72B | ~874 GB | ~165 GB | ~67 GB |

浙公网安备 33010602011771号