HelloSun commited on
Commit
446c618
·
1 Parent(s): ae9bb4c

勘誤:修正與實測資料不符的敘述

Browse files

本 repo 的 4 處敘述與自己的量測結果/程式碼不一致,本次逐條修正:

1. README §1 的「arena 2.90 GiB = 266 個槽位 / peak RSS 10.3 GiB」與第 3 節
矩陣矛盾。實際 4 GiB 預算是 arena 1.79 GiB、164 槽、peak RSS 2233 MB
(2.18 GiB)。10.3 GiB 那組數字在任何 RAM 預算下都不存在,而且與其他四列
一致的「RSS ≈ arena + 390 MB」關係不符(2.90 GiB arena 不可能配 10.3 GiB RSS)。
2. README §1 把回答寫成乾淨的 `capital of France is **Paris**.`。實際輸出是
`'m happy to help! The capital of France is **Paris**.<|end▁of▁sentence|> of * {@link ...`
—— 答完後有 EOS 與 Java 樣板碼亂補。改為逐字引用並註明退化風險。
3. results/verify_dequant.txt 印 21 列,但 blk.0.ffn_down_exps 與
blk.26.ffn_down_exps 各被重複印一次(6 列重複),獨立比對只有 15 列。
移除重複列,README §6.1 的 21/21 改為 15/15。
4. README §4.2 由三組 A/B 宣稱「明確 AVX512 不比 -march=native 慢」。但
native 那一列自身三次重跑是 1.71/1.09/1.03(相差 1.66x),內部落差大於
列間差距,沒有統計力。改為只能宣稱「三種設定都能正確產出且不踩 assert」,
速度無法區分。

另外補上三項原本漏報、但影響重現的事實:
- 第 3 節矩陣實際量測用的是 `-march=native -mno-amx-*`,不是第 4.2 節那組可攜
選項(JSON build.note 有記,README 未揭露)。AMX 同樣關閉,數學等價。
- 矩陣用 `SDQ_RESERVE_MB=2048`,pager 程式預設是 900 MB。此值直接決定 arena
大小,不帶此變數重跑會得到不同 arena 與槽位數。§4.3 已補上重跑指令。
- 矩陣每級 2 輪用的是不同 prompt,表中 tok/s 是兩輪平均,不是同段文字重跑。
- 32 GiB repeat 1 的生成結果是退化的(整段沒有 Paris),已註明且說明那兩輪
prompt 不同、不構成受控對照。

HARDWARE.md:MemTotal 修正為實測值 2,097,117,292 kB(原 272 為誤植);
lsblk 輸出更新為 2026-10-12 實測,並註明只有 nvme0n1(EBS)承載 / ,
其餘為會變動編號的 instance storage。

已核對但確認正確、未更動的部分:
- 模型規格(arch deepseek4 / 43 層 / 256 expert / 用 6 / shared 1 / hidden 4096 /
expert FFN 2048 / ctx 1048576 / gating_func 4 / scale 1.5 / swiglu_clamp=10.0
×43 / hash_layer_count 3)逐一對照實際 GGUF metadata。
- 權重總量 136,662,446,656 B = 127.27 GiB;shard 1 為 metadata-only
(n_tensors=0, 5,257,664 B)。
- IQ3_S/MXFP4 量化型別與 layer 26 為唯一 MXFP4 gate/up 層:直接解析各 shard
的 tensor table 確認。
- 單一 expert 11.125 MiB(42 層)/ 12.75 MiB(layer 26)與 routed expert 合計
120.00 GiB:由型別 block size 精算,與敘述完全一致。
- §5 五項修正與 llama.cpp 836d5717 原始碼一致(patch 可乾淨 apply;
ggml_swiglu_clamp、IQ3_S->Q8_K / MXFP4->Q8_0、shared expert 在
build_moe_ffn 之外、AMX 預設 OFF 且以 __AMX_INT8__ 註冊)。
- results/moe_dump_bitexact.md 的 dump 格式(meta[6]、三段式檔名、含 out)
與 sdq_moe.cpp 的 SDQ_MOE_DUMP 實作一致,無需修改。

