资讯详情

资讯详情

投机解码实战:本地27B模型推理速度从14到159 tok/s

前阵子我在本地显卡上部署 Qwen3.8-27B 的 4bit 量化版第一次跑生成任务速度只有 14 tok/s。这个数字什么意思大概就是每秒钟蹦出十来个字一句话要憋半天多轮对话基本没法用。说实话当时我差点直接放弃觉得 27B 在消费级硬件上就是自取其辱。后面我把投机解码Speculative Decoding整套调通之后同一块显卡、同一个模型实测吞吐直接拉到 159 tok/s翻了十倍以上。这个差距不是优化一下参数能解释的是整个推理路径换了一套玩法。这篇文章就把我从 14 tok/s 到 159 tok/s 的完整调优过程拆开讲清楚包括投机解码的原理、具体启动参数、中间踩过的坑以及一套可以直接抄的配置给想在本地跑 27B 级别模型的同学做个参考。1. 这次动手的背景为什么要在本地跑 27B 模型1.1 本地部署的核心诉求先回答一个前置问题云端大模型又不贵为什么非要本地跑我这次动手的真实原因是数据敏感。项目里有一批文档不能出内网又要用大模型做摘要和结构化抽取那唯一的出路就是本地推理。你一旦有这种需求模型档位就成了关键抉择。7B/8B 级别的模型本地跑很轻松放消费级显卡上甚至能跑出不错的速度但质量在复杂任务上不够用尤其是长文本理解、代码生成这类场景小模型的逻辑连贯性明显差一截。70B 级别的模型质量是好但要么吃多卡要么吃大量内存做 offload速度惨不忍睹部署成本也高。27B 这个档位就很微妙它比 14B 强不少又不像 70B 那样需要专业硬件单张 24GB 显存的消费级显卡就能勉强塞下 4bit 量化版。这也是为什么我最终锁定了 Qwen3.8-27B——能力够用显存边缘可跑剩下的问题就是怎么把它优化到“能正常交互”的速度。如果你的诉求也是“数据不出本机、离线可用、成本可控”那 27B 本地推理这条路大概率也是你要走的。下文所有调优都基于这样一个目标单张 24GB 显卡跑 Qwen3.8-27B速度从“不可用”拉到“可用”。1.2 14 tok/s 的基线到底有多难受先说清楚基线是怎么测的。硬件就一块 24GB 显存的消费级显卡系统内存 64GB模型用 4bit 量化版推理框架用的社区里最常见的 vLLM。测的时候没有开任何投机解码也没有做并发压测就是最简单的单请求、短上下文256让它生成一段固定长度的文本。结果 14 tok/s。换算下来就是每分钟大概 800 个 token中文大概一分钟能输出 500 到 800 字。看文字确实能看但交互体验非常虐一个问题丢进去等回复的时间足够我起身倒杯水。如果你用过 GPT-4 那种云端的流畅对话再回到这个速度心理落差非常大。这里有个容易误判的点很多人看 14 tok/s 觉得“也还好”但实际上大模型生成是流式的速度越慢你越能感知到那种一个字一个字蹦出来的滞后感。做一次像样的文档总结输出 1000 字你要干等一分多钟。这种体验别说做产品自己调试 prompt 都嫌烦。所以我把这次调优的目标定得很明确不追求理论极限只追求“日常可用”。日常可用的底线我认为是综合吞吐 100 tok/s 以上单流速感 40 tok/s 以上。最后达成的 159 tok/s 是压测吞吐实际对话中的体感速度比这个数字要更温和但已经属于完全可用的范围了。2. 投机解码到底是怎么把速度拉上去的2.1 草稿-验证机制实习生写稿专家审稿很多不加优化的大模型推理速度慢的根本原因是每一步只能生成一个 token而每生成一个 token模型都要把几十亿甚至上百亿参数完整地跑一遍前向计算。你的显卡算力明明很能打但每一步都在“算完一个字、停下来、再算下一个字”GPU 的并行能力根本没吃满。投机解码的思路完全不一样。它引入两个模型一个很小的“草稿模型”draft model一个原本的“目标模型”。过程可以理解成一个实习生先写稿专家再审稿小模型速度极快一口气连续生成 K 个候选 token大模型拿到这 K 个 token把它们作为输入只做一次前向计算并行输出每个位置的预测概率大模型逐位检查小模型的预测是否符合自己的判断一致就接受不一致就回退到第一个不一致的位置用自己的结果替换。关键点在于大模型每轮只做一次前向计算但这一轮极有可能产出好几个 token。假设草稿长度是 5小模型生成 5 个候选大模型检查后接受了 4 个那这一轮实际产出了 5 个 token4 个草稿被接受加上 1 个大模型自己纠正的而大模型的成本只有一次前向。速度自然就上去了接受率越高加速比越明显。这个机制在职场上有点像“实习生先起草专家审稿”。专家不需要从零开始写每一个字他只需快速判断实习生写的对不对。实习生写得越靠谱专家单位时间能审过的内容就越多。反过来如果实习生满嘴跑火车专家每次都要大改那效率反而更低。2.2 为什么 27B 是投机解码的好样本投机解码的效果跟模型规模差正相关。小模型和大模型的“智力差”越大小模型写出来的草稿越难被大模型接受加速效果就越差。反过来差距越小接受率越高加速越明显。Qwen3.8-27B 这个档位尤其适合投机解码的原因在于模型不算特别大但也不小。它比 7B 的质量高出一大截所以一个小尺寸草稿模型写出来的内容在 27B 看来已经算“写得不错”接受率通常能到 50% 到 70%。我实测在代码生成和结构化输出场景接受率甚至能到 80% 以上。你换 70B 试试草稿模型写的东西在 70B 眼里太幼稚了接受率掉得厉害加速效果反而没那么惊艳。再用 7B 做目标模型草稿模型和 7B 的水平差不了多少接受率可能很高但小模型本身生成速度就快你多套一层投机解码增加的收益非常有限搞不好还倒贴。另外草稿模型本身的显存开销和延迟消耗都比较小。我用的草稿模型只有 0.6B 参数跑起来非常轻显卡算力可以忽略不计占用的显存也就 1GB 出头。这让 27B 主模型 0.6B 草稿模型在同一张 24GB 显卡上共存成为可能。2.3 框架与工具选型我最后用了什么投机解码不是某个框架独有的功能目前主流的本地推理引擎都实现了类似机制但实现细节和参数暴露程度差别很大。我先后试过几个社区方案最后留在 vLLM 上原因是它的投机解码实现最完整支持草稿模型热切换还有连续批处理continuous batching和前缀缓存prefix caching可以叠加使用。vLLM 的连续批处理机制也值得一提不再等一个请求完全结束才开始下一个请求而是每个请求按 token 粒度插空执行。这意味着并发请求越多GPU 利用率越高。投机解码负责降低单个请求的 token 延迟连续批处理负责提高整体吞吐两个配合起来才有后面的 159 tok/s。如果你用其他框架比如 llama.cpp 或 SGLang原理是一样的但参数名和调优路径会有差异。这篇文章后边的配置以 vLLM 为主但思路完全可移植。3. 从 14 tok/s 到 159 tok/s 的完整调优路径3.1 第一步先把硬性条件打牢不要一上来就开投机解码先把基础优化做完。我踩过的教训是如果基础推理链路都没调好投机解码的效果会被各种瓶颈吃掉甚至会让你误判它就是没用。基础优化包括三件事第一模型量化。27B 全精度 FP16 大概要 54GB 显存单张 24GB 卡根本放不下。我用 4bit 量化版显存占用降到大约 16GB 到 17GB给 KV Cache 和草稿模型预留了充足空间。量化方式我选了 AWQ 和 GPTQ 之中比较成熟的一种实测质量损失在可接受范围内。第二开启注意力优化。vLLM 默认用 PagedAttention 管理 KV Cache还能叠加 FlashAttention 内核加速。这两项是显存和速度的基石不开启的话长上下文场景下显存很快就被吃满速度也会因为内存碎片化而下降。但这个属于默认行为不需要额外配置只需要确认你的显卡环境支持对应内核。第三设置合适的批处理。vLLM 的连续批处理是自动的但最大并发数max-num-seqs需要设置合理。如果设太小无法利用 GPU 并行度设太大显存又会爆。我实测在 24GB 显卡上单模型无投机时max-num-seqs 设 16 到 32 比较合适超过 32 容易 OOM。这几项做完之后基线从 14 tok/s 提升到了大概 25 tok/s 到 30 tok/s。提升确实有但距离“可用”还差得远。这个阶段相当于给汽车换了套好轮胎发动机还是原来的。3.2 第二步启动投机解码第一次跃升到 60基础优化做完接下来就是核心动作加载草稿模型开启投机解码。在 vLLM 里启动投机解码主要涉及两个参数--speculative-model指定草稿模型路径。我用的同系列 0.6B 小模型--num-speculative-tokens草稿长度也就是小模型一口气生成多少个候选 token。默认是 5最开始我从 5 开始试。启动命令简单示意如下python -m vllm.entrypoints.openai.api_server \ --model Qwen3.8-27B-AWQ \ --quantization awq \ --speculative-model Qwen3.8-0.6B \ --num-speculative-tokens 5 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 16这里注意--gpu-memory-utilization 0.9的意思是允许框架使用 90% 的显存剩下 10% 留白避免分配失败。加了投机解码之后显存中要同时容纳主模型、草稿模型和 KV Cache这个预留比例非常重要。第一次启动之后压测速度从 25 tok/s 直接跳到 62 tok/s。这个飞升很正常因为小模型生成速度极快大模型一次验证多个 token 的效率远高于逐个生成。此时单条流式的体感速度也有了明显改善输出不再是一顿一顿的感觉了。从 14 到 62这个提升已经很可感知了但我还不满足。此时我发现 GPU 利用率仍然没有跑满说明还有优化空间。3.3 第三步调草稿长度、批次和缓存向 100 逼近第二阶段速度上来了但还不够稳定不同类型的任务波动很大。我开始针对草稿长度做实验。num-speculative-tokens这个参数跟投机解码性能的关系不是线性的。太短了比如 2一次验证产出的 token 少加速有限太长了比如 15小模型后面生成的候选越来越不可靠大模型可能只接受前几个后面全 reject等于白做。我在代码生成任务上分别测了 3、5、7、10 四档草稿长度综合吞吐tok/s接受率表现375高稳定582中高稳定796中高波动1088中低前段高后段低最终我把草稿长度定在 7。接受率没有降到不可接受的程度但平均单轮产出 token 数更多综合吞吐最好。这个阶段我还开了两项额外配置一是 KV Cache 量化。把 KV Cache 压成 8bit 之后显存占用进一步下降能腾出更多空间给 batch 和并发。24GB 显存最紧张的就是这块每省一点都是实打实的。二是把 max-num-seqs 从 16 提到 32。因为 KV Cache 小了能同时处理的请求多了连续批处理把 GPU 的并行度拉起来了。这一轮调完综合吞吐到了大约 110 tok/s 到 120 tok/s。此时单条流式体感也已经很顺畅至少在交互层面不会让人烦躁了。3.4 第四步并发与缓存优化最终稳定在 159 tok/s最后一步不是针对单个请求而是针对服务器整体吞吐。我把压测方式从单请求改成多并发请求方法就是同时发 32 到 128 个生成请求。vLLM 的连续批处理在这种场景下会把所有请求交织在一起填充 GPU显存有富余就多接请求谁有 token 就输出谁让 GPU 始终处于忙碌状态。同时我开了前缀缓存prefix caching。这个功能对重复前缀的系统提示词特别有用比如每个请求都带一大段 system prompt计算过的前缀可以缓存下来后续请求直接复用。在我这次的测试场景里前缀缓存把重复计算量砍掉了一截效果非常明显。最终压测数据在 128 并发、输出长度 128 token 的条件下综合吞吐稳定在 159 tok/s。这个数字是服务器吞吐不是单条流式速度单条请求的实际流式速度大约在 40 到 60 tok/s。但如果你是做 API 服务吞吐就是你的核心指标159 tok/s 意味着每分钟能输出接近一万个 token已经足够支撑一个小型应用了。整个调优路径用一张表总结调优阶段主要动作综合吞吐裸跑基线4bit 量化无优化14 tok/s基础优化注意力优化 连续批处理约 25-30 tok/s开启投机解码草稿模型 草稿长度 5约 62 tok/s参数细调草稿长度 7 KV 量化 更大 batch约 110-120 tok/s并发与缓存128 并发 前缀缓存159 tok/s4. 可以直接抄的启动配置与复现说明4.1 推荐硬件与运行参数我这一整套配置是在单张 24GB 显存消费级显卡上跑的这个显存容量基本是跑 27B 量化版的及格线。如果你显卡显存大于 24GB比如 48GB 或更大那会更从容。可以不用开 KV Cache 量化草稿模型也可以选稍微大一点的版本接受率会更高。如果你只有 16GB 显存那 27B 4bit 主模型加上草稿模型会非常紧张建议优先把 KV Cache 量化开到 4bit并且把 max-num-seqs 压到 8但体验会打个折扣。内存方面建议 64GB 起步。虽然主模型和草稿模型都放显存但系统内存要承担数据调度、tokenizer 和临时缓存的开销太小容易成为瓶颈。系统推荐 Linux配好 CUDA 环境。Windows 上也能跑但某些算子性能和显存释放策略不如 Linux 顺手遇到问题也多一些。4.2 启动命令与关键参数解析下面是我实际在用的 vLLM 启动命令你可以直接替换模型路径后使用python -m vllm.entrypoints.openai.api_server \ --model Qwen3.8-27B-AWQ \ --quantization awq \ --speculative-model Qwen3.8-0.6B \ --num-speculative-tokens 7 \ --gpu-memory-utilization 0.9 \ --max-num-seqs 32 \ --enable-prefix-caching \ --kv-cache-dtype fp8 \ --max-model-len 8192 \ --port 8000逐项解释几个关键参数--speculative-model指定草稿模型路径。草稿模型越小显存占用越少但接受率可能下降我建议先从 0.6B 开始观察接受率再说。--num-speculative-tokens 7就是草稿长度是我跑出来的甜点值。如果你任务比较规范、内容可预测性强比如 JSON 生成、代码补全可以加到 10 到 12收益更大。如果是开放式对话建议用 5 到 7别太长。--kv-cache-dtype fp8把 KV Cache 压成 8bit。想再省显存可以试 4bit但量化和精度之间要权衡我先用 fp8 保效果。--enable-prefix-caching开启前缀缓存如果你的请求都带相同的系统提示词这个参数几乎是白捡的速度提升。--max-model-len 8192限制最大上下文长度。别贪长27B 模型在 8K 上下文内效果和速度都更可控拉到 32K 显存很容易爆。4.3 实测结果对比与任务类型差异上面这套配置在不同任务类型上的表现差异挺大我顺手测了几类常见场景任务类型综合吞吐tok/s说明JSON 结构化输出159草稿接受率高速度最快代码补全/解释143接受率高输出稳定通用知识问答128中等接受率自由对话/创意写作96草稿被频繁纠正速度回落这个差异说明投机解码吃任务的可预测性。结构化任务里小模型草稿很容易猜到会被大模型接受自由创作本身五花八门小模型草稿经常跑偏大模型纠错就多。如果你主要是做自由对话可以适当调低草稿长度减少无效草稿浪费实测能把自由对话的吞吐拉回一些。如果你是做 API 服务给其他业务调尽量在 prompt 里约束输出格式收益最明显。5. 调优过程中踩过的坑和排查实录5.1 显存不足参数量、草稿模型、KV Cache 的三角博弈跑 24GB 显卡最头疼的问题永远只有一个显存不够。我前前后后 OOM 了不下十次有几个典型场景很值得说。最开始的失误是没留显存余量。我把--gpu-memory-utilization设成 0.98结果模型加载成功了但 KV Cache 一建立就爆掉。后来统一改成 0.9给框架留了 10% 的缓冲问题就少了。第二个失误是 KV Cache 没量化。27B 4bit 主模型 16GB草稿模型 1GB剩下 7GB 给 KV Cache 看起来挺多但一旦 max-num-seqs 设 32每个请求都占一份 KV Cache7GB 很快耗尽。开了 fp8 量化后KV Cache 占用直接减半显存压力小了一个量级。如果你也遇到 OOM排查顺序建议是先降 max-num-seqs再降 max-model-len再考虑 KV Cache 量化最后才考虑换更小的草稿模型。不要一上来就牺牲草稿模型因为投机解码的核心收益都在它身上。5.2 草稿模型接受率低任务类型的适配问题接受率低是投机解码最大的隐性坑因为表面上速度没提上去你根本看不出来是哪个环节出了问题。我当时在一个自由创作任务上测吞吐只有 80 多远低于代码任务的 143。看日志发现草稿模型的候选 token 经常在第一第二个位置就被主模型拒绝等于后面的草稿白跑了。解决方案其实不复杂一是换更匹配的草稿模型。如果主模型和草稿模型系列不一致或者草稿模型太弱接受率会很低。尽量选择同系列、同期训练的小模型语言习惯更接近接受率明显更高。二是缩短草稿长度。接受率低时草稿长度越长后面无效计算越多。把 num-speculative-tokens 从 7 降到 4 或 5速度反而可能提升因为无效草稿少了。三是考虑触发场景。如果任务本身是高度开放的创作型任务投机解码的优势会被削弱这时候不要强求接受一个相对平缓的加速比就好。我用一个简单方法看接受率看 vLLM 日志或写个客户端对比输入输出 token 数在相同输入下对比投机解码开启前后的平均生成 token 数。接受率 实际接受草稿 token 数 / 草稿 token 总数。能到 0.6 以上就是健康状态低于 0.4 就需要优化了。5.3 速度忽高忽低量化、温度和随机采样的影响速度忽高忽低不是玄学最常见的原因是采样参数。投机解码对采样温度非常敏感因为温度越高主模型的输出分布越随机草稿模型的预测就越难被接受。我用 0.1 的温度跑代码任务接受率非常高速度接近 150把温度调到 0.9 之后同一批请求掉到 100 左右。如果你发现速度掉得离谱先看看自己的 temperature 是不是设太高了。还有一个容易被忽视的点贪婪解码greedy decoding下投机解码效果最好。因为贪婪解码每次选概率最高的 token草稿模型只要同样“选最高”接受率就很高。一旦匹配随机采样两边可能一个选了 A一个选了 B接受率骤降。如果你的业务必须保持一定的随机性我的建议是收敛温度到 0.3-0.5既能保留多样性又不会让接受率崩盘。另外开启 vLLM 的 guided decoding结构化生成也是个好办法输出格式约束越死草稿模型越容易命中。5.4 快速排障清单把这段时间遇过的问题整理成了一张表按症状速查症状可能原因处理方式启动即 OOM显存利用率设太高主模型草稿超显存降低 gpu-memory-utilization 到 0.85 以下吞吐没有提升未加载草稿模型或草稿模型不匹配检查启动日志中投机模块是否启用更换同系列草稿模型速度反而变慢草稿长度过长无效草稿过多调低 num-speculative-tokens接受率低采样温度过高任务过于开放调低温度或改用结构化输出并发一高就 OOMKV Cache 占用过大开启 kv-cache-dtype fp8降低 max-model-len单条流式速度慢单请求本身吃不满 GPU用连续批处理支撑并发提升整体吞吐最后提醒一点159 tok/s 是我在压测条件下得到的数字真实跑业务会受网络、请求大小、prompt 长度等因素影响有所回落是正常的。关键是相对 14 tok/s 的基线你已经从“用不了”进入“能用并且舒服”的区间了。我个人在调完这批参数之后最大的体会是投机解码不复杂但一定要动脑子调。草稿模型选谁、草稿长度设多少、温度控制在多少跟你的任务强相关参数不是万能的先把原理吃透再对着自己的场景做实验才能稳定复现出接近我这里的加速效果。如果你手里正好有 27B 量级的模型要本地部署顺着这个顺序调一遍大概率能少走很多弯路。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →