模型压缩:量化、剪枝、蒸馏与部署优化实战

模型压缩让大模型在有限显存与延迟预算下可用,是边缘部署与降本增效的关键。本文系统讲解模型压缩四大技术:量化(PTQ/QAT/INT8/INT4/FP16)、剪枝(结构化/非结构化)、知识蒸馏(logits/特征/教师学生)、低秩分解,并结合 ONNX/TensorRT/TVM 部署优化、显存与吞吐权衡、精度保持评估方法,以及 LLM 量化与边缘部署的实战案例。

大模型的参数与显存成本同步膨胀:70B 模型的 FP16 权重就要 140GB 显存,消费级硬件与边缘设备根本无法容纳。模型压缩通过量化、剪枝、蒸馏、低秩分解四类手段,在不明显损伤精度的前提下大幅压缩模型体积与推理成本。本文给出完整的压缩技术栈与工程决策框架。

压缩全景:四大技术路线

模型压缩解决的是「精度-体积-速度」的三角矛盾。四大技术路线各有所长:

量化(Quantization):把高精度权重(FP32/FP16)映射到低精度(INT8/INT4),直接压缩体积并加速。是 LLM 与边缘部署的主流手段。

剪枝(Pruning):移除冗余权重或结构,稀疏化后要么直接省参数、要么靠稀疏算子加速。

知识蒸馏(Distillation):用大模型(教师)指导小模型(学生),让学生模型以小体积逼近教师能力。

低秩分解(Low-Rank Factorization):把权重矩阵分解为两个小矩阵的乘积,用低秩近似替代原始稠密矩阵。

技术压缩原理体积收益速度收益精度风险
量化降低数值位宽4-8 倍2-4 倍中
剪枝移除冗余权重2-10 倍需稀疏算子中高
蒸馏大模型教小模型5-50 倍5-50 倍中
低秩分解矩阵低秩近似2-4 倍依赖算子高

工程上四者常组合使用:蒸馏出一个较小的架构 → 量化到 INT8 → 再做结构化剪枝,可以在精度可控的前提下把模型压缩一到两个数量级。

量化基础:PTQ 与 QAT

量化的核心是把连续浮点值映射到离散整数值,同时最小化信息损失。基础是对称量化公式:

$$x_q = \text{round}\left(\frac{x}{s}\right), \quad \hat{x} = x_q \times s, \quad s = \frac{\max|x|}{2^{b-1}-1}$$

其中 $s$ 是缩放因子(scale),$b$ 是位宽。量化的关键难点是动态范围——权重的分布通常集中在零附近,直接用 min-max 映射会浪费低比特的表示空间。

PTQ(Post-Training Quantization,训练后量化):训练完成后直接量化,无需重新训练。用一小部分校准数据统计激活值的动态范围。成本低、速度快,是默认起点。但激活值范围对量化误差敏感,低比特(INT4)下精度下降明显。

QAT(Quantization-Aware Training,量化感知训练):在训练过程中模拟量化误差(直通估计 STE),让模型学会对量化噪声鲁棒。精度更高,但需要重训,成本高。

# PyTorch 官方 PTQ 流程
import torch
import torch.quantization as tq

# 准备:校准模型(统计激活范围)
model.eval()
model.qconfig = tq.get_default_qconfig("fbgemm")   # 8-bit 对称量化
tq.prepare(model, inplace=True)

# 校准:喂入代表性样本,统计 min/max
with torch.no_grad():
    for batch in calib_loader:
        model(batch)

# 转换:实际量化
tq.convert(model, inplace=True)

# 推理时使用量化算子(fbgemm/x86 后端加速)
output = model(input_tensor)
对比PTQQAT动态量化
是否需要训练否是否
校准数据需要(几百条)不需要不需要
INT8 精度损失1%-3%<1%权重 INT8、激活 FP32
适用快速压缩、CV/NLP低比特、精度敏感仅权重量化(如 LLM)

INT8 / INT4 实战:LLM 量化

LLM 的规模让低比特量化成为刚需。权重激活分布的两个特性(outlier 特征、逐通道尺度差异)决定了量化必须精细设计。

GPTQ:基于二阶信息(Hessian)的逐层(layer-wise)量化方法,通过 OBS 思想的优化逐列补偿量化误差,INT4 下 7B 模型精度几乎不损。

