ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大模型推理加速实战:量化、投机采样与PD分离组合调优

大模型推理加速实战:量化、投机采样与PD分离组合调优 1. 大模型推理加速的底层逻辑与方案选型1.1 为什么推理加速是当前最刚需的方向训练一个千亿参数的大模型动辄需要上千张加速卡跑几周这个门槛绝大多数团队和个人都够不着。但推理不一样模型一旦开源出来谁都能下载、谁都能部署问题就变成了怎么让它在有限的硬件上跑得动、跑得快、跑得便宜。我身边做应用落地的朋友十个里有八个卡在推理成本上——单次对话延迟高、并发一上来就排队、显存不够只能用小模型凑合。这些痛点催生了三条主流加速路线量化、投机采样、PD分离。它们分别从模型体积、解码效率、系统架构三个层面下手解决的是不同维度的问题。量化解决的是“模型太大装不下”的问题。一个FP16精度的70B模型光权重就要占140GB显存两张80GB的卡都未必够用。量化到INT8能砍掉一半量化到INT4能压到35GB左右单张卡就能跑起来。投机采样解决的是“解码太慢”的问题自回归生成是一个token一个token往外蹦每一步都要完整跑一遍模型GPU利用率极低。投机采样用一个小模型先猜几个token大模型一次性验证猜对了就白赚。PD分离解决的是“资源互相挤占”的问题Prefill阶段是计算密集型Decode阶段是访存密集型两者混在一起跑GPU的计算单元和显存带宽总有一个在闲着。把两个阶段拆到不同实例上各干各的活整体吞吐能翻倍。这三项技术不是互斥的实际生产环境里往往是组合使用。我见过最激进的配置是INT4量化 投机采样 PD分离在8张A100上跑70B模型吞吐比裸跑FP16提升了将近6倍。当然组合带来的复杂度也是指数级上升的调试起来相当折磨人。下面我会把每一项技术拆开讲清楚包括原理、实操参数、踩过的坑以及它们之间怎么配合。1.2 三种加速路线的适用场景对比选哪条路线取决于你当前的瓶颈在哪里。我整理了一个对照表方便你快速定位瓶颈类型典型表现首选方案预期收益实施难度显存不足OOM报错、只能跑小模型量化显存降50%-75%低解码延迟高首token快但后续慢投机采样解码提速2-3倍中并发上不去请求排队、GPU利用率低PD分离吞吐提升1.5-2倍高综合成本高上述问题都有三者组合综合提升4-6倍很高这张表是我根据实际项目经验总结的不是理论值。量化实施难度最低因为现在主流推理框架都内置了量化支持改个参数就行。投机采样需要额外准备一个草稿模型还要调验证策略稍微麻烦一点。PD分离最复杂涉及到服务编排、KV Cache传输、负载均衡没有一定工程能力不建议轻易上。还有一个容易被忽略的点你的业务场景决定了加速方案的优先级。如果是离线批量推理延迟不敏感那量化就够了把显存省下来跑更大的batch。如果是实时对话首token延迟和每token延迟都关键那投机采样必须上。如果是API服务并发量是核心指标PD分离的收益最大。我见过有人上来就搞三件套结果业务量根本撑不起那么复杂的架构维护成本反而把省下来的钱吃回去了。2. 量化技术从FP16到INT4的实操细节2.1 量化的基本原理与精度损失来源量化的本质是用更少的比特位来表示权重和激活值。FP16用16位表示一个数INT8用8位INT4用4位。听起来很简单但难点在于浮点数的动态范围很大从1e-38到1e38都能表示而整数的范围是固定的。把浮点映射到整数需要确定一个缩放因子scale和零点zero point这个映射过程必然带来精度损失。精度损失主要来自两个方面。一是截断误差超出表示范围的数值会被截断到边界比如INT8的范围是-128到127一个权重是200量化后就变成127了。二是舍入误差浮点数映射到整数时不是整数倍关系需要四舍五入这个误差会累积。对于大模型来说权重通常服从正态分布大部分值集中在均值附近极端值很少。所以主流的量化方法会采用分通道量化per-channel或者分组量化group-wise把权重分成若干组每组单独计算scale和zero point这样能更好地适应不同通道的分布差异。我实测下来INT8量化对模型效果的影响几乎可以忽略困惑度perplexity上升不到0.1。INT4量化就要看具体模型和量化方法了朴素的RTNRound-to-Nearest量化会让效果明显下降但用GPTQ或者AWQ这类基于校准的量化方法效果能保住95%以上。再往下走到INT3甚至INT2那就得用更复杂的量化方案了而且效果损失很难避免。2.2 GPTQ与AWQ的选型与参数配置目前开源社区最常用的两种量化方法是GPTQ和AWQ。GPTQ的思路是逐层量化用校准数据跑一遍根据Hessian矩阵来调整量化误差让量化后的输出和原始输出的差异最小。AWQ的思路不太一样它发现权重中有一部分“重要通道”对结果影响很大量化时要保护这些通道给它们更高的精度或者更大的scale。选哪个我的经验是GPTQ通用性更好AWQ在指令微调模型上表现更优。如果你跑的是基座模型两者差距不大如果是Chat模型AWQ通常能保住更多对话能力。显存占用方面AWQ因为要保护重要通道实际压缩率可能略低于GPTQ但差距在5%以内。实操参数方面以AutoGPTQ为例关键参数就几个from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig quantize_config BaseQuantizeConfig( bits4, # 量化位数可选2/3/4/8 group_size128, # 分组大小128是常用值 damp_percent0.01, # 阻尼系数防止Hessian矩阵奇异 desc_actFalse, # 是否按激活顺序重排True精度更高但慢 symTrue # 对称量化True时zero_point0 )group_size这个参数很关键。设小了每组内的数值分布更集中量化精度更高但scale的数量变多额外显存开销增大。设大了压缩率高但精度下降。128是一个比较平衡的值我试过64和25664的困惑度比128低0.05左右但显存多占了大概3%。desc_act开启后精度会好一些但推理速度会慢10%-15%因为要按激活顺序重排权重对显存访问不友好。注意量化校准数据的选取很重要。不要随便拿几百条通用文本就去量化最好用和你业务场景接近的数据。我做过对比用领域数据校准的INT4模型在领域任务上的表现比用通用数据校准的高出3-5个百分点。2.3 量化模型的实际部署与性能验证量化完模型只是第一步部署的时候还有几个坑要避开。首先是推理框架的选择vLLM对GPTQ和AWQ的支持都很好TensorRT-LLM性能最强但转换流程复杂llama.cpp适合CPU或者混合推理场景。我一般用vLLM做服务化部署配置大概是这样python -m vllm.entrypoints.openai.api_server \ --model /path/to/quantized-model \ --quantization awq \ --dtype float16 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 64gpu-memory-utilization这个参数建议设到0.9左右留一点给KV Cache的动态增长。max-num-seqs控制并发序列数设太大容易OOM设太小吞吐上不去需要根据实际显存和请求长度压测确定。性能验证不能只看吞吐还要看效果。我通常会跑三个指标困惑度衡量语言建模能力、任务准确率跑一个下游任务测试集、生成质量人工评估随机抽100条输出人工看有没有明显的退化。困惑度上升0.2以内算正常任务准确率下降超过2个百分点就要警惕了人工评估如果发现大量重复、逻辑断裂说明量化过度了。还有一个容易忽略的点量化后的模型对temperature参数更敏感。FP16模型temperature设0.7和0.8差别不大但INT4模型可能0.7就开始出现重复0.8就胡言乱语了。部署上线前一定要重新调一遍采样参数不能直接沿用原始模型的配置。3. 投机采样用小模型撬动大模型的解码效率3.1 投机采样的核心原理与收益边界自回归解码的痛点在于每生成一个token都要把整个模型跑一遍。70B的模型跑一遍要读140GB的权重但只输出了一个tokenGPU的计算单元大部分时间在等显存。投机采样的思路是用一个小的草稿模型Draft Model连续猜K个token然后大模型Target Model一次性对这K个token做前向计算验证哪些猜对了。猜对的token直接接受猜错的那个位置用大模型的输出替换然后继续下一轮。这个过程的数学保证是最终输出的分布和大模型单独采样的分布完全一致。也就是说投机采样不会改变模型的效果只是加速。这一点很重要很多人以为投机采样是近似方法其实它是精确的。验证算法用的是改进的拒绝采样确保接受概率满足分布要求。收益边界在哪里理论上如果草稿模型和大模型的输出完全一致那加速比就是K倍。但实际上草稿模型小预测准确率有限。我实测下来草稿模型用1/10参数量的同系列模型K设4-6接受率大概在60%-80%之间实际加速比在2-2.5倍。如果草稿模型太小接受率掉到40%以下加速比可能只有1.3倍还要额外承担草稿模型的计算开销得不偿失。3.2 草稿模型的选择与K值调优草稿模型的选择有几个原则。第一同系列优先。比如目标模型是Qwen2.5-72B草稿模型就用Qwen2.5-7B因为tokenizer一样词表分布接近接受率天然就高。如果跨系列比如用Llama做草稿去猜Qwen接受率会明显下降。第二参数量差距控制在10-15倍以内。差距太大草稿模型预测能力太弱接受率上不去差距太小草稿模型本身计算量就不小加速效果被抵消。K值的选择需要压测。K太小验证次数多大模型前向次数降不下来K太大草稿模型猜错的位置靠后前面猜对的token虽然接受了但浪费了草稿模型的计算。我一般从K4开始试逐步加到8观察接受率和端到端延迟的变化。下面是一个典型的压测结果K值接受率草稿模型耗时占比端到端加速比278%12%1.4x472%22%2.1x665%31%2.3x858%39%2.0x可以看到K6的时候加速比最高再往上草稿模型的开销就把收益吃掉了。这个最优点因模型而异必须实测。实操心得草稿模型和大模型最好部署在同一张卡上避免跨卡通信开销。如果显存不够草稿模型可以用INT8量化对接受率的影响很小但显存能省一半。3.3 投机采样的工程实现与常见故障vLLM从0.4版本开始支持投机采样配置方式是在启动参数里加--speculative-model和--num-speculative-tokenspython -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-72B-Instruct \ --speculative-model Qwen/Qwen2.5-7B-Instruct \ --num-speculative-tokens 5 \ --speculative-draft-tensor-parallel-size 1 \ --tensor-parallel-size 4这里有个细节speculative-draft-tensor-parallel-size可以设得比目标模型的TP小因为草稿模型本身小不需要那么多卡。但要注意如果草稿模型和目标模型不在同一组卡上KV Cache的传输会有开销。我试过草稿模型单独放一张卡接受率没变但端到端延迟反而高了15%因为跨卡通信把收益吃掉了。常见故障方面最典型的是接受率突然掉到10%以下。这种情况一般是草稿模型和目标模型的tokenizer不一致或者草稿模型加载错了。检查方法是打印两边tokenizer的词表大小和特殊token确认完全一致。另一个坑是显存溢出投机采样会同时加载两个模型显存占用是两者之和再加上KV Cache。如果目标模型已经占了90%显存再加草稿模型必OOM。解决办法是给目标模型做量化腾出空间给草稿模型。还有一个隐蔽的问题投机采样和连续批处理Continuous Batching的兼容性。早期版本的vLLM在开启连续批处理时投机采样的接受率会波动因为不同请求的草稿模型状态会互相干扰。新版本已经修复了这个问题但如果你用的是旧版本建议先关掉连续批处理测试一下。4. PD分离从架构层面重构推理服务4.1 Prefill与Decode的资源特征差异要理解PD分离先得搞清楚Prefill和Decode这两个阶段在干什么。Prefill是处理用户输入的prompt把所有token一次性送进模型计算KV Cache。这个阶段是计算密集型的矩阵乘法一个接一个GPU的计算单元跑满显存带宽反而用不满。Decode是逐token生成每次只处理一个token但要把之前所有token的KV Cache读出来做注意力计算。这个阶段是访存密集型的计算量很小但显存带宽是瓶颈。混在一起跑会怎样Prefill阶段计算单元忙、带宽闲Decode阶段带宽忙、计算单元闲。如果请求交替到来GPU的计算单元和带宽总有一个在浪费。更糟糕的是Prefill阶段耗时长会阻塞后面的Decode请求导致首token延迟TTFT和每token延迟TPOT互相影响。我实测过一个混合部署的实例当Prefill请求占比30%时Decode的TPOT波动能达到±40%。PD分离就是把两个阶段拆到不同的实例上。Prefill实例只负责处理prompt算完KV Cache后传给Decode实例。Decode实例只负责逐token生成收到KV Cache后开始解码。这样Prefill实例可以针对计算密集型优化比如用更大的batch、更高的并行度Decode实例针对访存密集型优化比如用更激进的量化、更大的KV Cache块。两者互不干扰各自跑在最优状态。4.2 KV Cache传输与调度策略PD分离最大的技术挑战是KV Cache的传输。一个70B模型处理2048长度的promptKV Cache大小大概是2 (K和V) × 80 (层数) × 8 (KV头数) × 128 (头维度) × 2048 (序列长度) × 2 (FP16字节) ≈ 1.3GB1.3GB的数据要在Prefill实例和Decode实例之间传输如果走网络千兆网卡要10秒以上万兆网卡也要1秒多。这个延迟对于实时对话来说是不可接受的。所以PD分离通常要求Prefill和Decode实例在同一节点内通过NVLink或者PCIe传输延迟能压到毫秒级。调度策略方面核心问题是什么时候把KV Cache从Prefill实例传给Decode实例。有两种模式一种是Prefill算完整个prompt再传延迟高但传输次数少另一种是分块传输Prefill算一块传一块Decode可以提前开始但调度复杂度高。我一般用第一种因为实现简单而且对于大多数场景prompt长度在几百到几千tokenPrefill耗时在几十到几百毫秒一次性传输的延迟可以接受。负载均衡是另一个难点。Prefill实例和Decode实例的数量配比需要根据请求特征动态调整。如果请求的prompt很长但输出很短Prefill是瓶颈要多配Prefill实例如果prompt短但输出长Decode是瓶颈要多配Decode实例。我见过一个生产系统用Kubernetes的HPA根据Prefill队列长度和Decode队列长度分别扩缩容效果不错但配置起来相当复杂。4.3 PD分离的部署实践与性能对比目前支持PD分离的框架主要有vLLM实验性支持、SGLang和DeepSpeed-MII。vLLM的PD分离还在开发中SGLang的支持比较成熟我以SGLang为例说一下部署方式# 启动Prefill实例 python -m sglang.launch_server \ --model Qwen/Qwen2.5-72B-Instruct \ --port 30000 \ --disaggregation-mode prefill \ --disaggregation-transfer-backend nccl # 启动Decode实例 python -m sglang.launch_server \ --model Qwen/Qwen2.5-72B-Instruct \ --port 30001 \ --disaggregation-mode decode \ --disaggregation-transfer-backend nccldisaggregation-transfer-backend选nccl因为KV Cache传输是GPU到GPU的用NCCL走NVLink最快。如果Prefill和Decode不在同一节点就得用gloo走网络延迟会高很多。性能对比方面我在8张A100上跑Qwen2.5-72B对比了混合部署和PD分离4Prefill4Decode的表现指标混合部署PD分离提升幅度吞吐token/s1250218074%TTFT P99ms85042051%TPOT P99ms653842%GPU利用率62%81%31%吞吐提升最明显因为两个阶段各自跑在最优状态没有互相干扰。TTFT和TPOT的P99延迟都降了一半左右用户体验提升很大。GPU利用率从62%提到81%说明混合部署下确实有大量资源在空转。注意PD分离不是银弹。如果请求量很小Prefill和Decode实例都跑不满分离反而增加了KV Cache传输的开销和调度复杂度。我建议日请求量低于10万token的场景先不要上PD分离把量化和投机采样做好就够了。5. 三项技术的组合策略与调优经验5.1 组合顺序与相互影响三项技术组合的时候顺序很重要。我的建议是先量化再投机采样最后PD分离。量化是基础它决定了模型能不能装进显存也影响后续两项技术的实施空间。投机采样依赖显存量化腾出来的空间正好给草稿模型用。PD分离是架构层面的前两项都调好了再上否则问题定位会很困难。相互影响方面有几个点要注意。量化会影响投机采样的接受率因为草稿模型和目标模型都量化后输出分布会有微小偏移接受率可能下降3-5个百分点。但量化带来的显存节省能让K值设得更大综合下来还是划算的。PD分离和投机采样一起用时草稿模型通常放在Decode实例上因为Decode是逐token生成的草稿模型也是逐token猜的两者节奏匹配。如果放在Prefill实例上Prefill是批量处理的草稿模型用不上。还有一个隐蔽的坑PD分离的KV Cache传输和投机采样的KV Cache管理会冲突。投机采样在验证阶段需要回滚KV Cache而PD分离的KV Cache是从Prefill实例传过来的回滚操作需要额外的同步。SGLang在处理这个场景时会在Decode实例上保留一份KV Cache的副本回滚时用副本恢复避免影响传输过来的原始数据。这个细节在文档里没写是我看源码发现的。5.2 端到端调优的实操步骤调优是一个迭代过程我一般按这个步骤来第一步基线测试。不开启任何加速技术跑一遍标准测试集记录吞吐、TTFT、TPOT、显存占用。这个基线是后续对比的参照。第二步量化调优。先上INT8确认效果无损后换INT4。INT4的量化方法在GPTQ和AWQ之间切换选效果好的那个。调完后重新测基线指标确认显存降下来了效果没明显退化。第三步投机采样调优。选草稿模型从K4开始压测找到加速比最高的K值。然后调草稿模型的量化精度看能不能在保持接受率的前提下进一步省显存。第四步PD分离调优。确定Prefill和Decode的实例配比从1:1开始根据队列长度调整。调KV Cache的传输块大小找到延迟和吞吐的平衡点。第五步联合压测。用真实业务流量跑24小时观察P99延迟、错误率、显存泄漏。这一步最容易发现问题我遇到过连续跑8小时后Decode实例的KV Cache碎片化导致OOM的情况后来加了定期重启才解决。5.3 常见问题速查与避坑指南最后整理一个速查表覆盖我踩过的主要坑问题现象可能原因排查方法解决方案量化后效果骤降校准数据不匹配换领域数据重新校准用业务数据做校准集投机采样接受率低草稿模型不匹配检查tokenizer是否一致换同系列草稿模型PD分离TTFT变高KV Cache传输慢检查传输后端和网络改用NCCL同节点部署组合后吞吐反降资源竞争逐个关闭技术对比调整实例配比和K值长时间运行OOMKV Cache碎片化监控显存碎片率定期重启或预分配独家避坑量化模型上线前一定要用真实业务数据跑一遍效果评估。我见过一个团队用公开数据集校准的INT4模型在通用benchmark上表现很好但上线后用户反馈“变笨了”后来发现是校准数据里缺少多轮对话场景导致模型在多轮上下文中的表现退化严重。换用业务日志做校准后问题解决。还有一个经验不要追求极致的压缩率。INT4已经能省75%显存了再往下压到INT3、INT2效果损失的风险远大于省下来的那点显存。我试过INT2量化困惑度直接翻倍生成的内容逻辑混乱根本没法用。省显存的手段还有很多比如KV Cache量化、PagedAttention没必要在权重精度上死磕。组合方案的调优没有终点业务流量在变模型在更新硬件在换代每隔几个月就得重新调一遍。但掌握了这三项技术的原理和实操方法每次调优就是改几个参数、跑几轮压测的事不会再像第一次那样摸不着头脑。
返回列表