0%

GPTQ 算法

一句话定位:无需训练的 INT4 量化方案——逐层逐列量化,并用二阶信息把”已产生的量化误差”补偿到还没量化的权重上,使 4bit 也能保持较好精度。

1. 核心思想:误差补偿

朴素的逐权重取整(round-to-nearest)会让每个权重的误差独立累积,INT4 下整层输出严重偏移。

GPTQ 的关键洞察:量化的目标不是让权重接近原值,而是让该层的输出接近原输出。因此:

  • 当某一列权重被量化产生误差后,立刻调整尚未量化的其余列,用它们来”抵消”这个误差对层输出的影响;
  • 这样误差不再单调累积,而是被后续权重不断吸收补偿。

2. 实现要点

  • 逐层进行(layer-wise):一层层独立求解,把全局问题拆成若干可解的小问题;
  • 逐列(按输入维度)顺序量化:每量化一列,就把残余误差按最优方向分摊到剩余未量化列;
  • 用二阶信息(Hessian 近似):以该层的重构误差为目标,其最优补偿方向由 Hessian H ≈ 2XXᵀ(X 为该层输入激活)决定——即用校准数据的激活二阶统计量衡量”哪些权重更重要、误差该怎么分摊”。实现上借助 Cholesky 分解等手段保证数值稳定与效率。

3. 优势

  • 一次校准即可,无需训练:只需少量校准数据跑前向、收集激活统计,属于 PTQ 家族(4.2)的高级方法,成本远低于 QAT(4.3);
  • INT4 精度可用:这是它的最大价值——把 4bit 从”不可用”推到”可生产”,显存直降约 75%,使大模型能在单卡部署;
  • 生态成熟(AutoGPTQ 等),与主流推理引擎(vLLM、TensorRT-LLM)集成良好。

局限:量化耗时随模型规模上升(需逐层做矩阵分解);对校准集分布仍有依赖;主要量化权重(weight-only),激活仍为高精度,因此收益主要来自访存与显存而非整数算力。

参考:论文《GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers》(Frantar et al., 2022)

QAT(量化感知训练)

一句话定位:把量化的误差在训练阶段就暴露给模型,让模型主动学会适应量化噪声——精度保留最好,但要付出训练资源与数据的代价。

1. 机制:伪量化节点 + STE

1 伪量化(Fake Quantization)

  • 在训练图中插入伪量化节点:对权重/激活执行”量化→反量化”(quantize 后立刻 dequantize)。
  • 前向计算因此带上了量化后的数值误差(数值等价于低精度推理),但张量仍以浮点存储、训练照常进行。
  • 于是模型在训练过程中”看得见”量化噪声,会自发把权重调整到对量化更鲁棒的区域(如避免产生极端 outlier)。

2 STE(Straight-Through Estimator)近似梯度

  • 问题:round() 的导数几乎处处为 0,梯度无法回传,训练会停滞。
  • STE 的做法:反向传播时直接把梯度”穿过”取整操作(视其导数为 1,通常配合对截断范围外的梯度置零)。
  • 这是一个有偏但极其有效的近似,使量化网络可用标准 SGD/Adam 训练。

2. 优势与代价

  • 优势:精度保留更好。尤其在低位宽(INT4 及以下)、小模型、或对精度极敏感的任务上,QAT 明显优于 PTQ(4.2)——因为模型参数本身已针对量化优化过。
  • 代价
    • 需要训练资源(GPU 时长,接近一次微调的成本);
    • 需要训练数据(且要有代表性;对只拿到开源权重、没有原始数据的场景不可行);
    • 需要改动训练流程与代码,工程链路更长,迭代慢。

3. 适用场景

  • 精度敏感且量化收益必须拿到的场景(如端侧部署、极低位宽需求);
  • PTQ 已尝试且精度不达标;
  • 具备训练数据与算力,且模型会长期稳定使用(一次投入长期摊销)。

反之,若追求快速上线、无训练数据、INT8 即可满足 → 直接用 PTQ。

参考:PyTorch 官方文档 Quantization(QAT 章节)

知识蒸馏

一句话定位:让小模型(Student)去学大模型(Teacher)的输出分布而非仅学标准答案,从而以远低的推理成本继承大模型的能力。

1. 机制:软标签承载类间相似性

  • 硬标签(one-hot 真实标签)只告诉模型”正确答案是猫”;
  • 软标签是 Teacher 输出的完整概率分布,它还告诉模型”这张图有 70% 像猫、25% 像狐狸、0.01% 像卡车”——即类间相似性结构。这部分信息(Hinton 称为 dark knowledge)是硬标签完全没有的,也是蒸馏有效的根本原因。

