直接回答

大模型量化不是一个通用的压缩开关,而是一份完整的部署契约:它必须说明量化对象是权重、激活还是 KV Cache,数值表示是 W4A16 还是 W8A8,量化算法与校准数据如何生成制品,以及哪个运行时内核负责执行。更低的名义位宽可能减少存储或内存流量,却不保证延迟更低、总显存更少或模型行为合格。只有在目标硬件和真实负载上同时通过业务质量、安全、延迟、显存与 Goodput 门禁,候选制品才具备生产价值。

核心要点

  • 必须把 GPTQ、AWQ、SmoothQuant 等算法,与 INT4/INT8 数值表示、GGUF 容器和推理运行时分层讨论。
  • 名义位宽只能估算权重负载下界;KV Cache、缩放参数、工作区、内存分配器与并发共同决定服务容量。
  • 基础 Checkpoint、Tokenizer、Chat Template、量化器版本、校准数据、打包布局和制品校验和都属于模型契约。
  • 基准应拆成同引擎受控赛道与最佳生态赛道,不能拼接不同引擎的 tokens/s 后宣布算法排名。
  • 灰度只接纳同时满足业务质量、安全、延迟、显存和 Goodput 门槛的量化制品。

大模型量化究竟改变了什么

量化使用有限的离散值近似原始实数张量。常见的仿射量化可以写成:

text
q = clip(round(x / scale) + zero_point, q_min, q_max)
x_hat = scale * (q - zero_point)

缩放因子与可选零点可以按整个张量共享,也可以按通道、分组或数据块计算。粒度越细,通常越能跟随局部分布,但会引入更多元数据,并依赖不同的打包方式与内核。

W4A16 表示 4 位权重和 16 位激活,W8A8 表示 8 位权重和 8 位激活。但这些标签仍未说明分组大小、对称或非对称映射、累加精度、未量化模块、张量布局,以及 KV Cache 是否仍使用 FP16 或 BF16。

flowchart LR B["基线 Checkpoint"] --> C["校准与量化器"] C --> A["量化制品"] A --> R["运行时与内核"] R --> H["目标硬件"] H --> W["真实工作负载"] W --> G{"质量与服务门禁"} G -->|通过| P["灰度晋级"] G -->|失败| X["拒绝或修订"]

权重、激活与 KV Cache 是三个独立对象

三类量化解决的瓶颈不同,不能用“模型已经 INT4”一笔带过。

量化对象 主要目标 典型风险 必测指标
权重 减少制品体积和权重内存流量 层级质量退化、打包布局无内核支持 业务质量、加载时间、Decode 表现
激活 使用低精度矩阵计算 异常值和累加误差 任务切片质量、Prefill 延迟
KV Cache 降低上下文长度和并发带来的内存增长 长上下文质量退化 长上下文验收率、容量、ITL

仅权重量化不会自动减少 KV Cache。长 Prompt 和高并发场景中,KV Cache 可能反而成为显存主导项。分页、隔离与容量规划可参考独立的 KV Cache 工程指南

算法、格式、容器与运行时不能混排

GPTQ、AWQ、GGUF、bitsandbytes 和 llama.cpp 经常出现在同一张“量化方法对比表”中,但它们解决的是不同层级的问题。

层级 代表对象 回答的问题
数值表示 INT8、INT4、FP8、NF4 张量使用哪些值与位布局?
量化算法 GPTQ、AWQ、SmoothQuant 如何选择缩放参数或舍入后的权重?
加载或转换路径 bitsandbytes、LLM Compressor、TorchAO 在哪个阶段完成转换或低精度加载?
模型容器 GGUF 张量与元数据如何打包?
推理运行时 vLLM、llama.cpp、Ollama、TensorRT-LLM 哪些内核负责调度并执行请求?

GGUF 官方规范 定义的是带可扩展元数据、张量信息、对齐规则和内存映射能力的二进制文件格式。GGUF 可以承载不同编码的量化张量,但它本身不是与 GPTQ 或 AWQ 同层的算法。

运行时支持也会持续变化。vLLM 当前量化文档 维护方法与硬件兼容矩阵,并没有承诺每种量化制品都能在所有设备上高效运行。生成制品前应固定运行时版本,并根据当前官方矩阵验证目标路径。

GPTQ、AWQ 与 SmoothQuant 的机制差异

三种方法优化的是不同近似问题。论文能够证明机制及其报告实验,不能证明一个方法在所有模型、硬件和运行时上永久胜出。