Files changed (3) hide show
  1. HARDWARE.md +11 -5
  2. README.md +56 -13
  3. results/verify_dequant.txt +1 -7
HARDWARE.md CHANGED
@@ -10,7 +10,7 @@ RAM 預算是不是硬性上限」的歧義。
10
  |---|---|
11
  | CPU | Intel Xeon Platinum 8559C,2 socket × 48 core × 2 thread = **192 logical CPU 可見** |
12
  | **實際算力配額** | cgroup v2 `cpu.max = 1600000 100000` → **16.0 CPU** |
13
- | 實體 RAM | **2,000.0 GiB = 1.953 TiB**(`MemTotal` 2,097,117,272 kB) |
14
  | Swap | **1,600.0 GiB = 1.5625 TiB**(`SwapTotal` 1,677,721,504 kB) |
15
  | cgroup v2 記憶體上限 | `memory.max = 104,000,000,000 B` = **96.86 GiB / 104.0 GB** |
16
  | **實際綁住本專案的上限** | `/root/memkill.py 80 2 64` → **單一子行程 64 GiB**、opencode 及其全部子行程合計 **80 GiB**、取樣間隔 2 s |
@@ -73,12 +73,18 @@ pager 去算 arena 大小」再量 peak RSS,**不是**四個各自受限的容
73
 
74
  ```
75
  $ stat -fc %T /root → overlayfs
76
- $ lsblk -o NAME,SIZE,ROTA,MODEL
77
- nvme0n1 7.8T 0 Amazon Elastic Block Store ← / 在這上面
78
- nvme1n1 3.5T 0 Amazon EC2 NVMe Instance Storage
79
- nvme3n1 3.5T 0 Amazon EC2 NVMe Instance Storage
 
 
80
  ```
81
 
 
 
 
 
82
  `/` 是 **overlayfs**,底層是 **Amazon Elastic Block Store**(EBS)——
83
  那是**網路-attached 區塊裝置**,不是本地 NVMe SSD。
84
  把指標命名為 `SSD MiB/token` 在這個環境下是有誤導性的:
 
10
  |---|---|
11
  | CPU | Intel Xeon Platinum 8559C,2 socket × 48 core × 2 thread = **192 logical CPU 可見** |
12
  | **實際算力配額** | cgroup v2 `cpu.max = 1600000 100000` → **16.0 CPU** |
13
+ | 實體 RAM | **2,000.0 GiB = 1.953 TiB**(`MemTotal` 2,097,117,292 kB) |
14
  | Swap | **1,600.0 GiB = 1.5625 TiB**(`SwapTotal` 1,677,721,504 kB) |
15
  | cgroup v2 記憶體上限 | `memory.max = 104,000,000,000 B` = **96.86 GiB / 104.0 GB** |
16
  | **實際綁住本專案的上限** | `/root/memkill.py 80 2 64` → **單一子行程 64 GiB**、opencode 及其全部子行程合計 **80 GiB**、取樣間隔 2 s |
 
73
 
74
  ```
75
  $ stat -fc %T /root → overlayfs
76
+ $ lsblk -o NAME,TYPE,SIZE,ROTA,MODEL # 2026-10-12 實測
77
+ nvme0n1 disk 7.8T 0 Amazon Elastic Block Store ← / 在這上面
78
+ nvme1n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
79
+ nvme2n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
80
+ nvme3n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
81
+ nvme4n1 disk 3.5T 0 Amazon EC2 NVMe Instance Storage
82
  ```
83
 
84
+ > 附帶 instance storage 的磁碟編號會隨開機/熱插拔變動(nvme1n1–nvme4n1 之間,
85
+ > 這台機器上是 1、2、3、4 都在,`md0` 是其中的 RAID0)。**只有 `nvme0n1` 對本專案
86
+ > 有意義**,因為它才是 `/`(overlayfs)底下的那一顆;其餘都是 instance storage。
87
+
88
  `/` 是 **overlayfs**,底層是 **Amazon Elastic Block Store**(EBS)——