温度(Temperature)T 的作用:

1
p_i = softmax(z_i / T)
  • T=1 即普通 softmax,Teacher 的分布通常非常尖锐(正确类接近 1),其余类的相对信息被压平、几乎学不到;
  • T > 1 使分布变平滑,放大非目标类之间的相对差异,让 Student 能学到这些相似性信息;
  • 推理时恢复 T=1。

2. 联合 Loss

Student 用软标签 + 硬标签的加权组合训练:

1
L = α · L_soft(Student(T), Teacher(T)) + (1-α) · L_hard(Student, 真实标签)
  • L_soft 通常用 KL 散度,在温度 T 下对齐两者分布(梯度需乘 T² 做尺度补偿);
  • L_hard 是常规交叉熵,防止 Student 被 Teacher 的错误带偏;
  • α 控制两者权重,是主要调参点。

3. 适用场景与工程价值

  • 把大模型能力迁到小模型以降本:这是压缩语境下的核心用途——线上用小模型服务,推理成本(显存、延迟、卡数)成倍下降;
  • 常见形态:大 LLM 生成高质量输出/推理过程 → 作为训练信号蒸馏到小模型(如生成式蒸馏、思维链蒸馏);
  • 与量化/剪枝的区别:蒸馏是换一个更小的模型(结构可自由设计),量化是保持结构降低位宽,剪枝是删掉部分结构。三者可叠加。

在 4.7 的四维评估体系里,”小模型替换(蒸馏)”应与量化、剪枝放在同一张表里比较,而不是被当作另一类问题。

参考:论文《Distilling the Knowledge in a Neural Network》(Hinton et al., 2015)

压缩方案的四维评估体系

一句话定位:面试考的不是”你会不会量化”,而是”你能不能量化地决策“。把所有压缩手段放进同一张表、用统一四维打分,是把技术选型讲成工程判断的关键产出物。

1. 四个维度

维度 含义 常用指标
效果损失 % 压缩后模型质量下降幅度 任务准确率 / 困惑度 / 业务核心指标的相对下降
成本降低 % 服务同样流量所需资源下降 显存占用、卡数、单位请求成本($/1k tokens)
吞吐提升倍数 单位时间处理能力 QPS / tokens-per-second 相对基线的倍数
稳定性 是否引入长尾与异常 P99 延迟、异常率/失败率、输出异常(乱码、截断)比例

关键点:稳定性最容易被忽略却最致命——量化可能让平均指标看着没变,却在特定输入上产生崩坏输出(长尾风险);剪枝/蒸馏也可能在少数类别上大幅退化。必须看 P99 与异常率,而不只看均值(同 5.4 基线方法论)。

2. 配合业务可接受阈值

评估表本身不做决策,决策来自业务预先设定的阈值,例如:

“效果损失 < 1%,换取成本降低 40%” → 接受;
“效果损失 3%,成本降 50%” → 拒绝(超出质量红线)。

先与业务方约定阈值,再用表格筛选,避免陷入”哪个技术更先进”的无效争论。

3. 小模型替换纳入同一框架

重要方法论:蒸馏出的小模型 / 直接换用更小的开源模型,应与量化、剪枝在同一张表里比较(见 4.5)。

理由:业务只关心”效果、成本、吞吐、稳定性”,不关心手段。很多时候”换个 7B 小模型”比”把 70B 量化到 INT4”更划算——如果不放进同一框架,就会漏掉这个更优解。

4. 产出物:选型对比表模板

方案 效果损失% 成本降低% 吞吐提升× 稳定性(P99/异常率) 实施成本 结论
基线 FP16 0(基准) 0 1.0× 基准 参照
PTQ INT8
GPTQ INT4 低-中
QAT INT8/INT4 高(需训练)
结构化剪枝 + 微调 中-高
蒸馏小模型替换
直接换小模型

使用要求(承接 5.4 / 5.5):固定输入分布与环境、区分预热与稳定态、报告 P50/P99、单变量对比、数据留档。

参考:MLPerf Inference Benchmark 方法论

量化基础:INT8/INT4 数值表示与精度损失

一句话定位:量化就是用低位整数近似表示浮点数,用可控的精度损失换取显存、带宽与算力上的三重收益。

1. 数值表示:scale 与 zero-point

把浮点范围 [min, max] 线性映射到低位整数区间(INT8 为 [-128, 127]):

1
2
q = round(x / scale) + zero_point
x ≈ (q - zero_point) * scale
  • scale:缩放因子,决定每个整数刻度代表多大的浮点跨度;
  • zero_point:零点偏移,使浮点 0 能被精确表示。

