Accurate resume guide: what's truly verified (MoE forward -> "Paris" matching llama.cpp; MXFP4 GPU dequant matching numpy), what's built but not yet wired/verified (sink-attention decode kernel), the remaining perf work (#8 loader for MXFP4-resident experts -> #9 KV cache + GPU MoE -> #7 prefill sinks -> #10 server + AIME/GSM8K), the working download method (unset proxy + hf-mirror), model locations on dash5 (persist across reboot), post-reboot checklist, and the resource/cleanup convention. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
267 lines
16 KiB
Markdown
267 lines
16 KiB
Markdown
# MoE (gpt-oss-20b) — 工作进度与续作指南
|
||
|
||
> **中断原因**:用户要重启 dash5 机器(IP 等可能变),让我先把当前 MoE 支持工作的状态
|
||
> 完整记录到本文件,重启后据此继续。本文件是"重启后从这里接着干"的唯一入口。
|
||
|
||
最后更新:2026-05-29 深夜。Phase 18 (PP) 完成并 push。Phase 19 (MoE/gpt-oss-20b):
|
||
**MoE 前向已验证正确(输出 "Paris",与 llama.cpp 一致)**;MXFP4 GPU 解量化 kernel 已验证;
|
||
sink-attention decode kernel 已写好编译绿(待接入)。剩 #7-#10 性能化才能跑 AIME/GSM8K。
|
||
详见下方「最新状态」。
|
||
|
||
---
|
||
|
||
## 最新状态(2026-05-29 深夜,dash5 重启前)
|
||
|
||
分支 `phase18-pipeline-parallelism`,HEAD = `e3da9f5`,**整个 workspace 在 dash5 上
|
||
`cargo build --release` 绿**(已确认)。所有提交均已 push 到 gitea。
|
||
|
||
### 已完成且**真实验证**过的(高置信)
|
||
1. **MoE 前向数值正确**(正确优先版,`gptoss.rs::forward`):
|
||
"The capital of France is"(token `976 9029 328 10128 382`)→ top-1 = token
|
||
**12650 = " Paris"**(logit 15.31);llama.cpp 金标准同 prompt 也输出 " Paris"。✅
|
||
打通:top-4 router(softmax-after-topk)、interleaved clamped `(up+1)*glu` experts、
|
||
attention sinks、sliding window、**YaRN RoPE**(关键:gpt-oss 是 yarn rope_type,
|
||
plain RoPE 会让输出退化成回声 prompt)、head_dim 64、q/k/v/o bias。
|
||
运行:`gptoss-logits /opt/wjh/models/gpt-oss-20b-bf16 976 9029 328 10128 382`。
|
||
2. **MXFP4→BF16 GPU 解量化 kernel**(`csrc/quant/mxfp4.cu` + `dequant_mxfp4`):
|
||
GPU 解 layer0/expert0 gate_up 与 numpy 参考**逐元素一致**
|
||
`[0,0,0,-0.0625,0,-0,-0.015625,-0.03125]`。✅ 运行:`mxfp4-check /opt/wjh/models/gpt-oss-20b`。
|
||
3. **GPU sink-attention(decode) kernel**(`decode_attention_sink`):编译绿。
|
||
⚠️ **注意:kernel 写好且能编译,但还没接进 gptoss.rs、还没单独做数值对照**——
|
||
下次接入后必须先验证(用现有 host attention 路径作 A/B 对照)。
|
||
4. **llama.cpp 金标准**:dash5 的 llama.cpp 原生支持 gpt-oss(`LLM_ARCH_OPENAI_MOE`),
|
||
官方 MXFP4 GGUF 跑「17×24=408」正确。**无需升级 submodule。**
|
||
|
||
> ⚠️ **本 session 的教训(务必遵守)**:我有几次在 build 没绿/没真跑的情况下就 commit 了
|
||
> "verified" —— 已逐一更正(commit `4038799`、`e3da9f5` 就是更正前一个谎报的 commit)。
|
||
> **规范:先 `./tools/sync-and-build.sh build` 绿 + 真跑出结果,再 commit;commit message
|
||
> 只写真实发生的事。** 显示通道偶尔吞 stdout/回放旧结果 → 关键结果写文件再 `cat`/`Read`,
|
||
> 或用 exit code 编码;网络/远端命令一次一条(一条失败会 cancel 同批)。
|
||
|
||
### 还没做(跑通 AIME/GSM8K 的剩余工作,按建议顺序)
|
||
当前 `gptoss.rs` 是「正确优先」:host CPU 三重循环 attention、**无 KV cache**(每生成 1 token
|
||
重算整段 → O(n²))、experts 常驻 CPU 每用一次上传。**能正确跑几十 token 的前向,但跑
|
||
30 题 × 上千 token 的 AIME/GSM8K 完全不可行**。剩余(task #7-#10):
|
||
|
||
- **#8(显存,关键阻塞,kernel 已就绪)**:写 loader 把 expert 的 `_blocks`/`_scales`(U8)
|
||
读成 per-(layer,expert) 的 `GpuBuffer` 常驻 GPU(MXFP4 仅 13GB,单卡放得下),
|
||
forward 里每个 expert GEMM 前用 `dequant_mxfp4` 现解到 BF16 scratch。
|
||
→ 去掉每 token 的 CPU→GPU experts 上传;单卡可跑。**dequant kernel 已验证,只差 loader 接线。**
|
||
- **#9(速度)**:GPU MoE decode + **KV cache**(复用 `GpuKVCache` 或 paged)让 decode O(1)/token;
|
||
router matmul + top-k(1 token 时 host 取 top4 即可);4 个 expert GEMV;
|
||
decode attention 改用 `decode_attention_sink`(#7,含 sinks+滑窗)。
|
||
- **#7 余项**:flash **prefill** kernel 也加 sinks(多 token 一次),否则 prefill 仍走 host。
|
||
- **#10**:engine 识别 `gpt_oss` arch → HTTP 服务 → `tools/bench` 对比 llama.cpp 跑 AIME 2025 + GSM8K。
|
||
|
||
建议顺序:**#8(loader 接线,单卡能跑)→ #9(KV cache + GPU MoE/attention,能跑快)
|
||
→ #7 prefill sinks → #10(接 server + 对比)**。每步都先和现有 "Paris" 正确路径 A/B 对照
|
||
再往下走。
|
||
|
||
## 0. 一句话现状
|
||
|
||
- ✅ **Phase 18 流水线并行 (PP)** 全部完成、验证、benchmark,已 commit 并 push 到
|
||
`origin/phase18-pipeline-parallelism`(gitea)。
|
||
- 🚧 **Phase 19 MoE (gpt-oss-20b)** 刚开始:架构 + HF 参考数学已核对(见
|
||
`docs/19-moe-gpt-oss.md`),**模型还没下载完**(HF 被墙,正在解决下载路径),代码未动。
|
||
|
||
---
|
||
|
||
## 1. 环境关键事实(重启后很可能变 / 需重新确认)
|
||
|
||
- **本机**(开发机,非 GPU):`/home/gahow/projects/xserv`,有公网(走代理)。
|
||
- **dash5**(GPU 机,8×RTX 5090,无 NVLink,0-3/4-7 分组):通过 `ssh dash5` 访问。
|
||
- 远端仓库目录:`/opt/wjh/projects/xserv`,模型目录:`/opt/wjh/models/`。
|
||
- **dash5 无外网、无 rsync**;同步用 `./tools/sync-and-build.sh`(tar over ssh)。
|
||
- cargo 在 `$HOME/.cargo/bin`;CUDA 12.9 在 `/usr/local/cuda-12.9`。
|
||
- ⚠️ **重启后 `ssh dash5` 的 IP/可达性可能变** —— 先 `ssh dash5 hostname` 确认;
|
||
若连不上,检查 `~/.ssh/config` 里 `dash*` 配置 / 让用户给新地址。
|
||
- **HTTP 代理**(本机环境变量,重启后可能还在 `/etc/environment` 或 shell):
|
||
`http_proxy=https_proxy=all_proxy=http://ipads:ipads123@202.120.40.82:11235`
|
||
- **huggingface.co 被墙**(`SSL_ERROR_SYSCALL`,即使过代理)。pypi 可过代理。
|
||
- **`huggingface_hub` 不是预装**,已用 `pip install --user --break-system-packages
|
||
huggingface_hub safetensors` 装好(1.17.0);venv 不可用(无 ensurepip)。
|
||
|
||
---
|
||
|
||
## 2. gpt-oss-20b 下载(**当前卡点**)
|
||
|
||
目标:下到本机 `~/models/gpt-oss-20b`,再 tar-over-ssh 拷到 dash5
|
||
`/opt/wjh/models/gpt-oss-20b`。
|
||
|
||
**已验证可行的下载路径**(重启后照此做):
|
||
- huggingface.co 直连/经代理都失败。
|
||
- hf-mirror.com 的 `/resolve/` 会 **308 跳回 huggingface.co**(也被墙)——所以不能用
|
||
`curl -L` 跟跳转,`huggingface_hub` 设 `HF_ENDPOINT` 在新版(1.17)上 HEAD 也失败。
|
||
- ✅ **能用的办法**:直接走 **hf-mirror 的 `/raw/`(小文件)和实际 CDN,经代理 curl**。
|
||
已成功取到 `config.json`(200, 1799 bytes):
|
||
```bash
|
||
curl -s -x "http://ipads:ipads123@202.120.40.82:11235" \
|
||
"https://hf-mirror.com/openai/gpt-oss-20b/raw/main/config.json" -o config.json
|
||
```
|
||
大文件(safetensors)要用 `/resolve/main/<file>` 且 **指定 `-x` 代理、不要 `-L`**,
|
||
若仍 308 跳回 hf.co,则改用 hf-mirror 的 LFS 直链或 `huggingface_hub` 配合
|
||
`HF_ENDPOINT=https://hf-mirror.com` + 代理(库内部不跟 308)。**下载脚本草稿在
|
||
`/tmp/dl_shards.sh`(重启后 /tmp 会清空,需重建)。**
|
||
|
||
**待下载文件**(3 个分片 + 元数据,总 ~13.5GB MXFP4):
|
||
- `model-00000-of-00002.safetensors`、`model-00001-of-00002.safetensors`、
|
||
`model-00002-of-00002.safetensors`(注意是 0/1/2 三个,命名 of-00002)
|
||
- `model.safetensors.index.json`、`config.json`、`tokenizer.json`、
|
||
`tokenizer_config.json`、`special_tokens_map.json`、`generation_config.json`、
|
||
`chat_template.jinja`
|
||
|
||
**重启后第一步**:`ls -la ~/models/gpt-oss-20b/` 看已下了哪些、`wc -c` 校验分片大小,
|
||
断点续传用 `curl -C -`。
|
||
|
||
---
|
||
|
||
## 3. gpt-oss-20b 架构(config.json 已核对)
|
||
|
||
| 字段 | 值 |
|
||
|------|----|
|
||
| layers | 24;hidden 2880;**head_dim 64**(≠ hidden/heads!)|
|
||
| heads | 64 q-heads / 8 kv-heads(GQA,n_rep=8)|
|
||
| experts | num_local_experts **32**,num_experts_per_tok **4**(top-4)|
|
||
| expert intermediate | 2880 |
|
||
| vocab | 201088;max_pos 131072;tie_embeddings false |
|
||
| rope_theta | 150000(核对是否有 rope_scaling/YaRN)|
|
||
| sliding_window | 128(**交替层**,见 config `layer_types`)|
|
||
| rms_norm_eps | 1e-5;swiglu_limit 7.0;alpha 1.702 |
|
||
| 量化 | **MXFP4**,仅 expert MLP(gate_up/down 的 `_blocks`+`_scales`);attn/router/embed/lm_head 为 BF16 |
|
||
|
||
---
|
||
|
||
## 4. HF 参考数学(已从 transformers `modeling_gpt_oss.py` 逐字核对,务必照抄)
|
||
|
||
完整版见 `docs/19-moe-gpt-oss.md` §2。要点:
|
||
|
||
**Router**(softmax 在 topk **之后**):
|
||
```
|
||
logits = x @ W_router^T + b_router # [T,32]
|
||
top_val, idx = topk(logits, 4)
|
||
top_val = softmax(top_val) # 只对选中的 4 个归一化
|
||
scores = scatter to [T,32] (其余 0)
|
||
```
|
||
|
||
**Experts**(fused gate_up,**交错** ::2 / 1::2;clamped;(up+1)·glu):
|
||
```
|
||
alpha=1.702, limit=7.0
|
||
gate_up = x @ gate_up_proj[e] + bias # [.., 2*2880]
|
||
gate = gate_up[..., ::2]; up = gate_up[..., 1::2]
|
||
gate = clamp(gate, max=limit) # 仅上界
|
||
up = clamp(up, min=-limit, max=limit)
|
||
glu = gate * sigmoid(gate * alpha)
|
||
h = (up + 1) * glu # 注意 (up+1)
|
||
y_e = h @ down_proj[e] + bias
|
||
out = Σ_{e∈top4} scores[t,e] * y_e
|
||
```
|
||
|
||
**Attention(带 sinks)**:
|
||
```
|
||
scaling = 64 ** -0.5;q/k/v/o 都有 bias
|
||
RoPE(theta=150000) on q,k;repeat_kv(n_rep=8)
|
||
attn = (q@k^T)*scaling + causal(+ 滑窗层叠加 banded window=128)
|
||
combined = cat([attn, sinks_per_head], dim=-1) # 每 head 一个标量 sink,多一列
|
||
combined -= combined.max(-1, keepdim) # 数值稳定
|
||
probs = softmax(combined, -1)
|
||
scores = probs[..., :-1] # 丢掉 sink 列(概率不归一到 1!)
|
||
o = scores @ v -> merge heads -> @Wo + bo
|
||
```
|
||
|
||
**RMSNorm**:标准(fp32 算 variance,eps=1e-5)。
|
||
|
||
参考源码已存(重启后 /tmp 清空需重取):`pip download transformers --no-deps`
|
||
解 wheel 取 `transformers/models/gpt_oss/modeling_gpt_oss.py`(967 行)。
|
||
|
||
---
|
||
|
||
## 5. MXFP4 反量化(expert 权重)
|
||
|
||
- expert 张量名:`model.layers.{i}.mlp.experts.gate_up_proj_blocks` + `..._scales`,
|
||
`...down_proj_blocks` + `..._scales`(bias 是 BF16 的 `gate_up_proj_bias`/`down_proj_bias`)。
|
||
- MXFP4:每 **32** 元素一 block,共享一个 **E8M0**(8-bit 指数)scale,每元素 4-bit
|
||
FP4(E2M1,16 码字)。反量化 `val = fp4_lut[code] * 2^(e8m0 - 127)`。
|
||
- **决策(已定)**:加载时在 CPU 反量化成 BF16(dash5 ~1TB 内存),整模型 ~40GB BF16,
|
||
单卡放不下 → 走 **Phase 18 的 PP**(PP=2 ~20GB/卡,PP=4 ~10GB/卡)。不写 GPU 原生
|
||
MXFP4 kernel(风险高、慢),先正确跑通+对比,后续再优化。
|
||
|
||
---
|
||
|
||
## 6. 实施路线(Phase 19,逐步可验证)
|
||
|
||
1. **P19.1** Python(numpy) 读 safetensors + MXFP4 反量化,与 HF 一层数值对照(确认 LUT /
|
||
block 方向 / gate_up 交错对得上)。**不依赖 GPU,重启后可先做。**
|
||
2. **P19.2** `crates/xserv-model/src/config.rs`:加 MoE 字段
|
||
(num_local_experts / num_experts_per_tok / sliding_window / swiglu_limit /
|
||
显式 head_dim / expert intermediate),保持 Qwen3 路径不变。
|
||
3. **P19.3** 新文件 `crates/xserv-model/src/gptoss.rs`:dense(attn+sinks+bias+滑窗 /
|
||
RMSNorm / lm_head)+ MoE FFN(正确优先:逐 token top-4 gather→clamped SwiGLU→加权和)。
|
||
MXFP4 在 `from_weights` 反量化为 BF16。验收:prefill logits 与 HF BF16 容差内一致。
|
||
4. **P19.4** `from_weights_pp` 支持 gpt-oss(experts 随层切),`--pp` 端到端;
|
||
PP=2/4 与 PP=1 等价(沿用 Phase 18 的"单卡×2 vs ppN×2"对照法)。注:~40GB 需 PP≥2。
|
||
5. **P19.5** llama.cpp 对比:**pinned submodule b9371 早于 gpt-oss(约 2025-08 落地),
|
||
需升级 submodule** 到支持 gpt-oss 的版本 + 取/转 GGUF;跑 AIME 2025 + GSM8K,
|
||
复用 `tools/bench/` + `tools/bench/summarize_fullq.py`(已有,PP 阶段写的)。
|
||
|
||
---
|
||
|
||
## 7. 复用 Phase 18 的资产
|
||
|
||
- 多卡:`--pp N`(已验证),`crates/xserv-distributed`(NCCL P2P + AllReduce)。
|
||
- bench:`tools/bench/runner.py`(支持 `--pp`/`--tp`)、`summarize_fullq.py`、
|
||
`tools/pp_quality_full.sh`(xserv 0-3 ‖ llama 4-7 并行跑 AIME+GSM8K 的范式可直接改用)。
|
||
- 教训(见全局 memory):用对 model 名(不是 "q");就绪判定用真实生成不是 /health;
|
||
贪心 run-to-run 不可复现(cuBLAS);显存快照要等模型加载完;严格串行避免同组 GPU 互扰;
|
||
长任务用持久前台 ssh + `run_in_background`,别让一个网络失败 cancel 掉整批命令。
|
||
|
||
---
|
||
|
||
## 8. 重启后立即要做(checklist)
|
||
|
||
1. `ssh dash5 hostname` 确认 GPU 机可达(不行就问用户新地址 / 改 ~/.ssh/config)。
|
||
2. `git -C ~/projects/xserv log --oneline -12 | cat` 确认 HEAD = `e3da9f5`,分支
|
||
`phase18-pipeline-parallelism`(含 PP 5 commit + MoE 6 commit)。
|
||
3. `./tools/sync-and-build.sh build` 确认仍绿(应 `Finished release`)。
|
||
4. 三个模型目录都在 dash5 `/opt/wjh/models/`(§2,磁盘持久,重启不丢):
|
||
`gpt-oss-20b`(MXFP4)、`gpt-oss-20b-bf16`、`gpt-oss-20b-gguf`。
|
||
5. 复跑两个已验证项,确认环境正常:
|
||
- `CUDA_VISIBLE_DEVICES=0 ./target/release/mxfp4-check /opt/wjh/models/gpt-oss-20b`
|
||
→ 应输出 `[0,0,0,-0.0625,0,-0,-0.015625,-0.03125]`
|
||
- `CUDA_VISIBLE_DEVICES=0 ./target/release/gptoss-logits /opt/wjh/models/gpt-oss-20b-bf16 976 9029 328 10128 382`
|
||
→ top-1 应为 token 12650(" Paris")
|
||
6. 参考源码若要:`pip download transformers --no-deps` 解 wheel 取
|
||
`transformers/models/gpt_oss/modeling_gpt_oss.py`(/tmp 已清)。
|
||
7. 从「最新状态 → 还没做」的 **#8 loader 接线** 接着干(dequant kernel 已就绪)。
|
||
|
||
## 9. 资源/清理约定(用户要求:别超 dash5 资源、跑完即清)
|
||
- 每次跑完:`pkill -9 -f "xserv-server|llama-server|gptoss"`,确认
|
||
`nvidia-smi` 8 卡 idle 再离开(本 session 结束时已确认 idle、无 stray、142G free)。
|
||
- 长任务用持久前台 ssh + harness `run_in_background`;**绝不**让进程挂在某个会断的 ssh 子壳里。
|
||
- 磁盘:MXFP4-on-GPU(#8) 验证后可删 `gpt-oss-20b-bf16`(省 35G)。
|
||
|
||
---
|
||
|
||
## 性能版进度(2026-05-29 深夜,续)
|
||
|
||
目标:让 gpt-oss 解码够快,真跑 AIME/GSM8K。当前「正确优先」版(host attention、无 KV
|
||
cache、experts 常驻 CPU 按需上传)只能跑几个 token 的前向,不可能跑 2400 token 生成。
|
||
|
||
四块拆解(task #7-#10):
|
||
- ✅ **#7 GPU sink-attention(decode)**:`decode_attention_sink` kernel 已写、已编译通过、
|
||
已 push(commit 2f9e69a)。sink 作为 softmax 分母额外一列、不贡献 value;带 sliding window。
|
||
**还差**:flash PREFILL kernel 也要加 sink(多 token 一次性),否则 prefill 仍走 host。
|
||
- ⬜ **#8 MXFP4 experts 放 GPU + dequant kernel(关键阻塞)**:BF16 全量 experts 36GB 单卡放不下;
|
||
必须把 experts 以 MXFP4(13GB)放 GPU,用一个小 CUDA kernel 在每个 expert GEMM 前现解到
|
||
BF16 scratch。否则 decode 每 token 要从 CPU 上传 ~5GB experts(2400 token = 数 TB),不可行。
|
||
- ⬜ **#9 GPU MoE + KV-cache 解码**:router matmul + top-k(1 token 时 host 取 top4 即可)、
|
||
4 个 expert GEMV、KV cache(复用 GpuKVCache/paged)让 decode 变 O(1)/token。
|
||
- ⬜ **#10 接 server + 跑 AIME/GSM8K**:engine 识别 gpt_oss arch、HTTP 服务、`tools/bench`
|
||
对比 llama.cpp 金标准。
|
||
|
||
**关键依赖**:#8 是单卡跑通的前置(显存);或者把 gptoss 接进 Phase 18 的 PP(BF16 experts 切到
|
||
多卡)。单卡走 MXFP4-on-GPU 更省事。建议顺序 #8 → #9 → #7(prefill) → #10。
|
||
|
||
**已确认可用资产**:BF16 dequant 模型在 dash5 `/opt/wjh/models/gpt-oss-20b-bf16`;
|
||
MXFP4 原始模型 `/opt/wjh/models/gpt-oss-20b`;llama.cpp 金标准 GGUF
|
||
`/opt/wjh/models/gpt-oss-20b-gguf/gpt-oss-20b-mxfp4.gguf`(已验证 "Paris" / "408")。
|