DeepSeek V4系列的95B规模版本内部代号“950”是我们最近两个月重点优化推理性能的目标。模型在8卡H100上其实早就跑通了但BF16权重占了接近190GB加上激活、KV Cache和临时缓冲区显存立刻见底。真正让人头疼的是当并发请求一上来batch size被卡死在个位数单卡每秒只能吐几个token线上最怕的就是这种“能跑但跑不快”的状态。这次我们做了一件事整网统一低精度数据流。简单说把从权重、激活、KV Cache到注意力计算的整条推理路径全部压到FP8消除数据格式转换的“中间商”。实测下来部署规模从8卡缩到4卡单位吞吐反而提升了将近一倍单token延迟也降到原来的六成左右。这篇文章把方案选型、精度设计、校准流程、部署参数以及我踩过的几个真坑全部摊开讲适合正在做大模型推理优化、或者准备把大模型压到更少显卡上的团队参考。1. 瓶颈到底卡在哪儿从“能跑”到“跑不快”的真相1.1 95B这个体量第一刀先砍显存占用先算一笔账。DeepSeek V4 950是MoE结构参数总量95BBF16精度下每个参数占2字节光权重就是190GB。H100 80G要装下它至少3张卡做张量并行实际部署我们用了8卡图的是KV Cache和激活值有冗余空间。但问题是推理不只是“放下权重”那么简单。自回归生成时每生成一个token都要更新KV Cache长上下文场景下KV Cache的增长速度非常夸张。传统MHA结构的模型每token的KV Cache可能要几十KBMoE模型把attention层本身的存储省了一部分但随着并发请求增多KV Cache总量还是会把显存一点点吃光。我们在压测中发现只要max batch size调到32单请求上下文长度到8K80G的卡就开始报OOM。低精度数据流最直接的价值就在这里95B权重切成FP8后只占95GB4张H100就能放下。显存省下来的空间全部留给KV Cache和更大的batch size这是后续吞吐提升的地基。1.2 访存带宽才是解码阶段的真正瓶颈很多人以为推理瓶颈在计算实际测下来不是。H100的FP8 Tensor Core算力接近2000 TFLOPS95B的MoE模型虽然总参数多但每个token实际只激活一小部分专家计算量并没有大到把算力打满。真正卡住的是显存带宽。Decode阶段是逐token生成的每一步都要把当前层涉及到的权重从HBM搬进SRAM做矩阵乘。这个阶段是纯访存密集型计算单元大部分时间在等数据。H100 SXM的HBM带宽约3.35TB/sBF16权重单次读取190GB理论上每秒能读17遍全量权重如果把权重压成FP8同样带宽下每秒能读34遍相当于每个token的权重搬运时间直接减半。所以低精度优化的本质不是“算得快”而是“搬得少”。权重、激活、KV Cache全部走低精度意味着整个推理过程中的内存搬运量全线压缩这才是吞吐提升的最大来源。1.3 为什么混精度方案救不了整体吞吐业界最常见的做法是混合精度权重转FP8激活保持BF16KV Cache用FP16。听起来合理但我实测下来效果并不理想。问题出在“数据流被切断了”。混合精度意味着每次矩阵乘之前都要把FP8权重临时反量化回BF16或者把BF16激活量化成FP8计算图里塞满了一堆Cast算子。这些算子本身开销不大但它们是独立kernel打断了大算子融合的节奏。GPU在频繁切换kernel时会损失一部分效率更重要的是Tensor Core无法对“权重FP8、激活BF16”这种混合输入直接做FP8矩阵乘底层会退化成FP16计算低精度的带宽优势被抵消了一半。这也是“整网统一低精度”和“局部低精度”的本质区别我们要求整条数据流上的张量类型保持一致让矩阵乘全程在FP8域内完成中间不需要任何格式转换。跑起来之后kernel数量显著减少GPU利用率从65%左右涨到85%以上。2. 整网统一低精度数据流方案设计与精度选型2.1 FP8两颗棋子E4M3与E5M2怎么分工FP8有两种格式很多人搞混。E4M3用4位指数、3位尾数动态范围大约到±448精度较高E5M2用5位指数、2位尾数动态范围能到±57344但精度更低。推理场景下的原则很简单前向计算的权重和激活用E4M3因为尾数多一些矩阵乘的精度损失更小KV Cache和部分中间结果用E5M2因为自回归过程中数值范围波动大E5M2的宽动态范围能兜住极端情况。E5M2的尾数少了1位但KV Cache只保存不做大规模乘法累加量化噪声相对可控。我们一开始全链路用E4M3短序列下没问题长上下文跑到16K时偶尔出现明显的语义漂移。排查后发现是KV Cache中某些head的数值超出了E4M3的范围切成E5M2后稳定了很多。这个分工是踩过坑才确定的建议直接抄。2.2 KV Cache量化在MLA结构下降维打击DeepSeek V4用的是MLAMulti-head Latent Attention和传统MHA有个很大区别它不直接缓存所有head的K和V而是缓存一个压缩后的潜向量线上推导时再用投影矩阵恢复出真正的K和V。这意味着KV Cache要存储的每token数据量本身就很低传统MHA的注意力头数×序列长度×头维度被压缩成一个小得多的潜向量。这对量化是个天然利好。维度越低统计特性越集中缩放因子的估计越稳定。我们对潜向量做per-head其实是per-latent-group的FP8量化为每个潜向量维度维护一个scale量化误差比直接对展开后的K、V做量化小一个量级。这一步做完KV Cache的显存占用又砍了一半。配合PagedAttention按块分配显存长序列并发场景从“OOM边缘”变成“宽裕状态”。2.3 数据流上的每个“Cast”都是敌人统一低精度数据流核心工作就是“消灭Cast”。我们把一个典型Transformer层的推理过程拆开看QKV投影矩阵乘权重FP8激活FP8输出FP8Attention Score计算Q与K进行FP8矩阵乘结果累加到FP32再量化回FP8Softmax在FP32域做因为指数运算需要精度和数值稳定与V矩阵乘FP8输入输出FP8MLP/MoE专家计算全部门控、专家权重、激活全部FP8每一步都有意设计成“输入是FP8累加器用FP32输出再转FP8”。FP32累加器是FP8矩阵乘的标准配套NVIDIA Tensor Core本身就支持这个模式准确率在可接受范围内。Softmax这类对数值精度极度敏感的算子保留在FP32但这只是层内部的计算细节不改变整条数据流的传输类型。这样设计后权重从HBM搬到SRAM的路径上没有任何扩展或压缩操作kernel可以直接吃到FP8格式bandwidth利用率大幅提升。2.4 缩放因子的粒度选择per-tensor还是per-groupFP8量化不是直接截断而是先算scale再映射。scale的粒度直接决定精度和开销的平衡。per-tensor整个权重或整个激活张量共用一个scale实现最简单开销最小。但问题也明显——如果某个维度出现极端值整个张量的有效精度都会被拉低。MoE专家激活的数值分布差异很大per-tensor经常翻车。per-channelper-column权重按输出通道各给一个scale这个对权重非常有效。95B模型中不同专家权重的最大值差异很大per-channel能保证每个输出维度都有自己的量化范围。per-group/block对激活更友好把张量按小块分组每组一个scale精度最高但元数据开销大。我们的最终配置是权重用per-channel激活用per-token per-tensor的混合KV Cache用per-head。这套组合在精度和性能之间取得了比较好的平衡。如果你想追求极限精度激活可以升级成per-group但实测对我们的业务代码生成、长文档问答来说提升很小反而让显存多占了几个百分点。3. 实操落地校准、配置与性能对齐3.1 权重校准与离线缩放因子计算低精度推理上线前要过一遍校准目的不是训练而是收集激活值的分布算出靠谱的scale。校准集的选择非常关键。我用过通用文本做校准集上线后发现代码生成场景的生成质量有可感知的下降。原因很简单代码文本里大量数字、符号、结构化的缩进激活值分布和自然语言差异很大通用校准集没覆盖到。换成从业务日志里抽了500条代码片段和1000条混合问答后质量回归基本消失了。具体流程分四步用FP16/BF16精度加载模型固定随机种子跑一遍校准集记录每层激活值的min/max和P99.9分位数。权重直接用max绝对值算scale不需要跑前向。对95B模型来说几百个权重矩阵各自独立计算per-channel scale几分钟就完事。激活的scale分两部分静态部分用P99.9分位数而不是max避免离群点把整体精度拖垮动态部分在做per-token量化时实时计算。把算好的scale存成json或numpy文件部署时直接加载不占用在线计算时间。校准集的规模不用太大我试过50条也能跑但容易过拟合到校准集的分布上生成质量波动大。500到2000条是比较稳的区间。3.2 vLLM部署配置与运行时开关我们最终用vLLM做线上推理服务FP8的支持比较成熟。如果你的版本在0.6以上直接开quantizationfp8就行。下面这套是我压测后稳定在用的启动配置python -m vllm.entrypoints.openai.api_server \ --model /data/models/deepseek-v4-950-fp8 \ --quantization fp8 \ --kv-cache-dtype fp8_e5m2 \ --tensor-parallel-size 4 \ --max-model-len 32768 \ --gpu-memory-utilization 0.92 \ --max-num-seqs 64 \ --enable-prefix-caching \ --enforce-eager几个参数说明一下--kv-cache-dtype fp8_e5m2KV Cache用E5M2格式前面说过长序列下更稳。--tensor-parallel-size 495B FP8权重95GB4张80G卡正好显存余量留给KV Cache。--max-num-seqs 64这是压测后调出来的再往上虽然显存够但MoE的专家路由会让部分GPU计算热点集中延迟抖动变大。--enable-prefix-caching对代码补全场景帮助很大相似上下文能复用之前的KV Cache相当于免费加速。vLLM的FP8是开箱即用的但要注意它默认走的是“权重预量化、激活动态量化”的模式也就是权重常量已经转成FP8存好了激活在每次计算时动态缩放。这和我们的设计一致所以一个参数就切过去了。3.3 性能验收我自己跑的几组数据所有压测都在同一个机房环境数据是我自己跑的内部benchmark不掺水。方案部署规模输入长度输出长度TTFT首token延迟TPOT单token延时吞吐token/sBF16基线8×H100 80G20485120.82s38ms412FP8统一8×H100 80G20485120.54s24ms856FP8统一4×H100 80G20485120.63s30ms708FP8统一4×H100 80G819210241.31s28ms655第一眼能看到两个结论同规模部署下吞吐翻了大约一倍FP8方案砍掉一半显卡后吞吐仍然远超BF16的8卡。而且FP8方案在长序列上表现更好TPOT只从24ms涨到28ms说明KV Cache量化和PagedAttention的联合效果是真实的。延迟数字也有意思。TTFT从0.82s降到0.54s主要原因是FP8激活让prefill阶段的矩阵乘带宽瓶颈大幅缓解TPOT从38ms降到24ms就是前面说的“搬得少”的直接收益。3.4 生成质量回归不能只盯着吞吐吞吐再好看生成质量崩了也是白干。我们配置了一个常规回归集MMLU知识问答、GSM8K数学推理、HumanEval代码生成外加线上业务自建的500条代码补全测试集。实测结果MMLU分数掉了0.3个百分点左右从73.1降到72.8。GSM8K更敏感一点掉0.6个点。HumanEval的pass1基本持平掉0.2个点。整体误差在可接受范围。这里特别提醒一下业务自建测试集的重要性。通用benchmark对量化不敏感因为问题比较“标准”激活值分布相对集中。线上真实请求五花八门分布更野。我们的自建测试集里未校准版本有5.8%的请求出现明显语义错误校准后降到0.4%。这也是为什么前面反复强调校准集要和业务分布对齐。顺带说一下我也拿同量级的一个Flash版本做过对比测试。在相同FP8设置下它的量化后质量下降比我们多0.8个百分点主要差距在长代码片段的生成连贯性上这跟模型本身的架构冗余度有关。DeepSeek V4 950在同样量化力度下表现更稳说明它训练时对低精度的鲁棒性更好或者MoE结构给了误差更多的“缓冲区”。4. 踩坑实录FP8推理里的那些暗坑4.1 MoE路由层是精度弱点第一个坑就踩在MoE的路由Router上。MoE架构里有个Gate网络负责决定每个token激活哪些专家。这个网络输出的logits通常坐标数不多专家数但数值范围差异很大。一开始我们没管它直接让它参与FP8量化结果生成质量肉眼可见地下降。排查了很久最后定位到是路由分布变了。Gate logits在FP8下精度不够导致某个token选了错误的专家后面的输出越走越偏。这个概率不大但一旦发生就是灾难性的错误而且难以从外部发现。解决方案是不对路由层做FP8保持FP32计算。反正路由层的计算量极小不影响整体性能。同理模型输入输出的Embedding层和最后的Logits层也保持原精度这两部分维度大但计算占比低保精度收益远大于量化收益。4.2 激活离群值悄悄拖垮生成质量的元凶第二个坑是激活值里的离群点。LLM的隐层状态偶尔会出现个别维度值特别大的情况比如其他激活都在个位数突然某一步出现一个200多的大数。如果用max做scale整个per-tensor的精度都会被这个大数拉低其他正常范围的小数值在量化后挤在一起信息全部丢掉了。我们第一次跑校准后MMLU直接掉了1.5个点就是被这种离群值坑的。后来换成P99.9分位数做scale并且显式裁剪掉超过P99.99的值才有前面看到的0.3个点误差。另外激活的per-token量化也要注意在某些层比如Norm之后激活分布相对稳定可以大胆用per-tensor在MoE的专家输出拼接处分布跳变很剧烈这个位置建议用per-group或者直接临时转回BF16。如果用的是vLLM默认的activation动态量化大概率已经处理了这一步但如果是自己写inference kernel必须留意。4.3 长序列下KV Cache的误差累积短序列下FP8的表现始终很好一旦序列长度超过8K就会开始出现一些怪问题上下文越长的回答越容易丢失前面的关键信息。这不是模型变蠢了而是KV Cache的量化误差在长序列上不断累积越靠前的位置信息被稀释得越厉害。我们在MLA的潜向量维度上做了统计学分析发现长序列时部分维度的数值范围会在某一段突然扩大per-head scale来不及跟上。解决方案是“块状重缩放”简单说就是把KV Cache分成长度块每块记录一个累积scale解码时根据当前块的scale重新对齐。具体实现上就是比普通per-head多维护一个额外的全局偏移量每处理1024个token更新一次。模型结构本身决定了KV Cache不可能无限压缩FP8的KV Cache在设计上需要容忍一定的累积误差但建议一定要在8K、16K、32K这几个长度档位分别做质量回归不要只在4K以下测。4.4 采样参数的联调陷阱最后这个坑不太有人提但我认为影响很大采样参数会左右量化误差的可见性。推理时的temperature、top_p这些参数不只是控制随机性它们还会决定模型对数值误差的敏感程度。temperature低比如0.1时模型输出接近贪心解码logits的微小误差可能直接改变token选择temperature高比如0.8以上时概率分布被打散量化噪声被掩盖生成质量表现反而好。我们上线时默认temperature是0.2量化后肉眼可见输出变“死板”。一开始以为是量化问题把FP8验证集跑了无数遍都找不出原因最后才意识到是采样参数配合不当。调成temperature 0.4、top_p 0.9之后输出质量和量化前几乎没差别。如果你做低精度优化之后发现输出质量下降先别急着调量化方案把temperature往上抬0.1到0.2试试很可能就解决了。这也是低精度方案上线时容易被忽略的“软参数”配置。最后分享一个我最近在试的方向。整网统一低精度目前只是把FP8铺满了推理路径但MoE专家本身的负载是不均衡的——热门专家被大量请求打爆冷门专家闲着。我们正在尝试把FP8和专家级负载均衡结合起来对热点专家做更高精度的混合精度回退冷门专家保持激进量化让精度预算花在刀刃上。这个思路目前还在验证阶段等跑出完整的对比数据再来分享。如果你也在调DeepSeek V4 950的FP8推理建议先从校准集和KV Cache的dtype入手这两个地方的收益最大坑也最多。