ARTICLE DETAIL

资讯详情

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

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

大模型推理加速实战:量化、投机采样与PD分离组合优化 1. 大模型推理加速的底层逻辑与方案选型大模型推理这件事真正上手部署过的人都知道训练只是烧钱推理才是烧命。一个 70B 参数的模型FP16 精度下光权重就要占 140GB 显存两张 80G 的卡刚够把模型塞进去留给 KV Cache 的空间几乎为零batch size 只能开到 1吞吐量惨不忍睹。更别提线上服务还要面对并发请求、长上下文、首 token 延迟这些硬指标。所以推理加速不是“锦上添花”而是“能不能用”的问题。我接触过的加速手段大致可以分成三个层次模型层面的量化压缩、解码策略层面的投机采样、系统架构层面的 PD 分离。这三者不是互斥关系实际生产环境里往往是叠加使用的——先量化把模型压小再用投机采样提升单次解码效率最后用 PD 分离把预填充和解码拆到不同机器上做资源隔离。理解这三者的原理和适用边界比盲目堆硬件有用得多。1.1 为什么推理加速必须从这三个方向入手先看一组我实测的数据。同样一个 13B 的模型在单张 A100 上跑配置方案显存占用首 token 延迟解码吞吐 (tokens/s)FP16 原始26GB320ms45INT8 量化14GB280ms78INT4 量化8GB260ms112INT4 投机采样9GB270ms168INT4 投机 PD 分离8GB6GB210ms240这张表基本说明了问题。量化直接砍显存和带宽压力投机采样在不改变模型输出的前提下提升解码效率PD 分离则解决的是“预填充和解码互相拖累”的系统级问题。三者叠加吞吐量能翻五倍以上。但这里有个关键认知不是所有场景都需要全套方案。如果你只是本地跑个 7B 模型做个人助手量化就够了投机采样和 PD 分离的复杂度不值得。如果是线上服务要扛几百 QPS那三个都得考虑。选型的第一原则是看你的瓶颈在哪——是显存不够、还是延迟太高、还是吞吐上不去。1.2 三种技术的适用边界与组合策略量化解决的是“模型太大装不下”和“显存带宽是瓶颈”的问题。它的本质是用更低的数值精度表示权重和激活值代价是精度损失。INT8 量化通常精度损失在 1% 以内基本无感INT4 量化损失会明显一些但在很多任务上仍然可接受。再往下走还有三元量化、二值量化那些就属于研究性质了生产环境慎用。投机采样解决的是“解码阶段每步只出一个 token 太慢”的问题。它的核心思想是用一个小模型draft model快速生成多个候选 token再用大模型target model一次性验证。因为大模型的前向计算是并行的验证 k 个 token 和验证 1 个 token 的时间差不多所以只要 draft model 的命中率够高就能实现加速。这里的关键参数是 draft 长度和接受率后面会详细讲。PD 分离解决的是“预填充阶段和解码阶段资源需求不同”的问题。预填充是计算密集型需要大量算力做矩阵乘法解码是访存密集型瓶颈在显存带宽。把这两个阶段放在同一张卡上就会出现“预填充时解码请求被阻塞”的情况。PD 分离就是把它们拆到不同的实例上各自用最适合的资源配置。注意PD 分离的代价是 KV Cache 需要在实例间传输网络带宽会成为新的瓶颈。如果你的集群网络是 10Gbps 以下PD 分离的收益可能被传输开销吃掉。2. 量化技术从 FP16 到 INT4 的实操细节量化是我最早接触的加速手段也是踩坑最多的。市面上量化方案五花八门GPTQ、AWQ、GGUF、SmoothQuant、bitsandbytes每个都有自己的适用场景和坑点。我按实际使用体验来拆解。2.1 量化精度的选择依据与显存计算先算一笔账。模型显存占用 参数量 × 每参数字节数 KV Cache 激活值开销。以 70B 模型为例FP1670B × 2 bytes 140GBINT870B × 1 byte 70GBINT470B × 0.5 bytes 35GBKV Cache 的计算公式是2 × layers × heads × head_dim × seq_len × batch × bytes。以 Llama-2-70B 为例80 层、64 个 head、head_dim 128序列长度 4096batch 1FP16 精度2 × 80 × 64 × 128 × 4096 × 1 × 2 10.7GB所以 FP16 下 70B 模型实际需要 140 10.7 ≈ 151GB两张 80G 卡刚好够但没余量。INT4 量化后权重只要 35GB单张 80G 卡就能跑还能留 30GB 给 KV Cache 和激活值batch size 能开到 8 左右。但 INT4 的精度损失需要评估。我的经验是通用对话任务 INT4 基本够用代码生成和数学推理任务建议至少 INT8。因为这两类任务对数值精度敏感INT4 量化后容易出现“看起来对但细节错”的情况。2.2 GPTQ、AWQ、GGUF 三种量化方案的实测对比这三种是我用得最多的各有优劣GPTQ是最早流行的方案基于二阶信息做逐层量化。优点是生态成熟几乎所有推理框架都支持缺点是量化过程慢70B 模型量化要几个小时而且对校准数据集敏感。AWQ是后来者核心思路是“激活感知”——不是所有参数都同等重要保护那些对激活值影响大的权重通道。实测下来 AWQ 在同等比特数下精度略优于 GPTQ量化速度也快一些。我现在默认用 AWQ。GGUF是 llama.cpp 生态的格式特点是支持 CPUGPU 混合推理量化等级非常细Q2_K 到 Q8_0 有十几种。适合本地部署场景尤其是显存不够需要 offload 到 CPU 的时候。但 GGUF 的 GPU 推理效率不如 GPTQ/AWQ因为它的 kernel 优化主要针对 CPU。方案量化速度精度保持GPU 推理效率适用场景GPTQ慢中高生产环境 GPU 部署AWQ中高高生产环境首选GGUF快中高中本地/边缘部署2.3 量化实操以 AWQ 为例的完整流程我以 AWQ 量化一个 13B 模型为例走一遍完整流程。第一步准备校准数据。AWQ 需要一批代表性数据来统计激活值分布通常 128 到 512 条就够。数据要覆盖你的目标场景比如做客服就用客服对话做代码就用代码片段。from datasets import load_dataset dataset load_dataset(json, data_filescalib_data.json, splittrain) calib_data [item[text] for item in dataset.select(range(256))]第二步执行量化。用 AutoAWQ 库from awq import AutoAWQForCausalLM from transformers import AutoTokenizer model_path meta-llama/Llama-2-13b-hf quant_path llama-2-13b-awq model AutoAWQForCausalLM.from_pretrained(model_path) tokenizer AutoTokenizer.from_pretrained(model_path) quant_config {zero_point: True, q_group_size: 128, w_bit: 4, version: GEMM} model.quantize(tokenizer, quant_configquant_config, calib_datacalib_data) model.save_quantized(quant_path)这里q_group_size是关键参数128 是默认值。它表示每 128 个权重共享一个量化 scale值越小精度越高但开销越大。我试过 64 和 128精度差异在 0.3% 左右但 64 的推理速度慢 5%所以 128 是性价比最高的。第三步验证精度。量化完必须跑一遍评测不能只看 loss。我通常用 lm-eval-harness 跑几个标准任务lm_eval --model hf --model_args pretrainedllama-2-13b-awq --tasks hellaswag,arc_easy --batch_size 8对比原始模型的分数如果掉点超过 2%就要考虑换量化方案或者调整参数。实操心得量化后的模型一定要用真实业务数据做 A/B 测试。我遇到过一次标准评测掉点只有 0.5%但实际业务里某些长尾 query 的回答质量明显下降后来发现是校准数据没覆盖那类场景。3. 投机采样用小模型撬动大模型的解码效率投机采样Speculative Decoding是我认为最“优雅”的加速方案因为它不改变模型的输出分布——理论上输出和原始模型完全一致。这个特性让它在对精度敏感的场景里特别有价值。3.1 投机采样的数学原理与接受率分析核心思想可以用一句话概括用便宜的小模型猜用昂贵的大模型验。具体流程是draft model 自回归生成 γ 个 token然后 target model 对这 γ 个 token 做一次并行前向得到每个位置的概率分布。然后从前往后逐个验证如果 draft token 在 target 分布下的概率满足接受条件就接受否则拒绝并从 target 分布重新采样一个 token。接受率的数学表达是 min(1, p_target(x) / p_draft(x))。这个公式的含义是如果 target model 认为这个 token 的概率比 draft model 还高那无条件接受如果更低就以一定概率接受。关键洞察是接受率取决于 draft model 和 target model 的分布接近程度。如果 draft model 是 target model 的蒸馏版本接受率能到 80% 以上如果是完全不同的模型接受率可能只有 50%。加速比的公式近似为加速比 接受率 × γ / (1 γ × c)其中 c 是 draft model 相对 target model 的计算开销比。当 c 很小时加速比趋近于接受率 × γ。3.2 Draft Model 的选型与训练策略Draft model 的选择直接决定加速效果。我试过三种方案方案一同系列小模型。比如 target 用 Llama-2-70Bdraft 用 Llama-2-7B。这是最省事的方案接受率通常在 60-70%。优点是 tokenizer 一致不需要额外处理缺点是 7B 模型本身也不小draft 开销占比高。方案二蒸馏小模型。用 target model 的输出蒸馏一个 1B 左右的模型。接受率能到 75-85%而且 draft 开销极低。缺点是需要额外训练成本高。方案三n-gram 小模型混合。对于有大量重复模式的场景比如代码生成用 n-gram 做 draft 命中率很高。我实测在代码补全任务上纯 n-gram draft 的接受率能到 60%加上小模型能到 85%。Draft 方案接受率Draft 开销训练成本适用场景同系列小模型60-70%中无快速上线蒸馏小模型75-85%低高追求极致性能n-gram 混合60-85%极低无代码/结构化输出3.3 投机采样的参数调优与实测数据投机采样有几个关键参数需要调γdraft 长度一次猜多少个 token。太小加速不明显太大浪费计算。我的经验值是 4 到 8。实测在 13B target 1B draft 的配置下γ5 时加速比最高达到 2.3 倍γ8 时反而降到 2.1 倍因为拒绝后的浪费增加了。温度参数投机采样对温度敏感。温度越高draft 和 target 的分布差异越大接受率越低。实测 temperature0 时接受率 82%temperature1 时降到 65%。所以如果你的业务需要高温度采样投机采样的收益会打折扣。验证策略有两种——贪婪验证和概率验证。贪婪验证是 target model 取 argmaxdraft token 完全匹配才接受概率验证是按概率接受。前者接受率低但实现简单后者接受率高但需要额外随机数。生产环境建议用概率验证。# 投机采样核心逻辑伪代码 def speculative_decode(target_model, draft_model, input_ids, gamma5): while not finished: # draft 阶段小模型自回归生成 gamma 个 token draft_tokens [] draft_probs [] for _ in range(gamma): logits draft_model(input_ids draft_tokens) probs softmax(logits[-1]) token sample(probs) draft_tokens.append(token) draft_probs.append(probs[token]) # verify 阶段大模型并行验证 target_logits target_model(input_ids draft_tokens) target_probs [softmax(l) for l in target_logits] # 逐个验证 accepted 0 for i, token in enumerate(draft_tokens): p_target target_probs[i][token] p_draft draft_probs[i] if random() min(1, p_target / p_draft): accepted 1 else: # 拒绝后从修正分布采样 corrected max(0, target_probs[i] - draft_probs[i]) new_token sample(corrected / corrected.sum()) draft_tokens[i] new_token break input_ids.extend(draft_tokens[:accepted1])注意事项投机采样在 batch size 较大时收益会下降。因为 target model 的验证计算量随 batch 增长而 draft model 的开销也线性增长。实测 batch1 时加速 2.3 倍batch8 时只有 1.4 倍。所以投机采样最适合低并发、低延迟的场景。4. PD 分离系统架构层面的推理优化PD 分离Prefill-Decode Disaggregation是最近一年才火起来的概念但它的思想其实很朴素不同阶段用不同资源。4.1 预填充与解码阶段的资源特征差异先理解这两个阶段的本质区别。预填充阶段处理完整的输入 prompt做一次前向计算。这个阶段是计算密集型的因为要处理几百到几千个 token 的矩阵乘法。GPU 利用率能到 90% 以上瓶颈在算力。解码阶段每次只生成一个 token做一次前向。这个阶段是访存密集型的因为要读取整个模型的权重和 KV Cache但计算量很小。GPU 利用率通常只有 20-30%瓶颈在显存带宽。把这两个阶段放在同一张卡上就会出现资源错配预填充时算力打满但显存带宽闲置解码时显存带宽打满但算力闲置。更糟糕的是当一个大 prompt 正在预填充时所有解码请求都要排队等待导致首 token 延迟飙升。我实测过一个场景单卡部署 13B 模型当并发请求里有长 prompt4096 token时其他请求的 token 间延迟从 30ms 飙升到 200ms。这就是预填充阻塞解码的典型表现。4.2 PD 分离的架构设计与 KV Cache 传输PD 分离的架构是这样的预填充实例P 实例只负责处理 prompt算完 KV Cache 后传给解码实例D 实例解码实例只负责自回归生成收到 KV Cache 后开始逐 token 输出。关键设计点实例配比P 实例和 D 实例的数量比取决于你的请求特征。如果平均 prompt 长度是 1000 token输出长度是 200 token那 P 和 D 的计算量比大约是 5:1但 D 的耗时更长因为自回归。我的经验是 P:D 1:2 到 1:4 比较合适。KV Cache 传输这是 PD 分离最大的开销。KV Cache 的大小是 2 × layers × heads × head_dim × seq_len × bytes。以 13B 模型、4096 序列长度、FP16 为例大约 1.6GB。如果网络是 25Gbps传输需要 0.5 秒这个延迟对首 token 来说是不可接受的。所以 PD 分离必须配合高速网络100Gbps 以上或者 RDMA。而且 KV Cache 传输要和计算重叠——P 实例算完一层就传一层而不是全部算完再传。调度策略需要一个全局调度器来决定哪个请求分配给哪个 P 实例以及 KV Cache 传给哪个 D 实例。调度目标是最小化首 token 延迟和最大化吞吐。常见策略有轮询、最少连接、基于负载预测的调度。4.3 PD 分离的部署实操与性能验证我用 vLLM 的 disaggregated prefill 功能做过一次实测。部署架构是 2 个 P 实例 4 个 D 实例网络是 100Gbps RDMA。配置 P 实例python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-2-13b-hf \ --port 8000 \ --kv-transfer-config {kv_connector:PyNcclConnector,kv_role:kv_producer}配置 D 实例python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-2-13b-hf \ --port 8001 \ --kv-transfer-config {kv_connector:PyNcclConnector,kv_role:kv_consumer}实测结果对比指标单卡部署PD 分离首 token 延迟 (P50)320ms180ms首 token 延迟 (P99)1200ms350ms解码吞吐45 tokens/s110 tokens/s长 prompt 影响严重阻塞无阻塞P99 延迟的改善最明显从 1200ms 降到 350ms。这是因为单卡部署时长 prompt 会阻塞整个队列导致尾部延迟爆炸PD 分离后长 prompt 只在 P 实例上排队不影响 D 实例的解码。实操心得PD 分离的收益在网络带宽足够时非常明显但如果网络是瓶颈反而会引入额外延迟。我建议先用 100Gbps 以上的网络做验证如果条件不具备优先考虑量化和投机采样。5. 三种技术的组合实战与常见问题排查单独用某一种技术都有收益但真正的生产环境需要组合使用。我分享一个实际的组合案例和踩过的坑。5.1 组合方案的设计与参数配置场景是线上客服机器人模型是 13B日均请求 50 万次要求首 token 延迟 500msP99 1s。我的组合方案量化AWQ INT4权重从 26GB 降到 8GB单卡能跑投机采样draft model 用 1B 蒸馏模型γ5接受率 78%PD 分离2 个 P 实例 4 个 D 实例100Gbps RDMA参数配置的关键点量化用 group_size128平衡精度和速度投机采样的 draft model 用 target model 蒸馏tokenizer 完全一致PD 分离的 KV Cache 传输用分层传输和计算重叠调度器用最短队列优先避免长 prompt 阻塞最终性能首 token 延迟 P50210msP99420ms吞吐 240 tokens/s完全满足要求。5.2 常见问题速查表与排查思路问题现象可能原因排查方法解决方案量化后精度掉点严重校准数据不匹配用业务数据跑评测重新准备校准数据投机采样加速比低接受率低统计接受率换更接近的 draft modelPD 分离首 token 延迟高KV Cache 传输慢测网络带宽升级网络或减少传输量量化后推理速度反而慢kernel 不支持看 GPU 利用率换支持的推理框架投机采样输出不一致验证逻辑有 bug对比原始输出检查概率验证实现PD 分离 OOMKV Cache 累积看显存曲线限制并发或加 D 实例5.3 独家避坑经验与性能调优建议几个我踩过的坑分享出来让大家少走弯路。坑一量化后的模型不要直接用于蒸馏。我试过用 INT4 量化的模型做 teacher 去蒸馏 draft model结果 draft model 学到的分布和 target model 偏差很大接受率只有 40%。正确做法是用 FP16 的原始模型做蒸馏。坑二投机采样的 draft model 要和 target model 用同一个 tokenizer。不同 tokenizer 会导致 token 对齐问题接受率暴跌。我试过用 Qwen 的 draft 配 Llama 的 target接受率只有 20%完全不可用。坑三PD 分离的 KV Cache 传输要考虑内存对齐。不同实例的显存地址可能不对齐直接传输会出错。vLLM 的 PyNcclConnector 已经处理了这个问题但自己实现的话要注意。坑四量化模型的 batch size 不要开太大。INT4 量化后虽然显存省了但激活值还是 FP16batch 太大激活值会 OOM。我实测 13B INT4 模型在 80G 卡上 batch size 最多开到 16再大就 OOM。坑五投机采样在流式输出场景要特殊处理。因为 draft token 可能被拒绝流式输出时不能立即把 draft token 发给客户端要等验证完再发。否则客户端会看到 token 回退体验很差。性能调优的优先级建议先做量化收益最直接再做投机采样收益次之但实现简单最后做 PD 分离收益最大但复杂度最高。如果资源有限前两个就能带来 3-4 倍的提升。最后分享一个小技巧量化、投机采样、PD 分离这三个技术的参数不是独立的。比如量化后模型变小draft model 的相对开销占比会变化投机采样的最优 γ 值也会变。所以每次调整一个技术都要重新调优其他技术的参数。我通常用网格搜索的方式先固定两个调第三个迭代两三轮就能找到较优组合。
返回列表