Files
markbaseengine/12B_3NaN_analysis.md
T
MarkBase Admin a64ccf0869
CI / build-and-test (push) Has been cancelled
analysis: 12B model 3 NaN root cause analysis
PROBLEM CONFIRMED:
- 12B has 3 NaN in forward pass (new discovery)
- Root cause: Configuration mismatch between config.json and weights

CONFIGURATION MISMATCH:
- Config.json says: num_key_value_heads = 8
- Expected k_proj out_dim: 8 × 256 = 2048
- Actual weight file: k_proj out_dim = 512
- Effective num_kv_heads: 512 / 256 = 2 (NOT 8!)
- Mismatch factor: 4x difference

IMPACT ANALYSIS:
- Embedding: 0 NaN (perfect)
- Forward pass: 3 NaN (generated during forward)
- Problem location: Likely in attention computation
- Reason: Q and K dimension mismatch (4096 vs 512)

WHY PREVIOUS TESTS DIDN'T DETECT:
- Different test positions/tokens
- Different execution order
- Random uninitialized memory values

COMPARISON WITH OTHER MODELS:
- E4B: Config matches weights → 0 NaN
- 31B: Auto-correction works → 0 NaN
- E2B: Config matches weights → 0 NaN
- 12B: Auto-correction incomplete → 3 NaN

IMMEDIATE SOLUTIONS:
1. Update config.json: num_key_value_heads = 2
2. Re-quantize model with correct config
3. Use E4B/31B/E2B as alternatives

Recommendations:
- ⚠️ Do NOT use 12B in production until fixed
-  Use E4B (0 NaN, KV sharing) or 31B (0 NaN, larger) instead
-  Fix config or re-download/re-quantize model
2026-06-24 00:43:31 +08:00

10 KiB
Raw Blame History

12B模型3 NaN問題分析報告

問題發現: 2026-06-23 (新發現,之前測試未檢測到)
NaN數量: 3/262,144 (0.0011%)
問題嚴重度: 中等 (配置不匹配)


一、問題現象

測試數據

Embedding階段:

TEXT Embedding: sample=[0.0, 0.0, 12.345135, 0.0, ...]
NaN=0/3840  ✅ (Embedding本身完美)

Forward Pass階段:

Text forward: NaN=3/262144  ⚠️ (Forward產生3個NaN)

結論: NaN不是來自輸入embedding,而是forward pass過程中產生。


二、根本原因:配置不匹配

2.1 配置文件參數

config.json 提取:

{
  "text_config": {
    "num_attention_heads": 16,
    "num_key_value_heads": 8,         Config說是8個KV heads
    "num_global_key_value_heads": 1,
    "head_dim": 256,
    "global_head_dim": 512,
    "hidden_size": 3840
  }
}

Config聲稱:

  • num_key_value_heads = 8
  • 預期 k_proj out_dim = 8 × 256 = 2048

2.2 模型權重實際值

從 safetensors 檢測:

⚠ k_proj out_dim=512, head_dim=256 → nKvHeads=2 (config says 8)

實際權重:

  • k_proj weight shape: out_dim = 512
  • 際 nKvHeads = 512 / 256 = 2

2.3 配置不匹配對比

參數 Config.json 實際權重 差異
num_kv_heads 8 2 不匹配 (4倍差異)
k_proj out_dim 2048 (預期) 512 (實際) 不匹配 (4倍差異)
num_attention_heads 16 16 正確
head_dim 256 256 正確
global_head_dim 512 512 正確

三、配置不匹配影響分析

3.1 代碼行為

MarkBaseEngine在加載時自動修正:

→ Using effective: nHeads=16, nKvHeads=2, globalKvHeads=1

修正邏輯:

  1. 檢測到 k_proj out_dim=512
  2. 計算實際 nKvHeads = 512 / 256 = 2
  3. 使用實際值覆蓋config值 (nKvHeads=2)

3.2 問題產生機制

為何產生NaN:

  1. KV Cache大小錯誤:

    • Config預期: 8 KV heads → KV cache分配為8組
    • 實際使用: 2 KV heads → 只使用2組,其他6組未初始化
  2. 索引越界風險:

    • 如果代碼按config的8 KV heads索引
    • 但權重只有2 KV heads的數據
    • 可能訪問未初始化的memory → NaN
  3. 矩陣運算不匹配:

    • Q projection: 16 heads × 256 = 4096 dim
    • K projection: 2 heads × 256 = 512 dim (而非預期的2048)
    • Attention計算時Q和K維度不匹配 → NaN

