Files
markbaseengine/TEXT_NAN_FIX_PLAN.md
MarkBase Admin ac75faa0cc
Some checks failed
CI / build-and-test (push) Has been cancelled
Initial commit: E4B-MarkBase model integration with passing tests
- E4B-MarkBase model (42 layers, 4.4GB) loaded successfully
- All Phase 1-6 tests passed (model loading, forward pass, vision/audio towers, token generation, performance)
- All stress tests passed (5/5 in 127.6s)
  - Concurrent inference
  - Memory stress (67.5 tok/s, 0 NaN)
  - Continuous generation
  - Batch processing
  - Long-running stability
- Swift Metal inference engine with multimodal support
2026-06-23 18:12:35 +08:00

217 lines
6.4 KiB
Markdown
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.
# TEXT NaN修复方案基于Audio经验
## 问题分析
### Buffer重用冲突链
**TEXT Layer forward流程**:
```
1. Attention阶段使用temps.h多次:
- Line 84-87: input → temps.h (RMSNorm #1)
- Line 89-91: temps.h → temps.q
- Line 105-106: temps.h → temps.k
- Line 108-109: temps.h → temps.v
- Line 171-172: temps.attn → temps.h (覆盖 #1)
- Line 180-182: input → temps.h (RMSNorm #2覆盖 #1)
- Line 185-187: temps.h → temps.ns
2. FFN阶段使用temps.h:
- Line 53-54: temps.gate → temps.h (down proj)
3. PostFFN阶段使用temps.h多次:
- Line 207-209: input → temps.h (RMSNorm #3覆盖 #2)
- Line 225-227: temps.gating → temps.h (覆盖 #3)
- Line 235-238: temps.h → input (最终输出)
```
**关键问题**:
- `temps.h`被多次重用5次写入attention 3次FFN 1次postFFN 2次
- `input`被多次覆盖residual add + 最终输出
- 类似Audio的多轮操作竞争buffer
### Audio修复对比
**Audio问题**:
```
多轮操作竞争tempBuffer:
- applySubsampleConv → tempBuffer
- applyInputProjection → subsampleBuf (修复)
- applyDepthwiseConv1D → tempBuffer冲突
- applySiLU → tempBuffer冲突
- applyResidualAdd → tempBuffer冲突
```
**Audio修复方案**:
```swift
// layerBuffer67MBaudio layers
let layerBuffer = engine.device.makeBuffer(length: 67 * 1024 * 1024, options: .storageModeShared)!
// audio layer使layerBuffertempBuffer
applyRMSNorm(..., output: layerBuffer)
applyDepthwiseConv1D(..., output: layerBuffer)
applySiLU(..., output: layerBuffer)
applyResidualAdd(..., output: layerBuffer)
```
## TEXT修复方案
### 方案1: Buffer隔离推荐
**创建attention专用buffer**:
```swift
// ForwardTempsattentionBuffer
public struct ForwardTemps {
public let q: MTLBuffer
public let k: MTLBuffer
public let v: MTLBuffer
public let h: MTLBuffer // FFN
public let attnH: MTLBuffer // NEW: Attention
public let gate: MTLBuffer
public let up: MTLBuffer
public let attn: MTLBuffer
public let gating: MTLBuffer
public let ns: MTLBuffer
public let io: MTLBuffer
public init(...) throws {
q = try buf(nHeads * maxHeadDim)
k = try buf(nKvHeads * maxHeadDim)
v = try buf(nKvHeads * maxHeadDim)
h = try buf(hiddenSize) // FFN
attnH = try buf(hiddenSize) // NEW: Attention
gate = try buf(maxIntermediate)
up = try buf(maxIntermediate)
attn = try buf(nHeads * maxHeadDim)
gating = try buf(256)
ns = try buf(max(hiddenSize, nHeads * maxHeadDim))
io = try buf(hiddenSize)
}
}
```
**修改LayerOptimized.swift**:
```swift
// Attention使attnHh
try rmsNorm(..., output: temps.attnH) // Line 87: attnH #1
try quantizedMatmul(..., input: temps.attnH, output: temps.q) // Line 91
try quantizedMatmul(..., input: temps.attnH, output: temps.k) // Line 106
try quantizedMatmul(..., input: temps.attnH, output: temps.v) // Line 109
try quantizedMatmul(..., output: temps.attnH) // Line 172: attnH #2
// Residual add使h
try eltwiseAdd(..., b: temps.attnH, output: temps.h) // Line 177: h
// Post-attention norm使attnH
try rmsNorm(..., input: temps.h, output: temps.attnH) // Line 182: attnH #3
// FFN使h
try quantizedMatmul(..., output: temps.h) // Line 54: FFNh
try eltwiseAdd(..., b: temps.h, output: input) // Line 57: residual
// PostFFN使h
try rmsNorm(..., output: temps.h) // Line 209: h
try eltwiseAddScaled(..., output: input) // Line 238:
```
**预期效果**:
- 避免attention和FFN竞争h buffer
- 类似Audio修复减少NaN风险
- 内存增加2560 Floats = 10KB微不足道
### 方案2: Input保护简化
**创建inputCopy buffer**:
```swift
// Layer forwardinput
let inputCopy = temps.io // 使io buffer
let blit = cmdBuf.makeBlitCommandEncoder()!
blit.copy(from: input, sourceOffset: 0, to: inputCopy, destinationOffset: 0, size: hiddenSize * 4)
blit.endEncoding()
// 使inputCopyinput
try eltwiseAdd(..., a: inputCopy, b: temps.h, output: input) // Line 177
try eltwiseAdd(..., a: inputCopy, b: temps.h, output: input) // Line 57
```
**预期效果**:
- 保护原始input不被过早覆盖
- 使用现有io buffer无额外内存
- 简化修改
### 方案3: 顺序修复(保守)
**检查每个kernel的NaN输出**:
```swift
// attentionh
let cmdBufDebug = engine.commandQueue.makeCommandBuffer()!
try rmsNorm(..., output: temps.h, cmdBuf: cmdBufDebug)
cmdBufDebug.commit()
cmdBufDebug.waitUntilCompleted()
checkNaN(temps.h, "After RMSNorm")
// quantizedMatmul
// ...NaN
```
**预期效果**:
- 精确定位哪个kernel产生NaN
- 保守修复,最小修改
- 调试时间长
## 推荐修复路径
### 优先级排序
1. **方案1**Buffer隔离- 推荐,彻底修复
2. **方案2**Input保护- 简化,快速修复
3. **方案3**(顺序检查)- 调试,精确定位
### 实施步骤方案1
1. 修改ForwardTemps.swift添加attnH
2. 修改LayerOptimized.swift使用attnH
3. 测试验证NaN消除
4. 性能测试确认无影响
### 时间预估
- 方案1: ~30分钟修改+测试)
- 方案2: ~15分钟快速修复
- 方案3: ~60分钟深度调试
## 验证方法
### 测试代码
```swift
// Layer forward
let layerOutputPtr = input.contents().assumingMemoryBound(to: Float.self)
let layerNaNCount = Array(UnsafeBufferPointer(start: layerOutputPtr, count: hiddenSize)).filter { $0.isNaN }.count
print("Layer output NaN: \(layerNaNCount)/\(hiddenSize)")
assert(layerNaNCount == 0, "Layer produced NaN!")
```
### 测试模型
- E2B: 已加载成功,可用于测试
- E4B: Layer 34缺失跳过
- 12B: Layer 1缺失跳过
## 总结
**TEXT NaN问题性质**:
- Buffer重用冲突类似Audio
- Temps.h多次覆盖
- Input多次residual
**修复方向**:
- Buffer隔离Audio经验
- 减少重用
- 专用buffer
**预期效果**:
- NaN消除类似Audio修复
- 性能无损
- 内存微增10KB
**下一步**:
1. 选择修复方案推荐方案1
2. 实施修改
3. 测试验证
4. 完成TEXT NaN修复
---
**创建时间**: Day 3 Session~4小时
**基于**: Audio NaN修复经验67%就绪)
**目标**: TEXT 95%就绪零NaN