news 2026/9/20 5:01:33

ik_llama.cpp Smart Expert Reduction(SER)CPU 端修复深度解析:从 NaN 污染到 `-ser` 参数的正确使用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ik_llama.cpp Smart Expert Reduction(SER)CPU 端修复深度解析:从 NaN 污染到 `-ser` 参数的正确使用

ik_llama.cpp Smart Expert Reduction(SER)CPU 端修复深度解析:从 NaN 污染到-ser参数的正确使用

【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp

SER(Smart Expert Reduction,智能专家缩减)是 ik_llama.cpp 提供的一项 MoE(Mixture of Experts)推理优化能力,它允许用户通过命令行直接指定参与计算的活动专家(active experts)数量,从而在不改动模型权重的前提下灵活控制推理开销。本文以 PR 415 - Fix SER (CPU) 为骨架,剖析该功能在 CPU 端一个隐蔽内存污染 Bug 的根因与修复思路,并结合仓库源码说明-ser参数的解析逻辑、触发条件、复现方法与配套的 CUDA 后续修复(PR 416),帮助开发者在真实部署中规避“输出变成DDDDDDDD/GGGGGGG”这类经典故障。

SER 是什么:从命令行直接控制活动专家数

在 MoE 模型中,每一层通常包含大量专家(如 DeepSeek-V3/R1 系列的 256 个专家),但每次前向传播只会按门控路由结果激活其中一小部分。活动专家数越多,计算量越大;越少则越快但可能损失质量。ik_llama.cpp 将这种“专家数裁剪”暴露成了命令行开关,正如 docs/parameters.md 所记录的:

参数说明默认值
-ser, --smart-expert-reduction专家缩减参数Kmin,t,使用自定义的活动专家数-1, 0

该文档进一步指出,该功能“Powerful, basically REAP from just command line”(功能强大,基本相当于直接在命令行实现 REAP 剪枝方案)。例如设置-ser 1,6时,t = 1表示使用固定数量的专家,K_min = 6则意味着每层只使用 6 个专家,而不是模型默认数量。这一点在 PR 415 的对话记录 中也有实际验证:测试者使用-ser 6,1即可复现故障。

参数解析:-ser在源码中的真实取值逻辑

-ser的参数解析位于 common/common.cpp:

if (arg == "-ser" || arg == "--smart-expert-reduction") { CHECK_ARG auto values = string_split_pairs<int,float>(argv[i], ','); if (values.size() == 1) { params.min_experts = values.front().first; params.thresh_experts = values.front().second; } else { invalid_param = true; } return true; }

从源码可以确认以下几点:

  • 参数格式为逗号分隔的整数,浮点数二元组,分别写入params.min_expertsparams.thresh_experts
  • 当只传入一个值(如-ser 6,1解析后其实是一个二元组;若只传单个值则视为缺省组合)时,仍按同一对字段处理;
  • 这两个字段随后在构建各架构计算图时被消费。例如 src/graphs/build_llama.cpp、src/graphs/build_deepseek2.cpp 等各 MoE 架构的图构建代码中,都通过n_expert/n_expert_used等变量控制专家矩阵乘法的形状。参数默认值-1, 0表示“不启用缩减”,即维持模型默认的活动专家数。

同时,该参数的帮助文本也注册在 common/common.cpp:

options.push_back({ "*", "-ser, --smart-expert-reduction", "experts reduction (default: %d,%g)", params.min_experts, params.thresh_experts});

故障现象:DDDDDDDDGGGGGGG输出从何而来

PR 415 的提出背景是社区报告 SER 可能产生乱码输出(garbage)。作者 ikawrakow 在 PR 描述中给出了精确的根因分析:

当实际使用的专家数少于指定的活动专家数时,专家矩阵乘法结果中有一些行从未被赋予任何值。正常情况下这不是问题,因为这些行在最终求和得到专家结果之前会被乘以零。但如果这些未被计算的行中出现了InfNaN值,我们就会得到 NaN,进而导致乱码输出。

这段描述对应到代码层面,正是 PR 415 对话中 ubergarm 复现的断言崩溃:

/home/w/projects/ik_llama.cpp/ggml/src/iqk/iqk_mul_mat.cpp:16600: GGML_ASSERT(fms.S[j] > 0) failed

