# 模型量化(一):量化原理和方法详解

部署运行你感兴趣的模型镜像

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(qz)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每个维度都有他自己的尺度

  • 位宽: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)quantdequant**。


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_xzp_x∈[0,255]

这是 PTQ/QAT 中最常见的组合:W:对称/逐通道;A:非对称/逐张量。硬件支持最好。


4.2 权重的 szp(W8,per-channel,对称)

以线性层权重 (W∈R[Cin,,Cout])(W\in\mathbb{R}^{[Cin,,Cout]})(WR[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 激活的 szp(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=255ba,zpx=round!(0sxa)[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,(xqzpx)

为更稳健,可用百分位(如 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])(sxsw[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)(sxsw[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(后训练量化,最常用起点)

不用(或很少)训练,靠少量数据做校准(统计激活范围)。

  1. 动态量化(Dynamic Quant)

    • 权重离线 INT8;激活在运行时用当前 batch 的统计量动态量化。

    • 最省事(尤其 CPU / Linear),加速有限,精度一般能保。

  2. 静态量化(Static Quant)

    • 先用校准集跑一遍,统计激活分布,固定 s、z;再导出 INT8 模型。

    • 配合后端(TensorRT / FBGEMM / OpenVINO / ONNX Runtime)→ 真加速

    • 观察者(决定范围):MinMax / MovingAverage / Percentile / MSE/KL(更稳)

  3. 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(权重侧更激进)

  1. GPTQ(W-only,常做 INT4):列/块级误差最小化,W4 精度很强,大模型常用。

  2. AWQ(Activation-aware W Quant):激活感知,挑选更该保真的权重通道,W4 也稳

  3. SmoothQuant:把激活的 outlier“平滑”到权重,降低激活动态范围,助力 W8A8


6. NF4 量化

上面有提到过W-only 中的NF4量化,接下来我们重点讲讲这个过程。


6.1 NF4 的流程(权重矩阵 (W))

以线性层/卷积层的权重矩阵为例(按输出通道或列方向切块最常见):

  1. 分块(Blockwise)

    • 将 (W) 按列(或行)划成若干块,每块长度例如 64 or 128

    • 目的是让每块内的数值范围更“同质”,用一个尺度就能归一化好该块。

  2. 块内归一化(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))(零均值、单位尺度)。

  3. 码本映射(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]=arg⁡min⁡k∈[0,15]∣w′[j]−ck∣ q[j] = \arg\min_{k\in[0,15]} |w'[j]-c_k| q[j]=argk[0,15]minw[j]ck

    • 保存 4bit 索引 q[j](打包在字节里) + 该块的尺度 σ(FP16/FP32)\sigma(FP16/FP32)σFP16/FP32

  4. 反量化(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):通常走 TensorRTONNX 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)**不涉及激活量化,自然也就没有“动态/静态”之分。

您可能感兴趣的与本文相关的镜像

PyTorch 2.7

PyTorch 2.7

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

idealmu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值