GPTQ 使用近似二阶信息选择舍入

GPTQ 原始论文 描述了一种训练后、仅权重的一次性量化方法。它利用近似二阶信息,在逐层量化过程中更新舍入决策。生产落地时真正需要回答的是:目标模型是否受支持,校准样本能否代表业务输入,以及部署运行时是否拥有匹配制品布局的高效内核。

论文中的量化耗时、质量和加速结果只属于其报告的模型、硬件、软件与参数,不能直接迁移成新服务的容量结论。

AWQ 通过激活感知保护关键通道

AWQ 原始论文 描述的是激活感知的仅权重量化。它观察激活分布,识别关键权重通道,并搜索等价的逐通道缩放以降低关键通道的量化误差,而不是保留一个混合精度子集。该方法不依赖反向传播或权重重建。

AWQ 不保证必然比 GPTQ 更快或质量更高。模型结构、分组大小、校准数据、运行时内核与工作负载会共同改变结果。AWQ 术语页 进一步解释其机制和适用边界。

SmoothQuant 把激活难题迁移到权重

SmoothQuant 原始论文 面向 W8A8 推理。它通过等价变换平滑激活异常值,将部分量化难度迁移到权重,使两个矩阵乘操作数更适合整数计算。迁移强度和运行时实现仍然是具体部署中的参数,而不是跨模型常量。

flowchart TB Q{"当前部署瓶颈"} --> W["权重内存受限"] Q --> A["激活计算受限"] Q --> K["长上下文容量受限"] W --> G["评估 GPTQ 或 AWQ 路径"] A --> S["评估 W8A8 或 FP8 路径"] K --> V["评估 KV Cache 量化"] G --> E["基准验证精确制品"] S --> E V --> E

为什么位宽不等于服务显存

理论权重负载只能作为下界:

text
ideal_weight_bytes = parameter_count * bits_per_weight / 8

真实文件和常驻权重还包含缩放因子、零点、分组元数据、未量化层、Tokenizer、对齐填充和容器开销。服务运行时还会分配 KV Cache、临时激活、CUDA Graph 或编译缓冲区、通信缓冲区、队列与内存碎片。

因此,“7B INT4 只需要 4 GB”不是完整的工程结论。它没有说明 4 GB 指模型文件、加载后权重、空闲进程、单请求峰值,还是满足并发目标的安全容量。LLM 推理工程指南 解释了为什么 TTFT、Token 间延迟、批处理和调度必须联合评测。

低精度不保证推理加速

只有当内存流量或低精度计算收益大于额外开销时,量化路径才会更快。常见失败模式包括:

  • 运行时在每次算子执行前把权重反量化到更宽类型;
  • 制品打包布局与现有内核不匹配;
  • 未支持模块回退到更慢的算子;
  • 短 Prompt、小 Batch 无法摊薄启动与转换开销;
  • Decode 受 KV Cache 流量或调度限制,而不是受权重读取限制;
  • 启动阶段重打包增加加载耗时和发布抖动。

Prefill 与 Decode 必须分开统计。候选制品可能提高吞吐却恶化首 Token 延迟,也可能减少权重显存却不能提升有效并发。

建立可复现的量化制品契约

量化制品必须能够追溯到不可变基线和完整配方。这份契约应与制品一同保存,而不是只存在于容易丢失的部署工单中。

json
{
  "artifact_id": "support-model-w4a16-awq-r3",
  "base_checkpoint": "registry.example/model",
  "base_revision": "immutable-revision",
  "tokenizer_revision": "immutable-revision",
  "chat_template_sha256": "sha256:...",
  "quantizer": {
    "name": "awq",
    "version": "pinned-version",
    "weight_bits": 4,
    "activation_bits": 16,
    "group_size": 128,
    "symmetric": false,
    "excluded_modules": ["output_head"]
  },
  "calibration": {
    "dataset_id": "approved-calibration-slice",
    "dataset_sha256": "sha256:...",
    "sampling_policy": "language-and-task-stratified"
  },
  "packaging": {
    "format": "runtime-specific",
    "runtime": "pinned-runtime",
    "kernel": "verified-kernel-family"
  },
  "artifact_sha256": "sha256:...",
  "license": "verified"
}

校准集需要覆盖生产语言、Prompt 长度、业务领域、工具调用和安全敏感输入。“几百条通用样本就足够”不是普遍规则,样本数量只有与覆盖度和稳定性检验一起记录才有意义。

