Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

9.7 动手:为 Lab 3 选一套并行策略

前面六节给了公式、清单与陷阱,但真正动手时面对的问题只有一个:手上这台机器(1–8 张卡)、这个模型、这份数据,我该敲哪几条命令?

这一节把前面所有结论压缩成一条五步决策流程,并用两组具体硬件把账算完:① 单张 24 GB 卡上的 QLoRA;② 8 张 80 GB 卡上的全参微调。最后给出配置对照表、吞吐验收方法与排错清单,并接回 Lab 3。

9.7.1 五步决策流程

五步决策表(按顺序走,每步只回答一个问题)
步 问题 用什么算 决策规则
① 显存估算 模型状态要多少?激活要多少? Mstate=16NM_{\text{state}}=16N(9.1.2);激活按 token 数与层数(9.1.3) 得到两个数:状态 MsM_s、激活 MaM_a(都要留 15%–30% 余量)
② 是否分片 Ms>M_s \gt 单卡可用显存吗? Ms/kM_s/k(9.2.4) 差距小 → ZeRO-1/2;差距大 → ZeRO-3 / FSDP2;能用 PEFT 就别分片
③ 是否 TP / PP 单层放得下吗?层数够分吗? 单层参数与激活量;层数是否被 PP 整除 单层放不下 → TP(≤ 单机卡数);只是整体大 → 优先 PP 或 ZeRO-3
④ 批大小与梯度累积 一次能塞多少 token?等效批要多大? Bglobal=K×M×Bmicro×kB_{\text{global}}=K\times M\times B_{\text{micro}}\times k 先定 BmicroB_{\text{micro}}(受激活限制)与 BglobalB_{\text{global}}(受学习率配方限制),再解 KK
⑤ 精度与重计算 用 bf16 还是 fp8?开不开重计算? 9.5.1、9.5.3 bf16 为默认;激活紧张先开选择性重计算;有 FP8 硬件且框架支持再考虑 fp8
为什么「先算账再动手」在微调场景里更值钱

预训练一次实验要几天,配错了损失很大但至少过程漫长;微调(尤其 Lab 3 这类任务)常常几分钟就能跑完一轮,于是很多人直接「试参数」——试出来的配置往往既没跑满硬件、又无法解释。

五步流程真正的价值是给出可解释的起点:每个数字从哪里来、哪个约束先被撞到、下一步该改哪个旋钮。这与 工具箱 的「先测基线再找瓶颈」是同一个方法论,只是这里的「基线」是算出来的而不是跑出来的。

一句工程经验:先用最小的配置把链路跑通(10 步、小数据),再按五步放量。绝大多数「跑不起来」与算法无关,而是环境、数据格式与显存配置的组合问题(Lab 3 排错清单)。

9.7.2 模型参数核对:把清单变成数字

建模前的参数核对表

动手前先把模型的结构参数抄下来,后面所有账都要用到它们:

符号 含义 典型来源 例(7B 级开源模型)
NN 总参数量 权重文件 / model.num_parameters() 7×1097\times10^{9}
LL 层数 配置的 num_hidden_layers 32
hh 隐藏维 hidden_size 4096
nhn_h 注意力头数 num_attention_heads 32
nkvn_{\text{kv}} KV 头数 num_key_value_heads(GQA 时小于 nhn_h) 8
VV 词表大小 vocab_size 151,936
TT 序列长度(截断后) 数据与 max_length 2048

为什么要核对 nkvn_{\text{kv}} 与 VV:GQA 让 KV cache 与 Q,K,VQ,K,V 投影的参数量按 nkv/nhn_{\text{kv}}/n_h 缩小,张量并行按头切分时要按 nkvn_{\text{kv}} 与 nhn_h 分别检查整除性(9.3.3);词表 VV 决定了输出头与 embedding 的参数量,它们常是首尾流水线段的显存大户(9.4.5 的负载均衡讨论)。

例 9.7.1 · 25 亿参数模型的显存账与三种方案