fms.S[j] > 0的断言失败意味着在 iqk 矩阵乘法(该仓库的 iqk 量化内核路径,位于 ggml/src/iqk/)内部,缩放因子出现了非正值——这正是被Inf/NaN污染的典型表现。与 iqk flash-attention 模板 中float S = fms.S[j];的取值方式相互印证,可以推断fms.S承载了专家结果行缩放/累加的关键中间状态。

作者进一步解释了为什么这个 Bug 具有“偶发性”:

是否出现Inf/NaN取决于计算之前发生了什么,因为相同的内存还被其他操作用来存放结果。这就是为什么这个问题并不总是显现(但只要对话足够长,DDDDDDDDGGGGGGGGG输出最终会出现)。

也就是说,未被初始化的行读取的是内存中的“旧数据”。如果旧数据恰好是有限值,乘零后不影响最终结果;一旦旧数据是上一步操作遗留的Inf/NaN,乘零依旧产生 NaN,NaN 沿专家求和一路传播,最终表现为大段重复字符乱码。这也是该问题难以在短会话中定位的原因。

修复思路:显式初始化未使用的专家结果行

PR 415 的修复核心是:当使用的专家数少于活动专家数时,确保矩阵乘法结果中未使用的行被显式设置为确定值(清零/初始化),而不是依赖“乘零”这一隐含假设。修复后,即使这些行后续乘以零,也不会再把内存中的Inf/NaN带入最终累加结果。该 PR 同时声明“类似的修复还需要应用于 CUDA 实现,留待后续 PR”——这一后续工作正是 PR 416 - Fix SER (CUDA)。

值得注意的是,本次修复被合并进主线后,PR 416 的作者 ubergarm 反馈“即使不应用 PR 416,重新编译带 CUDA 的主线后也已无法复现错误”,并给出了一个完整的验证命令(详见下文)。而 ikawrakow 随后指出 CUDA 上更难触发该 Bug,并补充了专门的压力复现方法。两个 PR 共同组成了 SER 功能完整性的关键一环,这一修复也已被记录在 README.md 的 Fixes 列表中:

Fix SER. CPU: PR 415 CUDA: PR 416

如何复现与验证:来自 PR 对话的真实测试命令

使用 llama-server 验证(PR 415 测试者环境)

ubergarm 在 PR 415 中使用的 CPU 复现/验证命令核心参数为-ser 6,1(配合 CPU-only 编译),修复前出现上述断言崩溃,修复后工作正常。随后他在 PR 416 中给出了一套完整的 DeepSeek-V3 混合推理命令,用于在 CUDA 构建下验证 SER 的稳定性:

CUDA_VISIBLE_DEVICES="0" \ ./build/bin/llama-server \ --model /mnt/raid/hf/DeepSeek-V3-0324-GGUF/DeepSeek-V3-0324-IQ2_K_R4/DeepSeek-V3-0324-IQ2_K_R4-00001-of-00005.gguf \ --alias ubergarm/DeepSeek-R1-IQ2_K_R4 \ --ctx-size 131072 \ -ctk f16 \ -mla 3 -fa \ -amb 512 \ -fmoe \ -ser 6,1 \ --n-gpu-layers 63 \ --override-tensor exps=CPU \ --parallel 1 \ --threads 24 \ --host 127.0.0.1 \ --port 8080

这条命令的关键点在于:-ser 6,1将活动专家数固定为 6;--override-tensor exps=CPU配合--n-gpu-layers 63实现“专家权重留在 CPU、其余层部分上 GPU”的混合部署(该选项的通用用法可参考 docs/parameters.md),使专家矩阵乘法实际跑在 CPU 路径上,从而更容易触发/验证 CPU 端修复。相关参数补充说明:

  • -mla 3:DeepSeek 系列 MLA(Multi-head Latent Attention)模式选项,README.md 记录了 MLA 支持;-fa启用 Flash Attention;
  • -ctk f16:K 缓存使用 f16 类型;
  • -amb 512:与 attention 相关的缓冲配置;
  • -fmoe:融合 MoE 相关计算路径。

使用 llama-cli 压力测试(PR 416 作者环境)

ikawrakow 在 PR 416 对话中给出了更针对性的复现方法,使用 Qwen3-30B-A3B(IQ5_K量化)配合“思考模型”进行长会话压力测试:

./bin/llama-cli -m ../ncuda/junk.bin -t 16 -ngl 100 -c 20000 -cnv -p " " -rtr -fa -s 1234 \ -ot "blk\.29\.ffn=CPU,blk\.[3-4][0-9]\.ffn=CPU" -ser 6,1