89
  那是**網路-attached 區塊裝置**,不是本地 NVMe SSD。
90
  把指標命名為 `SSD MiB/token` 在這個環境下是有誤導性的:
README.md CHANGED
@@ -46,9 +46,21 @@ tags:
46
  expert arena,是把這個模型塞進小機器的正確做法。這就是 SDQ(SSD→RAM→CPU
47
  expert pager)的原始設計。
48
 
49
- **本機實測**:RAM 預算 4 GiB(arena 2.90 GiB = 266 個槽位)時,整個 127.27 GiB 的
50
- 模型跑在 **peak RSS 10.3 GiB** 裡面,並且正確回答
51
- `The capital of France is` → **`capital of France is **Paris**.`**
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  ---
54
 
@@ -91,8 +103,15 @@ expert pager)的原始設計。
91
  | 磁碟單流 `dd oflag=direct` | 寫 1.1 GB/s、讀 **2.0 GB/s** |
92
  | cgroup 檔案系統 | **唯讀** → 無法為每一級建立獨立限額 |
93
 
94
- 設定:ctx=2048、ubatch=128、`-t 16`、temp=0、每級 2 輪。
95
- 編譯:`GGML_NATIVE=OFF` + 明確列 AVX512/VNNI/BF16,**AMX 關閉**(見第 5 節 (e))。
 
 
 
 
 
 
 
96
 
97
  > 三個容易搞混的地方,這裡先講清楚:
98
  > 1. `nproc` 在這台機器上回 16,是因為環境變數 `OMP_NUM_THREADS=16`,
@@ -102,6 +121,8 @@ expert pager)的原始設計。
102
  > 3. `--ram-budget-mb` 是 **pager budget,不是 OS 限額**;真正會殺行程的是
103
  > `memkill.py` 的 64 GiB 單行程上限。這也是本 repo 沒做「原生路徑 vs 分頁路徑」
104
  > 端到端 A/B 的原因(原生路徑要 127.27 GiB 進 RSS,一定被殺)。
 
 
105
 
106
  | RAM 預算 | arena (GiB) | slots | decode tok/s | prefill tok/s | hot % | cold % | SSD MiB/tok | 全程 SSD 讀取 | peak RSS (MB) | swap | 每層命中率 min..max |
107
  |---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
@@ -113,6 +134,13 @@ expert pager)的原始設計。
113
  * `hot %` = expert 已在 RAM arena、不需要碰 SSD;`cold %` = 必須回 SSD `pread`。
114
  * **重點:127.27 GiB 的模型在 2.2 GiB peak RSS 裡跑得動**(0.69 tok/s),
115
  給到 30.9 GiB 得到 1.71 tok/s(2.48×)、SSD 讀取量降到 23.5%。
 
 
 
 
 
 
 
116
  * 4 GiB 那一級 `hot = 0%` 不是 bug:一個 token 在 43 層各用 6 個 expert,
117
  工作集下限就是 43 × 6 = 258 個 expert ≈ 2.8 GiB,而這一級的 arena 只有 1.79 GiB
118
  → 任何一次請求都必然是 miss。
@@ -171,9 +199,20 @@ cmake --build build -j --target sdq-chat
171
  | AVX512 + VNNI + BF16(Skylake-X / Ice Lake+) | 1.48 / 1.10 / 1.18 | 正確輸出,無 assert |
172
  | `-march=native -mno-amx-*`(同一台機器) | 1.71 / 1.09 / 1.03 | 正確輸出,但需要那個 `-mno-` hack |
173
 
174
- 三者的差距落在同一組 run 的抖动裡(`build` 那一列自己就在 1.03–1.71 之間跳),
175
- 所以**用明確的 AVX512 設定不會比 `-march=native` 慢**。ARM / 其他架構請用
176
- llama.cpp 自��的預設值(不要帶任何 `GGML_NATIVE` 或 AVX 選項)。
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  ### 4.3 跑(RAM 預算單位 MiB)
179
 
