资讯动态

大模型塞进小显存:量化、剪枝与推理加速的工程实践

发布时间:2026/9/30 8:12:59 来源:尧图企业网站定制
上个月我把一个 7B 参数的模型硬塞进了一块 8GB 显存的消费级显卡跑通了实时推理。项目代号就叫Model-Optimizer本质上是一套围绕模型压缩、推理加速和显存规划的工具链。整个过程踩了不少坑也沉淀了一些可以复用的经验这篇文章就把这套东西的核心思路、实操步骤和调参记录完整摊开讲。我默认读这篇文章的人有基本的 PyTorch 使用经验知道什么是 forward、什么是张量但未必系统做过模型优化。无论你手头是 LLM、多模态模型还是传统的 Transformer 结构只要遇到模型太大跑不动推理太慢显存不够这类问题这篇文章的思路基本都能直接套用。1. 项目定位为什么要做这样一个模型优化工具1.1 模型越来越大工程化的真实痛点先别急着谈技术选型描述一下我最初遇到的场景。训练好的模型在 GPU 服务器上跑得好好的一换到推理环境就各种出问题。要么是显存直接爆掉要么是延迟高到没法用要么是吞吐量上不去、成本压不下来。最难受的是模型结构稍微变一下前面的优化工作可能就白做了。我做Model-Optimizer的初衷特别朴素把优化模型这件事从手工操作变成半自动化的流程。代码里能自动完成的就不要人肉调参能在编译期做掉的就不要留到运行时。目标是让一个具备基础工程能力的开发者输入一个模型 checkpoint 和一份部署配置就能拿到一个体积更小、推理更快、显存占用更低的新模型。这个定位决定了项目的整体走向。它不是像 PyTorch 那样的训练框架也不是像 CUDA 那样的底层编程接口而是夹在中间的一层把模型读取进来做分析和变换再输出一个可以被推理引擎高效执行的产物。1.2 Model-Optimizer 的整体设计思路与目标场景设计上参考了 ONNX Runtime 和 TensorRT 的 pipeline 思路但做了一层更通用的抽象。核心流程分四步profile 分析先跑一遍模型统计各算子的耗时、显存占用、数据流形状。变换优化根据 profile 结果和目标硬件选择量化、蒸馏、剪枝、算子融合等策略。编译导出生成优化后的模型文件可以选择直接落地为 ONNX 或 TensorRT engine。评测回归用一套多维指标验证优化效果防止精度损失超出接受范围。举个例子一个典型的目标场景是公司有一个线上问答机器人原来用的是 FP16 的 7B 模型部署在 A10 显卡上单卡并发 8 路。业务方希望在不换卡的前提下把并发翻倍响应时间控制在 1 秒以内。这个需求拆解下来就是显存占用必须降单请求延迟必须降精度不能明显变差。三个目标在数学上是互相牵制的Model-Optimizer 要做的就是在这三者之间找到一个最优的平衡点。1.3 工具边界它优化什么、不优化什么说实话市面上没有万能工具。优化这个词太宽泛了我得明确边界。Model-Optimizer 不碰训练过程不做分布式训练加速也不改优化器如 AdamW的算法逻辑。它专注的是模型训练完成之后、正式部署上线之前这一段工程化环节。这个边界划得很清楚如果你要优化的是训练收敛速度请去看深度学习训练框架的文档如果你要优化的是部署后的推理表现那这里讲的东西才对口。最初我把这个边界写进项目 README 的第一行就是不想让使用者产生错误的预期。另外一个非常重要的边界是Model-Optimizer 不做无损保证。任何优化手段无论是量化还是剪枝本质上都是在利用模型的冗余性做取舍。所谓的无损只是说精度损失在可接受的误差带之内而不是数学意义上的完全等价。理解这一点后续所有调参决策都会变得更清晰。2. 四个核心优化手段量化、蒸馏、剪枝、算子融合2.1 量化从 FP16 到 INT8/INT4精度损失的账怎么算量化是Model-Optimizer里最优先落地的手段因为它的收益最直接模型体积直接缩减推理速度线性提升显存占用同步下降。原理可以用一句话说清把神经网络里默认的 32 位或 16 位浮点权重换成 8 位甚至 4 位的整数来存储和计算。但怎么换和换了之后损失多少才是关键。我在项目里默认支持两种量化方案方案精度体积缩减推荐场景动态量化FP16→INT8约 2 倍CPU 推理模型结构复杂不好做校准静态量化FP16→INT8约 2 倍加速更明显GPU 推理算子类型规整动态量化只量化权重激活值在运行时动态统计静态量化则连激活值一起量化需要事先用校准数据跑几轮统计出激活值的动态范围。静态量化一旦校准数据没选好精度掉得特别厉害后面单开一节细说。INT4 量化我也加了支持但因为位数太低对校准数据集的要求近乎苛刻。实测下来7B 模型如果只用 100 条校准样本做 INT4 量化困惑度可能会从 6.8 飙到 9 以上加到 1000 条并做好混合精度分层量化才能控制在 7.2 以内。所以说 INT4 不是不能做而是要在数据质量和量化粒度上花更多心思。2.2 蒸馏用小模型学大模型的答案风格还是推理过程蒸馏和量化是两种不同维度的压缩。量化是在同一个模型结构下改变精度蒸馏则是直接把模型换小让小模型去模仿大模型的输出行为。Model-Optimizer 里实现的蒸馏模块默认支持两种蒸馏目标软标签蒸馏和特征蒸馏。软标签蒸馏的做法是让大模型teacher对一批样本输出概率分布这个分布里包含了模型对每个类别的信心程度。比如一张图片分类任务大模型可能认为猫的概率是 0.7狗是 0.2兔子是 0.1。这个概率分布比单纯的硬标签猫信息量更大。小模型student的优化目标就是让自己的输出分布尽量接近这个大模型的分布使用 KL 散度或者交叉熵来度量两个分布的差距。特征蒸馏更深入一层不只是看输出还让中间层的特征表示也尽量对齐。做法是取 teacher 的某一层输出和 student 的对应层输出计算 L2 loss 或者用一层简单的映射网络把两边映射到同一个空间后算距离。我个人的建议是先做软标签蒸馏效果好再加特征蒸馏。特征蒸馏的调参复杂度会高一个数量级因为它引入了额外的 loss 权重参数、层对齐策略和可能的映射网络结构稍不留神就会让小模型训练发散。2.3 剪枝与稀疏化结构剪枝和半结构化剪枝的取舍剪枝这件事我在 Model-Optimizer 里一开始做的是非结构化剪枝把权重矩阵里绝对值接近零的元素直接置零。效果很尴尬模型虽然稀疏了但是因为硬件对稀疏矩阵的计算支持不友好实测推理速度几乎没有提升甚至因为要存储索引信息反而变慢了。后来我调整了方向重点支持半结构化剪枝和结构化剪枝。半结构化剪枝也叫 N:M 剪枝比如 2:4 剪枝的意思就是每 4 个连续元素里保留 2 个。NVIDIA 的 Ampere 架构对这类稀疏模式有专门的硬件加速指令实测可以把矩阵乘法速度提升接近一倍。代价是精度会有损失需要剪完之后用原始数据做若干轮的微调来恢复精度。结构化剪枝则是直接删掉一整个注意力头、一整个 FFN 层或者一整个卷积通道。模型结构本身变了所以不需要特殊的运行库支持在任何硬件上都能生效。问题是删除结构之后精度掉得非常凶必须配合蒸馏才能恢复到可接受的水平。我的实践结论是如果目标硬件是 NVIDIA 且算力在 8.0 以上优先考虑半结构化剪枝如果是纯 CPU 部署或者硬件不确定结构化剪枝更稳但要做蒸馏兜底。2.4 算子融合与计算图优化优化器层面对 KV Cache 的管理算子融合是常被忽视但其实边际收益最大的一类优化。神经网络在框架里看起来是一个个独立的算子op每个算子执行时都有内核启动开销、显存读写开销。如果把多个相邻算子合并成一个这些开销就只付一次。最简单的例子LayerNorm后面的Relu可以合并成一个算子矩阵乘法后的Add和Activation也可以融合。在 Model-Optimizer 里我实现了一个计算图重写模块专门做这类融合。以 TensorRT 的风格为参照重写之后计算图节点数量通常能减少 20% 到 30%。大模型场景下还有一个特殊优化KV Cache 的内存管理。在自回归生成中每个 token 都要读取之前所有 token 的 Key 和 Value 向量。如果 KV Cache 不做规划显存会随时间线性增长最终触发 OOM。Model-Optimizer 里做了一个 KV Cache 的预分配和分页管理模块类似操作系统里的虚拟内存分页把 KV Cache 按固定大小分块按需分配和释放。实测一个 13B 模型在长序列推理场景下这个模块可以把显存峰值降低 40% 左右。3. 实操过程如何用 Model-Optimizer 完成一次端到端优化3.1 阶段一profile 基线先搞清楚时间花在哪做优化最忌讳的一件事就是不知道瓶颈在哪就胡乱调优。我给 Model-Optimizer 设计的第一步永远是 profiling跑一遍模型拿到各阶段耗时表格。在项目里我封装了一个简单的run_profile()接口它会记录下面三类信息算子级耗时每个算子的 GPU 时间、CPU 时间、调用次数。显存级统计峰值显存、显存变化曲线、各模型子模块占用。数据流形状每个中间张量的形状、数据类型、是否可以被量化。拉出 profile 报告之后先看耗时到底花在计算还是访存上。实测经验是小 batch 场景下访存和内核启动开销往往占大头这时候优先做算子融合大 batch 场景下计算量占大头优先做量化和半结构化剪枝会有更好的收益。有一回我把一个视觉模型优化了一整轮延迟从 35ms 降到了 28ms但距离目标 20ms 还是差得远。回头查 profile 才知道瓶颈根本不在模型计算上而是数据加载环节的 CPU 预处理太慢导致 GPU 一直在空等。优化了一次数据管道延迟直接掉到了 18ms。这个教训非常深刻先测数据流全链路再针对模型本身动刀。3.2 阶段二量化 精度校准的完整流程确定了要做量化之后推荐流程是这样的第一步准备校准数据集。我建议至少挑 500 到 1000 条和真实业务分布一致的样本。数据数量固然重要但分布更重要。如果业务里大量是短文本校准数据却拿了一堆长文本激活值范围会估偏产出模型精度会很拉胯。第二步使用 Model-Optimizer 的calibrate()接口执行校准。这个接口内部会用校准数据正向推理若干轮统计每一层的激活值分布然后选择合适的缩放系数。我默认使用的校准方法是MinMax 结合百分位对于权重直接取 min/max 即可对于激活值为了抗离群点默认取 99.99% 分位点实测比纯 min/max 稳很多。第三步生成量化后的模型。在 Model-Optimizer 里调用quantize()传入原模型和校准统计结果输出一个量化版本。这个量化版本可以用 PyTorch 直接加载推理也可以导出成 ONNX 再交给 TensorRT。第四步评测精度。我亲自踩过的最大的坑就在这里只测一个指标不够。比如只测测试集上的 top-1 accuracy结果可能是 78.2% 对 78.1%看起来很安全但如果看具体某几个类别的表现可能已经崩了。所以我建议至少看三个维度的指标整体 accuracy、代表性困难样本上的输出一致性、以及输出分布的 KL 散度。3.3 阶段三编译导出与目标硬件适配优化后的模型最终要落在某个具体的推理引擎上运行。Model-Optimizer 里做了一个统一的导出接口同一套优化结果可以导出成不同格式。在 NVIDIA 显卡上我优先推荐导出成 TensorRT engine。TensorRT 的层融合和 kernel 自动调优非常成熟同样一套 INT8 参数TensorRT 可能比 PyTorch 再快一倍。但注意 TensorRT 和具体 GPU 型号绑定换卡就得重新构建 engine。在 CPU 场景下导出成 ONNX 并用 ONNX Runtime 加载是更通用的做法。ONNX Runtime 的 CPU 推理对 INT8 模型有不错的优化特别是配合动态量化时效果很明显。我用一台 8 核虚拟机实测INT8 动态量化的 BERT 模型推理速度比 FP32 快约 3 倍。导出之后做一次完整对比指标FP16 原版INT8-TensorRT单次推理延迟 (ms)22.59.8P95 延迟 (ms)31.212.6峰值显存 (GB)3.81.9Accuracy78.4%78.1%看到没有延迟降了一半还多显存减了一半精度只掉了 0.3 个百分点。这个 trade-off 在绝大多数业务里是可以接受的。3.4 阶段四评测回归不只看准确率评测回归阶段最容易犯的错误是只看准确率这一个维度。准确率确实重要但它回答不了服务可不可用的问题。我建立了一套四维评测框架后来基本固定下来了延迟维度看平均延迟、P50、P95、P99。P99 尤其重要它反映的是最差情况下的体验很多线上问题都是 P99 过高导致的超时。吞吐维度看单卡每秒能处理多少请求、多少 token。吞吐直接决定部署成本和扩容计划。资源维度看显存峰值、CPU 占用、内存占用。这决定了能不能塞进目标硬件。质量维度看模型在业务测试集上的表现包括传统的衡量指标、困难样本的人工评估。这四类指标之间常有冲突比如通过降低 batch size 减少延迟但代价是吞吐下降通过增大 batch size 提升吞吐又会推高显存。Model-Optimizer 的评测模块会把四类指标输出为一张矩阵方便按业务权重做决策。同时建议做一下长尾样本的回归测试这是发现优化副作用的有效手段几乎所有优化手段在长尾样本上的表现都会比常规样本差得多。4. 调优参数与踩坑记录那些文档里不会写的事4.1 校准数据集100条和1000条的差距这个值得单独拎出来讲。我做了一组对照实验用的同一个 7B 模型同一个量化算法只有校准数据量不同。实验结果是冷冰冰的100 条校准数据模型困惑度从 6.8 恶化到 8.5300 条校准数据困惑度改善到 7.41000 条校准数据困惑度回到 7.0。数量当然不是唯一的变量数据质量和覆盖度同样关键。更稳妥的做法是先在业务数据上做一次聚类然后每类各取一部分样本拼成一套覆盖面均匀的校准集。很多项目没有足够的标注数据来选校准集退而求其次可以用模型训练时用的验证集子集或使用公开数据集中尽量贴近业务分布的样本。另外校准数据应该避免使用模型没见过的完全陌生分布否则激活值范围统计就会失真。4.2 量化粒度与混合精度不同层用不同位宽量化粒度是一个常被忽略但影响巨大的参数。默认情况下量化可以按整个张量做一个统一的 scale也可以按每一行、每一列或者每一个 block 单独算 scale。粒度越细精度保留越好但带来的额外开销也越大。Model-Optimizer 默认推荐按per-channel对权重来说和per-token对激活来说的粒度做量化这是精度和性能之间的一个平衡点。此外不是所有层都适合量化到同一位宽。像 embedding 层和最后的 lm_head 层它们的权重参数数量巨大且对精度影响非常敏感适合保留为 FP16而 attention 里的 QKV 投影矩阵是矩阵乘法的大头对量化不敏感适合压到 INT8 甚至是 INT4。这种做法叫混合精度量化Model-Optimizer 支持在配置文件中逐层指定位宽quant_config: default_bitwidth: 8 exceptions: - layer_pattern: embed_tokens bitwidth: 16 - layer_pattern: lm_head bitwidth: 16 - layer_pattern: self_attn.q_proj bitwidth: 8对标transformers库的模型命名规律用正则或者通配符匹配层名。实测下来只保留这两处敏感层为 FP16其余全部 INT8精度几乎和无损一样而显存节省依然接近一半。4.3 显存碎片与缓存策略实测中的数据在长序列推理场景下显存碎片问题会变得非常明显。传统 PyTorch 的显存分配器按需申请、按需释放长时间运行时显存会被切成很多不连续的小块新的申请也许物理空间够但就是分配不出来触发 OOM。我把缓存策略从即用即申请改成预分配大块并池化复用之后24 小时压测时间内的 OOM 次数从 17 次降到了 0 次。具体做法是设置一个显存池在模型加载阶段一次性申请峰值大小的空间之后所有 KV Cache 都从池里分配。方式听着简单但这种思路在 PyTorch 官方杉杉文档里并不显眼属于工程调优中非常实战的一环。如果你的场景是高频生成聊天强烈建议关注这一块。4.4 常见问题速查表问题现象可能原因排查思路量化后精度崩了掉超过3%校准数据分布和真实数据偏差太大扩大校准集规模、按业务分布聚类采样INT8 推理比 FP16 还慢硬件不支持 INT8 加速指令/量化算子未融合确认算力版本、开启算子融合、改用 TensorRT导出 ONNX 失败模型中有自定义算子注册自定义算子或改用脚本化导出生成到一半显存 OOMKV Cache 无上限增长开启分页 KV Cache、限制最大生成长度量化后输出全相同缩放系数为 0 或数值溢出检查激活值统计是否出现全零、调整百分位参数5. 优化效果评估与后续演进方向5.1 用数据说话延迟、吞吐、显存、精度四维评估整体跑完一轮优化之后我用一个 13B 对话模型、单张 A10 显卡做了基准测试。结论可以给到你作为参照。FP16 基线延迟 220ms/请求吞吐 4.5 请求/秒峰值显存 14.2GB各项指标距离生产部署还有明显差距。INT8 混合精度方案延迟 118ms/请求吞吐 8.3 请求/秒峰值显存 7.6GB精度差异在可接受范围内。再叠加半结构化剪枝后延迟 96ms/请求吞吐 10.1 请求/秒显存掉到 7.1GB但精度进一步下滑需要搭配微调来恢复。每一轮调优都先做评估再做决策哪怕只改动了一个参数也不省略。项目做久了之后发现跳过任何一次评测后续问题排查的成本都会加倍。5.2 从单机到集群优化策略如何迁移单机优化做完之后会遇到一个很现实的问题线上部署不止一台机器每台机器型号还未必相同。在一台 A100 上生成的 TensorRT engine换到 A10 上根本跑不起来。这是硬件绑定的宿命。我的方案是搭建一个针对多目标的构建流水线针对目标硬件矩阵分别构建对应的优化产物运行时按机器型号选择加载。同时支持快速模式和极致模式快速模式用 ONNX Runtime跨硬件通用极致模式用 TensorRT engine限指定硬件使用。此外优化策略可以通过导出配置文件来保留。同一份配置在开发、预发、生产三套环境上产出一致的模型这是工程上线的基本要求。5.3 后续我打算做的几件事Model-Optimizer 目前已经能覆盖量化、蒸馏、剪枝、算子融合这几大主流手段但我自己觉得还有几个值得投入的方向。一个是在线量化感知训练QAT的回传支持。目前量化主要走训练后量化PTQ对极端压缩场景如 INT4 加高压缩比剪枝的使用效果依然有限。如果能把 QAT 的整个流程串起来意识和实操层面都会更完整。另一个是自动化的优化策略搜索。现在的配置基本靠人工经验如果能让代码自动基于 profile 数据尝试不同组合并选出最优方案工具会更适配初级使用者。最后是更细粒度的 KV Cache 量化支持。长序列推理场景里 KV Cache 占用提升速度很快如果把 KV Cache 从 FP16 量化到 INT8显存还能再省一半这个收益在当前大模型推理的工程实践里非常可观。结语一些大实话做 Model-Optimizer 这段时间最大的体会是模型优化不是一个算法问题而是一个系统工程问题。量化算法、蒸馏策略、剪枝方案这些每一块单拎出来都有现成的论文和开源实现真正困难的是把这些手段组合起来配合业务数据、目标硬件和线上负载找到那个刚刚好的配置点。如果你也要做类似的事情我建议从 profiling 做起先搞清楚瓶颈在哪然后从量化入手这是性价比最高的第一步精度受损了再补蒸馏跑通了再考虑剪枝。顺序反过来会容易让人在一开始就陷入调参泥潭。最后提醒一句优化后的模型一定要做长线观测。训练时的评测集、校准集和线上真实数据分布会随着时间漂移建议在推理服务的日志里记录输入分布定期回测优化模型的精度表现。这是很多人容易漏掉但在实际部署中非常关键的环节。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