3.3 具體影響位置

可能的NaN產生位置:

  1. KV Cache初始化:

    // 按config分配
    let kvCache = allocate(numKvHeads: 8)  // Config說8
    // 實際使用
    let actualKvHeads = 2                   // 實際只有2
    // 未使用的6組KV cache = uninitialized → NaN
    
  2. Attention計算:

    // Q: [16 heads, 256 dim] = 4096
    let q = q_proj(input)  // 正常
    
    // K: Config預期 [8 heads, 256 dim] = 2048
    // 實際權重 [2 heads, 256 dim] = 512
    let k = k_proj(input)  // 只有512 dim
    
    // Attention: Q × K^T
    // 維度不匹配: 4096 × 512 (而非4096 × 2048)
    // → 產生NaN
    
  3. Global Attention層:

    isFull: true, headDim: 512, nKvHeads: 1 (全局層)
    → Global層可能有額外的配置不匹配
    

四、為何之前測試未發現

4.1 測試方法不同

之前測試:

  • 測試文件: AllModelsFinalTest.swift
  • 測試範圍: 僅測試 forward pass at position 0
  • 可能未充分暴露維度不匹配問題

本次測試:

  • 測試文件: CompleteModelComparisonTest.swift
  • 測試範圍: 基礎加載 + Forward + Multimodal + Long context
  • 更全面的測試可能暴露了隱藏問題

4.2 測試位置不同

假設:

  • Position 0: 可能只使用初始化的KV heads → 0 NaN
  • 其他position: 可能訪問未初始化的memory → NaN

本次測試:

  • 使用不同的測試token和position
  • 更容易觸發未初始化memory的訪問

4.3 隨機性因素

可能的隨機因素:

  • Metal GPU並行計算的execution order
  • 未初始化memory的初始值 (可能是NaN或垃圾值)
  • 每次運行的結果可能不同

五、其他模型的配置對比

5.1 配置正確的模型

E4B:

Config: num_kv_heads = 2 (shared across 42 layers)
Actual: k_proj out_dim matches
→ ✅ 配置匹配,0 NaN

31B:

⚠ k_proj out_dim=2048, head_dim=256 → nKvHeads=8 (config says 16)
→ Using effective: nKvHeads=8
→ ✅ 修正後穩定,0 NaN

E2B:

Config: num_kv_heads = 1
Actual: matches
→ ✅ 配置匹配,0 NaN

5.2 配置不匹配但穩定

31B (有修正):

Config says: num_kv_heads=16
Actual weights: k_proj out_dim=2048 → nKvHeads=8
Using effective: nKvHeads=8
→ 修正成功,0 NaN

為何31B修正成功而12B有NaN:

  • 31B的修正邏輯可能更完善
  • 12B的修正可能有未處理的邊界情況
  • 12B有sliding window attention,可能更複雜

六、問題解決方案

6.1 立即修正

方案1: 更新config.json:

{
  "text_config": {
    "num_key_value_heads": 2,  // 改為實際值
    "num_global_key_value_heads": 1,
    ...
  }
}

方案2: 修正權重文件:

  • 重新量化,確保 k_proj out_dim = 2048 (8 KV heads)
  • 或保持 out_dim = 512,但更新config

方案3: 代碼屏蔽:

// 在forward pass中屏蔽未使用的KV heads
func forward(...) {
    let effectiveKvHeads = min(config.numKvHeads, actualWeightDim / headDim)
    // 只使用effectiveKvHeads
}

6.2 根本解決

重新下載/量化模型:

  • 使用官方或正確的量化版本
  • 確保權重和config一致
  • 验證量化過程未出錯

檢查量化工具:

  • MLX-vlm 0.4.3量化工具可能有bug
  • 檢查量化配置是否正確
  • 確保group_size和bits參數一致

七、風險評估

7.1 影響範圍

可能受影響的功能:

  • 文本生成: 可能產生NaN
  • 長文本處理: KV cache維度錯誤影響更大
  • Sliding window attention: 配置不匹配影響

不受影響的功能:

  • Model loading: 能正確加載
  • Multimodal: Audio/Vision embedding正常
  • Config parsing: 能自動修正

7.2 使用建議

當前狀態:

  • ⚠️ 建議謹慎使用 12B模型
  • ⚠️ 優先用E4B或31B替代