Tokenizer 与 Chat Template 也必须固定。模板变化可能比量化本身产生更明显的输出差异,从而破坏归因。

用双赛道建立可复现基准

可靠评测需要把算法效果与生态成熟度分开。

同引擎受控赛道

固定基础 Checkpoint、Revision、Tokenizer、Chat Template、校准切片、评测集、服务版本、硬件、请求调度与测量代码。在可行时只改变量化配方。它回答:在受控条件下,量化选择究竟改变了什么?

最佳生态赛道

允许每个候选使用其最成熟的运行时和内核,但保持模型身份、工作负载、质量门禁和硬件等级可比。它回答:每套可部署技术栈在生产形态下能够交付什么?

不能把不同引擎、Batch、Prompt 长度与输出长度下的 tokens/s 放在同一张表中,随后宣布算法排名。

维度 必须保留的证据
业务质量 业务验收率或任务分数,并给出置信区间
行为完整性 工具调用有效率、JSON/Schema 有效率、拒答和安全切片
上下文 短、中位与尾部 Prompt,长上下文检索或推理
延迟 TTFT、ITL 或 TPOT、端到端延迟分位数
容量 峰值显存、稳定并发、排队时间、OOM 率
效率 相同服务目标下的吞吐和 Goodput
运维 启动时间、制品来源、回滚耗时、错误率

Goodput 只统计同时满足质量和延迟目标的请求,可以避免“吞吐很高但结果不可用”的候选获胜。

可运行的量化候选门禁

下面的 Python 程序不执行量化,而是验证已经测量的候选,防止团队仅凭位宽或吞吐决定晋级。它只依赖标准库。

python
from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any


REQUIRED_METRICS = {
    "business_acceptance": (0.0, 1.0),
    "structured_output_validity": (0.0, 1.0),
    "safety_pass_rate": (0.0, 1.0),
    "ttft_p95_ms": (0.0, None),
    "itl_p95_ms": (0.0, None),
    "peak_memory_gib": (0.0, None),
    "goodput_rps": (0.0, None),
}


def number(value: Any) -> bool:
    return isinstance(value, (int, float)) and not isinstance(value, bool)


