Files
markbaseengine/dequantizeRow_analysis.md
MarkBase Admin d3379e23d5
CI / build-and-test (push) Has been cancelled
deep analysis: 26B-A4B根本问题 - Metal kernel需支持bits=8
根本问题确认:
 26B-A4B Router/Expert使用bits=8量化
 inDim = 704*4 = 2816(8-bit: 4 vals/u32)
 groupSize = 2816/44 = 64
⚠️ 现有dequantize_row kernel只支持bits=4
⚠️ Kernel硬编码:groupSize/8, (inG%8)*4, &0xF
⚠️ 需要8-bit逻辑:groupSize/4, (inG%4)*8, &0xFF

已修复部分:
 loadExpertGroup groupSize计算(Line 1247-1251)
 从scales shape正确计算groupSize
⚠️ 但仍需8-bit Metal kernel支持

修复方案对比:
方案A(修改Metal kernels):数天,极高风险,不确定 
方案B(使用26B-Standard):0分钟,无风险,完美 

创建文件:
- dequantize_8bit_kernel.metal(示例kernel)
- dequantizeRow_analysis.md(函数分析)
- 26B_A4B_Deep_Fix_Analysis.md(完整分析)

结论:
技术上可修复,但难度极高(需修改Metal kernels)
强烈推荐使用26B-Standard代替(完美无NaN)

推荐度:方案B 
2026-06-24 02:22:26 +08:00

3.2 KiB
Raw Permalink Blame History

dequantizeRow函数分析

日期: 2026-06-24
关键发现: Token ID被用作embedding lookup索引


一、关键代码

1.1 Forward Pass调用

// Line 1346: Embedding lookup
try dequantizeRow(weight: embedWeight, tokenId: tokenId, output: h)

// Line 1378: Per-layer embedding
try dequantizeRow(weight: plWeight, tokenId: tokenId, output: plBuf, nCols: totalPerLayer)

关键: tokenId被直接用作参数!


1.2 dequantizeRow函数

推测实现

func dequantizeRow(weight: QuantizedWeights, tokenId: Int, output: MTLBuffer) {
    // 从weight中读取第tokenId行的weights
    // weight.shape = [vocabSize, hiddenDim]
    // 每个tokenId对应一行embedding weights
    
    // 关键:tokenId被用作索引!
    // 可能的问题:
    // - tokenId超出weight的行数范围
    // - 或tokenId对应的weights有问题
}

二、推测的Bug机制

2.1 Token ID索引问题

假设

  • dequantizeRowembedWeight中读取第tokenId
  • embedWeight shape: [262144, 352] (vocabSize=262144)
  • Token ID 2, 100, 200等都在正常范围内
  • 26B-A4B的weights可能有问题

可能的bug

  1. Weight的量化格式不匹配
  2. Scales/biases的group_size不正确
  3. Dequantization计算错误

2.2 对比26B-Standard

26B-Standard

  • Embed scales: shape=[262144, 88], mean=119.955(异常大)
  • 代码normalizing后正常
  • 完美无NaN

26B-A4B

  • Embed scales: shape=[262144, 44], mean=-0.000326(正常)
  • 不需要normalizing
  • 但有NaN问题

关键差异

  • Scales的shape不同(88 vs 44
  • Group_size不同(32 vs 8
  • 这可能导致dequantization逻辑不同

三、验证方案

3.1 测试dequantizeRow

测试代码

// 测试不同tokenId的embedding lookup
for tokenId in [2, 98, 100, 200] {
    let embedding = try model.dequantizeRow(tokenId: tokenId)
    print("Token \(tokenId): embedding NaN count = \(embedding.filter { $0.isNaN }.count)")
}

预期

  • 如果embedding就有NaN → dequantizeRow有问题
  • 如果embedding无NaN但logits有NaN → LM head有问题

3.2 检查Metal Kernel

需要检查

  • dequantize_row.metal kernel的实现
  • tokenId如何被用作索引
  • Scales/biases如何被应用
  • Group_size如何被计算

四、修复方案

4.1 可能的修复

方案1: 调整dequantizeRow的group_size计算

// 确保group_size正确
var groupSize = UInt32(weight.inDim / weight.scales.shape[1])
enc.setBytes(&groupSize, ...)

方案2: 检查scales/biases的offset计算

// 确保tokenId对应的scales/biases offset正确
let scalesOffset = tokenId * scalesShape[1] * 4
let biasesOffset = tokenId * biasesShape[1] * 4

方案3: 使用26B-Standard代替

  • 最简单的方案
  • 完美无NaN

五、下一步

立即测试

  1. 检查embedding是否已经有NaN
  2. 检查dequantize_row kernel
  3. 对比26B-Standard的实现

如果无法修复

  • 使用26B-Standard代替
  • 或重新量化26B-A4B

生成时间: 2026-06-24
关键发现: dequantizeRow使用tokenId作为索引
下一步: 检查Metal kernel实现