模型压缩与高效架构
核心矛盾:模型越大越强,但部署越贵。压缩就是在这条曲线上找更优的点。
一、四条技术路线
| 路线 | 做法 | 压缩率 | 精度损失 | 工程成熟度 |
|---|---|---|---|---|
| 量化 | 降低数值精度 | 2–8× | 极小(4bit) | 极高,直接可用 |
| 剪枝 | 去掉不重要的权重/结构 | 2–10× | 中 | 中 |
| 蒸馏 | 大模型教小模型 | 5–50× | 小–中 | 高 |
| 高效架构 | 换更省的 Attention/FFN | 2–10× | 极小 | 高(换模型即可) |
优先级:量化 > 换高效模型 > 蒸馏 > 剪枝。
二、量化:先做这个
见 推理优化 的详细做法。要点回顾:
| 精度 | 7B 权重 | 建议 |
|---|---|---|
| BF16 | 14 GB | 训练与高精度推理 |
| FP8 | 7 GB | H100+ 首选 |
| INT8 | 7 GB | 通用安全选择 |
| INT4 (AWQ) | 3.5 GB | 部署默认 |
| 2–3 bit | ~2 GB | 效果风险大 |
量化后的精度评估(必做)
python
# 不要只跑通用基准,要跑你自己的任务集
before = eval_suite(model_fp16, suite)
after = eval_suite(model_int4, suite)
print(f"fp16 {before['rate']:.3f} → int4 {after['rate']:.3f}")
# 差异 > 2 个点就要考虑换量化方法或部分层保持高精度混合精度量化:对敏感层(如第一层、最后一层、attention 输出)保留 8bit,其余 4bit,常能挽回大部分损失。
三、知识蒸馏:小模型学大模型
| 类型 | 学什么 | 适用 |
|---|---|---|
| Logit 蒸馏 | 学教师的完整概率分布(软标签) | 有教师权重时 |
| 特征蒸馏 | 学中间层表示 | 结构相近时 |
| 数据蒸馏 | 用教师生成的数据训练 | LLM 时代主流 |
LLM 时代的实用蒸馏
python
# 用强模型生成高质量数据,训小模型(最常见、最有效)
def distill_dataset(questions, teacher, student_path):
rows = []
for q in questions:
ans = teacher(q) # 可加 CoT、可多次采样取最佳
rows.append({"messages": [
{"role": "user", "content": q},
{"role": "assistant", "content": ans}]})
save_jsonl(rows, student_path) # 然后走 SFT,见 L4 微调| 关键点 | 说明 |
|---|---|
| 数据要覆盖目标任务分布 | 蒸馏的本质是「用教师标注你的场景数据」 |
| 拒绝采样 | 生成多次,用规则/更强模型筛掉差的 |
| 保留教师推理链 | 对推理类任务,学过程比学结论更有效 |
| 小模型也会学到教师的坏习惯 | 需要人工抽检 |
效果经验:用强模型蒸馏 8000–20000 条领域数据,中小模型在窄任务上常能接近大模型 90%+ 的表现,成本降 10–50 倍。
四、剪枝:谨慎使用
| 类型 | 做法 | 能否真正加速 |
|---|---|---|
| 非结构化剪枝 | 把小的权重置 0 | 通常不能(稀疏矩阵在 GPU 上不一定快) |
| 结构化剪枝 | 整行/整头/整层删掉 | 能 |
| 层丢弃 | 直接删掉若干层 | 能(LLM 中常发现中间层可删) |
python
# 结构化剪枝:按重要性删掉注意力头(示意)
import torch
def head_importance(model, calib_batches):
"""用校准数据统计每个头的输出范数,范数小的可剪。"""
imp = {}
hooks = []
for name, mod in model.named_modules():
if name.endswith("attn"):
def hook(m, i, o, n=name):
# o: (B, T, heads*d) → 按头切分求范数
b, t, d = o.shape
heads = 12
o = o.view(b, t, heads, d // heads)
imp[n] = o.abs().mean(dim=(0, 1))
hooks.append(mod.register_forward_hook(hook))
with torch.no_grad():
for x in calib_batches: model(x)
for h in hooks: h.remove()
return imp剪枝的现实
剪枝需要「剪完再微调恢复」,工程量大、效果不稳定。 在能用量化和蒸馏解决的场景,不要先上剪枝。
五、高效架构:换模型就够了
| 技术 | 作用 | 代表 |
|---|---|---|
| MQA / GQA | 多个 query 共享一组 KV,大幅减少 KV Cache | LLaMA 2/3、Qwen |
| MLA | 低秩压缩 KV | DeepSeek |
| SwiGLU | 更好的 FFN 性价比 | LLaMA 系 |
| MoE | 稀疏激活:参数多但每次只用一部分 | Mixtral、DeepSeek-MoE、Qwen-MoE |
| 滑窗/稀疏注意力 | 降低 O(T²) | Mistral、Longformer |
| 线性注意力 / SSM | 长序列线性复杂度 | Mamba 系 |
| FlashAttention | 不改变数学结果,只优化显存与速度 | 通用 |
MoE 的关键权衡
| 优势 | 代价 |
|---|---|
| 同等算力下参数更多、能力更强 | 显存占用按总参数算(不是激活参数) |
| 推理时只激活部分专家,速度快 | 负载不均衡、路由训练不稳 |
| 微调时专家容易遗忘 |
记住这条:MoE 模型的「激活参数」决定速度,「总参数」决定显存。部署前看清两个数字。
六、端侧部署:另一套约束
| 约束 | 应对 |
|---|---|
| 内存小(手机 4–8GB 可用) | 3–4bit 量化 + 1–3B 小模型 |
| 无 GPU 或弱 GPU | GGUF/llama.cpp、MNN、Core ML |
| 功耗与发热 | 限制上下文、批处理、按需加载 |
| 隐私 | 全本地,不出设备 |
端侧的实际定位:窄任务、离线可用、隐私敏感,而不是通用助手。
七、怎么选压缩方案(决策流程)
延迟/成本超标?
├─ 是 → 先量化(INT8/INT4),评估精度
│ ├─ 精度可接受 → 结束(90% 情况在这里解决)
│ └─ 精度掉了 → 混合精度 / 换 AWQ / 敏感层保高精度
├─ 还不够 → 换更小模型 + 蒸馏自己场景的数据
├─ 显示还不够 → 检查是否真的需要这么长上下文 / 这么多候选
└─ 仍不够 → 才考虑剪枝 / 换架构