2. 精度损失从哪来

  • 舍入误差(rounding)round() 带来的固有误差,位宽越低越大(INT4 只有 16 个刻度);
  • 动态范围截断(clipping):超出所选范围的值被截断;
  • 异常值(outlier)是主要杀手:LLM 激活中常出现极少数远超其余值数十倍的离群值。由于 scale 由 max 决定,个别 outlier 会把 scale 撑得极大,导致绝大多数正常值挤在极少几个刻度里,有效精度崩塌。这也是 SmoothQuant、AWQ 等方法专门处理 outlier 的原因。

3. 三组关键取舍

  • 对称 vs 非对称
    • 对称(zero_point=0):计算更快、实现简单,适合分布近似零均值的权重
    • 非对称:能贴合偏斜分布(如 ReLU 后全为正的激活),精度更好但多一次零点运算。
  • 粒度:逐张量 / 逐通道 / 逐组
    • 逐张量(per-tensor):全张量一个 scale,开销最小、精度最差(最易被 outlier 毁掉);
    • 逐通道(per-channel):每个输出通道一个 scale,精度明显更好,是权重量化的常规选择;
    • 逐组(per-group):通道内再按 128/64 个元素分组,各组独立 scale,INT4 场景近乎必需,代价是额外存储 scale 与更复杂的 kernel。
    • 规律:粒度越细 → 精度越高、元数据与计算开销越大

4. 收益(为什么值得做)

  • 显存减半以上:FP16→INT8 约省 50%,→INT4 约省 75%,可放更大模型或更多 KV Cache/并发(见 3.4);
  • 带宽降低:LLM decode 阶段是访存密集(memory-bound),读权重的字节数减半基本等于延迟减半,这是量化提速最主要的来源;
  • 低精度算力更高:现代 GPU 的 Tensor Core 对 INT8/FP8/INT4 提供数倍于 FP16 的理论算力。

参考:论文《A Survey of Quantization Methods for Efficient Neural Network Inference》

PTQ(训练后量化)

一句话定位:拿已训练好的模型,用少量数据”标定”一下就完成量化——成本极低、不碰训练流程,是生产上快速上线的首选路径

1. 机制:校准集统计激活分布

  • 权重是静态的,直接统计其分布即可确定 scale;
  • 激活是动态的,取决于输入。PTQ 的做法是拿少量校准集(通常几百到上千条代表性样本)跑前向推理,统计各层激活的数值分布(min/max、直方图),据此确定每层的 scale 与 zero_point。
  • 校准方法常见有 max、百分位(如 99.99%,主动截断 outlier)、KL 散度最小化(TensorRT 的经典做法)等。
  • 校准集必须与真实业务分布一致,否则 scale 选错,线上精度显著劣化。

2. 优势

  • 成本极低:只需推理不需反向传播,几分钟到几十分钟即可完成;
  • 不改训练流程:无需原始训练数据与训练代码,拿到权重就能做——对使用第三方开源模型的场景尤其关键;
  • 工程链路短:TensorRT / TensorRT-LLM、PyTorch 均有成熟工具链支持,可直接接入部署流程(3.8)。

3. 精度表现与边界

  • INT8 通常损失可控:多数 LLM/CNN 在 INT8 PTQ 下精度损失很小(常在 1% 以内),是性价比最高的档位;
  • INT4 需更精细的算法:位宽降到 4 位后,仅靠简单校准的 PTQ 精度会明显下降,需要引入误差补偿类方法(如 GPTQ,见 4.4)或 outlier 处理(SmoothQuant/AWQ),并配合逐组量化(见 4.1);
  • 若 PTQ 精度仍不达标且精度要求苛刻 → 升级到 QAT(见 4.3)。

选型直觉:先 PTQ INT8 验证收益,不够再上 GPTQ/INT4,精度硬要求才动 QAT——按成本从低到高逐级尝试。

参考:NVIDIA TensorRT 量化文档

模型剪枝

一句话定位:把”不重要”的权重删掉来减小模型。面试的关键不是剪枝算法,而是能不能分清理论 FLOPs 下降实际墙钟加速——这两件事经常完全脱节。

1. 非结构化剪枝

  • 做法:按重要性(如权重绝对值大小)逐个把权重置零,不考虑位置分布。
  • 优点:压缩率高——可以剪掉很高比例的权重而精度损失很小,因为剪枝的自由度最大。
  • 致命问题:产生的稀疏是不规则的(随机散布的零)。
    • 通用硬件(GPU/CPU)的矩阵乘依赖规整的稠密计算与合并访存,遇到不规则稀疏无法有效跳过零元素;
    • 稀疏格式(CSR 等)还带来索引开销与随机访存。
    • 结论:通用硬件上难获得实际加速,常常只省了存储、墙钟时间几乎不变(除非有专用稀疏加速支持,如 NVIDIA Ampere 的 2:4 结构化稀疏)。

