ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

ik_llama.cpp 逐层量化实践:用 llama-imatrix 层相似度(LIM)分数指导 `--custom-q` 按层量化

ik_llama.cpp 逐层量化实践:用 llama-imatrix 层相似度(LIM)分数指导 `--custom-q` 按层量化 ik_llama.cpp 逐层量化实践用 llama-imatrix 层相似度LIM分数指导--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 仓库中的一项探索性工作在计算重要性矩阵imatrix的同时为每个张量/每个层计算 Layer Importance ModificationLIM分数并以此为据通过llama-quantize --custom-q为不同层分配不同的量化位宽。读完本文你将掌握如何运行llama-imatrix --layer-similarity收集层间余弦相似度、如何解读排序后的层重要性输出以及如何把它转化为一份可复制的按层量化正则配置并在困惑度PPL与模型体积之间做权衡。背景与动机从均匀量化到按层量化传统量化流程对模型所有层的同一类张量使用相同的量化类型例如对整个模型统一执行q4_k_m。这种做法没有考虑一个事实不同层、不同张量对量化误差的敏感程度并不相同。某些层被压得更狠输出质量几乎不受影响而另一些层稍有扰动困惑度就会明显恶化。ik_llama.cpp 的llama-quantize提供了--custom-q参数允许用户用正则表达式为每一类张量指定独立的量化类型。PR #326WIP Compute per layer LIM Scores during imatrix作者 ubergarm2025-04-13 创建、2025-04-16 关闭的设想正是对给定张量的各层按其对量化误差的敏感度进行排序然后利用--custom-q正则决定哪些层量化得更狠、哪些层保留更多位宽从而在保持生成质量低困惑度的同时比所有层统一量化类型占用更少内存。该 PR 自述为实验性质This is mostly vibe code并在后续讨论中被更完善的实现取代——最终落地为llama-imatrix --layer-similarity简写-lsim在 examples/imatrix/imatrix.cpp 中实现。本文以该文档为骨架结合当前仓库源码还原这一完整的技术路径。LIM 方法原理为什么用余弦相似度衡量层重要性LIM 分数源自论文Layer-Wise Quantization: A Pragmatic and Effective Method for Quantizing LLMs Beyond Integer Bit-LevelsDumitru 等人arXiv:2406.17415。其核心直觉是一个层对其接收的输入嵌入改变得越多它就越重要。在实现层面PR #326 的初始尝试是对同一个张量类型例如ffn_down计算相邻两个层的输入激活之间的余弦相似度相似度越低说明该层对输入的改造越大因而越重要。llama-imatrix天然具备收集激活的条件——它在回调解中能同时拿到每个矩阵乘的输入激活src1-data与输出激活t-data。当前仓库 examples/imatrix/imatrix.cpp 中对应的数学实现非常直白static inline double cosine_similarity(int n, const float * x, const float * y) { double sumxy 0, sumx2 0, sumy2 0; for (int j 0; j n; j) { sumxy x[j]*y[j]; sumx2 x[j]*x[j]; sumy2 y[j]*y[j]; } double cos_sim sumx2 0 sumy2 0 ? sumxy/sqrt(sumx2*sumy2) : 0; return cos_sim; } static inline void collect_cos_similarity(int nrow, int n, const float * x, const float * y, std::pairdouble, int p) { for (int row 0; row nrow; row) { p.first cosine_similarity(n, x, y); p.second 1; x n; y n; } }std::pairdouble, int的第一分量累加所有 token 行nrow上的余弦相似度之和第二分量记录行数最终平均值即该层的cos_sim分数。收集路径layer / attention / ffn 三组分数在 collect_imatrix 回调解中-lsim模式维护三组独立的统计量见 examples/imatrix/imatrix.cpp 与 examples/imatrix/imatrix.cppm_layer_sim每个 block 的首个张量输入与上一层首个张量输入之间的相似度度量整个层对输入的改变m_attn_sim每个 block 内 FFN 张量名字含.ffn_的输入与层首张量输入之间的相似度度量 attention 子层的改变量m_ffn_sim当层内遇到 FFN 张量时将上一时刻保存的m_ffn_input与当前输入对比度量 FFN 子层的改变量。从源码结构看实现巧妙地复用了 imatrix 的回调机制is_named_imatrix_tensorexamples/imatrix/imatrix.cpp保证blk.*张量必然进入回调而layer_index()负责从张量名如blk.25.ffn_down_exps.weight解析出层号从而把分数挂到对应的层索引上。层号边界切换时还会做尺寸一致性检查Oops: inconsistent ffn vs last_input size防止跨层误配。实操第一步运行 llama-imatrix 收集层相似度启用方式-lsim是llama-imatrix的专属参数与主程序参数分离解析见 examples/imatrix/imatrix.cpp 的独立参数循环不会污染 common 参数解析。用法./build/bin/llama-imatrix \ --verbosity 1 \ --layer-similarity \ -m /path/to/Model-Q8_0.gguf \ -f calibration_data.txt \ -o imatrix-$(git rev-parse --short HEAD).dat \ --ctx-size 512 \ --numa numactl \ --threads 128文档中的真实运行示例DeepSeek-V3-0324 Q8_0671B 模型61 个 block3 个 dense 前导层 58 个 routed expert 层numactl -N 1 -m 1 \ ./build/bin/llama-imatrix \ --verbosity 1 \ --layer-similarity \ -m /mnt/ai/models/ubergarm/DeepSeek-V3-0324-GGUF/DeepSeek-V3-0324-Q8_0.gguf \ -f calibration_data_v5_rc.txt \ -o /mnt/ai/models/ubergarm/DeepSeek-V3-0324-GGUF/imatrix-ubergarm-DeepSeek-V3-0324-ik_llamacpp-$(git rev-parse --short HEAD).dat \ --ctx-size 512 \ --numa numactl \ --threads 128命令中涉及的关键参数说明参数含义-m输入模型路径建议使用 f16 或 Q8_0 等近无损精度的 GGUF激活统计才可靠-f校准文本如wiki.train.raw或自建语料tokenize 后按 chunk 逐块前向计算-oimatrix 输出文件文档示例中拼接git rev-parse --short HEAD以便区分版本--layer-similarity/-lsim开启层相似度收集结束后打印排序后的层重要性--ctx-size 512上下文长度llama-imatrix默认即为 512--numa numactl/--threads 128多路服务器上的 NUMA 绑定与线程数普通机器可省略运行过程与输出解读文档记录了一次完整运行的输出。模型加载后计算阶段会逐 chunk 打印进度与即时困惑度compute_imatrix: tokenizing the input .. compute_imatrix: tokenization took 309.837 ms compute_imatrix: computing over 213 chunks with batch_size 512 compute_imatrix: 37.90 seconds per pass - ETA 2 hours 14.55 minutes [1]60.9619,[2]10.7701,[3]5.8724,[4]3.7883,[5]2.9691,[6]2.5089,[7]2.2199,[8]2.0199,[9]1.9095, ... [210]3.5342,[211]3.5134,[212]3.4930,[213]3.4727, Final estimate: PPL 3.4727 /- 0.03300值得注意的两点MoE 专家覆盖警告save_imatrix: entry blk.60.ffn_down_exps.weight has partial data (99.61%) 1 out of 256 experts are missing data Storing **but be aware**——对 256 专家的 DeepSeek MoE 层某些专家在本次校准语料中未被路由激活imatrix 会照常存储但明确告警后续量化时这些张量的统计不完全。checkpoint 保存每 10 个 chunk 自动保存一次.dat文件中途中断可结合--in-file续跑见 examples/imatrix/README.md。排序后的层重要性输出计算结束后-lsim会打印三组排序结果分别对应层整体、attention 子层、FFN 子层打印逻辑见 print_layer_importance按|cos_sim|升序排列越靠前、cos_sim 越小 该层对输入的改变越大 越重要 sorted layer importances 0: Layer 0, cos_sim 0.517453 1: Layer 60, cos_sim 0.59436 2: Layer 8, cos_sim 0.857555 3: Layer 3, cos_sim 0.858137 ... 60: Layer 42, cos_sim 0.971662 sorted attention importances 0: Layer 0, cos_sim 0.13174 1: Layer 8, cos_sim 0.516951 ... sorted ffn importances 0: Layer 7, cos_sim 0.571293 1: Layer 10, cos_sim 0.590428 ...从文档的实验记录看DeepSeek-V3-0324 上排在最需优先量化保护前列的 routed expert 层3–60 区间为3, 4, 5, 6, 7, 8, 15, 17, 22, 23, 24, 25, 53, 57, 58, 59, 60——这 17 个层被选作 A/B 实验中的优先分配更多 bit对象。实操第二步把层排序翻译成--custom-q正则得到层排序后下一步就是用llama-quantize --custom-q表达哪些层多给 bit、哪些层少给 bit。--custom-q的语法为regex1type1,regex2type2,...对匹配不同正则的张量使用不同量化类型参数解析见 examples/quantize/quantize.cpp 附近帮助文本见 examples/quantize/quantize.cpp。文档提供了两组互为对照的完整配置量化后模型体积完全一致仅哪 17 个 routed expert 层获得更多 bit不同。基线FIRST-N-IQ3_K_R4朴素取前 17 层# Routed Experts (3-60) (CPU) # Prioritize first 17 layers with larger quants blk\.[3-9]\.ffn_down_exps\.weightiq5_k_r4 blk\.[1][0-9]\.ffn_down_exps\.weightiq5_k_r4 blk\.[2-5][0-9]\.ffn_down_exps\.weightiq4_k_r4 blk\.60\.ffn_down_exps\.weightiq4_k_r4 blk\.[3-9]\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.[1][0-9]\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.[2-5][0-9]\.ffn_(gate|up)_exps\.weightiq3_k_r4 blk\.60\.ffn_(gate|up)_exps\.weightiq3_k_r4量化后的张量构成llama_model_loader 输出llama_model_loader: - type f32: 361 tensors llama_model_loader: - type iq6_k: 1 tensors llama_model_loader: - type q6_0_r4: 61 tensors llama_model_loader: - type iq3_k_r4: 82 tensors llama_model_loader: - type iq4_k_r4: 75 tensors llama_model_loader: - type iq5_k_r4: 567 tensors对照组COSSIM-IQ3_K_R4按 cos_sim 排序选 17 层# Routed Experts (3-60) (CPU) # Prioritize quantizing 17 layers given by lowest cos similarity scores with larger bpw quant size blk\.3\.ffn_down_exps\.weightiq5_k_r4 blk\.4\.ffn_down_exps\.weightiq5_k_r4 blk\.5\.ffn_down_exps\.weightiq5_k_r4 blk\.6\.ffn_down_exps\.weightiq5_k_r4 blk\.7\.ffn_down_exps\.weightiq5_k_r4 blk\.8\.ffn_down_exps\.weightiq5_k_r4 blk\.15\.ffn_down_exps\.weightiq5_k_r4 blk\.17\.ffn_down_exps\.weightiq5_k_r4 blk\.22\.ffn_down_exps\.weightiq5_k_r4 blk\.23\.ffn_down_exps\.weightiq5_k_r4 blk\.24\.ffn_down_exps\.weightiq5_k_r4 blk\.25\.ffn_down_exps\.weightiq5_k_r4 blk\.53\.ffn_down_exps\.weightiq5_k_r4 blk\.57\.ffn_down_exps\.weightiq5_k_r4 blk\.58\.ffn_down_exps\.weightiq5_k_r4 blk\.59\.ffn_down_exps\.weightiq5_k_r4 blk\.60\.ffn_down_exps\.weightiq5_k_r4 ## remainder blk\.[3-9]\.ffn_down_exps\.weightiq4_k_r4 blk\.[1-5][0-9]\.ffn_down_exps\.weightiq4_k_r4 # Same for gate/up blk\.3\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.4\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.5\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.6\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.7\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.8\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.15\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.17\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.22\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.23\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.24\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.25\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.53\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.57\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.58\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.59\.ffn_(gate|up)_exps\.weightiq4_k_r4 blk\.60\.ffn_(gate|up)_exps\.weightiq4_k_r4 ## remainder blk\.[3-9]\.ffn_(gate|up)_exps\.weightiq3_k_r4 blk\.[1-5][0-9]\.ffn_(gate|up)_exps\.weightiq3_k_r4 blk\.60\.ffn_(gate|up)_exps\.weightiq3_k_r4从这两份配置可以提炼出几条可迁移的写作规则正则基于 GGUF 张量名如blk\.3\.ffn_down_exps\.weight.必须转义用[3-9]、[1][0-9]、[1-5][0-9]等字符类表达层号区间按规则出现的先后顺序匹配ffn_(gate|up)_exps用括号分组一次覆盖ffn_gate_exps与ffn_up_exps若同一张量被多条规则命中需要自行确保规则顺序正确、互斥避免歧义本例中更重要的 17 层用iq5_k_r4down/iq4_k_r4gate、up其余层降档到iq4_k_r4/iq3_k_r4模型总 bit 数不变。A/B 实验同一体积下按层分配带来的 PPL 差异文档记录了一次完整的 A/B 对比两个 quant 体积完全一致唯一差异是哪些 routed expert 层获得更高 bit 数FIRST-N无脑给前 17 个 routed expert 层3–19更多 bitCOSSIM根据--layer-similarity输出的 cos_sim 排序给得分最低最重要的 17 层更多 bit。结果V3-0324 上单次实验* FIRST-N Final estimate: PPL 3.3193 /- 0.01830 * COSSIM Final estimate: PPL 3.3151 /- 0.0182作者 ubergarm 的结论使用 PR#328 的llama-imatrix --layer-similarity [-lsim]决定优先量化哪些层在 V3-0324 的单次实验中比朴素地使用前 17 层获得了略好的困惑度。虽然差距仍在噪声范围内但若进一步把分数应用到 attention 层量化上可能还有提升空间本次实验未涉及 attention 层。需要强调的诚实表述这只是一次实验、差距在误差带内不能据此断言cos_sim 方法全面胜出它证明了按层分配 bit 的量化管线在工程上是可行的且分数来源可以替换为任何你认为更合理的判据。讨论与局限性方法学上的质疑文档中最有信息量的部分是项目维护者 ikawrakow 与 compilade 对初始 LIM 实现的逐条质疑它们对理解该方法边界至关重要。1. 反直觉结果ffn_down竟然最不重要PR #326 初始版本按张量类型逐层计算输入余弦相似度后输出显示ffn_down的 LIM 分数接近 0例如 Layer 0 -0.0005、Layer 1 0.0003而attn_q_a/ffn_gate_shexp等却高达 -0.9 量级。ikawrakow 一针见血如果ffn_down是最不重要的张量那就太讽刺了因为众所周知ffn_down的量化误差对可观测的量化质量影响最大。2. 跨层比较在概念上的问题ikawrakow 进一步指出llama-imatrix收集到的输入激活变化并非由正在考察的那一个张量单独造成而是该张量之后所有线性和非线性运算GELU、SiLU、RoPE 变体、归一化组合等共同作用的结果相邻两个层之间同一张量类型输入激活的差异可以粗略估计整个层的重要性但把它归因于这个特定张量类型的重要性那就牵强了。compilade 补充了同样的判断这里实际计算的是模型各线性运算跨层输入的余弦相似度其结果未必与张量相对重要性挂钩。3. 更合理的替代方向层内输入-输出对比compilade 提出的改进思路是利用llama-imatrix已经同时掌握的每个线性运算自己的输入src1-data与输出t-data在层内计算输入/输出点积并归一化来得到 LIM 分数使每个层独立评估但该方案只对输入输出同维的方阵矩阵乘直接成立且跨 token 的聚合方式大概率是平均论文中并未明确。ikawrakow 也回应称他曾在私有仓库中利用输入/输出激活推导过量化修正但同样看不出如何据此得出张量重要性分数。这些讨论直接促成了 PR #326 的关闭Closing this in favor of implementation in PR#328也让最终落地在-lsim中的实现聚焦于**层级layer / attention / ffn 三档**的相似度排序而不是张量类型级的 LIM 分数。读者若沿用此方法应把分数当作层相对重要性的经验排序而非严格的理论依据。进阶验证用--show-statistics交叉检查 imatrix文档最后提供了用主线上游实验性参数--show-statistics检查 imatrix 文件的方式用于交叉验证不同层、不同张量的激活统计特征./build/bin/llama-imatrix \ --in-file /path/to/DeepSeek-V3-0324.imatrix \ --show-statistics输出为按张量名汇总的统计表每一行给出某个层某张量的Σ(Bias)、Min、Max、μ、σ、% Active、样本数N、Entropy、E (norm)、ZD Score等指标。例如attn_kv_a_mqa在浅层Layer 8具有极高的 Max 值30.78与低熵5.66而深层Layer 54更平稳Max 2.68、熵 11.81ffn_down_exps在 Layer 60 出现异常大的 Σ(Bias)1.4e9对应文档前面1 out of 256 experts are missing data的告警。这类指标可以与 cos_sim 排序互相印证辅助判断哪些层值得优先保护。总结从 LIM 探索到-lsim落地的完整链路回顾整条技术路径可以归纳为一条可复用的量化工作流准备用 f16 / Q8_0 模型与有代表性的校准语料运行llama-imatrix --layer-similarity -m model.gguf -f train.txt -o imatrix.datexamples/imatrix/imatrix.cpp 的-lsim分支会自动输出三组排序决策从sorted layer/attention/ffn importances中挑出 cos_sim 最低最重要的层确定每层的目标 bit 预算表达把选择翻译为llama-quantize --custom-q的正则列表语法见 examples/quantize/quantize.cpp验证用llama-perplexity或llama-imatrix --in-file ... --show-statistics对比不同分配方案的 PPL 与统计特征。同时应牢记文档与讨论揭示的边界该方法最初是 WIP 实验PR #326 已关闭-lsim是其继承者PR #328余弦相似度提供的是经验性层排序单个实验显示 COSSIM 方案与朴素 FIRST-N 方案 PPL 差距在噪声范围内对于 MoE 模型还要留意校准语料未覆盖全部专家导致的 partial data 告警。在把结论推广到其他模型尤其非 DeepSeek 架构之前建议先用-lsim复跑一次本模型的分数再决定量化预算的分配。【免费下载链接】ik_llama.cppllama.cpp fork with additional SOTA quants and improved performance项目地址: https://gitcode.com/GitHub_Trending/ik/ik_llama.cpp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表