AWQ(Activation-aware Weight Quantization):基于「权重的重要性由激活决定」的观察,通过保留少量重要通道的精度、对不重要通道做缩放保护,实现无需重训的 INT4 量化。

# llama.cpp 使用 GGUF 量化(实际部署中最常用)
# 不同量化档位:Q4_K_M / Q5_K_M / Q8_0 等
# Q4_K_M:4bit 综合,体积约 4.5GB(7B 模型)
# Q8_0:8bit,体积约 7GB,精度接近 FP16
from llama_cpp import Llama

llm = Llama(
    model_path="models/Qwen2.5-7B-Instruct-Q4_K_M.gguf",
    n_ctx=8192, n_gpu_layers=-1,   # 全部层上 GPU
    verbose=False,
)
output = llm("中国的首都是", max_tokens=64)
# 用 llama.cpp 将模型转成 4bit GGUF
python convert_hf_to_gguf.py \
  --outfile models/Qwen2.5-7B-Instruct-Q4_K_M.gguf \
  --outtype q4_k_m \
  models/Qwen2.5-7B-Instruct/
量化档位位宽7B 模型体积精度(近似)适用
FP1616~14GB100%基准
INT8 / Q8_08~7GB~99.5%通用部署
INT4 / Q4_K_M4~4.5GB~98%消费级 GPU
INT4 (GPTQ/AWQ)4~4GB~99%服务化推理
2bit2~2.5GB~95%内存极限场景

动态量化在 LLM 里也很常用:权重量化到 INT4/INT8,激活保持 FP16,KV cache 也做 FP16/INT8。这种「混合精度」在 vLLM 中通过 quantization="awq" 参数即插即用,无需修改推理代码。

剪枝:结构化与非结构化

剪枝把权重矩阵中的冗余元素置零。按粒度分为两类:

非结构化剪枝(Unstructured):置零任意位置的权重,稀疏度可极高(50%-90%),但产生不规则稀疏,需要专门的稀疏算子(如 cuSPARSE)才能加速,通用推理框架中往往只有显存收益而无速度收益。

结构化剪枝(Structured):整行/整列/整个通道(Channel)/整层移除,保持矩阵规则形状,兼容通用硬件加速。适合 CNN(通道剪枝)与 Transformer(移除 Attention head 或 FFN 中间维度)。

剪枝类型粒度加速效果框架支持精度风险
非结构化单个权重需稀疏算子低中
通道剪枝Channel好(规则稀疏)中中
结构化(层/头)模块好高高
1x1 卷积/微块细粒度块好高(NVIDIA 2:4)低

Magnitude Pruning(幅值剪枝):把绝对值最小的权重置零,是最简单有效的方法。进阶的 Lottery Ticket Hypothesis(彩票假设) 认为存在稀疏子网络能匹配甚至超越原网络的性能。

2:4 稀疏:NVIDIA 在 Ampere+ 架构支持的半结构化稀疏——每 4 个权重中恰好 2 个非零,配合稀疏张量核可带来近 2 倍加速,是推理时「结构稀疏」的工程甜点。

# 幅值剪枝:置零最小幅度的权重
import torch

def magnitude_prune(weight: torch.Tensor, sparsity: float = 0.5):
    """按幅度剪枝:保留最大 sparsity 比例的权重。"""
    mask = torch.zeros_like(weight, dtype=torch.bool)
    k = int(weight.numel() * (1 - sparsity))
    flat = weight.view(-1).abs()
    _, top_idx = torch.topk(flat, k)
    mask.view(-1)[top_idx] = True
    return weight * mask          # 被 mask 为 False 的位置置零

weight = torch.randn(256, 256)
pruned = magnitude_prune(weight, sparsity=0.5)
print(f"非零比例: {(pruned != 0).float().mean():.3f}")   # ~0.5

剪枝的陷阱是稀疏度不等于速度:不规则稀疏在通用算子下反而可能更慢。生产实践应优先结构化剪枝或 NVIDIA 2:4 半结构化稀疏,并在剪枝后做少量重训(Prune-then-Finetune)恢复精度。

知识蒸馏