@@ -185,12 +224,11 @@ llama.cpp 自己的預設值(不要帶任何 `GGML_NATIVE` 或 AVX 選項)
185
  ```
186
 
187
  `--ram-budget-mb` 是**總 RAM 預算**(不是「還有多少可用」):pager 會扣掉當下的
188
- RSS 與一份預留量(`SDQ_RESERVE_MB`,預設 900 MB)之後,剩下的才是 arena。
189
-
190
- 重跑記憶體實驗:
191
 
192
  ```bash
193
- python3 sdq-dsv4/memory_levels_dsv4.py \
194
  --model ./models/UD-IQ4_NL/DeepSeek-V4-Flash-0731-UD-IQ4_NL-00001-of-00004.gguf \
195
  --bin ./llama.cpp/build/bin/sdq-chat \
196
  --levels-gib 4 8 16 32 --repeat 2 --n-predict 48
@@ -360,7 +398,12 @@ python3 sdq-dsv4/verify_dequant_dsv4.py
360
  | `blk.26.ffn_gate_exps` | MXFP4 | **0.0** |
361
  | `blk.26.ffn_down_exps` | MXFP4 | **0.0** |
362
 
363
- 21/21 列 **bit-exact**(差異精確為 0,不是「很小」)。
 
 
 
 
 
364
 
365
  ### 6.2 整個 expert 前向對照
366
 
 
46
  expert arena,是把這個模型塞進小機器的正確做法。這就是 SDQ(SSD→RAM→CPU
47
  expert pager)的原始設計。
48
 
49
+ **本機實測**:RAM 預算 4 GiB(arena **1.79 GiB** = 164 個槽位)時,整個 127.27 GiB 的
50
+ 模型跑在 **peak RSS 2,233 MB ≈ 2.18 GiB** 裡面(詳見第 3 節的矩陣與
51
+ `results/memory_levels_dsv4-4-8-16-32g.json`),代價是 0.69 tok/s。
52
+
53
+ 實際生成內容(逐字,`--temp 0`):
54
+
55
+ ```
56
+ 'm happy to help! The capital of France is **Paris**.<|end▁of▁sentence|> of * {@link com.google.api.client.util.Data#nullOf(Class)} ...
57
+ ```
58
+
59
+ 語意上是對的(`Paris`),但**不是乾淨的一行答案**:模型在答完之後吐了
60
+ `<|end▁of▁sentence|>`,後面還接著 Java 樣板碼的亂補。這是第 6.4 節那個
61
+ decode 期不確定性的一部分,不同 RAM 預算/無關設定會換掉尾巴(32 GiB 那一次甚至
62
+ 整段退化、第一個 token 就是 `<|end▁of▁sentence|>`,完全沒有 `Paris`)。
63
+ 要重現請照第 3 節的設定跑,不要引用「乾淨回答」這種說法。
64
 
65
  ---
66
 
 
103
  | 磁碟單流 `dd oflag=direct` | 寫 1.1 GB/s、讀 **2.0 GB/s** |
104
  | cgroup 檔案系統 | **唯讀** → 無法為每一級建立獨立限額 |
105
 
106
+ 設定:ctx=2048、ubatch=128、`-t 16`、temp=0、每級 2 輪、`SDQ_RESERVE_MB=2048`。
107
+
108
+ > ⚠️ **這張矩陣實際量測時用的不是下面第 4.2 節那組可攜編譯選項**,而是
109
+ > `-march=native -mno-amx-int8 -mno-amx-tile -mno-amx-bf16`(見 JSON 的
110
+ > `build.note`)。AMX 同樣是關閉的,所以兩者在數學上等價;但 tok/s 是在**前者**
111
+ > 量到的,直接引用時請照實說明,不要當成第 4.2 組選項的實測值。
112
+ > 這個矩陣另外用了 `SDQ_RESERVE_MB=2048`,而 pager 的**程式預設值是 900 MB**
113
+ > (`sdq_pager.cpp`)。這個值直接決定 arena 大小(`arena = 預算 − RSS − 預留`),
114
+ > 不帶這個環境變數重跑會得到比表中更大的 arena 與不同的槽位數。
115
 