def validate(candidate: dict[str, Any]) -> list[str]:
    errors: list[str] = []
    for field in ("artifact_id", "base_revision", "artifact_sha256"):
        if not isinstance(candidate.get(field), str) or not candidate[field]:
            errors.append(f"{field} must be a non-empty string")

    metrics = candidate.get("metrics")
    gates = candidate.get("gates")
    if not isinstance(metrics, dict) or not isinstance(gates, dict):
        return errors + ["metrics and gates must be objects"]

    for name, (lower, upper) in REQUIRED_METRICS.items():
        value = metrics.get(name)
        if not number(value) or value < lower or (
            upper is not None and value > upper
        ):
            errors.append(f"invalid metric: {name}")

    for name, rule in gates.items():
        if name not in REQUIRED_METRICS:
            errors.append(f"unknown gate: {name}")
            continue
        if not isinstance(rule, dict) or set(rule) != {"op", "value"}:
            errors.append(f"gate {name} needs op and value")
            continue
        op, limit = rule["op"], rule["value"]
        if op not in {"min", "max"} or not number(limit):
            errors.append(f"invalid gate: {name}")
            continue
        measured = metrics.get(name)
        if number(measured):
            passed = measured >= limit if op == "min" else measured <= limit
            if not passed:
                errors.append(
                    f"gate failed: {name} measured={measured} {op}={limit}"
                )
    return errors


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("candidate", type=Path)
    args = parser.parse_args()
    try:
        value = json.loads(args.candidate.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as error:
        parser.error(str(error))
    if not isinstance(value, dict):
        parser.error("candidate root must be an object")
    errors = validate(value)
    if errors:
        for error in errors:
            print(f"ERROR: {error}")
        raise SystemExit(1)
    print(f"candidate passed: {value['artifact_id']}")


if __name__ == "__main__":
    main()

保存为 quantization_gate.py,再检查一份真实测量结果:

bash
python quantization_gate.py candidate.json

输入应来自固定测试条件,而不是估算。以下数值只用于展示数据结构,不代表任何模型或算法的性能:

json
{
  "artifact_id": "support-model-w4a16-awq-r3",
  "base_revision": "immutable-revision",
  "artifact_sha256": "sha256:...",
  "metrics": {
    "business_acceptance": 0.94,
    "structured_output_validity": 0.99,
    "safety_pass_rate": 0.98,
    "ttft_p95_ms": 420,
    "itl_p95_ms": 38,
    "peak_memory_gib": 18.6,
    "goodput_rps": 7.4
  },
  "gates": {
    "business_acceptance": {"op": "min", "value": 0.92},
    "structured_output_validity": {"op": "min", "value": 0.98},
    "safety_pass_rate": {"op": "min", "value": 0.97},
    "ttft_p95_ms": {"op": "max", "value": 500},
    "itl_p95_ms": {"op": "max", "value": 45},
    "peak_memory_gib": {"op": "max", "value": 20},
    "goodput_rps": {"op": "min", "value": 6.5}
  }
}

灰度晋级与回滚

量化模型晋级应遵循模型和服务变更的同等门禁。离线基准通过后,不能直接替换全部基线实例。

stateDiagram-v2 草案 --> 可复现 可复现 --> 离线通过 离线通过 --> 影子流量 影子流量 --> 小流量灰度 小流量灰度 --> 正式晋级 影子流量 --> 已拒绝 小流量灰度 --> 已回滚
  1. 检查 Manifest 完整性和制品校验和。
  2. 使用精确基线完成离线质量与性能门禁。
  3. 使用代表性影子流量验证,不向用户返回候选输出。
  4. 按租户或工作负载灰度,避免不可追踪的随机制品切换。
  5. 监控质量代理指标、工具错误、Schema 失败、延迟、显存和 Goodput。
  6. 任一硬门禁失败时回滚到固定基线。

灰度期间必须保留与候选容量兼容的基线。需要重新构建或临时下载旧制品的流程不是真正可执行的回滚。

常见失败模式

用固定排行榜选型

“AWQ 适合速度、GPTQ 适合精度、GGUF 适合 CPU”不是长期有效的规则。它隐藏了模型支持、内核成熟度、制品来源和工作负载差异。

只测困惑度

困惑度可以暴露部分分布变化,却不能证明工具调用正确、结构化输出有效、多语言行为稳定、拒答策略一致或业务任务通过。

忽略校准数据治理

未版本化的校准数据会让制品无法复现。直接使用敏感生产 Prompt 还会引入隐私和授权风险。应记录数据血缘,并使用获批且具有代表性的切片。

比较不同模型 Revision

如果候选来自不同 Checkpoint、Tokenizer 或 Chat Template,就无法隔离量化影响。所有组件都应固定后再做质量归因。

把成功加载当成兼容

运行时能够加载制品,不代表它使用了预期内核。它可能重打包、对部分层回退,甚至走低效路径。必须检查日志和 Profile,再测量完整请求链路。

常见问题

INT8 一定比 INT4 更适合生产吗?

不一定。更宽的表示通常有更多质量余量,但生产适配是测量结果,不是位宽标签。拥有成熟内核并通过业务门禁的 INT4 制品,可能优于存在算子回退或容量不足的 INT8 路径。两者必须接受相同门禁。

量化一定能降低云端推理成本吗?

不一定。只有显存节省转化为有效并发,或低精度内核提高 Goodput 时,成本才可能下降。如果系统受队列、KV Cache 或低效内核限制,成本可能不变。应计算满足 SLO 的每个已验收请求成本,而不是原始 Token 成本。

QAT 一定比 PTQ 更准确吗?

不一定。量化感知训练允许优化器适应低精度,但结果取决于训练数据、目标函数、稳定性和最终部署格式。强 PTQ 方法可能已经足够,而控制不当的 QAT 也可能导致行为回归。必须比较最终制品。

同一个量化制品能在所有运行时中执行吗?

不能。容器、张量编码、打包布局、算子和内核都可能依赖运行时。应核对当前官方兼容文档、固定版本,并验证实际加载的内核路径。

微调前还是微调后量化?

取决于工作流。QLoRA 在训练 Adapter 时加载量化且冻结的基础模型,这与生成最终推理制品不是同一件事。Adapter 合并或组合后,仍需对实际服务制品重新评测与打包。可结合 LoRA 术语本地大模型部署指南 理解完整链路。

总结

大模型量化是一项端到端推理工程决策,不是固定压缩比例。先分清量化对象、数值表示、算法、容器和运行时,再保存完整制品契约。用同引擎受控赛道与最佳生态赛道评测候选,并把业务质量、安全、延迟、显存和 Goodput 设为晋级门禁,才能把一个低位宽文件转化为可部署、可审计、可回滚的模型制品。

参考资料