← 返回 Skill 列表
extension
分类: 开发与工程API Key 暂未确认

bonsai三元GGUF模型转ninfer模型03

3审计和验收:七道门、6 条负控、64 层 sweep、17 条装配审计、PASS 的边界

person作者: montherlandhubModelScope

验收链:七道门,一个判定,一个退出码

全部验收标准写在 src\verify_artifact.py 的模块注释里(第 1 行到 """ 结束), 逐条给出出处、判据、参考数值、以及为什么这道门存在。本技能是它的浓缩版。

.\dist\pack-ternary.cmd verify out\bonsai.ninfer --sweep
# 或直接:
.\dist\.venv\Scripts\python.exe verify_artifact.py <artifact> [选项]

| 选项 | 作用 | |---|---| | --gguf PATH | 源 GGUF。默认是 PQ2_0 路径——不覆盖就会拿错源去比。 | | --template PATH | 启用 STEP 1 的 provenance 交叉校验(identity + 借用对象存在性) | | --dump PATH | 引擎 embedding activation dump → 打开 STEP 5 | | --oracle-dump PATH | gemm_oracle.py dump / 旋转 harness 的 dump 目录 → 打开 STEP 5b | | --oracle-object NAME | gemm_oracle.py 检查哪个对象(默认 text/layers/3/mlp/gate_up) | | --sweep | 64 层全覆盖(48 GDN + 16 全注意力) | | --json | 末尾追加 {"artifact", "ok", "results":[{step,status,detail}]} | | --quiet | 已声明但从未被读取的死选项 |

退出码 0/1。末尾打印 N passed, N failed, N skipped 和 RESULT: PASSED 或 RESULT: FAILED -- do not load this artifact into the engine。 pack-ternary.cmd 把退出码转成桌面通知,FAIL 文案就是"不要装载"。

results 里的 step 键:provenance、inspect、payload_order、row_order、 row_order_sweep、assembly、signs、embedding、engine_oracles。

🚨 必须把同一个 --gguf 传给验收。 拿验证器默认的 PQ2_0 gguf 去验一份 PTQ1_0 制品, 比的是两套不同的权重,结果每个 row-order section 都以 matched=0 失败,而对制品本身 一个字都不说。pack-ternary.cmd 的注释里明确记着这条。


链路顺序

SKIP 不是 FAIL:需要本包产不出的东西(引擎 dump、nvcc dump)的门报 SKIP 并说明什么 能打开它。

STEP 1   inspect + provenance
STEP 2   payload order
STEP 3   row order            (+ 3b: --sweep 的 64 层覆盖)
STEP 3c  assembly audit       17 条位置比对 + 4 条负控
STEP 4   signs
STEP 5   embedding            需要引擎 activation dump
STEP 5b  engine oracles       需要 nvcc 内核 dump

负控是判据的一部分,不是装饰

上游原话(verify/oracle_rot.py):

"判据刻意做成双向:文档化变体必须吻合,且一族貌似合理但错误的变体必须全部失败。 单向检查分不清『内核是对的』和『判据太松』"——并把它列为 docs/03 第 4.2 节 6 条判据的 第 3 条。

所以 STEP 3c 不只审制品:它把同一套审计喂给 4 个故意错误的排布,并要求每个都被抓住。 一个存活的负控意味着判据无法失败,于是无论制品如何该门都报 FAILED——因为不能失败的判据 什么都证明不了。

6 条负控与实测

| 位置 | 负控 | 实测结果 | |---|---|---| | STEP 2 | offset 0(base/码平面) | 被拒:neg=119273 nonfinite=7749 median=1.5615 | | STEP 2 | rows*groups*base_bytes(PTQ1_0 落在 high 平面内) | 被拒:neg=118596 nonfinite=8552 median=1.3496(PQ2_0 上这个偏移就是真偏移,所以不提供该负控) | | STEP 3c | 交换两行 | 被抓:order_mismatches=2 | | STEP 3c | 整体旋转一行 | 被抓:order_mismatches=6144 | | STEP 3c | perm48 作用在行上(上游 bug) | 被抓:repeated=4064 missing=4064,与 MAPPING.json 记录的数字一致 | | STEP 3c | 重复一行、丢一行 | 被抓:repeated=2 missing=2 |

负控只在装配与排布这两道门上。 STEP 1/3/4 是等式与子集判定,不存在"貌似合理的错误 变体"这一类可比对象;要给它们配负控得先定义该变体。


STEP 1 — inspect + provenance

制品必须能通过 tools.artifact 重新打开(MAGIC "NINFER\x00\x02"、目录能解析、 payload 区间不重叠/不乱序/不越界)。强制:identity.model_id 与 identity.weights_id 非空,对象清单非空。

provenance(需要 --template):制品 identity 必须与模板逐字段相同;每个借用对象 (vision/*、mtp/*、frontend/*、text/draft_head*,此模型 353 个)必须存在于模板。 这条专抓"打错了模板"——别的门都发现不了,因为产出的 text 对象两种情况下都看着对。 模板找不到时报 SKIP 而非 FAIL。


STEP 2 — payload order

三元 payload 必须按 row-split 平面存储。两种候选排布解码语义完全相同,所以只有分布 指纹能分辨。scale 平面按格式实际所在的偏移读,偏移取自 row_split_geometry()—— 即 pack.py 和引擎加载器用的同一个函数:

PQ2_0_G128  base(32 B/组) | scale(2)              scale @ rows*groups*32
PTQ1_0_G128  base(24 B/组) | high(2) | scale(2)   scale @ rows*groups*26

每个采样对象强制:

| | 判据 | |---|---| | (a) | fp16 scale 平面 neg == 0 | | (b) | fp16 scale 平面 nan == 0(inf 也算进去,oracle 把它们并在一起) | | (c) | 0 < median(scale 平面) < 1 | | (d) | 仅 PQ2_0:\|first_code_zero_share - 0.3278\| <= 0.002 |

(d) 只对 PQ2_0:check_payload_order.py 在 if obj.format == "PQ2_0_G128" 下算码指纹, 而 PTQ1_0 的零码走的是另一条 qs 通道((prod * 3) >> 8 == 1),没有发布过理论占比。 与其编一个常数,不如把该门报为不适用,而 (a)-(c) 照常适用。

健康对象参考值(n=5120, k=6144):

PQ2_0   scale @ rows*groups*32 : neg=0  nan=0  median=0.0167847  max=0.44458
PTQ1_0  scale @ rows*groups*26 : neg=0  nan=0  median=0.016785
zero-share 0.3277  vs 理论 0.3278

PQ2_0 上同样的数字额外与已发布的 check_payload_order.py 交叉比对——一致性是 演示出来的,不是假定的。


STEP 3 — row order

两条 MAPPING.json 规则是 INFERRED 而非实测,且都是整行置换。行是被置换而不是被 变换,所以字节级行指纹是精确测试——这是数值相关性给不了的结构性自证,因为权重 活在旋转基里,相关性无意义。

规则 A — gdn_value_z

value_z = concat(tiled_to_grouped_heads(attn_qkv[4096:10240]),
                 tiled_to_grouped_heads(attn_gate[0:6144]))
48-head 置换必须在 HEAD 粒度上做,因为该张量的行轴是 48 head x 128 行:
    src_row = perm48(row // 128) * 128 + row % 128
    perm48(i) = (i % 3) * 16 + (i // 3)

MAPPING 的 risk 字段原话:if the engine test disagrees, flip this single permutation。 evidence:2026-09-19 由引擎测试 RESOLVED(原为 INFERRED)。ssm_conv1d 的 V 通道实测是 tiled 的、其规则已按 head 粒度置换(cos +0.99504);alpha/beta 是 48 行 = 48 head, 所以行粒度就是head 粒度,用同一置换实测 +1.00000 / +0.99587。

规则 B — attn_q_per_head_interleave

attn_q.weight 是 N=12288,视为 48 个 256 行的块。
块 0,2,...,46 = 24 个 query head(顺序不变);块 1,3,...,47 = 24 个 output-gate head。
目标: attention/query_key  = concat(query(6144), attn_k(1024))
      attention/gate_value = concat(gate(6144),  attn_v(1024))

evidence:INFERRED for main layers from the MTP layer——在 MTP 层实测过(逐块 cos +0.99982,24/24 与 24/24 块精确)。MTP 层是形状相同的全注意力层。

判据

每个 section 三条字符串必须全部出现在 oracle 输出里:

(a) "documented rule holds row-by-row: True"
(b) "rows DUPLICATED in the artifact: 0"
(c) "source rows MISSING from the artifact: 0"

这比已发布脚本更严——后者只看 report() 的布尔值。判据里用"多重集"措辞是刻意的: 集合成员测试不充分,因为重复的行仍然能找到源。artifact[a] 要直接与规则指名的 那一行比较,绝不靠搜索匹配指纹来定位。

覆盖

默认只覆盖 layer 0(规则 A)和 layer 3(规则 B)。规则是逐层的,一层不能代表另外 63 层。 --sweep 覆盖 MAPPING.json 列出的全部层:48 个 GDN 层(value_z)+ 16 个全注意力层 (query / key / gate)= 64。对象缺失的层记为 absent 并 FAIL 该 sweep;layer3-only 制品报 1/64 并非零退出,绝不可能静默通过。

check(pack.py)不是替代品:它比的是字节数,而 bug 历史证明字节数恰是这类检查的盲区。


STEP 3c — assembly audit

check_row_order 只覆盖两个被置换的规则。按文档应当逐字拷贝的对象此前无人验证: token_embedding、output_head、mlp/down、mlp/gate_up、gdn/query_key、 gdn/output、attention/output。pack.py 的 CHECK 3 只比它们的字节数——而字节数 恰恰是"拷贝顺序错了也照样精确正确"的量。

17 条位置比对

text/token_embedding                    <- token_embd.weight
text/output_head                        <- output.weight
layer 0  mlp/down                       <- ffn_down
layer 0  mlp/gate_up[gate 半]           <- ffn_gate
layer 0  mlp/gate_up[up 半]             <- ffn_up
layer 0  gdn/query_key                  <- attn_qkv[0:4096]
layer 0  gdn/output                     <- ssm_out
layer 0  gdn/value_z[0:6144]            <- attn_qkv[4096:10240] head-perm
layer 0  gdn/value_z[6144:]             <- attn_gate[0:6144]  head-perm
layer 3  mlp/down                       <- ffn_down
layer 3  mlp/gate_up[gate 半]           <- ffn_gate
layer 3  mlp/gate_up[up 半]             <- ffn_up
layer 3  attention/query_key[0:6144]    <- attn_q 偶数块
layer 3  attention/query_key[6144:]     <- attn_k[0:1024]
layer 3  attention/gate_value[0:6144]   <- attn_q 奇数块
layer 3  attention/gate_value[6144:]    <- attn_v
layer 3  attention/output               <- attn_output

17 而不是上游的 15:mlp/gate_up 被拆成 gate 半与 up 半,所以拼接边界也做位置比对, 而不是当成一整块审计。15 + 2 = 17。这比上游更强,不是更弱。

每条审计强制:无重复行、无缺失行、顺序不符为 0。加上 4 条负控(见上)。

格式通用性

已发布的 check_assembly.py 是 PQ2_0-only(if tt != 142: raise、CODE_BYTES = 32、 假设 scale 紧跟码平面)。而且 check_row_order.artifact_row_keys 里 GROUPS = 40 写死, 遇到 k=6144 的对象(48 组)直接 assert 失败——也就是说上游脚本根本验不了 attention/output 这类对象。

本包的指纹走 verify/ternary_rows.py,几何从 row_split_geometry() 读、GGML 块布局从 pack.py 的 block_to_planes() 读,于是只有一份布局定义。等价性实测而非假定:

python verify\ternary_rows.py --self-check <PQ2_0 制品> <PQ2_0 gguf>

要求它与已发布实现在 PQ2_0 上指纹完全相同(已通过:制品侧 True、GGUF 侧 True), 所以对 PQ2_0 这是重构而非第二意见。PQ2_0 侧的等价性另行实测:query_key / gate_value 与上游 helper 的逐行指纹完全相同。


STEP 4 — signs

制品的 text/hadamard_signs(扁平 F32,±1)和 text/hadamard_widths(I32 前缀和) 必须与源 GGUF 的 prism.hadamard.* 元数据块逐位相同——不是捐赠模板的。 由 check_signs.py 强制,判据是 np.array_equal(signs, meta_values)。

Bonsai-2-27B 的参考值:sign_widths = [5120, 6144, 17408](和 28672)、block_size 1024、 sign_mode explicit、transform normalized-sylvester-walsh-hadamard、 inverse_weight_names ['token_embd.weight']、gdn_v_grouped 1、401 个 folded weight 名 (含 inverse 共 402 个 folded 矩阵)。

GGUF 没有 prism.hadamard.* 键时这一步 SKIP 而非 FAIL——那它就不是 Prism folded 导出, 检查不适用。


STEP 5 — embedding

只有在引擎加载过制品并 dump 出 embedding 激活之后才能跑;没有 --dump 就 SKIP。 check_embedding.py 把引擎 dump 与一份独立的 numpy 重建对比:

h = s * (H * z),   z = decode(token_embedding_row[id])

H 是尺寸 1024 的归一化 Sylvester-Walsh Hadamard。要求最差相对 L2 ≤ 0.02,覆盖最多 8 个 token。

上游边界,必须复述,因为它限制了所有这些能证明什么:T=1 时两个候选激活布局重合, 所以只测 T=1 的测试永远发现不了布局或 stride 错误。docs/03 第 1.3 节记录了实测形态: T=1 cos +0.999997 OK,T=55 cos +0.006 错。制品侧的门完全看不到这一类。


STEP 5b — engine oracles

上面所有门都是字节级的,而 MAPPING.json 明说了原因:402 个三元矩阵活在 Hadamard 旋转基 里,所以它们没有一个能用数值相关性验证——只有字节核算、往返相等和引擎加载测试可以。 带 --oracle-dump 时:

gemm_oracle.py check <art> <object> <dump-dir>
    纯 numpy 解码,用 float64 重建 y = W' * (H * (s * (P * x)))。覆盖
    "到目前为止没有任何字节/尺寸/oracle 检查覆盖的东西:平面指针、2-bit 解码、
     本宽度的符号块,以及旋转如何组合成文档化的数学"。

oracle_rot.py <dump-dir>
    真实旋转内核 vs 显式 numpy 重建,双向:文档化变体必须吻合,
    且貌似合理但错误的变体必须全部失败。

产 dump 需要 verify/harness/ 里的独立 nvcc harness 加 CUDA 工具链,本包不构建。 没有 --oracle-dump 就 SKIP。


PASS 意味着什么

VERIFY 的裁决:任何 FAIL 都会传播——进程非零退出、pack-ternary.cmd 转成桌面通知、 文案是 "do not load it"。PASS 意味着制品清过了所有能跑的门。

它不是引擎加载测试和困惑度测量的替代品,那两者仍是数值质量的最后权威。

实测基线(2026-09-29,PTQ1_0 完整制品):

verify --sweep : 7 passed, 0 failed, 2 skipped   RESULT: PASSED
  provenance   identity 匹配模板,borrowed 353/353
  inspect      1126 对象,payload_offset 176,128
  payload_order scale @ 6,389,760 (26 B/组): neg=0 nan=0 median=0.0168;2/2 负控被拒
  row_order    5/5 section 三条硬门全中
  row_order_sweep 64/64 层
  assembly     17/17 位置比对 + 4/4 负控被抓
  signs        与源 GGUF 的 prism.hadamard.* 逐位相同,max|diff| = 0.0
  embedding    SKIP —— 需要引擎 activation dump
  engine_oracles SKIP —— 需要 nvcc harness dump

所以"完整无损"现在是实测结论,不再是推断。 没验的是第 5 与 5b 门。


相关技能

  • ninfer-ternary-format — 平面几何、块布局、perm48(负控 3 的数据来源)
  • ninfer-ternary-pack — 产物是怎么来的
  • ninfer-ternary-tooling — 怎么把诊断脚本单独跑起来
  • ninfer-ternary-traps — 代码与文档不一致的地方(改验收脚本前必读)