返回关卡地图
闯关题:0 / 2 XP 0 / 200

Level 25 | 零基础导学关卡

Quantization W8A16

先用小例子把必要知识学会,再用 2 道闯关题检查理解,最后回到 notebook 写真正的 PyTorch 作业。

25_Quantization_W8A16.ipynb QuantizationW8A16Absmax
概念输入
闯关检查
Notebook 作业
q=clamp(round(x·127/absmax),-128,127)
Mission 1 TODO 1 / TODO 2 / TODO 3

W8 量化:用绝对最大值确定刻度尺

先补的知识

  • int8 的有效范围通常看作 [-127, 127],对称量化会尽量让正负两侧共用同一把刻度尺。
  • 这里的 scale 表示整数刻度密度:scale = 127 / absmax。absmax 越大,每个浮点单位对应的 int8 刻度越少。
  • 量化不是简单 to(torch.int8):要先乘 scale,把浮点数放大到整数刻度,再 round 到最近整数。
  • round 后还要 clamp,防止极端值越界;最后才转成 torch.int8 存储。

图解原理

把浮点权重压成 int8,像把真实长度画到一把只有 127 格的尺子上。先找最大绝对值 absmax,决定“最远的点”应该贴到 int8 边界;再用同一把尺子把所有权重换算成整数刻度。

0. 先读量化函数的合同

输入是一块浮点权重,输出不是一个张量,而是两样东西:压缩后的 int8 权重,以及以后反量化要用的 scale。

输入weight float tensor 输出 1q_weight int8 tensor 输出 2scale = 127 / absmax
scale 是“刻度密度”

量化时乘 scale,反量化时除 scale。这个 notebook 的 scale 不是每个整数代表多少浮点值,而是 1 个浮点单位对应多少 int8 格。

记忆:q = round(w * scale),所以还原就是 w ≈ q / scale
主线:absmax 定尺子,乘 scale 上刻度,round/clamp 存 int8
absmaxweight.abs().max() scale127.0 / absmax multiplyweight * scale round最近整数 clamp + int8[-128,127]

这条线就是 TODO 1/2/3 的顺序。不要先转 int8,否则小数会直接被截断,量化误差会变得不可控。

1. 一个小数字例子
absmax3.0 scale127 / 3.0 ≈ 42.33 w=1.5round(1.5 * 42.33) = 64 w=-3.0round(-3.0 * 42.33) = -127
2. 最常见错误

scale 写反、忘记 round、忘记 clamp,都会让测试里的误差或 dtype 检查失败。

先乘 再 round 再 clamp 最后 int8

语法热身:量化三步

absmax = weight.abs().max()
scale = 127.0 / absmax
q = torch.round(weight * scale)
q = q.clamp(-128, 127)
q = q.to(torch.int8)

举一反三:对应 notebook TODO

TODO 1absmax = weight.abs().max() TODO 2scale = 127.0 / absmax TODO 3round → clamp → int8

迁移口诀:量化乘 scale,反量化除 scale;scale 方向别写反。

闯关题

这个 notebook 中为什么 scale 常用 127 / absmax?

回到 notebook 的作业

这里不直接写答案。你已经拿到足够输入,最后用 notebook 的 TODO 做举一反三。

Mission 2 TODO 4

W8A16 前向:权重反量化,激活保持半精度

先补的知识

  • W8A16 表示 weight 用 int8 存储,activation 仍用 fp16/bf16 等 16-bit 浮点参与计算。
  • 普通 PyTorch 的 F.linear 不能直接拿 int8 权重和 fp16 激活做这个玩具前向,所以先把 q_weight 反量化回浮点。
  • 本 notebook 的量化公式是 q = round(w * scale),因此反量化公式是 w ≈ q / scale。
  • 反量化后的权重要转成 x.dtype,保证和激活 dtype 对齐。

图解原理

W8A16 的核心是“存的时候省,算的时候稳”:权重平时压成 int8 省显存;真正做线性层前,再用 scale 还原成和激活同 dtype 的浮点权重。你写的 TODO 4 就是这条恢复路径。

0. 先看 W8A16 的两种 dtype
W8q_weight 存成 torch.int8 A16x 保持 fp16/bf16 等浮点激活 计算前q_weight.float() / scale
TODO 4 不是重新量化

前面已经把权重量化好了。这里要做的是反量化,再调用 F.linear,最后输出形状和普通 Linear 一样。

主线:int8 权重除以 scale,还原成和激活同 dtype 的浮点权重
q_weightint8 float()避免整数除法/溢出 / scale反量化 to(x.dtype)对齐激活 F.linearx, dequant_weight, bias

量化时是乘 scale,反量化时必须除以同一个 scale。这里最容易写错方向。

1. 为什么要 .float()

q_weight 是 int8 存储格式,先转浮点再除以 scale,才能得到近似原权重的浮点值。

2. 为什么要 .to(x.dtype)

激活是 A16,权重反量化后对齐到 x.dtype,避免线性层里 dtype 不匹配,也更贴近 W8A16 的含义。

语法热身:反量化后做线性层

dequant_weight = q_weight.float() / scale
dequant_weight = dequant_weight.to(x.dtype)
out = F.linear(
    x,
    dequant_weight,
    bias,
)

举一反三:TODO 4 检查表

量化方向q = round(w * scale) 反量化方向w = q.float() / scale dtype 对齐dequant_weight.to(x.dtype) 前向F.linear(x, dequant_weight, bias)

迁移口诀:W8 是存储格式,A16 是计算激活;做 Linear 前先把权重恢复成浮点。

闯关题

W8A16 中 A16 指的是什么?

回到 notebook 的作业

这里不直接写答案。你已经拿到足够输入,最后用 notebook 的 TODO 做举一反三。

完成 2 道闯关题后,本关即算完成;作业 checklist 用来辅助你回 notebook 练习。
进度只保存在当前浏览器 localStorage,分享 HTML 不会带走你的记录。
上一关:L24 下一关:L26