蒸馏的核心是「让学生模型模仿教师模型的输出分布,而不只是答案」。按蒸馏信号分为三类:

Logits 蒸馏:让学生学习教师模型的软标签(soft label),即经过温度 $T$ 缩放的输出概率分布。软标签携带了类别间的相对关系(「猫比狗更像狮子」),信息量远大于硬标签。Hinton 原始蒸馏损失:

$$\mathcal{L} = \alpha \cdot \text{CE}(y, \sigma(z_s)) + (1-\alpha) \cdot T^2 \cdot \text{KL}\left(\sigma(z_t/T) |\ \sigma(z_s/T)\right)$$

特征蒸馏:让学生网络的中间特征逼近教师网络的对应层特征(如 FitNets、DistilBERT 的隐藏层对齐),适合学生架构与教师不同构时。

LLM 蒸馏:教师 LLM 生成高质量样本(self-instruct 风格),让学生小模型以 SFT 方式学习,是当前大模型蒸馏的主流(如 MiniLLM)。

import torch
import torch.nn.functional as F
import torch.nn as nn

def distillation_loss(
    student_logits, teacher_logits, labels, T=4.0, alpha=0.5
):
    """Hinton 蒸馏损失:软标签 KL + 硬标签 CE。"""
    soft_targets = F.softmax(teacher_logits / T, dim=-1)
    soft_pred = F.log_softmax(student_logits / T, dim=-1)
    kl_loss = F.kl_div(soft_pred, soft_targets, reduction="batchmean") * (T * T)
    ce_loss = F.cross_entropy(student_logits, labels)
    return alpha * kl_loss + (1 - alpha) * ce_loss
蒸馏类型教师→学生信号学生架构典型应用
Logits输出分布(软标签)同构/异构分类、语言模型
特征蒸馏中间层特征异构CNN(FitNets)
关系蒸馏样本间距离异构度量学习
LLM 蒸馏生成样本 + logits小 LLMMiniLLM、DistilBERT
自蒸馏同一模型浅层学深层同构深层网络收敛

在线蒸馏 / 多教师蒸馏 是进阶方向:多个教师投票给一个学生,或教师学生同步训练(互相提升)。工程上蒸馏的收益取决于「教师优势」——如果教师模型本身质量不高,蒸馏收益有限。

低秩分解

低秩分解把稠密权重矩阵 $W \in \mathbb{R}^{m\times n}$ 近似为两个低秩矩阵的乘积 $W \approx UV^T$($U \in \mathbb{R}^{m\times r}$, $V \in \mathbb{R}^{n\times r}$,$r \ll \min(m,n)$),用 $r(m+n)$ 个参数近似 $mn$ 个参数。

**SVD(奇异值分解)**是最经典的方法:对 $W$ 做 SVD,保留前 $r$ 个奇异值,截断后的 $\hat{W} = U_r \Sigma_r V_r^T$ 是 Frobenius 范数意义下的最优低秩近似。

$$W \approx U_r \Sigma_r V_r^T, \quad \text{rank}=r$$

import torch

def low_rank_factorize(weight: torch.Tensor, r: int) -> tuple[torch.Tensor, torch.Tensor]:
    """SVD 低秩分解:返回两个小矩阵用于替换原权重。"""
    U, S, Vh = torch.linalg.svd(weight, full_matrices=False)
    # 只保留前 r 个奇异值
    U_r = U[:, :r]
    S_r = S[:r]
    Vh_r = Vh[:r, :]
    approx = (U_r * S_r) @ Vh_r
    rank_ratio = r * (weight.shape[0] + weight.shape[1]) / weight.numel()
    print(f"参数压缩比: {rank_ratio:.2%}")
    return U_r * S_r, Vh_r     # 两个低秩因子
分解方法原理适用层精度风险
SVD奇异值截断全连接层中
Tucker高阶张量分解CNN 卷积核中
CP 分解张量秩分解CNN高
Toeplitz结构约束分解卷积低(特殊结构)

低秩分解在浅层、宽层(如大 FFN、embedding 投影)上效果较好,深层语义信息密集处分解精度损失大。通常作为量化/剪枝之后的补充手段,单独使用很难达到与蒸馏相当的整体压缩比。

部署优化:ONNX / TensorRT / TVM