模型:N=2.5×109N=2.5\times10^{9},L=32L=32,h=3584h=3584,T=2048T=2048。硬件:单张 24 GB 卡。任务:在自定义指令数据上微调。按五步流程给出三种可行配置并比较。

解

① 显存估算(全参口径)。 Ms=16N=16×2.5×109=40×109 字节=40 GB.M_s=16N=16\times2.5\times10^{9}=40\times10^{9}\ \text{字节}=40\ \text{GB}. 单看这一项就超过 24 GB —— 全参微调在单卡上不可行。

激活:≈34×Bmicro×T×L\approx34\times B_{\text{micro}}\times T\times L。Bmicro=1B_{\text{micro}}=1 时 34×2048×32≈2.2×106 字节级⇒≈2.234\times2048\times32\approx2.2\times10^{6}\ \text{字节级}\Rightarrow\approx2.2 GB;Bmicro=4B_{\text{micro}}=4 时约 8.98.9 GB。

② 是否分片。 目标 24 GB 卡:可用预算按 24−224-2(系统与碎片)=22=22 GB 计。

方案 模型状态 激活(Bmicro=2B_{\text{micro}}=2) 合计 判断
全参 + 混合精度 4040 GB ≈4.4\approx4.4 GB 44.444.4 GB ✗ 不可行
全参 + 8-bit 优化器 2525 GB ≈4.4\approx4.4 GB 29.429.4 GB ✗ 仍超
全参 + 8-bit + CPU offload(m,vm,v 上主机) 2020 GB ≈4.4\approx4.4 GB 24.424.4 GB △ 勉强,且每步 PCIe 往返使速度显著下降
LoRA(bf16 主干 + 适配器) 55 GB + 0.10.1 GB ≈4.4\approx4.4 GB(Bmicro=2B_{\text{micro}}=2) ≈9.5\approx9.5 GB ✓ 可行且余量充足
QLoRA(4 bit 主干 + LoRA) 1.251.25 GB + 0.10.1 GB ≈4.4\approx4.4 GB ≈5.8\approx5.8 GB ✓✓ 最宽裕,BmicroB_{\text{micro}} 可继续放大

(LoRA 主干按 bf16 计 2N=52N=5 GB;QLoRA 主干按 4 bit 计 0.5N=1.250.5N=1.25 GB;适配器按 例 9.5.4 的口径 ≤0.1\le0.1 GB。)

④ 批大小与梯度累积。 以 QLoRA、Bmicro=8B_{\text{micro}}=8 为例(激活 ≈17.6\approx17.6 GB,仍有余量): Bglobal=K×Bmicro×k=K×8×1.B_{\text{global}}=K\times B_{\text{micro}}\times k=K\times8\times1 . 目标 Bglobal=128⇒K=16B_{\text{global}}=128\Rightarrow K=16。即「每 16 个 micro-batch 更新一次」。

⑤ 精度与重计算。 主干 bf16(QLoRA 为 4 bit 存储 + bf16 计算);适配器 fp32;先不开重计算,若 BmicroB_{\text{micro}} 提到 16 后 OOM 再开选择性重计算。

结论(推荐配置):QLoRA + r=8∼16r=8\sim16 + target_modules=[q_proj, v_proj] + Bmicro=8B_{\text{micro}}=8、K=16K=16、bf16 计算、选择性重计算按需开。■\blacksquare

∎
例 9.7.2 · 8 张 80 GB 卡上的全参微调

模型:N=7×109N=7\times10^{9},L=32L=32,h=4096h=4096,nh=32n_h=32,T=2048T=2048。硬件:8 张 80 GB 卡(同一节点、NVLink 互联)。目标:全参微调,等效批 256 条序列。按五步流程给出配置。

解

① 显存估算。 Ms=16×7=112M_s=16\times7=112 GB;若 Bmicro=2B_{\text{micro}}=2,激活 ≈34×(2×2048)×4096×32≈1.8×1010\approx34\times(2\times2048)\times4096\times32\approx1.8\times10^{10} 字节 ⇒≈17\Rightarrow\approx17 GB(口径:34 字节/(token·hidden 元素),见 例 9.1.2)。

