deep analysis: 26B-A4B根本问题 - Metal kernel需支持bits=8
CI / build-and-test (push) Has been cancelled

根本问题确认:
 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 
This commit is contained in:
MarkBase Admin
2026-06-24 02:22:26 +08:00
parent e82162e96b
commit d3379e23d5
4 changed files with 466 additions and 2 deletions
+146
View File
@@ -0,0 +1,146 @@
# dequantizeRow函数分析
**日期**: 2026-06-24
**关键发现**: Token ID被用作embedding lookup索引
---
## 一、关键代码
### 1.1 Forward Pass调用
```swift
// 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函数
**推测实现**
```swift
func dequantizeRow(weight: QuantizedWeights, tokenId: Int, output: MTLBuffer) {
// weighttokenIdweights
// weight.shape = [vocabSize, hiddenDim]
// tokenIdembedding weights
// tokenId
//
// - tokenIdweight
// - tokenIdweights
}
```
---
## 二、推测的Bug机制
### 2.1 Token ID索引问题
**假设**
- `dequantizeRow``embedWeight`中读取第`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
**测试代码**
```swift
// tokenIdembedding 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计算
```swift
// group_size
var groupSize = UInt32(weight.inDim / weight.scales.shape[1])
enc.setBytes(&groupSize, ...)
```
**方案2**: 检查scales/biases的offset计算
```swift
// tokenIdscales/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实现