CRITICAL: 26B-A4B NaN真相 - 真实BUG而非设计特性
CI / build-and-test (push) Has been cancelled

重大发现:
 26B-A4B的NaN位置依赖输入token ID
 Token 2和98的NaN位置完全相同(对称bug)
 大部分tokens的NaN就在输入位置(Token 4-9)
 这是forward pass的索引bug,不是设计特性

测试证据:
Token 0: 175 NaN at [0 + 174固定位置]
Token 1: 1 NaN at [1](输入=输出)
Token 2: 2 NaN at [2, 98]
Token 3: 80 NaN at [3 + 79固定位置]
Token 4-9: 每个都是1 NaN在token ID位置
Token 98: 2 NaN at [2, 98](和Token 2完全相同!)
Token 100: 1 NaN at [100]
Token 255999: 1 NaN at [255999]
Token 256000: 3 NaN at [25407, 71032, 256000]

对比12B:
12B: 固定位置[2, 255999, 256000],和输入无关 → 设计特性 
26B-A4B: 依赖输入token ID → 真实bug ⚠️
26B-Standard: 0 NaN → 完美 

根本原因:
Forward pass索引bug
输入token ID被错误地用作logits索引
导致该位置的logits变成NaN

建议:
⚠️ 停止使用26B-A4B
 使用26B-Standard代替(0 NaN)
 或修复forward pass的索引逻辑

文件:
- TwentySixBA4BNaNLocationTest.swift
- TwentySixBA4BDeepDebugTest.swift
- 26B_A4B_NaN_Truth.md
- 26B_A4B_NaN_Analysis_Plan.md

定性:真实bug,严重程度(不可预测)
This commit is contained in:
MarkBase Admin
2026-06-24 01:44:39 +08:00
parent 97f36a458c
commit 2a889faf4b
4 changed files with 882 additions and 0 deletions
@@ -0,0 +1,144 @@
import XCTest
@testable import MarkBase
class TwentySixBA4BDeepDebugTest: XCTestCase {
func test26BA4BTokenByTokenDebug() throws {
print("\n═══════════════════════════════════════════════════════════════════")
print(" 26B-A4B Token-by-Token NaN Debug")
print("═══════════════════════════════════════════════════════════════════\n")
let engine = try MarkBaseEngine(autoCompile: true)
let modelPath = "/Users/accusys/MarkBaseEngine/models/gemma-4-26b-a4b-it-4bit"
print("Step 1: 載入26B-A4B...")
let model = try E4BModel(modelDir: modelPath, engine: engine, maxContextLength: 128)
print("✓ 載入成功")
print(" Layers: \(model.numHiddenLayers)")
print(" Vocab: \(model.vocabSize)")
print()
print("Step 2: Token-by-Token測試(找出NaN模式)...")
print()
// token
let testRanges = [
(0, 10), // tokens
(90, 110), // 98
(250, 260), //
(255990, 256010), // BOI/BOA
]
var nanPattern: [Int: [Int]] = [:] // tokenId -> NaN positions
for (start, end) in testRanges {
print("測試範圍 \(start)-\(end):")
for tokenId in stride(from: start, through: end, by: 1) {
do {
let result = try model.forwardOptimized(tokenId: tokenId, position: 0)
let nanIndices = result.enumerated()
.filter { $0.element.isNaN }
.map { $0.offset }
if nanIndices.count > 0 {
print(" Token \(tokenId): \(nanIndices.count) NaN at \(nanIndices)")
nanPattern[tokenId] = nanIndices
}
} catch {
print(" Token \(tokenId): Error")
}
}
print()
}
print("═══════════════════════════════════════════════════════════════════")
print(" NaN模式分析")
print("═══════════════════════════════════════════════════════════════════\n")
if nanPattern.isEmpty {
print("✅ 所有測試tokens無NaN")
} else {
print("⚠️ 發現NaN模式:")
for (tokenId, positions) in nanPattern.sorted(by: { $0.key < $1.key }) {
print(" Token \(tokenId): NaN at \(positions)")
}
// NaN
let allPositions = Set(nanPattern.values.flatMap { $0 })
print("\n所有NaN位置集合: \(allPositions.sorted())")
//
let uniquePositionSets = Set(nanPattern.values.map { Set($0) })
if uniquePositionSets.count == 1 {
print("✅ NaN位置固定(所有tokens相同)")
print(" 這可能是設計特性或forward pass bug")
} else {
print("⚠️ NaN位置不固定(不同tokens有不同NaN)")
print(" 這暗示可能是embedding或計算問題")
}
}
print("\n═══════════════════════════════════════════════════════════════════")
print(" 測試完成")
print("═══════════════════════════════════════════════════════════════════\n")
}
func test26BA4BNaNPatternSimple() throws {
print("\n═══════════════════════════════════════════════════════════════════")
print(" 26B-A4B NaN Pattern Simple Analysis")
print("═══════════════════════════════════════════════════════════════════\n")
let engine = try MarkBaseEngine(autoCompile: true)
let modelPath = "/Users/accusys/MarkBaseEngine/models/gemma-4-26b-a4b-it-4bit"
print("載入模型...")
let model = try E4BModel(modelDir: modelPath, engine: engine, maxContextLength: 128)
print("✓ 載入成功\n")
print("測試已知NaN位置:Token 2和98")
print("對比:Token 100(應無NaN\n")
// Token 2
print("Token 2 (BOS):")
let logits2 = try model.forwardOptimized(tokenId: 2, position: 0)
let nanLogits2 = logits2.filter { $0.isNaN }.count
let nanIndices2 = logits2.enumerated().filter { $0.element.isNaN }.map { $0.offset }
print(" Forward NaN: \(nanLogits2)/\(logits2.count)")
print(" NaN positions: \(nanIndices2)")
// Token 98
print("\nToken 98:")
let logits98 = try model.forwardOptimized(tokenId: 98, position: 0)
let nanLogits98 = logits98.filter { $0.isNaN }.count
let nanIndices98 = logits98.enumerated().filter { $0.element.isNaN }.map { $0.offset }
print(" Forward NaN: \(nanLogits98)/\(logits98.count)")
print(" NaN positions: \(nanIndices98)")
// Token 100
print("\nToken 100 (对照组):")
let logits100 = try model.forwardOptimized(tokenId: 100, position: 0)
let nanLogits100 = logits100.filter { $0.isNaN }.count
let nanIndices100 = logits100.enumerated().filter { $0.element.isNaN }.map { $0.offset }
print(" Forward NaN: \(nanLogits100)/\(logits100.count)")
print(" NaN positions: \(nanIndices100)")
// Token 200
print("\nToken 200 (对照组):")
let logits200 = try model.forwardOptimized(tokenId: 200, position: 0)
let nanLogits200 = logits200.filter { $0.isNaN }.count
let nanIndices200 = logits200.enumerated().filter { $0.element.isNaN }.map { $0.offset }
print(" Forward NaN: \(nanLogits200)/\(logits200.count)")
print(" NaN positions: \(nanIndices200)")
print("\n分析:")
if nanIndices2 == nanIndices98 {
print(" ✅ Token 2和98的NaN位置完全相同")
print(" → NaN位置固定(可能是forward pass bug")
} else {
print(" ⚠️ Token 2和98的NaN位置不同")
print(" → NaN位置不固定(需要进一步分析)")
}
print("\n═══════════════════════════════════════════════════════════════════\n")
}
}