大模型压缩常用方法 一、为什么需要压缩Llama 3.1 405B FP16 ≈ 810 GB 显存。单张 H100 仅 80 GB。 压缩目标减少参数量 / 降低精度 / 迁移知识在精度损失可控的前提下降低部署门槛。三种核心方法方法原理适用阶段剪枝移除贡献小的权重或神经元训练后 / 训练中量化降低数值精度FP32→INT8/INT4推理部署蒸馏大模型teacher指导小模型student训练训练阶段二、剪枝Pruning2.1 原理训练后的网络中存在大量接近零的权重对输出贡献可忽略。剪枝即移除这些冗余连接。两种粒度非结构化剪枝逐权重置零(小于某个阈值)矩阵变稀疏。精度损失小但稀疏矩阵在 GPU 上的加速有限。 结构化剪枝按神经元 / attention head / 通道整块移除。矩阵仍为稠密但尺寸减小GPU 加速明显但精度下降更大。2.2 代码import torch import torch.nn.utils.prune as prune fc torch.nn.Linear(512, 256) print(f原始非零参数: {(fc.weight ! 0).sum().item()}) # 非结构化L1 范数最小的 30% 权重置零 prune.l1_unstructured(fc, nameweight, amount0.3) print(f剪枝后: {(fc.weight ! 0).sum().item()}) prune.remove(fc, weight) # 固化 mask # 结构化按 L2 范数剪掉 20% 的输出神经元 fc2 torch.nn.Linear(512, 256) prune.ln_structured(fc2, nameweight, amount0.2, n2, dim1) # 迭代剪枝多次剪枝 微调 def iterative_prune(model, rate0.2, rounds3): for r in range(rounds): for m in model.modules(): if isinstance(m, torch.nn.Linear): prune.l1_unstructured(m, nameweight, amountrate) # trainer.train(epochs2) ← 每次剪枝后微调 for m in model.modules(): if isinstance(m, torch.nn.Linear): try: prune.remove(m, weight) except: pass三、量化Quantization3.1 原理将 FP32 参数映射到低比特整数核心计算q round(w / scale) zero_point scale (max - min) / (qmax - qmin)两种策略PTQ训练后量化模型训完后直接转换无需重训。GPTQ、AWQ、bitsandbytes 均属此类。QAT量化感知训练训练时模拟量化误差模型自适应。精度更高但需重训。3.2 代码import torch # ═══ PyTorch 原生动态量化INT8═══ class SimpleModel(torch.nn.Module): def __init__(self): super().__init__() self.fc1 torch.nn.Linear(512, 256) self.fc2 torch.nn.Linear(256, 128) def forward(self, x): return self.fc2(torch.relu(self.fc1(x))) model SimpleModel() quantized torch.quantization.quantize_dynamic( model, {torch.nn.Linear}, dtypetorch.qint8 ) # 权重大小约降至 1/4FP32→INT8 量化参数开销 # ═══ bitsandbytes 4-bit 量化LLM 推理用═══ from transformers import AutoModelForCausalLM, BitsAndBytesConfig bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_quant_typenf4, # NormalFloat4 bnb_4bit_compute_dtypetorch.bfloat16, # 反量化后计算精度 bnb_4bit_use_double_quantTrue, # 量化因子也量化 ) model AutoModelForCausalLM.from_pretrained( Qwen/Qwen2.5-1.5B-Instruct, quantization_configbnb_config, device_mapauto, )四、知识蒸馏Knowledge Distillation4.1 原理用大模型teacher的 soft label 指导小模型student训练。核心在于温度参数 Tsoft_label softmax(logits / T)T 越高输出分布越平缓类别间的相对关系暗知识越明显。T1 退化为标准 softmax。蒸馏 loss 由两部分加权loss α · CrossEntropy(student, hard_label) (1-α) · KL(student/T, teacher/T) · T²4.2 代码import torch import torch.nn.functional as F from torch import nn # Teacher冻结 teacher nn.Sequential( nn.Linear(128, 256), nn.ReLU(), nn.Linear(256, 256), nn.ReLU(), nn.Linear(256, 10) ) teacher.eval() for p in teacher.parameters(): p.requires_grad False # Student目标小模型 student nn.Sequential( nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 10) ) def distillation_loss(s_logits, t_logits, labels, T4.0, alpha0.7): soft_s F.log_softmax(s_logits / T, dim1) soft_t F.softmax(t_logits / T, dim1) kd F.kl_div(soft_s, soft_t, reductionbatchmean) * (T * T) ce F.cross_entropy(s_logits, labels) return alpha * ce (1 - alpha) * kd # 训练循环 optimizer torch.optim.Adam(student.parameters(), lr1e-3) x, y torch.randn(32, 128), torch.randint(0, 10, (32,)) for epoch in range(10): optimizer.zero_grad() with torch.no_grad(): t_logits teacher(x) s_logits student(x) loss distillation_loss(s_logits, t_logits, y) loss.backward() optimizer.step() tp sum(p.numel() for p in teacher.parameters()) sp sum(p.numel() for p in student.parameters()) print(fTeacher: {tp:,} Student: {sp:,} 压缩比: {tp/sp:.1f}x)五、低秩分解Low-Rank Factorization将大矩阵分解为两个小矩阵的乘积W ∈ ℝ^(d×k) → W ≈ A B, A ∈ ℝ^(d×r), B ∈ ℝ^(r×k), r ≪ min(d,k) 参数量d×k → r×(dk)LoRA 即基于此原理——不更新原权重矩阵 W而是训练两个低秩矩阵 A 和 Bh W₀x (α/r) · BAx # W₀ 冻结只训 A 和 B六、方法对比方法压缩率精度损失推理加速是否需重训非结构化剪枝30~90%低不明显否可迭代微调结构化剪枝20~50%中明显建议微调INT8 量化~4×极低明显否INT4 量化~8×中明显否知识蒸馏由 student 规模决定中明显是低秩分解LoRA可训参数 ↓99%低推理时合并无损是七、工程建议推理部署4-bit 量化GPTQ / AWQ / bitsandbytes是成本最低的加速方案几分钟完成通常精度损失 1%。 端侧部署蒸馏得到小模型 → INT8 量化 → 移动端推理。蒸馏决定能力上限量化决定部署下限。 微调LoRA低秩分解可训参数仅占 0.1%~1%配合 4-bit 量化QLoRA可在单卡微调 70B 模型。