压缩后的模型要落地成可部署格式并充分发挥硬件加速。三条主流路径:

ONNX Runtime:开放模型交换格式,跨框架导出,图优化 + 支持各种 EP(Execution Provider,如 CPU/DirectML/CUDA),工程友好。

TensorRT:NVIDIA 专用推理引擎,对支持的网络做层融合、精度校准、kernel 自动调优,INT8/FP16 下吞吐最优。缺点:平台绑定 NVIDIA、构建时间长。

TVM / Apache TVM:编译器式优化,支持多后端(CPU/GPU/FPGA/专用加速器),自动图级 + 算子级优化。

# PyTorch → ONNX 导出(含动态维度)
import torch

class TinyBERT(nn.Module):
    def forward(self, input_ids, attention_mask):
        return self.bert(input_ids, attention_mask)[0]

model.eval()
dummy_ids = torch.randint(0, 30522, (1, 128))
dummy_mask = torch.ones(1, 128, dtype=torch.long)

torch.onnx.export(
    model,
    (dummy_ids, dummy_mask),
    "model.onnx",
    opset_version=17,
    input_names=["input_ids", "attention_mask"],
    output_names=["logits"],
    dynamic_axes={                       # 支持动态 batch 与序列长度
        "input_ids": {0: "batch", 1: "seq_len"},
        "attention_mask": {0: "batch", 1: "seq_len"},
    },
)
# ONNX Runtime 推理 + 启用 TensorRT EP
import onnxruntime as ort

providers = [("TensorrtExecutionProvider", {
    "trt_engine_cache_enable": True,    # 缓存 engine,避免重复构建
    "trt_fp16_enable": True,            # FP16 加速
}), "CUDAExecutionProvider"]

sess = ort.InferenceSession("model.onnx", providers=providers)
outputs = sess.run(["logits"], {
    "input_ids": input_ids.numpy(), "attention_mask": mask.numpy(),
})
部署路径加速方式平台构建成本最佳场景
ONNX Runtime图优化 + 算子融合跨平台低通用、快速上线
TensorRT层融合 + 精度校准NVIDIA 独占高GPU 生产吞吐
TVM编译优化 + 自动调优多后端高特殊硬件/异构

显存与吞吐权衡

压缩的收益最终要落到**显存(Memory Footprint)与吞吐(Throughput)**的可量化改善上。核心指标:

峰值显存 = 权重 + KV cache(LLM)+ 激活值 + 推理框架开销。量化直接压权重;KV cache 量化(FP16→INT8)与 PagedAttention 管理缓存是 LLM 推理的显存大头。

吞吐与延迟:压缩带来的提速取决于内存带宽瓶颈。LLM 推理是内存带宽密集型的(每生成一个 token 都要读一遍全部权重),权重从 FP16 降到 INT4,带宽需求降 4 倍,同硬件吞吐可提升 2-3 倍。

# 示例:7B 模型不同精度的 KV cache 显存对比
# 序列长 4096、40 层、32 头、head_dim=128
# FP16 KV cache = 2 × 4096 × 40 × 32 × 128 × 2B = ~2.7GB
# INT8 KV cache = 一半,~1.3GB
# GQA(分组查询注意力)进一步把 K/V 头数减半,显存再降
压缩目标关键手段收益量级
权重显存FP16→INT4 量化4 倍
KV cache 显存INT8 量化 + GQA2-4 倍
长序列显存PagedAttention + 分页消除碎片
端到端吞吐INT4 + Continuous Batching2-4 倍
边缘延迟蒸馏小模型 + TensorRT5-10 倍

权衡决策:压缩不是「越低越好」。每降一档位宽,精度风险上升一档。工程上的做法是逐档下探、卡在精度红线——先测 INT8 达标,再试 INT4,直到精度低于可接受阈值就回退一档。

压缩评估:精度保持度量

压缩必须回答「精度损失多少」,评估方法因任务而异:

任务精度度量可接受损失
分类Accuracy / F1<1%
CV 检测mAP<1%
LLM 生成Perplexity / 任务基准PPL 增幅 <5%
LLM 对话评测集质量分<2%
翻译BLEU<1 点