② 分片。 单卡 80 GB,112>80112 \gt 80 → 必须分片。

  • ZeRO-1(8 卡):4N+12N/8=28+10.5=38.54N+12N/8=28+10.5=38.5 GB/卡;
  • ZeRO-2(8 卡):2N+2N/8+12N/8=14+1.75+10.5=26.252N+2N/8+12N/8=14+1.75+10.5=26.25 GB/卡。

取 ZeRO-2:26.2526.25 GB(状态)+ 1717 GB(激活)+ 通信缓冲 ≈ 45 GB/卡,在 80 GB 卡上仍有约 40% 余量(若要继续放大 BmicroB_{\text{micro}},先开选择性重计算把激活压下来)。

③ 是否 TP / PP。 单层最大的是 FFN:h×4h=4096×16384≈6.7×107h\times4h=4096\times16384\approx6.7\times10^{7} 参数 ⇒\Rightarrow bf16 下 ≈0.13\approx0.13 GB,单卡轻松放下;层数 32,切成 2–4 段也整除。结论:不需要 TP,也不需要 PP —— 8 卡同节点时优先用更简单的方案(9.6.5 的配平原则:先满足硬约束再优化)。

④ 批大小与梯度累积。 目标 Bglobal=256B_{\text{global}}=256,k=8k=8: 256=K×Bmicro×8⇒K×Bmicro=32.256=K\times B_{\text{micro}}\times8\Rightarrow K\times B_{\text{micro}}=32 . 取 Bmicro=2B_{\text{micro}}=2、K=16K=16(激活约 17 GB,安全);或 Bmicro=4B_{\text{micro}}=4、K=8K=8(激活约 34 GB,接受它之前先开选择性重计算把激活压到十几 GB,且 KK 小意味着单步更快)。

⑤ 精度与重计算。 bf16 参数与梯度 + fp32 主权重(混合精度),fp16 不必要(需损失缩放);先不开重计算,若把 TT 提到 8192 或 BmicroB_{\text{micro}} 提到 8,再开选择性重计算(9.5.3)。

学习率联动:等效批从单卡的 BB 扩到 256256,若原来是 64,则按线性缩放规则把学习率放大 4 倍并加 warmup(9.2.3);先跑 100 步验证损失曲线形态,再决定是否改用平方根缩放。

结论(推荐配置):

并行:    DDP × ZeRO-2(8 卡同节点),不开 TP / PP
精度:    bf16 参数与梯度 + fp32 主权重,动态损失缩放关闭(bf16)
批:      B_micro = 4,K = 8,等效批 256(= 4 × 8 × 8)
重计算:  关闭(先),激活紧张时开 selective
优化器:  AdamW,lr 按线性缩放 ×4 + warmup 3%
checkpoint:torch.distributed.checkpoint 分片保存(每 500–1000 步 + 结束时)

■\blacksquare

∎

9.7.3 LoRA / QLoRA 的显存账重算与配置

LoRA 配置要素与显存影响
配置项 作用 对显存的影响 常用取值
rr(秩) 适配器容量 适配器参数量 ∝r\propto r(通常仍 <1%);影响很小 8∼648 \sim 64
target_modules 挂在哪几个投影上 挂得越多,适配器参数越多(q,vq,v → 全部线性层约 ×4\times4) 起手 [q_proj, v_proj]
lora_alpha 缩放系数(常取 2r2r) 无 16∼3216 \sim 32
lora_dropout 正则 无 0∼0.050 \sim 0.05(与主训练 dropout 一致)
主干精度 bf16 / 4 bit 主干占用 2N2N → 0.5N0.5N 4 bit(QLoRA)
max_length 序列长度 激活 ∝T\propto T,通常是最大旋钮 1024 起手,能放下再翻倍

