1. 量化的本质
把连续的浮点数(FP32/FP16/BF16)映射到有限个离散等级(INT8/INT4…),用更少比特存储/运算。
核心是一对参数:scale(步长 s) 和 zero-point(零点 z),把实数 (x) 变成整型 (q):
q=round(xs)+z,x^=s⋅(q−z)≈x q=\mathrm{round}\left(\frac{x}{s}\right)+z,\qquad \hat{x}=s\cdot(q-z)\approx x q=round(sx)+z,x^=s⋅(q−z)≈x
-
s 决定量化分辨率(越小越精细);
-
z 决定零点位置(不为 0 时能覆盖非对称分布);
-
误差来源:舍入误差 + 截断/饱和(clipping)。
-
为减小误差,就要选好 s、z 以及量化粒度(整张量/每通道/分组)和是否做校准/训练。
工程直觉:用更小的位宽,就像把“连续音量刻度”变成“档位开关”,档位越少,差值越大,声音还原越粗糙;但设备更省电、更小巧。
2. 常见量化维度(任何量化方法都会涉及)
-
对称 vs 非对称
-
对称:(z=0)(z=0)(z=0),多用于权重(分布常近似对称)。
-
非对称:(z≠0)(z\neq 0)(z=0),多用于激活(分布偏正)。
-
-
粒度(granularity)
- per-tensor(整张量同一 s,z)→ 简单但误差大;
s,z不和通道相关,得到的s,z是标量,应用到整个tensor上 - per-channel(常用)/ per-column / group-wise(折中)→ 精度更好,开销略增。
以per-channel 为例。假如权重参数维度是(Cin,Cout)Cout 是输出维度,则按照Cout量化,最终得到维度是(Cout)。
Y = X @ W, 其中 X: [B, Cin],W: [Cin, Cout]Cout的每一个维度都是在Cin乘和累加的结果,因此Cout每个维度都有他自己的尺度 - per-tensor(整张量同一 s,z)→ 简单但误差大;
-
位宽:INT8 最稳;INT4 压缩更狠但更易掉点;NF4 是“非线性 4bit”等级集,拟合权重分布更好。
-
量化对象
- W-only:只对权重参数量化,显存减少,不一定加速;
权重一般使用对称的INT8的per-channel量化,即sw[c]=max(∣min(w(c))∣,∣max(w(c))∣)127,wq(c)=round(w(c)sw[c]) ∈[−128,127]s_w[c] = \frac{\max(|\min(w^{(c)})|,|\max(w^{(c)})|)}{127}, w_q^{(c)} = \mathrm{round}\left(\frac{w^{(c)}}{s_w[c]}\right)\ \in [-128,127]sw[c]=127max(∣min(w(c))∣,∣max(w(c))∣),wq(c)=round(sw[c]w(c)) ∈[−128,127]
- W8A8:权重+激活都 INT8,配合 INT8 内核可实打实提速(需校准/训练)。
权重量化已经知道,激活量化并不是激活层量化,而是对某些层的输出tesnor结果进行量化,多为per-tensor,一般使用UINT8量化。
量化:
qx = round(x / sx + zpx);qw = round(w / sw);乘加:y_int32 = (qx - zpx) @ qw;反量化:y_fp = (sx * sw) * y_int32 + bias;- 有些层是不能被量化的,像激活层,残差,batchnorm 等必须反量化后才能输入
-
执行形态
- 纯整型推理:int8 GEMM/Conv 有效加速;
- Fake Quantization:训练/仿真阶段用的形态:数值仍跑 FP32/FP16,在图里插入
Quantize/Dequantize节点,用标定的s, zp做截断+四舍五入来模拟 INT8 的误差,用于 PTQ 校准或 QAT 微调。
3. transformer 中使用W8A8过程
-
Linear/GEMM:INT8×INT8→INT32(权重 per-channel 对称,激活 per-tensor 非对称)
-
LayerNorm / Softmax / GELU:保持浮点(FP16/FP32)
-
残差相加:在浮点域完成
-
需要进入下一个量化的 Linear 时,再把浮点结果再量化成激活的 UINT8
记号:
q()=量化round(x/s + zp);dq()=反量化(q - zp)*s;
激活:UINT8(scale=s_x,zero-point=zp_x,per-tensor);
权重:INT8(scale=s_w[c],per-channel,对称,zp=0);
GEMM:int8×int8→int32,随后做**(re)quant或dequant**。
3.1 Embedding(通常保留浮点)
x_fp = WordEmb + PosEmb + (可选)TypeEmb # FP
# 许多实现这里不量化;也可以在进入第一层 Linear 前再量化一次。
3.2 自注意力(Self-Attention)块(以 BERT 的“后置 LN”风格为例)
3.2.1 输入 → Q/K/V 三个线性投影(量化的 Linear)
# 如果上一步是浮点,需要先把输入量化成激活 8bit:
x_q = q(x_fp; s_x, zp_x) # UINT8
# 三个投影共用 x_q,不同权重各有自己的 per-channel s_w
Q_int32 = (x_q - zp_x) @ Wq_int8 # INT8xINT8→INT32
K_int32 = (x_q - zp_x) @ Wk_int8
V_int32 = (x_q - zp_x) @ Wv_int8
# 反量化到浮点以便稳定做注意力(缩放、softmax 等都在 FP)
Q_fp = (s_x * s_wq) * Q_int32 + b_q # FP
K_fp = (s_x * s_wk) * K_int32 + b_k # FP
V_fp = (s_x * s_wv) * V_int32 + b_v # FP
说明
为什么反量化? 注意力里的
QK^T / sqrt(d)、softmax对数值极敏感,通常保留 FP;若平台支持整型 attention(少见且复杂),也可在整型域近似,但工程上主流仍是回到 FP。
3.2.2 注意力计算(浮点域)
Scores_fp = (Q_fp @ K_fp^T) / sqrt(d) # FP
P_fp = softmax(Scores_fp) # FP
Ctx_fp = P_fp @ V_fp # FP # 也叫 attention 输出
3.2.3 输出线性(量化的 Linear)
# 把 attention 输出再量化成激活 8bit,进入下一次 Linear:
Ctx_q = q(Ctx_fp; s_ctx, zp_ctx) # UINT8
# 投影到隐层:
O_int32 = (Ctx_q - zp_ctx) @ Wo_int8 # INT8xINT8→INT32
O_fp = (s_ctx * s_wo) * O_int32 + b_o # FP
3.2.4 残差 + LayerNorm(浮点)
Res1_fp = x_fp + O_fp # 残差在 FP
Y1_fp = LayerNorm(Res1_fp) # FP
3.3 前馈网络(FFN)块(Linear → GELU → Linear)
3.3.1 第一个线性(量化的 Linear)
# 进入 FFN 前,把激活再量化为 8bit:
Y1_q = q(Y1_fp; s_y1, zp_y1) # UINT8
H_int32 = (Y1_q - zp_y1) @ W1_int8 # INT8xINT8→INT32
H_fp = (s_y1 * s_w1) * H_int32 + b_1 # FP
3.3.2 GELU(浮点)
A_fp = GELU(H_fp) # FP
3.3.3 第二个线性(量化的 Linear)
A_q = q(A_fp; s_a, zp_a) # UINT8
Z_int32 = (A_q - zp_a) @ W2_int8 # INT8xINT8→INT32
Z_fp = (s_a * s_w2) * Z_int32 + b_2 # FP
3.3.4 残差 + LayerNorm(浮点)
Res2_fp = Y1_fp + Z_fp # 残差在 FP
Y2_fp = LayerNorm(Res2_fp) # FP
# 这就是该层 TransformerBlock 的输出(浮点),
# 若下一层继续做量化 Linear,则在进入下一层前再做 q(Y2_fp)。
3.4 堆叠 L 层
对每层重复上面的 “量化 Linear → (de)quant 边界 → FP 激活 → 量化 Linear → 残差/LN(FP)” 流水线。
在工程里:
-
所有 Linear/Conv 都走 W8A8;
-
GELU / LayerNorm / Softmax / 残差相加 保持 FP;
-
需要给下一个量化 Linear 喂数据时,再把 FP 再量化 成 UINT8 激活。
3.5 输出头(CLS/Pooler/Classifier)
-
选择 1(保守):保持 浮点(最稳),
Y_L_fp → 池化/均值 → FC(fp) -
选择 2(进一步压缩):在分类 FC 前再量化一次,做 W8A8 FC;若掉点明显,回退浮点。
4. 如何计算s, zp
下面把 W8A8(权重8比特、激活8比特)里每个 s(scale)与 zp(zero-point)怎么得到讲清楚,并给最常用(工程默认)的计算方式。
4.1 约定与范围
-
权重(W):通常做 per-channel 对称 INT8
→ 每个输出通道一个s_w[c],zp_w=0(不算零点)。 -
激活(A):通常做 per-tensor 非对称 UINT8(也有用 INT8 的实现)
→ 每个量化点一个标量s_x与zp_x∈[0,255]。
这是 PTQ/QAT 中最常见的组合:W:对称/逐通道;A:非对称/逐张量。硬件支持最好。
4.2 权重的 s 与 zp(W8,per-channel,对称)
以线性层权重 (W∈R[Cin,,Cout])(W\in\mathbb{R}^{[Cin,,Cout]})(W∈R[Cin,,Cout]) 为例(若你存的是 [Cout, Cin] 就按“行”处理):
**校准/计算:**不需要跑数据集,直接看权重本身(可选用 max-abs、百分位、MSE 等策略确定阈值)。
对第 © 个输出通道(第 © 列):
αc=max!(∣min(W[:,c])∣,,∣max(W[:,c])∣),sw[c]=αc127,zpw[c]=0
\textstyle
\alpha_c=\max!\big(|\min(W[:,c])|,,|\max(W[:,c])|\big),\quad
s_w[c]=\frac{\alpha_c}{127},\quad
zp_w[c]=0
αc=max!(∣min(W[:,c])∣,,∣max(W[:,c])∣),sw[c]=127αc,zpw[c]=0
量化:
Wq[:,c]=round!(W[:,c]/sw[c])∈[−128,127]
W_q[:,c]=\mathrm{round}!\big(W[:,c]/s_w[c]\big)\in[-128,127]
Wq[:,c]=round!(W[:,c]/sw[c])∈[−128,127]
PyTorch 代码(按列量化):
def quantize_weight_perchannel_symmetric_int8(W): # W: [Cin, Cout]
eps = 1e-12
max_abs = W.abs().max(dim=0).values # [Cout]
s_w = (max_abs / 127.0).clamp_min(eps) # [Cout]
W_q = torch.round(W / s_w).clamp(-128, 127).to(torch.int8)
zp_w = torch.zeros_like(s_w) # 概念上是 0(不用存成 int8)
return W_q, s_w, zp_w # zp_w 恒为 0
若权重布局是
[Cout, Cin],把dim=0改成dim=1,量化时对行广播s_w.unsqueeze(1)。
4.3 激活的 s 与 zp(A8,per-tensor,非对称)
激活需要校准数据(几百到几千条即可),在前向期间为每个“量化点”(例如线性层输入/输出、卷积输出等)统计分布范围,再固化 s_x, zp_x。
常用做法 1:MinMax(或带分位点/EMA 的 MinMax)
对某个量化点的激活张量 (X\in\mathbb{R}^{[B,S,D]}),遍历校准 batch,累计:
a=min(all X),b=max(all X)
a=\min(\text{all }X),\quad b=\max(\text{all }X)
a=min(all X),b=max(all X)
然后(非对称 UINT8):
sx=b−a255,zpx=round!(0−asx)∈[0,255]
\textstyle s_x=\frac{b-a}{255},\quad
zp_x=\mathrm{round}!\Big(0-\frac{a}{s_x}\Big)\in[0,255]
sx=255b−a,zpx=round!(0−sxa)∈[0,255]
量化与反量化:
xq=clamp(round(X/sx+zpx),0,255),X^=sx,(xq−zpx)
x_q=\mathrm{clamp}(\mathrm{round}(X/s_x+zp_x),0,255),\quad
\hat X=s_x,(x_q-zp_x)
xq=clamp(round(X/sx+zpx),0,255),X^=sx,(xq−zpx)
为更稳健,可用百分位(如 0.1%–99.9%)或 MSE/KL 搜索 来选 ([a,b]) 而非极值。
- 分位点法(Percentile):丢掉两端少量离群值(如取 0.1%–99.9% 区间),用中间大多数数据定量化范围,快而稳、实现最简单。
- MSE 搜索:枚举(或搜索)一组候选阈值,对每个阈值先量化再反量化,选 重构误差(MSE)最小 的区间,精度通常最好但更耗时。
- KL 搜索:用直方图近似原分布 §,把量化后的分布近似为 (Q),选择使 KL(P‖Q) 最小的阈值,强调保持分布形状,实现较复杂、对直方图设置更敏感。
常用做法 2:ReLU 后的激活
若该量化点之后是 ReLU(输出非负),可令 ([a, b]=[0, \max(X)]),这会增大有效分辨率;zp_x 接近 0(UINT8)。
4.4 为什么反量化系数是 (sx⋅sw[c])(s_x\cdot s_w[c])(sx⋅sw[c])
INT8×INT8 的 GEMM/Conv 会先得到 int32 累加:
yint32(c)=∑i(xq[i]−zpx)⋅wq(c)[i]
y_{\text{int32}}^{(c)}=\sum_i (x_q[i]-zp_x)\cdot w_q^{(c)}[i]
yint32(c)=i∑(xq[i]−zpx)⋅wq(c)[i]
反量化到 FP:
y(c)≈(sx⋅sw[c]);yint32(c)+b(c)
y^{(c)}\approx (s_x\cdot s_w[c]);y_{\text{int32}}^{(c)} + b^{(c)}
y(c)≈(sx⋅sw[c]);yint32(c)+b(c)
-
s_x:该量化点激活的 per-tensor scale(标量) -
s_w[c]:该层第c个输出通道的权重 scale -
b:在 PTQ 推理里要么在 FP 域相加,要么先用同一系数折算成int32再相加
4.5 每层有没有不同的 s,zp?
-
有。每一个量化点(通常是每层的输入/输出)都有自己的
s_x,zp_x。 -
权重则是每层每个输出通道一个
s_w[c](对称时zp_w=0)。
5. 方法谱系
5.1 PTQ(后训练量化,最常用起点)
不用(或很少)训练,靠少量数据做校准(统计激活范围)。
-
动态量化(Dynamic Quant)
-
权重离线 INT8;激活在运行时用当前 batch 的统计量动态量化。
-
最省事(尤其 CPU / Linear),加速有限,精度一般能保。
-
-
静态量化(Static Quant)
-
先用校准集跑一遍,统计激活分布,固定 s、z;再导出 INT8 模型。
-
配合后端(TensorRT / FBGEMM / OpenVINO / ONNX Runtime)→ 真加速。
-
观察者(决定范围):MinMax / MovingAverage / Percentile / MSE/KL(更稳)。
-
-
W-only 权重量化(INT8 / NF4 4bit)
-
只压权重,激活仍 FP;几乎零精度损失,显存立减。
-
工程里最常见:bitsandbytes INT8/NF4、AutoGPTQ/AWQ(见 C 也相关)。
-
5.2. QAT(量化感知训练,精度最稳)
训练时在前向插入“假量化”节点(round/clip 不可导,用直通估计),反向仍浮点更新,让模型学会适应量化误差。
-
优点:W8A8 甚至 W4A8 都能把精度拉回;
-
代价:要再训(你可以沿用蒸馏的训练脚本,小步长再训 1–3 epoch)。
-
代表:LSQ/LSQ+(把 scale 当可学习参数,收敛快)、FakeQuant + Observers。
5.3. LLM/Transformer 专项 PTQ(权重侧更激进)
-
GPTQ(W-only,常做 INT4):列/块级误差最小化,W4 精度很强,大模型常用。
-
AWQ(Activation-aware W Quant):激活感知,挑选更该保真的权重通道,W4 也稳。
-
SmoothQuant:把激活的 outlier“平滑”到权重,降低激活动态范围,助力 W8A8。
6. NF4 量化
上面有提到过W-only 中的NF4量化,接下来我们重点讲讲这个过程。
6.1 NF4 的流程(权重矩阵 (W))
以线性层/卷积层的权重矩阵为例(按输出通道或列方向切块最常见):
-
分块(Blockwise)
-
将 (W) 按列(或行)划成若干块,每块长度例如 64 or 128。
-
目的是让每块内的数值范围更“同质”,用一个尺度就能归一化好该块。
-
-
块内归一化(Normalize)
-
对每个块 wblkw_\text{blk}wblk:
σ=RMS/Std(wblk)(也有用分位尺度/robust std) \sigma=\mathrm{RMS/Std}(w_\text{blk})\quad(\text{也有用分位尺度/robust std}) σ=RMS/Std(wblk)(也有用分位尺度/robust std)
w′=wblkσ+ε w' = \frac{w_\text{blk}}{\sigma+\varepsilon} w′=σ+εwblk -
这样 (w’) 大致服从 (N(0,1))(零均值、单位尺度)。
-
-
码本映射(Quantize with NF4 Codebook)
-
准备一组固定的 NF4 码本 C=c0,…,c15C={c_0,\dots,c_{15}}C=c0,…,c15(16 个非等距浮点值,依据正态分布的最优/近似最优代表值设计)。
-
对块内每个元素 w′[j]w'[j]w′[j],找到最近的代表值索引:
q[j]=argmink∈[0,15]∣w′[j]−ck∣ q[j] = \arg\min_{k\in[0,15]} |w'[j]-c_k| q[j]=argk∈[0,15]min∣w′[j]−ck∣ -
保存 4bit 索引
q[j](打包在字节里) + 该块的尺度 σ(FP16/FP32)\sigma(FP16/FP32)σ(FP16/FP32)。
-
-
反量化(Dequantize)
-
推理时,不需要恢复成全精度权重;内核常边乘边还原:
w^[j]=σ⋅C[q[j]] \hat{w}[j] = \sigma \cdot C[q[j]] w^[j]=σ⋅C[q[j]] -
实现上常把查表与 GEMM 融合(LUT→乘→累加,INT/FP16 累加)。
-
只量化权重,激活通常保持 FP16/FP32(或更高位)。因此 NF4 主要省显存与带宽;算力省的不如 W8A8 全整型。
6.2 为什么这么做
-
真实神经网络权重常近似 零均值、长尾 分布;
-
线性 INT4 等间距会对尾部浪费、对中心不够细;
-
NF4 非均匀码本 + 块级归一化:在 0 附近放更多级别、按块自适应尺度 → 误差明显降低;
-
码本是固定常量表,实现高效、解码开销低。
6.3 小示例(PyTorch 伪代码)
import torch
# 假设一个线性层权重 W: [Cout, Cin],按行分块(每行每 64 个元素一块)
BLOCK = 64
NF4_CODEBOOK = torch.tensor([
-1.5141, -1.0842, -0.7957, -0.6065,
-0.4689, -0.3618, -0.2725, -0.1946,
0.1946, 0.2725, 0.3618, 0.4689,
0.6065, 0.7957, 1.0842, 1.5141
], dtype=torch.float32) # 示例值:真实实现用更精确的码本
def nf4_pack_row(row):
# row: [Cin]
nblk = (row.numel() + BLOCK - 1) // BLOCK
idx_packed = [] # 存 4bit 索引
scales = [] # 存每块 sigma
for b in range(nblk):
seg = row[b*BLOCK:(b+1)*BLOCK]
if seg.numel() == 0: break
sigma = seg.pow(2).mean().sqrt() + 1e-12 # RMS/Std
wn = seg / sigma
# 最近邻码本索引
# [seg_len, 16] 的距离,取最小的索引
dist = (wn.unsqueeze(1) - NF4_CODEBOOK.view(1, -1)).abs()
q = dist.argmin(dim=1).to(torch.uint8) # [seg_len]
# 打包两个 4bit 到一个 byte (示例)
if q.numel() % 2 == 1:
q = torch.cat([q, torch.zeros(1, dtype=q.dtype)])
q_even = q[0::2]; q_odd = q[1::2]
packed = (q_even | (q_odd << 4)).to(torch.uint8) # [seg_len/2]
idx_packed.append(packed)
scales.append(sigma)
return idx_packed, torch.stack(scales)
def nf4_dequantize_row(idx_packed, scales):
# 解包 & 反量化
vals = []
for packed, sigma in zip(idx_packed, scales):
q_even = packed & 0x0F
q_odd = (packed >> 4) & 0x0F
q = torch.stack([q_even, q_odd], dim=1).flatten()
w_hat = sigma * NF4_CODEBOOK[q.long()]
vals.append(w_hat)
return torch.cat(vals)
# 用法:逐行量化
# for each row in W: idx_packed[row], scales[row] = nf4_pack_row(W[row])
真实库会:
用更精细的码本(通常通过对 (N(0,1)) 的最优代表值拟合得到);
更多工程优化:SIMD/向量化、CUTLASS/CUDA 融合 LUT-GEMM、块大小/对齐、标量类型选择(FP16/FP32 scale)。
7. 疑问
7. 1 动态量化和静态量化
下面这俩词都是在说激活如何量化(以及量化参数何时确定)。权重一般是离线量好的;区别主要在**激活的 s/zp(scale/zero-point)**是“离线固定”还是“运行时现算”。
7. 1.1 动态量化(Dynamic Quantization)
-
什么时候定激活的 s/zp:推理时根据当前 batch 的激活范围临时计算(min/max 或直方图),再把激活压成 INT8;乘加后再按该 batch 的 s 还原/继续计算。
-
权重:通常已离线量成 INT8(per-channel)。
-
特点:不需要校准数据;泛化好,部署简单;吞吐提升不错(CPU)。但每次前向都要做一点统计与量化/反量化,且区间不稳定时可能抖动一点精度。
-
PyTorch 对应:
torch.ao.quantization.quantize_dynamic(model, {nn.Linear}, dtype=torch.qint8)。
7. 1.2 静态量化(Static Quantization)
-
什么时候定激活的 s/zp:用一小段校准数据离线跑一遍,把激活的 s/zp“烤死”进模型(每层/每点都固定)。
-
权重:同样离线量化(per-channel)。
-
特点:推理时无需再次统计,算子可完全走 INT8(激活/权重都 INT8,累加 INT32),最快、最省;但需要校准集,且若线上分布偏离校准分布,精度可能受影响。
-
PyTorch/PTQ 流程:
prepare(插 observer)→ 校准(前向几百~几千样本)→convert(固化量化算子)。
7. 1.3 快速对照