116
  > 三個容易搞混的地方,這裡先講清楚:
117
  > 1. `nproc` 在這台機器上回 16,是因為環境變數 `OMP_NUM_THREADS=16`,
 
121
  > 3. `--ram-budget-mb` 是 **pager budget,不是 OS 限額**;真正會殺行程的是
122
  > `memkill.py` 的 64 GiB 單行程上限。這也是本 repo 沒做「原生路徑 vs 分頁路徑」
123
  > 端到端 A/B 的原因(原生路徑要 127.27 GiB 進 RSS,一定被殺)。
124
+ > 4. 表中每級的 tok/s 是**兩輪不同 prompt 的平均**(第 1 輪 `The capital of France is`、
125
+ > 第 2 輪中文 SSD 題),不是同一段文字的兩次重跑。原始逐輪數字在 JSON 裡。
126
 
127
  | RAM 預算 | arena (GiB) | slots | decode tok/s | prefill tok/s | hot % | cold % | SSD MiB/tok | 全程 SSD 讀取 | peak RSS (MB) | swap | 每層命中率 min..max |
128
  |---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
 
134
  * `hot %` = expert 已在 RAM arena、不需要碰 SSD;`cold %` = 必須回 SSD `pread`。
135
  * **重點:127.27 GiB 的模型在 2.2 GiB peak RSS 裡跑得動**(0.69 tok/s),
136
  給到 30.9 GiB 得到 1.71 tok/s(2.48×)、SSD 讀取量降到 23.5%。
137
+ * **32 GiB 那一級的 repeat 1 生成結果是退化的**:輸出從
138
+ `<|end▁of▁sentence|>` 就開始,後面接 Java 樣板碼亂補,整段沒有 `Paris`。
139
+ 同級的 repeat 2(中文 SSD 題)則正常回答。
140
+ 這兩輪的**熱命中率與 tok/s 相當接近**(65.2% vs 62.6%、1.86 vs 1.56 tok/s),
141
+ 但**它們用的是不同 prompt,所以這不是受控對照**,不能用來證明退化與分頁無關;
142
+ 只能說:在這個 RAM 預算下,回應品質並不穩定。不要把 32 GiB 那一級
143
+ 說成「能穩定答對」,相關的不確定性見第 6.4 節。
144
  * 4 GiB 那一級 `hot = 0%` 不是 bug:一個 token 在 43 層各用 6 個 expert,
145
  工作集下限就是 43 × 6 = 258 個 expert ≈ 2.8 GiB,而這一級的 arena 只有 1.79 GiB
146
  → 任何一次請求都必然是 miss。
 
199
  | AVX512 + VNNI + BF16(Skylake-X / Ice Lake+) | 1.48 / 1.10 / 1.18 | 正確輸出,無 assert |
200
  | `-march=native -mno-amx-*`(同一台機器) | 1.71 / 1.09 / 1.03 | 正確輸出,但需要那個 `-mno-` hack |
201
 