账本重算(NN 为总参数): Mstatic≈0.5N⏟4 bit 主干+16×r(din+dout)×#module⏟适配器五账单(小项).M_{\text{static}} \approx \underbrace{0.5N}_{\text{4 bit 主干}} + \underbrace{16\times r(d_{\text{in}}+d_{\text{out}})\times \#_{\text{module}}}_{\text{适配器五账单(小项)}} . 对 N=7×109N=7\times10^{9}、r=8r=8、64 个矩阵:3.5 GB+0.08 GB≈3.63.5\ \text{GB}+0.08\ \text{GB}\approx3.6 GB(例 9.5.4)。剩下的全部预算归激活与 KV cache。

为什么 LoRA 要把「省显存」的重点从模型搬到数据

在 QLoRA 下,模型状态只剩 3–4 GB 量级,于是预算的主要部分给了激活。这带来一个反直觉的后果:LoRA 微调的瓶颈常常是「序列长度」而不是「模型大小」。

所以 LoRA 调优的旋钮顺序完全不同:

  1. max_length(序列越长,激活线性增长;也是效果最直接的旋钮);
  2. B_micro 与梯度累积(把批凑够,同时控制激活);
  3. 选择性重计算;
  4. 最后才是 rr、target_modules 这类容量参数(它们几乎不影响显存)。

四步的顺序与全参训练一致:先解显存约束,再谈效果。区别只在于「哪一顶帽子戴着紧」。

# ── QLoRA 的最小可运行配置(单卡 24 GB 量级)──────────────────────────
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training

model_id = "[基座模型标识]"

bnb = BitsAndBytesConfig(
    load_in_4bit=True,                      # 4 bit 主干:主干占用 ≈ 0.5N 字节/参数
    bnb_4bit_quant_type="nf4",              # NF4 数据类型(QLoRA 配方)
    bnb_4bit_use_double_quant=True,         # 双重量化:再省一点缩放常数
    bnb_4bit_compute_dtype=torch.bfloat16,  # 计算精度用 bf16(不是 fp16,免损失缩放)
)
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb,
                                             device_map={"": 0})       # 单卡
model = prepare_model_for_kbit_training(model)   # 冻结主干、开梯度检查点等预处理

lora = LoraConfig(
    r=8, lora_alpha=16, lora_dropout=0.05,
    target_modules=["q_proj", "v_proj"],  # 先只挂注意力投影;不够再加
    task_type="CAUSAL_LM",
)
model = get_peft_model(model, lora)
model.print_trainable_parameters()        # 期望:可训参数 < 0.2%

# 训练循环与第 1 讲完全一致,只是多了三件事:
#   ① 只在 response 上算损失(mask 掉 prompt,否则模型学会复述问题)
#   ② 梯度累积 K 步更新一次
#   ③ 每 N 步打印 token/s、峰值显存与梯度范数(验收用,见 9.7.4)

9.7.4 吞吐预期与验证

验收指标:四个数、一条对照
指标 定义 怎么测 健康区间(示例,需按硬件校准)
峰值显存 单步峰值分配 torch.cuda.max_memory_allocated() 留 ≥20% 余量;逼近上限就该降 BmicroB_{\text{micro}} 或提 TT 前先优化
每步耗时 一次优化器步骤的墙钟 计时 20–50 步取中位数(跳过前几步预热) 波动 <10%;波动大说明数据管线或通信抖动
训练吞吐 token/s 或 samples/s tokens/step ÷ 每步秒数 与「单卡基线 × 卡数 × 扩展效率 EE」对账
扩展效率 EE S/kS/k(定义 9.1.3) 用同样的全局批跑 1 卡与 kk 卡各 20 步 单节点 8 卡常见 0.7–0.9;跨机遇瓶颈会掉到 0.5 以下
对照:效果 微调前 vs 微调后 固定问题集 + 固定解码参数,比较任务指标 必须给出对照,否则无法归因