7. 1.4 和 GPU W-only(bnb)关系
-
W-only(W8/NF4):只量化权重;激活保持 FP,因此**不涉及“动态/静态激活量化”**这件事。
-
若在 GPU 上做真正 W8A8 INT8(如 TensorRT/ORT CUDA INT8),那就需要**静态 PTQ(有校准)**或 QAT 来确定激活 s/zp。
7. 1.5 小示例(极简伪代码)
# 动态:每次前向
x_int8, sx = quantize_runtime(x_fp32) # 统计本 batch min/max
y_int32 = x_int8 @ W_int8 # INT8xINT8->INT32
y_fp32 = (sx * sW) * y_int32 + bias_corr
# 静态:离线校准后
# sX、sW 在 convert 后已固定在算子里
y_int32 = x_int8 @ W_int8
y_fp32 = (sX * sW) * y_int32 + bias_corr # 运行时不再统计
一句话:
- 动态量化:激活“现秤现卖”;
- 静态量化:激活“提前称好,贴死标签”。
7. 2 CPU 和GPU选择
7. 2.1 快速结论
-
概念层面:
-
动态量化(Dynamic)=激活的 scale/zero-point 在运行时(按 batch)估计。
-
静态量化(Static)=激活的量化参数在离线校准或训练阶段就固定好。
-
-
工程支持层面:
-
PyTorch 原生(torch.ao.quantization):成熟支持 CPU(FBGEMM/QNNPACK)。动态量化 API 基本是 CPU 路线;GPU 上没有官方通用的 CUDA INT8 动态量化内核。
-
GPU 真 INT8(W8A8):通常走 TensorRT 或 ONNX Runtime CUDA/TensorRT EP:
-
PTQ/静态量化(需校准) ✅ 常见、成熟。
-
QAT ✅ 常见、成熟。
-
动态量化(运行时再算激活尺度) ❌ 基本不做/不常见,因为部署图需要固定尺度让内核融合优化。
-
-
GPU W-only(bitsandbytes 的 W8/NF4):这是只量化权重的路径,激活仍是 FP,因此谈不上“动态/静态激活量化”。
-
7. 2.2 该怎么选
-
GPU 上要真正 W8A8(整型 GEMM) → 用 TensorRT 或 ORT CUDA/TensorRT,选择:
-
PTQ 静态量化(有一小段校准数据):最常用;
-
QAT(有训练资源、精度要求高)。
-
-
GPU 上想快速省显存/带宽 → 用 bitsandbytes W-only(W8 或 NF4),不涉及激活量化。
-
CPU 上 → PyTorch 原生 动态/静态 PTQ 都方便(动态无需校准,静态更快更稳)。
7. 2.3 一句话
-
“动态/静态”不是 CPU 专属;但在实际框架里:
-
CPU 易用“动态/静态 PTQ”;
-
GPU 主流是 静态 PTQ/QAT(TensorRT/ORT),动态激活量化几乎不用;
-
**W-only(bnb)**不涉及激活量化,自然也就没有“动态/静态”之分。
-
3799

被折叠的 条评论
为什么被折叠?