202
+ **這組數字不能支持「兩種編譯選項等速」的結論。** 每一列自己內部的落差
203
+ (`-march=native` 是 1.71 / 1.09 / 1.03,**同一組設定重跑三次**就差了 1.66×)
204
+ 已經大於列與列之間的差距,所以這個 A/B 沒有統計力,只能說:
205
+
206
+ > 三種設定在這台機器上都**能正確產出、都不會踩到 assert**;
207
+ > 至於哪個比較快,現有資料**無法區分**,需要更多輪次、且固定同一段
208
+ > prompt 與輸出長度重測(decode tok/s 與觸發到多少個不同 expert 強相關,
209
+ > 而 decode 期輸出本身對行程狀態敏感,見第 6.4 節)。
210
+
211
+ 換句話說:**AMX 必須關閉這個結論是確定的**(會撞 assert,重現步驟見 (e)),
212
+ 但「用明確的 AVX512 設定不會比 `-march=native` 慢」目前是**未證實**的。
213
+ 另外第 3 節那張 RAM 矩陣用的是 `-march=native -mno-amx-*`,若要讓它對應到
214
+ 第 4.2 節這組可攜選項,同樣需要先補一次有統計力的重測。
215
+ ARM / 其他架構請用 llama.cpp 自己的預設值(不要帶任何 `GGML_NATIVE` 或 AVX 選項)。
216
 
217
  ### 4.3 跑(RAM 預算單位 MiB)
218
 
 
224
  ```
225
 
226
  `--ram-budget-mb` 是**總 RAM 預算**(不是「還有多少可用」):pager 會扣掉當下的
227
+ RSS 與一份預留量(`SDQ_RESERVE_MB`,**程式預設 900 MB**)之後,剩下的才是 arena。
228
+ 第 3 節的矩陣是用 `SDQ_RESERVE_MB=2048` 量出來的,重跑要對齊這個值:
 
229
 
230
  ```bash
231
+ SDQ_RESERVE_MB=2048 python3 sdq-dsv4/memory_levels_dsv4.py \
232
  --model ./models/UD-IQ4_NL/DeepSeek-V4-Flash-0731-UD-IQ4_NL-00001-of-00004.gguf \
233
  --bin ./llama.cpp/build/bin/sdq-chat \
234
  --levels-gib 4 8 16 32 --repeat 2 --n-predict 48
 
398
  | `blk.26.ffn_gate_exps` | MXFP4 | **0.0** |
399
  | `blk.26.ffn_down_exps` | MXFP4 | **0.0** |
400
 
401
+ 15/15 列 **bit-exact**(差異精確為 0,不是「很小」)。
402
+
403
+ > 這裡是 5 個張量 × 3 個 row = **15 列**。原始輸出檔
404
+ > `results/verify_dequant.txt` 早先印成 21 列,但其中 `blk.0.ffn_down_exps` 與
405
+ > `blk.26.ffn_down_exps` 各被重複印了一次(每個 3 列,共 6 列重複),
406
+ > 所以「21/21」是重複計數造成的,不是真的做了 21 次獨立比對。已修正。
407
 
408
  ### 6.2 整個 expert 前向對照
409
 
results/verify_dequant.txt CHANGED
@@ -8,18 +8,12 @@ blk.0.ffn_up_exps.weight type=21 row=137 max|NumPy - ggml| = 0 EXAC
8
  blk.0.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
9
  blk.0.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
10
  blk.0.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
11
- blk.0.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
12
- blk.0.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
13
- blk.0.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
14
  blk.26.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
15
  blk.26.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
16
  blk.26.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
17
  blk.26.ffn_gate_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
18
  blk.26.ffn_gate_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
19
  blk.26.ffn_gate_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
20
- blk.26.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
21
- blk.26.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
22
- blk.26.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
23
 
24
- 21/21 bit-exact
25
  ALL BIT-EXACT
 
8
  blk.0.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
9
  blk.0.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
10
  blk.0.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
 
 
 
11
  blk.26.ffn_down_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
12
  blk.26.ffn_down_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
13
  blk.26.ffn_down_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
14
  blk.26.ffn_gate_exps.weight type=39 row=0 max|NumPy - ggml| = 0 EXACT
15
  blk.26.ffn_gate_exps.weight type=39 row=1 max|NumPy - ggml| = 0 EXACT
16
  blk.26.ffn_gate_exps.weight type=39 row=137 max|NumPy - ggml| = 0 EXACT
 
 
 
17
 
18
+ 15/15 bit-exact
19
  ALL BIT-EXACT