2. 结构化剪枝

  • 做法:按规整的结构单元整块删除——整个通道(channel)、整个注意力头(head)、整个层(layer)、或 FFN 的整组神经元。
  • 优点:剪完的模型仍是一个更小的稠密模型,可直接用标准算子跑,能真实提速(矩阵变小,访存与计算同步下降),无需特殊 kernel。
  • 代价:精度损失更大——粒度粗,整块删除难免带走有用信息,通常需要剪后微调(或蒸馏,见 4.5)来恢复精度。

3. 面试要点:理论 FLOPs vs 实际墙钟加速

必须能讲清这一对区别:

  • 理论 FLOPs 下降:把被剪权重的乘加操作直接从计数里减掉,是纸面数字。非结构化剪枝 90% 稀疏 → FLOPs 号称降 10 倍。
  • 实际墙钟加速(wall-clock speedup):取决于硬件能否真正跳过这些计算。不规则稀疏无法被有效利用 → 加速比可能接近 1;此外 LLM decode 阶段是访存密集(见 4.1),减少 FLOPs 本身也未必减少耗时。

因此评估剪枝方案必须实测端到端延迟/吞吐,并纳入 4.7 的四维评估体系,而不是拿 FLOPs 或稀疏率汇报收益。

参考:论文《The State of Sparsity in Deep Neural Networks》;综述《A Survey on Deep Neural Network Pruning》

FlashAttention 原理

一句话定位:Attention 慢的真正原因不是算力不够,而是HBM 读写太多(IO-bound)。FlashAttention 通过分块 + 片上融合计算,避免把 N×N 的注意力矩阵写进显存,从而同时省下时间和显存——而且是精确计算,不是近似

1. 关键洞察:瓶颈是 HBM 读写而非算力

标准 Attention 的执行过程会**物化(materialize)**中间矩阵:

  1. S = QKᵀ(N×N)→ 写入 HBM;
  2. 读回 S,算 P = softmax(S)(N×N)→ 再写入 HBM;
  3. 读回 P,算 O = PV → 写出。

问题:N×N 矩阵随序列长度平方级增长,反复在 HBM 与计算单元之间搬运。由于 HBM 带宽远低于 GPU 算力,实际耗时由访存量决定——即 Attention 是 memory-bound / IO-bound,GPU 算力大量空等。

2. 做法:Tiling 分块 + 片上融合 softmax

  • Tiling(分块):把 Q、K、V 切成能放进**片上 SRAM(共享内存)**的小块,按块循环计算。
  • 算子融合:在 SRAM 内对一个块连续完成 QKᵀ → softmax → 乘 V 的全部步骤,中间结果不落 HBM
  • Online Softmax:softmax 需要整行的最大值与求和做归一化,分块后无法一次看全行。解决办法是采用增量式(online)softmax——边遍历块边维护running max 与 running sum,并对已累积的输出做相应的重新缩放(rescale),最终得到与全局 softmax 完全一致的结果。

于是 N×N 的注意力矩阵从未被完整写入显存

3. 收益

  • 减少 HBM 读写量 → 墙钟时间显著下降(访存是瓶颈,所以省访存就是省时间);
  • 显存占用从 O(N²) 降到 O(N) → 可支持更长上下文;
  • 精确而非近似:区别于稀疏注意力、低秩近似等方法,FlashAttention 的输出与标准 Attention 数学上等价,这是它能被广泛默认启用的根本原因。
  • FlashAttention-2 进一步优化并行划分与工作分配(减少非矩阵乘操作、改进 warp 间划分),提升 GPU 占用率。

面试要求:不需要推公式,但必须能说清优化动机——“Attention 是 IO-bound,所以要减少 HBM 往返,用分块把计算搬到片上并融合,且靠 online softmax 保证结果精确”。

4. 可迁移话术

这与大数据里”减少 Shuffle 落盘与网络往返”是同一套思维:瓶颈在数据搬运而非计算,就把计算推到数据所在的高速层级并做算子融合(对应 1.1 中 Shuffle 磁盘/网络开销 ≈ HBM 读写瓶颈)。

参考:论文《FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness》(Dao et al., 2022) 及《FlashAttention-2》

Continuous / Dynamic Batching

一句话定位:批处理决定 GPU 能否被”喂满”。静态 Batching 因木桶效应大量空转,Continuous Batching 把调度粒度从”整个请求”细化到”每次迭代”,是 LLM 服务吞吐提升的关键一招。