配合如下编码谜题提示词反复生成长文本:

Encoded text:\noyfjdnisdr rtqwainr acxz mynzbhhx\nDecoded text:\nThink step by step\n\nEncoded text:\nsudlcg jncgpxoydflx ky lraebdtvlxmy nzbnkyaibh ttemgsdfqu gkdx pvsunvaauyacairrlxyy\nDecoded text:\n<think>

作者指出,这类“思考模型”在生成大量逐 token 输出前无需过多交互,Bug 更容易暴露:“思考过程一开始很正常,但最终会开始吐出GGGGG。该 PR 修复了这个问题。”他还观察到修复后模型在-ser 6,1下能正确解出谜题,而-ser 7,1仍然失败——说明 SER 的行为对专家数量选择高度敏感,验证时需要测试多种参数组合。

实践建议与注意事项

  1. 升级到包含 PR 415/416 修复的版本:这是避免 SER 乱码的前提。修复已被收录进 README.md 的 Fixes 列表,构建前确认代码已包含该修复。
  2. CPU 端-ser组合按需验证-ser Kmin,1是固定专家数的常用形态,但不同Kmin下模型表现可能差异显著(如 PR 416 中6,1成功而7,1失败),建议对目标模型逐一测试。
  3. 混合推理时留意内存复用风险:PR 415 揭示的根因是未初始化行读取了残留的Inf/NaN。长会话、多请求并发(--parallel)等场景下内存复用更频繁,故障更容易浮出水面;若观察到大段重复字符输出,应首先怀疑专家路径数值污染。
  4. -rtr等特性的协同:仓库 README 提醒,混合 CPU/GPU 推理时需谨慎使用-rtr(它会把留在 RAM 的张量重排为 row-interleaved 格式,而部分量化类型没有 CUDA 对应实现,会导致计算被迫留在 CPU)。排查 SER 相关问题时应一并考虑此类交互影响,详见 README.md。

小结

PR 415(CPU)与 PR 416(CUDA)共同修复了 SER 功能在专家数缩减场景下的数值污染问题,其根因——未初始化结果行中的Inf/NaN通过“乘零”假设泄漏进最终输出——是混合精度、内存复用型推理引擎中一类非常典型的隐患。理解这一修复,不仅能帮助你正确使用-ser参数规避乱码,也为你排查同类 MoE 推理数值问题提供了可复用的思路:先检查未计算路径的初始化状态,再怀疑缓存/内存复用带来的陈旧数据。

【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 5:01:18

npm EACCES 装不上 @openai/codex?TaoToken 通道这样填 Base URL 再跑 Agent

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 5:00:31

CAN与CANopen协议栈详解:从物理层到伺服控制的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 4:59:54

编码规范与测试用例设计:从能跑到能维护的工程实践指南

1. 编码环节&#xff1a;从“能跑”到“能维护”1.1 编码规范为什么不是形式主义教科书在讲编码的时候&#xff0c;通常会把“编码风格”“命名规范”“注释规范”这些内容放在最开始&#xff0c;很多同学觉得这部分就是“排版要求”&#xff0c;可有可无。但真到了项目里&…

作者头像 李华
网站建设 2026/9/20 4:59:35

CTF密码学实战:从古典密码到现代加密攻击技巧

1. 密码学竞赛的实战价值与学习路径在网络安全竞赛领域&#xff0c;CTF&#xff08;Capture The Flag&#xff09;中的密码学挑战向来是最考验选手基础功底与思维灵活性的模块。过去四年间&#xff0c;我作为战队密码学方向负责人&#xff0c;见证了太多选手从面对简单替换密码…

作者头像 李华
网站建设 2026/9/20 4:56:38

图解Transformer:从注意力机制到工程实践

1. 从视觉化学习到技术深挖&#xff1a;为什么我们需要图解Transformer&#xff1f;第一次看到Jay Alammar那篇《The Illustrated Transformer》时&#xff0c;我正在调试一个机器翻译模型。当时模型在长句处理上总是出现语义断裂&#xff0c;传统RNN的序列处理方式显然遇到了瓶…

作者头像 李华
网站建设 2026/9/20 4:56:36

把第三方代码装进生产实例:TREK 插件管理要过的 5 道关

把第三方代码装进生产实例&#xff1a;TREK 插件管理要过的 5 道关 【免费下载链接】TREK A self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more. 项目地址: https://gitcode.com/GitHub…

作者头像 李华