一个可复算的吞吐预期:N=7×109N=7\times10^{9}、每卡每步 Bmicro×T=4×2048=8192B_{\text{micro}}\times T=4\times2048=8192 token、8 卡、单卡有效算力 1×10141\times10^{14} FLOP/s(bf16、中等利用率)。单步计算量 ≈6NTtoken=6×7×109×8192≈3.4×1014\approx6NT_{\text{token}}=6\times7\times10^{9}\times8192\approx3.4\times10^{14} FLOPs,故 tcompute≈3.4 s,吞吐≈8192/3.4≈2,400 token/s/卡 ⇒ 总计≈1.9×104 token/s.t_{\text{compute}}\approx3.4\ \text{s},\qquad \text{吞吐}\approx8192/3.4\approx2{,}400\ \text{token/s/卡}\ \Rightarrow\ \text{总计}\approx1.9\times10^{4}\ \text{token/s}. 若实测只有这个数的一半,按 9.7.5 的顺序排查(先看 GPU 利用率、再看通信占比、再看数据管线)。本计算只用于判断「是否离预期一个数量级」,不用于承诺任何绝对值(有效算力随模型 / 精度 / 内核质量变化,需实测校准)。

为什么必须用「同样的全局批」做对照

因为全局批变了,学习率的最优值、每步的样本数、收敛曲线都会变——此时吞吐差异与效率差异混在一起,无法归因。

三条对照规则:

  1. 测吞吐时固定全局批(改动卡数时同步调 KK 或 BmicroB_{\text{micro}});
  2. 测效果时固定全局批与调度(否则「更快的配置」可能只是「更短的训练」);
  3. 报告时必须写出:卡数、精度、TT、BmicroB_{\text{micro}}、KK、是否重计算、是否分片——这正是 工具箱 「一次训练的最小记录集」在并行场景下的展开。

9.7.5 排错清单

OOM / 变慢 / 曲线异常:按症状查

症状 A:启动就 OOM(加载模型时、或 optimizer.step() 后)。

检查 判据 处方
模型状态是否超了 16N16N(或 10N10N)与卡容量比 分片(ZeRO-1/2/3、FSDP2)、8-bit 优化器、QLoRA
优化器是否在最初就分配 有些实现会在第 1 步前预分配 m,vm,v 提前用 max_memory_allocated 观测;必要时用 CPU offload
是否用了 fp32 训练 若没开混合精度,参数与梯度都翻倍 开 torch.autocast + bf16

症状 B:前向能跑,反向 OOM。

检查 判据 处方
是激活超了(B 类,9.1.5) 峰值出现在反向早期 梯度累积 + 减小 BmicroB_{\text{micro}};选择性重计算;长序列加序列并行
注意力是否被实体化 激活量里有 BnhT2B n_h T^{2} 项 换成 FlashAttention 一类实现(2.6)
是否有临时张量峰值 单步中间出现远高于稳态的尖峰 分块 / 融合;减小 bucket 大小

症状 C:显存够,但 GPU 利用率低、扩展效率差。

检查 判据 处方
通信是否重叠 profiler 里通信内核与计算内核是否并行 开梯度分桶、参数预取、算子融合(9.6.2)
气泡是否过大 用 P−1M+P−1\frac{P-1}{M+P-1} 算一下(9.4.2) 增大 MM、用交错调度
每卡 token 数是否太少 矩阵乘是否退化成小算子 增大 BmicroB_{\text{micro}} 或 TT;减少并行度
数据管线是否瓶颈 GPU 是否在等数据 num_workers、pin_memory、预处理搬到 worker(工具箱)

症状 D:损失异常(不降 / 尖峰 / nan)。