1. 静态 Batching 的木桶效应

  • 做法:攒够 N 个请求组成一个 batch,一起跑到全部完成才返回、才开始下一批。
  • 问题:同批请求的输出长度差异极大(有的生成 10 token,有的 1000 token)。batch 必须等最长的那个跑完,先完成的请求所占的计算槽位一直空转 → GPU 大量空闲
  • 这就是典型木桶效应,与 Spark Stage 必须等最慢 Task(长尾)完成、拖慢整个 Stage 完全同构(见 1.1 / 1.2)。

2. Continuous Batching(迭代级调度)

核心变化:调度粒度从”请求级”变为**”迭代级”(iteration-level)**。

  • 每生成一个 token(一次 decode 迭代)后重新审视 batch:
    • 完成即出队:已生成结束的请求立即返回并释放其槽位与 KV Cache block;
    • 新请求即插入:等待队列中的新请求立刻填补空出的槽位,无需等整批结束。
  • 效果:GPU 始终保持接近满载的有效 batch,吞吐显著提升,同时排队延迟下降。
  • 依赖:需要能灵活分配/释放 KV Cache 的显存管理,因此与 PagedAttention(3.5)天生互补;也常被称为 In-flight Batching(TensorRT-LLM 的叫法,见 3.8)。

3. Dynamic Batching(服务层聚合)

注意与 Continuous Batching 区分——层次不同

  • Dynamic Batching 在服务层:把短时间内到达的多个独立请求,按时间窗口(如最多等 5ms)或队列深度聚合成一个 batch 再送进模型。
  • 目的是提高 GPU 批量效率,代价是引入排队等待延迟(窗口越大吞吐越好、延迟越高,是典型的吞吐/延迟权衡)。
  • Triton Inference Server 的 Dynamic Batching 即为此类,适用于所有模型;而 Continuous Batching 针对自回归生成的迭代特性,两者可叠加使用。

4. 可迁移类比

Continuous Batching ≈ 流式微批处理(来一条处理一条、持续吞吐);Dynamic Batching ≈ 攒批处理(按窗口攒够再算)。这正是流计算里”逐条 vs 微批”的经典取舍。

参考:NVIDIA Triton Inference Server 文档 Dynamic Batching 章节

CUDA 编程模型基础

一句话定位:理解 GPU 的两件事——线程怎么被组织和调度数据放在哪一层存储。用大数据的调度与分层存储直觉去套,学习成本最低。

1. 线程层次:Grid → Block → Thread

  • Grid:一次 Kernel 启动的全部线程,由多个 Block 组成。
  • Block:线程块,块内线程可通过共享内存通信与 __syncthreads() 同步;一个 Block 必须整体调度到同一个 SM 上。
  • Thread:最小编程单位,通过 blockIdx * blockDim + threadIdx 计算全局索引。

关键区分(面试易错点):

  • Warp(32 线程)是真实的调度单位。硬件以 warp 为粒度取指与执行(SIMT),warp 内 32 线程执行同一条指令。因此若 warp 内线程走了不同分支,会产生warp divergence(分支被串行执行),性能下降。Block 大小通常取 32 的倍数就是这个原因。
  • SM(Streaming Multiprocessor)是执行硬件:包含 CUDA Core、寄存器文件、共享内存、warp 调度器。多个 Block 可并发驻留同一 SM(占用率 occupancy 由寄存器/共享内存用量决定)。

2. 显存层次(速度差数量级)

从快到慢:

层级 作用域 相对速度
寄存器 (Register) 单线程私有 最快(~1 周期)
共享内存 (Shared Memory) Block 内共享,片上 SRAM 很快(~几十周期)
L2 Cache 全 GPU 共享 中等
全局内存 (Global / HBM) 全 GPU + Host 可见 最慢(数百周期)

核心结论:优化 GPU 程序的主线是”把数据尽量留在高层级”——用共享内存复用数据、保证全局内存合并访问(coalesced),减少对 HBM 的读写次数。这正是 FlashAttention(3.7)的核心思想。

3. 可迁移类比(我的表达桥梁)

  • Warp 调度 ≈ Spark Task 调度:都是把大任务切成固定粒度的执行单元批量调度;warp divergence ≈ Task 内分支导致的负载不均。
  • 显存层次 ≈ 内存/磁盘分层存储:寄存器/共享内存 ≈ 内存缓存,全局内存(HBM) ≈ 磁盘/远端存储;”减少 HBM 读写” ≈ “减少 Shuffle 落盘与网络传输”,都是把 IO 成本而非算力当作首要瓶颈。

参考:《CUDA C++ Programming Guide》