ARTICLE DETAIL

资讯详情

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

ik_llama.cpp 量化策略梳理:从 PR 443 看 attn_q/attn_k/attn_v 与 MoE 张量的逐张量量化规则

ik_llama.cpp 量化策略梳理:从 PR 443 看 attn_q/attn_k/attn_v 与 MoE 张量的逐张量量化规则 人工智能大模型推理引擎本地部署模型量化模型优化【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp点击查看免费下载ik_llama.cpp 在llama-quantize中内置了一套复杂的逐张量per-tensor量化类型分配策略决定每个权重张量如attn_q、attn_k、attn_v、ffn_down等在给定输出量化档位ftype下实际使用哪种 ggml 量化类型。本文以仓库内 github-data/pull_requests/443 - Streamline a bit the quant strategies.md 记录的 PR #443 讨论为主线深入讲解这套策略的设计动机、关键规则与源码实现并演示如何用--custom-q自行覆盖默认策略。读完本文你将能理解为何同一个模型量化成同一 ftype 时不同张量会被赋予不同精度并掌握按需定制量化配比的方法。一、PR #443 背景一次精简量化策略的尝试PR #443 标题为 Streamline a bit the quant strategies由贡献者 Nexesenex 于 2025-05-22 提出最终状态为 Closed。它的核心内容可以用 PR 描述中的三句话概括为 4 和 6 专家的 MoE 模型HF 上常见的 GQA Frankenstein 缝合模型提升attn_k与attn_v的量化档位把既有的逐张量策略模式推广到新增的 IQ_K 系列量化类型如IQ2_K、IQ3_K、IQ4_K及其 R4 行交错变体修正一个误入 8 专家 MoE 规则的attn_q → Q8_0规则——因为attn_q张量远比attn_k/attn_v大直接抬到 Q8_0 会显著增加模型体积与用少量字节换精度的初衷相悖。虽然该 PR 最终被关闭未合入主分支但它触发了维护者 ikawrakow 与作者之间一轮非常有价值的评审对话恰好揭示了这套量化策略中许多为什么这么定的设计决策。这些讨论与当前 src/llama-quantize.cpp 中的实现高度吻合是我们理解量化策略的第一手材料。二、核心机制llama_tensor_get_type 如何为每个张量选型量化时每个张量的最终类型由 src/llama-quantize.cpp 中的llama_tensor_get_type()决定。函数按张量名如attn_v.weight、attn_k.weight、blk.0.ffn_down.weight逐类匹配结合以下信息输出最终类型输出 ftype即用户指定的量化档位如IQ2_XXS、IQ2_K、IQ1_S_R4模型超参数n_expert专家数、n_gqa()GQA 组数、n_vocab词表大小、model.type如 MODEL_8B / MODEL_70B是否提供 imatrixqs.has_imatrix用户显式覆盖--custom-q规则或--attn-q-type等单张量类型参数。2.1 专家数 / GQA 数驱动的升级规则在attn_v、attn_k、attn_q的处理分支中最醒目的模式是按n_expert与n_gqa提高量化精度。例如 src/llama-quantize.cpp} else if (name.find(attn_v.weight) ! std::string::npos) { if (qs.params-attn_v_type GGML_TYPE_COUNT) new_type qs.params-attn_v_type; else if (qs.model.hparams.n_expert 4) { // for the 4-8-expert model, bumping this to Q8_0 trades just ~128MB // TODO: explore better strategies new_type GGML_TYPE_Q8_0; } ...attn_k分支也有完全对称的处理src/llama-quantize.cpp} else if (name.find(attn_k) ! std::string::npos) { if (qs.params-attn_k_type GGML_TYPE_COUNT) new_type qs.params-attn_k_type; else if (qs.model.hparams.n_expert 4) { // for the 4-8-expert model, bumping this to Q8_0 trades just ~128MB // TODO: explore better strategies new_type GGML_TYPE_Q8_0; } ...源码注释明确写道对 4~8 专家的模型把attn_k/attn_v抬到Q8_0只增加约 128MB属于低成本换精度。这正是 PR #443 描述中bump attn_k and attn_v for the models with 4 and 6 experts所对应的实现逻辑——由于 MoE 模型的注意力张量在总权重中占比被 FFN 专家权重稀释抬高注意力投影的精度对整体体积影响很小却能明显改善量化质量。GQA 场景同样会触发升级例如 src/llama-quantize.cpp 中大量n_gqa() 2/n_gqa() 4条件把attn_v从较低档位抬到IQ4_K、IQ5_K等。其背后原因在 src/llama-quantize.cpp 的 70B 注释中说得最清楚In the 70B model we have 8 heads sharing the same attn_v weights. As a result, the attn_v.weight tensor is 8x smaller compared to attn_q.weight. Hence, we can get a nice boost in quantization accuracy with nearly negligible increase in model size by quantizing this tensor with more bits.即GQA 下attn_v被多个头共享张量尺寸远小于attn_q因此用更多位宽量化它能以几乎可忽略的体积代价换取可感知的精度提升。2.2 专家张量的非连续陷阱MoE 模型的专家 FFN 张量按层分布layer_info在解析层号时有一个值得注意的细节src/llama-quantize.cppBelieve it or not, experts in the FFN of Mixtral-8x7B are not consecutive, but occasionally randomly sprinkled in the model. Hence, simply dividing i_ffn_down by n_expert does not work for getting the current layer as I initially thought, and we need to resort to parsing the tensor name.也就是说专家权重在模型中并非按层连续排列因此用累计计数除以专家数来推算层号是行不通的必须从blk.%d.的张量名前缀解析出真实层号。量化策略中大量i_layer n_layer/8、i_layer 7*n_layer/8、use_more_bits(i_layer, n_layer)之类的首尾层/每隔几层给更多位规则src/llama-quantize.cpp都依赖这个解析结果用来保护对输出质量最敏感的浅层和深层。2.3 新量化类型IQ_K 系列的策略推广PR #443 的另一半工作是把既有模式apply the existing patterns to the new IQ_K quants。这一点在源码中可以看到清晰的对应物多个 ftype 分支里IQ2_K、IQ3_K、IQ4_K、IQ5_K以及*_R4行交错变体都遵循与老类型相同的结构。例如 src/llama-quantize.cpp 的LLAMA_FTYPE_MOSTLY_IQ2_KT分支if (name.find(attn_v.weight) ! std::string::npos) { if (qs.model.hparams.n_expert 4 || qs.model.hparams.n_gqa() 4) new_type GGML_TYPE_IQ4_K; else if (qs.model.hparams.n_gqa() 2) new_type GGML_TYPE_IQ3_K; else new_type GGML_TYPE_Q2_K; } else if (qs.model.hparams.n_expert 8 name.find(attn_k) ! std::string::npos) { new_type GGML_TYPE_Q4_K; } else if (qs.model.hparams.n_expert 8 (name.find(blk.0.ffn_down) ! std::string::npos || ...)) { new_type GGML_TYPE_IQ3_K; } else if (qs.model.hparams.n_expert 8 name.find(attn_q) ! std::string::npos) { new_type GGML_TYPE_Q4_K; }IQ1_S_R4/IQ1_M_R4分支src/llama-quantize.cpp与此结构完全同构只是把类型换成IQ4_K_R4、IQ3_K_R4、Q2_K_R4等 R4 变体。可见按专家数/GQA 数给 attn 与早期 FFN 层分配更高精度是贯穿所有档位的统一设计语言新量化类型只是套用同一套模式的载体。2.4 关于attn_q的精度策略与词表大小条件评审中 ikawrakow 问了一个尖锐的问题为什么Q5_K_S之上要加词表大小条件为什么降低位数的决策要依赖词表大小——对应源码中的 src/llama-quantize.cppelse if (ftype LLAMA_FTYPE_MOSTLY_Q5_K_S) { if (qs.model.hparams.n_vocab 127999 (qs.model.type MODEL_8B || qs.model.type MODEL_70B)) new_type GGML_TYPE_Q6_K; }以及attn_q分支的 src/llama-quantize.cppelse if (ftype LLAMA_FTYPE_MOSTLY_Q5_K_S) { if (qs.model.hparams.n_vocab 127999 (qs.model.type MODEL_8B || qs.model.type MODEL_70B)) new_type GGML_TYPE_Q4_K; // attn_q 反而降档 }Nexesenex 的解释是见 PR 评审对话attn_q在 Llama 3 模型上非常耐受低精度量化即使降到低一档perplexity 不升反降约 0.005IQ4_XS - IQ3_S也有类似观察而attn_v恰恰相反、非常敏感。因此作者从 attn_q 省下的位数拿去补贴 attn_v。这也是Q5_K_S下attn_q降档、attn_v升档这对跷跷板规则的来历。至于为何限定词表大小 127999且仅限 8B/70B 架构PR 中并未给出完整结论ikawrakow 本人也承认还没查过可以推断这是为了把该规则限定在已验证过的大词表 Llama 3 系列模型上避免在其它架构上贸然生效——从源码结构看这是一条相当保守的架构白名单策略。2.5 关于 8 专家 的争议评审中 ikawrakow 三次追问为什么限制 8个专家最终结论是Looks OK apart from the 8condition for MoE models. I dont think it is needed.作者坦言只是没有测试过更大规模的 MoE不想踩雷随后表示可以去掉该限制最终 PR 也被关闭。这一轮交锋实际上反映了该仓库量化策略的演进方式规则宁可保守、也要有实测支撑未经测试的专家规模不轻易套用新规则。这与当前源码中n_expert 4/n_expert 8并存、而 8 以上没有专门分支的现状是一致的——更大的 MoE 依赖--custom-q自行定制。三、实战用 --custom-q 覆盖默认量化策略维护者在评审中明确表示This may make it more convenient for some people, but I basically just use--custom-qthese days.这可能让一些人更方便但我现在基本只用--custom-q。这说明--custom-q是官方推荐的精细化量化手段值得单独展开。3.1 用法与解析逻辑llama-quantize的帮助信息examples/quantize/quantize.cpp说明--custom-q regex1type1,regex2type2...: use this to specify custom quantization type rules.即用逗号分隔多条正则类型规则。底层实现位于 src/llama-quantize.cpp对每个张量名依次用正则做std::regex_search第一条命中的规则生效if (qs.params-custom_quants) { using CustomQ std::pairstd::string, ggml_type; auto q_rules *static_castconst std::vectorCustomQ*(qs.params-custom_quants); for (auto rule : q_rules) { std::regex pattern(rule.first); if (std::regex_search(name, pattern)) { custom_type rule.second; break; } } }随后在 src/llama-quantize.cpp 中custom_type拥有最高优先级直接覆盖前面所有按 ftype 推导出的类型if (custom_type GGML_TYPE_COUNT) { new_type custom_type; LLAMA_LOG_INFO(Using custom type %s for tensor %s\n, ggml_type_name(new_type), name.c_str()); }3.2 使用前提有一个容易踩的坑当默认输出类型本身不是量化类型例如输出为 f32/f16时--custom-q规则会被整体忽略量化器会打印警告src/llama-quantize.cppif (params-custom_quants !ggml_is_quantized(default_type)) { LLAMA_LOG_WARN(%s: ignoring --custom-q rules because default type %s is not quantized\n, __func__, ggml_type_name(default_type)); }因此--custom-q必须配合一个量化的 ftype 使用才有意义。3.3 典型示例假设要复刻 PR 讨论中attn_q 降一档、attn_v 升一档的思路可以在llama-quantize中这样写# 对 attn_q 用 IQ3_S对 attn_v 用 IQ4_K其余张量遵循 IQ3_S 档位的默认策略 llama-quantize --custom-q attn_q\.weightIQ3_S,attn_v\.weightIQ4_K \ model-f16.gguf model-iq3s.gguf IQ3_S同样可以针对 MoE 模型定制专家张量# 前 1/8 层 ffm_down 给更多位规则需要能匹配 blk.0.ffn_down 等张量名 llama-quantize --custom-q blk\.0\.ffn_down\.weightIQ4_K \ model-f16.gguf model-iq3s-moe.gguf IQ3_S注意规则是正则表达式.需要转义成\.否则会匹配任意字符张量名形如blk.0.attn_q.weight、blk.0.ffn_down.weight。量化过程中命中的张量会打印Using custom type ...日志可用--dry-run先行预览分配结果。3.4 单张量类型参数更结构化的替代方案除--custom-q外量化器还提供一组结构化参数直接绑定到特定张量名examples/quantize/quantize.cpp参数作用张量--attn-q-typeattn_q.weight--attn-k-typeattn_k.weight--attn-v-typeattn_v.weight--attn-qkv-typeattn_qkv.weight部分架构 QKV 合并--attn-output-typeattn_output.weight--ffn-gate-typeffn_gate--ffn-down-typeffn_down--ffn-up-typeffn_up--ffn-gate-inp-typeffn_gate_inp帮助信息里还给出了一条经验法则examples/quantize/quantize.cppUsually, attn-q-type can be one type below the chosen ffn type, and attn-v-type should be one type above. attn-qkv-type replaces the types attn-q, attn-k and attn-v on some models.即attn_q通常可比 FFN 主类型低一档attn_v则宜高一档——这与 PR #443 中反复出现的attn_q 耐低精度、attn_v 敏感经验完全一致也解释了默认策略里大量升级/降级规则的动机。四、从 PR 到实现的闭环这套策略如何验证量化策略的最终效果可以借助仓库中的工具验证perplexity 评测examples/perplexity/perplexity.cpp 用于对比量化前后困惑度是判断attn_q 降档是否真的不掉点的标准手段量化质量检查--dry-run见 examples/quantize/quantize.cpp 的 usage可预览每个张量的目标类型而不实际写文件imatrix 辅助量化策略中多处!qs.has_imatrix条件如 src/llama-quantize.cpp表明是否提供 imatrix 会直接改变某些张量的默认类型——有 imatrix 时策略更激进例如前 1/8 层给更多位没有时则保守回退。仓库内 github-data/pull_requests 目录收录了大量此类 PR 讨论记录如量化相关的 #334、#393、#434 等它们与 github-data/discussions 中的量化实验贴互为印证是理解这套策略演进历史的绝佳材料。五、小结PR #443 虽然最终被关闭但它完整展示了 ik_llama.cpp 量化策略的三条设计原则这些原则在 src/llama-quantize.cpp 的llama_tensor_get_type()中均有落地实现按张量敏感度分配精度attn_v尤其 GQA 共享时与attn_k体积小、敏感度高宜升档attn_q耐受低精度可降档补贴前者按模型结构适配专家数n_expert 4/ 8与 GQA 组数n_gqa() 2/ 4决定注意力与 FFN 张量的升降档70B/8B 大词表架构另有专属规则可覆盖、可定制内置策略只是默认值--custom-q与各--*-type参数允许用户逐张量重写规则以正则匹配、首条命中优先且仅在默认类型为量化类型时生效。理解这套机制后你不仅能解释为什么同一个 IQ3_S 模型里各张量类型不同还能针对自己的模型包括 HF 上的 4/6 专家 GQA Frankenstein 缝合模型用--custom-q精确调配位数在体积与质量之间找到自己的平衡点。赞分享人工智能大模型推理引擎本地部署模型量化模型优化【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp点击查看免费下载相关推荐ik_llama.cpp 量化策略调优指南面向 GQA 与 MoE 模型的逐张量位宽分配实践ik_llama.cpp 量化策略调优指南面向 GQA 与 MoE 模型的逐张量位宽分配实践 导读 本文以 ik_llama.cpp 开源仓库中收录的 Pul人工智能大模型推理引擎本地部署模型量化模型优化ik_llama.cpp 量化精度权衡实战IQ2_K 与 LLaMA-3.1 注意力张量attn_v / attn_output的 bpw 分配策略ik_llama.cpp 量化精度权衡实战IQ2_K 与 LLaMA 3.1 注意力张量attn_v / attn_output的 bpw 分配策略 导读人工智能大模型推理引擎本地部署模型量化模型优化ik_llama.cpp 量化调优实战从 PR 23 看 IQ4_K 的 attn_v 精度策略与 Gemma-2 权重不对称性ik_llama.cpp 量化调优实战从 PR 23 看 IQ4_K 的 attn_v 精度策略与 Gemma 2 权重不对称性 本文基于 ik_llama.人工智能大模型推理引擎本地部署模型量化模型优化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表