在线动态 FP8 量化 128GB 怎么装下 134GB 权重
T AGENT 开发纪实 · EP31 · MiniMax-H3 六期原理课 ②
答案不是"塞",是"换一种方式存":磁盘上的权重一个字节没动,vLLM 加载时才现转 FP8——每个数从 16 位变 8 位,内存直接减半。
磁盘原始
134GB
FP8 内存
~67GB
个校准集
0
实测加载
89.17GB
💡 本节要点:EP31:在线动态 FP8 量化——磁盘上的 134GB 权重一个字节没动,vLLM 加载时才现转 FP8 存显存,内存减半(134→~67GB);校准集:0 个
起点问题
四个问题:128GB 怎么装下 134GB?
- Q1 · 134GB 怎么装进 128GB? · 不是物理上"塞得下",是内存里"换一种更省的方式存"——本期拆开看每一步。
- Q2 · 磁盘权重被量化过吗? · 没有。134GB 是 BF16 原样,shard 索引文件可以证明——量化发生在加载时,不落盘。
- Q3 · 在线和离线量化差在哪? · 离线要拿校准集先统计一遍数据分布;在线是加载/前向时用 absmax 现算缩放因子。
- Q4 · FP8 凭啥省一半? · BF16 一个数占 16 位,FP8 占 8 位。同样参数,内存减半——硬件还原生支持 FP8 矩阵乘。
核心矛盾: 省内存的不是"存得巧",而是"加载时把数值表示整个重写一遍"
💡 本节要点:起点问题:134GB 怎么装进 128GB?磁盘权重被量化过吗?在线和离线量化差在哪?FP8 凭啥省一半?——四个问题指向同一个答案:加载时重写数值表示
设计决策
三步组合拳:量化 + 统一内存 + 余量管理
① FP8 在线量化 134 → ~67GB 权重加载时现转 FP8 内存直接减半 → ② 统一内存驻留 demand paging GB10 统一内存 按需换页进 GPU → ③ 余量管理 ~102GB 红线 常驻留 18-19GB 峰值不超限
- 🧮 · 在线动态量化 · absmax 逐 tensor 现算缩放因子——免校准集,任何输入都能跑,鲁棒。
- ⚙️ · Cutlass 原生内核 · 量化算子绑 NVIDIA Cutlass FP8 kernel(原生 CUDA),不碰 Triton——SM121 上 Triton FP8 编不出来。
- 🎯 · 精度豁免边界 · 6 个 ignored 层 + AdaLN 保 BF16/FP32——量化只动注意力与 MLP 主体,数值敏感点全避开。
💡 本节要点:三步组合拳:① FP8 在线量化(134→67GB)② 统一内存按需驻留(demand paging)③ 余量管理(常驻 ~102GB,留 18-19GB)——量化只是第一步,装下靠的是整套协同
核心代码 1 · 量化配置
fp8-quant.json:一段 JSON 说清楚量化策略
{
"method": "fp8", // 量化到 8 位浮点
"activation_scheme": "dynamic",// 激活的缩放因子:动态现算
"ignored_layers": [ // 这 6 层保持高精度,不量化
"video_patch_proj", // 视频 patch 投影(输入域)
"audio_patch_proj", // 音频 patch 投影(输入域)
"time_embedder.proj_in", // 时间条件注入(进)
"time_embedder.proj_out", // 时间条件注入(出)
"final_layer.video_out", // 视频输出头(输出域)
"final_layer.audio_out" // 音频输出头(输出域)
]
}
理解: 三段 JSON 就是量化策略的全部: 量什么 (FP8)、 怎么定缩放 (动态现算)、 哪里不碰 (6 个敏感层)。启动时 --diffusion-quantization-config 直接读它,--force-cutlass-fp8 强制走原生内核。
💡 本节要点:核心代码 1:fp8-quant.json——method=fp8, activation_scheme=dynamic, 6 个 ignored_layers(两个 patch 投影、time embed 的 proj_in/proj_out、双输出头)
核心代码 2 · 在线动态机制
加载时现转 FP8:每个数从 16 位变 8 位
# 加载阶段:权重现转 FP8 存显存(不落盘、不改 checkpoint)
w_bf16 = load_shard("model-00013.safetensors") # 磁盘原样 BF16
w_fp8, scale = quantize_to_fp8(w_bf16) # 8 位 + 每 tensor 一个 scale
model.load_state_dict({name: w_fp8}) # 显存占用减半
# 前向阶段:激活也量化,但 scale 动态现算
scale_x = x.abs().amax() / FP8_MAX # absmax 逐 tensor 现算
x_fp8 = (x / scale_x).to(torch.float8_e4m3fn) # 免校准集、无分布假设
为什么"动态": 静态量化要先拿校准集跑一遍代表数据、统计出固定 scale——模型刚发布、没有现成校准集,选不好直接崩。 动态 absmax 每步现算 ,任何输入都有一组正确的 scale,鲁棒性换掉那一点点运行时开销。
💡 本节要点:核心代码 2:在线动态量化机制——权重加载时 weight_cast 现转 FP8 存显存;激活用 absmax 每 tensor 现算 scale(免校准集、无分布假设)
核心代码 3 · Cutlass 内核绑定
260 个量化器绑原生 CUDA,AdaLN 保 BF16
def _bind_native_cuda_fp8_activation_quantizers(model, ...):
# 给全模型 260 个需要激活量化的线性层,
# 把量化器绑定到原生 CUDA FP8 kernel(不走 Triton)
for module in model.modules():
if isinstance(module, CutlassFP8ScaledMMLinearKernel):
module.register_activation_quantizer(quantizer)
# quantizer.forward = forward_cuda 原生入口
class MiniMaxH3AdalnProj(nn.Module):
def forward(self, x):
x = x.to(_BF16_DTYPE) # AdaLN 激活强制保持 BF16
... # 调制向量对精度最敏感,不量化
Cutlass 内核里的数学: 每个 FP8 线性层内部完成「量化 → FP8×FP8 矩阵乘 → FP32 累加 → 反缩放」,一次前向多模态都在原生 CUDA 路径上跑——这也是 --force-cutlass-fp8 要保证的:不落回任何软件模拟。
💡 本节要点:核心代码 3:260 个激活量化器绑原生 CUDA FP8 kernel(forward_cuda,CutlassFP8ScaledMMLinearKernel);AdaLN 激活保持 BF16——FP8×FP8→FP32 累加→反缩放
理解型踩坑 ① · 校准集困境
为什么不是"离线静态量化"?
离线静态的路走不通: 静态量化需要先用一批代表数据跑一遍模型、统计出每个 tensor 的固定 scale。MiniMax-H3 刚发布,社区没有现成量化配方,也没有能代表"用户提示词分布"的校准集——校准集选不好,量化后的模型直接崩。
在线动态绕开了它: 动态量化不预统计——每次前向用 absmax 现算 scale,输入是什么就用什么算。没有校准、没有分布假设,换来的是"任何提示词都能跑"的确定性。这是失败链逼出来的正确答案(EP33 展开完整失败链)。
没有校准集 没有社区配方 → 离线静态 scale 可能崩 → 在线动态 absmax 现算 → 免校准 鲁棒可复现
💡 本节要点:踩坑 1:为什么不做离线静态量化?——校准集困境:新模型没有社区配方、没有代表数据;选不好校准集直接崩。在线动态免校准、无分布假设,是失败链逼出来的正确答案
理解型踩坑 ② · fail-closed 决策链
为什么是 FP8?因为其他路都死了
BF16 硬扛 118-119GB 超限 → INT8 SM121 无指令 → Triton FP8 编译失败 → Cutlass FP8 原生跑通
- BF16 硬扛 · 不量化直接装,峰值 118-119GB——超过 128GB 物理上限,中途 OOM。
- INT8 没有路 · GB10 的 SM121 没有 INT8 指令,dispatch_scaled_mm 直接失败——这条路硬件就不通。
- Triton 编不出 · Triton 的 FP8 内核在 SM121 编译失败。剩下唯一的原生路径:NVIDIA Cutlass。
方法论: 每一步失败都返回可诊断的错误,而不是悄悄降级—— fail-closed :宁可停,不可错。失败一次换一条路,最终收敛到硬件唯一支持的原生方案。
💡 本节要点:踩坑 2:fail-closed 决策链——BF16 硬扛 118-119GB 超限 → INT8 在 SM121 无指令(dispatch_scaled_mm 失败)→ Triton FP8 编译失败 → Cutlass FP8 原生成功。一次失败,换一条路
理解型踩坑 ③ · 量化边界
哪些层"不能"量化:精度敏感点清单
| 位置 | 层 | 为什么豁免 |
|---|---|---|
| 输入域 | video/audio patch 投影 | VAE latent 进模型的第一道门,误差会被 50 层放大 |
| 条件注入 | time_embedder proj_in/out | 时间步条件,数值范围大且陡,量化损失最敏感 |
| 输出域 | final_layer 双输出头 | 去噪终点,直接决定生成质量;输出还保持 FP32 |
| 调制向量 | AdaLN(shift/scale/gate) | 每个 block 的条件控制核心——激活强制保 BF16 |
| 教训: 量化不是"全量一刀切"—— 输入域、条件注入、输出域、调制向量 这四个位置对数值误差最敏感,全部避开。省内存的是注意力与 MLP 主体,那里数值冗余度高、量化最安全。 | ||
| > 💡 本节要点:踩坑 3:量化边界——6 个 ignored_layers + AdaLN 保 BF16:输入域(patch proj)、条件注入(time embed)、输出域(双输出头)、调制向量(AdaLN)全避开;量化只动注意力/MLP 主体 |
横向话题 · 内核与硬件
Cutlass 内核:量化能跑起来的硬件前提
- 💡 · GB10 有 FP8,无 INT8 · SM121 的指令集里没有 INT8 矩阵乘——所以 INT8 路线直接判死,FP8 是唯一原生路径。
- 🔧 · CutlassFP8ScaledMMLinearKernel · NVIDIA 开源的模板化 CUDA 内核库,FP8×FP8 矩阵乘 + FP32 累加 + 缩放,全部原生 CUDA。
- 🧬 · vLLM 量化栈 + 开源内核 · 量化配置、weight_cast、kernel 绑定全部来自 vLLM 开源栈 + Cutlass——本项目只做组合与排障。
GB10 硬件 FP8 指令 → Cutlass 内核 原生 CUDA → vLLM 量化栈 weight_cast + 绑定 → 在线 FP8 134→67GB
启示: 量化的"能不能成",一半看算法、一半看硬件——FP8 不是最优选择,而是 SM121 上 唯一 能跑的原生选择。硬件决定了决策空间的边界(EP32 展开)。💡 本节要点:横向话题:Cutlass 内核与硬件前提——GB10 SM121 有 FP8 无 INT8;CutlassFP8ScaledMMLinearKernel 原生 CUDA;vLLM 量化栈 + NVIDIA Cutlass,全开源
验证数据 · 内存账本
账本逐级核对:134 → 67 → 89.17 → 92,957
134GB
磁盘 BF16 原样(未量化)
~67GB
FP8 理论内存(减半)
89.17GB
实测加载 / 532s
92,957 MiB
请求后分配(<102GB 红线)
| 阶段 | 数值 | 说明 |
|---|---|---|
| 磁盘 | 134.16 GiB BF16 | index total_size 66.28GB(transformer)——证明从未离线量化 |
| FP8 理论 | ~67 GiB | 权重 16→8 位,内存减半 |
| 实测加载 | 89.1659 GiB / 532.257s | FP8 权重 + text encoder/VAE 开销 + 激活缓冲 |
| 请求后 | 92,957 MiB | 低于 102GB 常驻红线,留 18-19GB 余量 |
注:口径:FP8 权重约 67GB,但模型整体内存还含 VAE、激活与 CUDA 上下文,所以实测 89GB——账本对得上,且全程未触碰 128GB 上限。
💡 本节要点:验证:内存账本 134GB(BF16 磁盘)→ ~67GB(FP8 理论)→ 89.17GB(实测加载)→ 92,957 MiB(请求后峰值);加载耗时 532s
总结
省内存的不是"存得巧",是"加载时重写"
—— 134GB 一个字节没动,index 文件可证明,量化不落盘
磁盘原样 BF16
—— 加载时现转 8 位,absmax 逐 tensor 现算 scale,免校准集
在线动态 FP8
—— 260 个量化器绑原生 CUDA,FP8×FP8 → FP32 累加 → 反缩放
Cutlass 原生内核
—— 6 个 ignored 层 + AdaLN 保 BF16,量化只动注意力与 MLP 主体
精度边界
量化把 134GB 压到 67GB,但 67GB 还是装不进任何"显存"—— 它是怎么"住进"统一内存的?
- 📐 · EP30 · 模型原理 · 流水线拆解 ✅
- 🧮 · EP31 · 量化原理 · 本期:在线动态 FP8 ✅
- 🖥️ · EP32 · 硬件适配 · 下期:GB10 统一内存 + SM121 四个"不支持"
本期证据:fp8-quant.json(量化配置)· patches/minimax_h3_transformer.py(Cutlass 绑定 + AdaLN)· 本地权重目录实测(BF16 证明)· docs/RESULTS.md
💡 本节要点:总结:磁盘原样 BF16 → 在线动态 FP8(免校准)→ Cutlass 原生内核 → 统一内存驻留;下期 EP32:GB10 硬件适配——SM121 四个'不支持'与统一内存架构