检查 判据 处方
学习率是否随批放大过头 是否按线性缩放放大且无 warmup 加 warmup;改用平方根缩放(9.2.3)
梯度尺度是否变了 等效批变化后梯度范数量级 打印 grad_norm,必要时梯度裁剪
fp16 是否在溢出 是否出现 inf/nan 且用了 fp16 换 bf16;检查动态损失缩放;用 autocast 而非手工 .half()
LoRA 的 loss mask 是否正确 是否把 prompt 也算进损失 只对 response 计算损失(Lab 3)
恢复训练是否弄丢状态 是否从 checkpoint 恢复并重置了数据流 恢复时一并恢复「已消费样本数」与优化器步数(9.6.3)
本节的七个高频误操作
  1. 照搬大厂配置。TP 8 × PP 16 是为几千卡与数百 B 参数设计的;在 8 卡上照搬只会得到「气泡大 + 通信多 + 利用率低」。
  2. 只调一个旋钮。显存、气泡、通信是联动约束:改 BmicroB_{\text{micro}} 就要重算 KK 与等效批;改 MM 就要看气泡与算子效率。
  3. 为了省显存先开 ZeRO-3。若 ZeRO-1/2 已经够,kk 小的场合 ZeRO-3 的每层 all-gather 会让训练更慢(9.2.4 的判据表)。
  4. 拿总吞吐当验收指标。应看扩展效率 EE 与「同样全局批」下的对比。
  5. 不测基线就调优。没有单卡 / 小规模基线,任何「提速」都无法证明。
  6. 忽略通信缓冲与碎片。账算得再准也要留 15%–30% 余量;OOM 常常差的就是那几 GB。
  7. 把 LoRA 的秩 rr 当成显存旋钮。rr 主要影响容量(效果),显存影响极小;显存旋钮是 max_length、BmicroB_{\text{micro}} 与重计算。

9.7.6 配置对照表与接回 Lab 3

四种典型硬件下的推荐配置
硬件 模型 推荐方案 模型状态 关键参数 主要风险
1 × 24 GB 7–13B(微调) QLoRA(4 bit + LoRA) ≈0.5N+\approx0.5N+ 小项 r=8∼16r=8\sim16;max_length 1024 起;KK 16 激活与 KV cache 超预算;先降序列长度
1 × 24 GB 1–3B(全参) 混合精度 + 8-bit 优化器 10N10N Bmicro=2B_{\text{micro}}=2;选择性重计算 优化器状态仍是主要开销
4 × 24 GB 7B(全参微调) DDP + ZeRO-2(或 FSDP) ≈7.75N/k\approx7.75N/k 量级 k=4k=4;KK 按等效批解 显存偏紧:先压序列长度
8 × 80 GB 7B(全参) DDP + ZeRO-2 26.2526.25 GB/卡 Bmicro=4B_{\text{micro}}=4、K=8K=8、等效批 256 通信未重叠 → 扩展效率低
8 × 80 GB 70B(全参微调) TP 4 × DP 2 + ZeRO-2,或 FSDP2 × 8 视分片而定 TP 留在节点内;KK 与 MM 联动 单层 embedding / 输出头超预算时需调切分
8 × 80 GB 70B(微调) LoRA + ZeRO-2 主干 2N/k2N/k + 适配器 target_modules 起手 q,vq,v 秩太小效果不足:先扫 rr

读表方式:先看「模型状态」一列能否装进你的卡,再看「关键参数」,最后看「主要风险」——这张表是五步流程的查表版本。

接回 Lab 3 的路径

Lab 3 走的是本表的第一行(1 × 24 GB + QLoRA)。建议按下面的顺序动手:

  1. 跑通链路(30 分钟):小模型 + 小数据 + 10 步,确认分词、mask、保存都能走通;此时不要碰任何并行配置。
  2. 量显存与吞吐(30 分钟):用 9.7.4 的四个指标建基线;把 max_length 从 512 逐步提到 2048,观察峰值显存。
  3. 做对照实验(1–2 小时):全参(不可行时用「LoRA vs 较大 rr」代替)× LoRA 的显存 / 吞吐 / 效果三方对比;报告里给出「可训参数比例」与「任务指标」两个数。
  4. 把结论写进实验记录:哪些约束先被撞到、下一个旋钮是什么。这正是 工具箱 要求的「三行结论」。

若你手上恰好有多卡,可以额外做一件事:用同样的全局批测一次扩展效率 EE(定义 9.1.3)。哪怕只有 2 张卡,这个练习也会让你对「通信与气泡到底吃掉了多少」有第一次真实的手感。