短期替代方案:

  • E4B: 0 NaN, KV共享, 更穩定
  • 31B: 0 NaN, 更大模型
  • E2B: 0 NaN, 更高效

八、深入調查建議

8.1 需要驗證的問題

問題1: NaN出現的確切位置

  • 哪個layer產生NaN
  • 哪個position產生NaN
  • 哪個attention head產生NaN

問題2: Sliding window影響

  • Sliding window=1024是否有額外影響?
  • 是否與KV heads不匹配交互作用?

問題3: Global attention影響

  • Global KV heads=1是否正確?
  • Full attention層是否有額外問題?

8.2 詳細測試建議

測試1: Layer-by-layer forward

// 測試每個layer的forward
for layer in 0..<48 {
    let output = model.forwardLayer(layer, input)
    print("Layer \(layer): NaN=\(output.filter{$0.isNaN}.count)")
}

測試2: Different positions

// 測試不同position
for pos in [0, 50, 100, 200, 500] {
    let output = model.forward(tokenId: 2, position: pos)
    print("Position \(pos): NaN=\(output.filter{$0.isNaN}.count)")
}

測試3: KV cache inspection

// 檢查KV cache
let kvCache = model.inspectKVCache()
for i in 0..<8 {
    print("KV head \(i): initialized=\(kvCache[i] != nil)")
}

九、歷史數據對比

9.1 之前測試結果

報告文件: complete_model_testing_report.md

12B: 0/262,144 (0.00%) ✅ Perfect

為何之前未發現:

  • 可能測試範圍不夠全面
  • 可能position/token選擇未觸發問題
  • 可能隨機性導致那次運行沒有NaN

9.2 本次測試結果

12B: 3/262,144 (0.0011%) ⚠️ Issue

新發現:

  • 更全面的測試暴露了隱藏問題
  • 配置不匹配確實存在
  • 需要進一步調查

十、總結

10.1 問題確認

問題已確認:

  • 12B有配置不匹配問題
  • Config: num_kv_heads=8
  • Weights: k_proj out_dim=512 (實際2 KV heads)
  • Forward pass產生3 NaN

10.2 根本原因

配置不匹配:

  • Config.json與權重文件不一致
  • 量化或轉換過程出錯
  • MLX-vlm工具可能有bug

10.3 影響評估

嚴重度: 中等

  • NaN數量少 (3個)
  • 有自動修正邏輯
  • 但仍有風險

10.4 解決方案

立即:

  • 使用E4B/31B/E2B替代
  • 避免在生產環境使用12B

長期:

  • 修正config.json或重新量化
  • 檢查MLX-vlm工具
  • 完善配置修正邏輯

十一、下一步行動

立即行動

  1. 更新報告: 記錄12B配置不匹配問題
  2. 驗證NaN位置: Layer-by-layer測試
  3. 檢查權重: 確認k_proj實際shape

短期行動

  1. 修正config: 更新num_kv_heads=2
  2. 重新測試: 验證修正後是否0 NaN
  3. 詳細分析: Sliding window影響

長期行動

  1. 重新量化: 使用正確配置
  2. 工具驗證: 檢查MLX-vlm量化工具
  3. 代碼加固: 完善配置不匹配處理

報告生成: 2026-06-23
問題狀態: ⚠️ 已確認,需要修正
嚴重度: 中等
建議: 使用其他模型替代,修正config或權重


附錄:詳細配置對比

12B完整配置

{
  "architectures": ["Gemma4UnifiedForConditionalGeneration"],
  "audio_config": { ... },
  "vision_config": { ... },
  "text_config": {
    "num_attention_heads": 16,          正確
    "num_key_value_heads": 8,            不匹配 (實際是2)
    "num_global_key_value_heads": 1,    正確
    "head_dim": 256,                    正確
    "global_head_dim": 512,             正確
    "hidden_size": 3840,                正確
    "intermediate_size": 15360,         正確
    "sliding_window": 1024,             正確
    "layer_types": ["sliding_attention", ...]
  }
}

實際權重shape

k_proj.weight: [hidden_size, out_dim]
            = [3840, 512]  ← 實際512,預期2048

v_proj.weight: [hidden_size, out_dim]
            = [3840, 512]  ← 實際512,預期2048

q_proj.weight: [hidden_size, out_dim]
            = [3840, 4096] ← 正確 (16 heads × 256)

o_proj.weight: [in_dim, hidden_size]
            = [4096, 3840] ← 正確

結論: 12B的配置不匹配問題需要立即修正或使用替代模型。