Files
markbaseengine/Tests/MarkBaseTests/TwentySixBA4BDeepDebugTest.swift
MarkBase Admin 2a889faf4b
CI / build-and-test (push) Has been cancelled
CRITICAL: 26B-A4B NaN真相 - 真实BUG而非设计特性
重大发现:
 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,严重程度(不可预测)
2026-06-24 01:44:39 +08:00

144 lines
7.4 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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")
}
}