即时自测 · 9.7

Q1 N=7×109N=7\times10^{9}、L=32L=32、T=2048T=2048:在单张 24 GB 卡上,全参微调为什么不可行?给出两个可行替代方案并说明各自代价。

Q2 8 张 80 GB 卡、7B 模型、目标等效批 256:给出 BmicroB_{\text{micro}}、KK 与并行配置,并说明为什么不需要 TP / PP。

Q3 QLoRA 配置里,显存的主要旋钮是哪些?为什么 rr 不是其中之一?

Q4 你的 8 卡训练扩展效率只有 0.4,显存充足。写出你的排查顺序(至少四步)与每步的检查动作。

参考要点

Q1 不可行:模型状态 16N=11216N=112 GB >24\gt24 GB(再加激活必爆)。替代方案:① QLoRA——主干 4 bit(0.5N=3.50.5N=3.5 GB)+ 极小适配器,代价是低秩表达能力受限;② 全参 + 8-bit 优化器 + CPU offload——模型状态约 2020 GB 勉强可放,代价是每步 PCIe 往返导致训练显著变慢。(也可答「多卡分片」,但那超出单卡前提。)

Q2 ZeRO-2(8 卡)下每卡模型状态 26.2526.25 GB;Bmicro=4B_{\text{micro}}=4、K=8K=8 时 256=4×8×8256=4\times8\times8 ✔(T=2048T=2048 时激活约 34 GB;若显存吃紧,先开选择性重计算)。不需要 TP:单层最大(FFN)仅约 0.13 GB(bf16),单卡放得下;不需要 PP:层数不是瓶颈(8 卡下 ZeRO-2 已解决整体显存),且同节点时越简单的方案风险越低。

Q3 主要旋钮:max_length(激活 ∝T\propto T)、BmicroB_{\text{micro}} 与 KK(激活 ∝Bmicro\propto B_{\text{micro}})、是否开选择性重计算、主干精度(4 bit vs bf16)。rr 不是旋钮,因为适配器参数量 =r(d+k)×=r(d+k)\times 模块数,相对主干(0.5N0.5N 或 2N2N)完全可忽略;rr 影响的是容量与效果,不是显存。

Q4 ① 看 GPU 利用率与 profiler:区分「在等通信」「在等数据」「在算小算子」;② 算气泡:P−1M+P−1\frac{P-1}{M+P-1},若 PP 大而 MM 小则加大 MM;③ 看通信是否重叠:通信内核是否与计算内核并行,否则调桶大小 / 预取;④ 看每卡 token 数:把 BmicroB_{\text{micro}} 或 TT 调大后重测;⑤ 最后再怀疑拓扑 / 硬件(例如 TP 跨了节点)。每一步都要只改一个变量并记录。

延伸阅读
  • 回到本讲起点:9.1 显存账本(16N16N 的推导)与 9.5 手段对照表(省多少 / 代价是什么)。
  • 动手入口:Lab 3 · 微调一个大语言模型;配套的 工程工具箱(显存估算、排查树、实验记录)。
  • 框架文档(截至 2025 年,以官方文档为准):PyTorch fully_shard / FSDP2、torch.distributed.pipelining、torch.distributed.checkpoint;DeepSpeed ZeRO 配置项;HuggingFace peft 与 bitsandbytes 的量化配置。

本节小结:把六节内容压成一条流程——算账(16N16N 与激活)→ 定分片 → 判 TP/PP → 解批大小与梯度累积 → 定精度与重计算。两条典型路径是:单卡 / 小卡用 QLoRA 把模型状态压到 3–4 GB 量级、把预算留给激活;多卡大显存用 DDP + ZeRO-2 配足够的等效批。两者共同的验收标准是「同样全局批下的扩展效率」与「微调前后的效果对照」,而不是总吞吐。参数核对与排错清单要长期留在手边——它们是把这一讲的知识转成工程习惯的那部分。