PPL(Perplexity) 是 LLM 压缩最敏感的粗筛指标:计算验证集上的困惑度,量化模型 PPL 上升即精度损失信号。PPL 变化先于任务指标,适合快速筛选量化档位。

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

def compute_perplexity(model, tokenizer, texts, stride=512):
    model.eval()
    total_nll, total_tokens = 0.0, 0
    for text in texts:
        encodings = tokenizer(text, return_tensors="pt")
        input_ids = encodings.input_ids
        with torch.no_grad():
            for i in range(0, input_ids.size(1) - 1, stride):
                inp = input_ids[:, i:i+stride]
                out = input_ids[:, i+1:i+stride+1]
                logits = model(inp).logits
                shift_logits = logits[:, :-1, :].reshape(-1, logits.size(-1))
                shift_labels = out[:, 1:].reshape(-1)
                nll = torch.nn.functional.cross_entropy(
                    shift_logits, shift_labels, reduction="sum")
                total_nll += nll.item()
                total_tokens += shift_labels.numel()
    return math.exp(total_nll / total_tokens)

# 对比 FP16 与 INT4 的 PPL,增幅 >5% 则回退档位

回归策略:任何压缩版本上线前必须在 Golden Set 上全量回归,并跑典型长尾用例。压缩后的模型可能出现「99% 用例没问题、1% 关键用例崩坏」的隐性退化,只有系统性回归能捕获。

实战案例:LLM 量化部署全流程

一个典型的 LLM 量化部署项目,路径如下:

[1] 基准测量:FP16 7B 的显存、延迟、PPL 基线
[2] 档位测试:INT8 → INT4(GPTQ/AWQ) → 2bit,逐档测 PPL
[3] 精度红线:PPL 增幅 <5%,任务基准(如 GSM8K)不降
[4] 部署格式:GGUF(本地)/ vLLM(服务化)
[5] 在线验证:影子部署对比量化版与 FP16 版输出质量
[6] 上线监控:显存、延迟、质量指标持续跟踪
# vLLM 服务化部署 AWQ 量化模型(零代码改动启用量化)
from vllm import LLM, SamplingParams

llm = LLM(
    model="Qwen/Qwen2.5-7B-Instruct-AWQ",   # 已量化权重
    quantization="awq",                      # 指定量化方法
    tensor_parallel_size=1,
    gpu_memory_utilization=0.85,
    max_model_len=8192,
)
outputs = llm.generate(["量化部署如何保证精度?"], SamplingParams(temperature=0.7))
print(outputs[0].outputs[0].text)

关键决策点:

  1. 量化方法:有重训预算选 QAT;无预算选 AWQ/GPTQ(按激活敏感度)
  2. 部署框架:服务化高并发用 vLLM;边缘单机用 llama.cpp GGUF
  3. 精度验证:PPL 粗筛 + 任务基准精测 + 影子部署终验

工程经验:LLM 量化的 80% 收益来自「FP16→INT4」这一档,往下的 2bit 收益递减且精度风险陡增。多数生产场景 Q4_K_M / AWQ-INT4 是性价比甜点。

总结

压缩技术核心机制体积收益速度收益精度风险工程要点
PTQ训练后直接量化4 倍2-3 倍中校准数据质量关键
QAT训练中模拟量化4 倍2-3 倍低需重训成本
INT4(GPTQ/AWQ)逐层 Hessian/激活感知8 倍3-4 倍低-中LLM 服务化标配
剪枝权重/通道移除2-10 倍需稀疏算子中优先结构化/2:4
蒸馏教师教学生5-50 倍5-50 倍中教师质量决定上限
低秩分解SVD 低秩近似2-4 倍依赖算子高浅层宽层效果好
ONNX/TensorRT/TVM编译优化-1.5-4 倍低按平台选型

模型压缩的成熟实践是「组合拳 + 逐档下探」:先蒸馏出小架构,再量化到目标位宽,最后用 TensorRT 等引擎榨干硬件性能。全程以 PPL 与任务基准为精度红线,把「能用的最小模型」变成「可上线的生产模型」。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ai」更多文章

  1. GPU 共享与调度:MPS、MIG 与多租户隔离
  2. 异构推理硬件:ROCm、Intel 与国产 NPU 适配实践
  3. 前缀缓存与语义缓存:KV 复用与重复计算消除