Files
xserv/docs/19-moe-gpt-oss.md
Gahow Wang c7d0750c32 moe(wip): gpt-oss-20b groundwork — config fields, arch doc, MXFP4 tools
Phase 19 start. config.rs: explicit head_dim (gpt-oss=64) + MoE fields
(num_local_experts, num_experts_per_tok, swiglu_limit, sliding_window,
layer_types) with accessors; Qwen3/GPT-2 paths unchanged (fall back to
hidden/num_heads when head_dim absent).

docs/19-moe-gpt-oss.md: architecture + exact HF reference math (router
softmax-after-topk, interleaved clamped (up+1)*glu experts, attention
sinks, alternating sliding window, rotate_half RoPE theta=150000,
head_dim 64), verified tensor layout, MXFP4 dequant plan.
docs/MOE_PROGRESS.md: resume/handoff snapshot.

tools/mxfp4_probe.py: inspect safetensors + validate MXFP4 decode (done).
tools/gptoss_dequant.py: MXFP4 experts -> plain BF16 safetensors dir so
the existing loader reads it (no MXFP4 in Rust for the first pass).

Verified: llama.cpp (dash5, LLM_ARCH_OPENAI_MOE) runs the gpt-oss-20b
MXFP4 GGUF correctly (17*24 -> 408) = the correctness oracle. MXFP4 decode
validated in numpy. Model + GGUF staged on dash5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 21:01:53 +08:00

6.6 KiB
Raw Blame History

Phase 19: MoE — gpt-oss-20b

目标:在 xserv 支持 MoE,用 openai/gpt-oss-20b 端到端跑通,并与 llama.cpp 在 AIME 2025 / GSM8K 上对比正确性与性能。MXFP4 expert 权重加载时反量化为 BF16整模型 ~40GB 单卡放不下 → 复用 Phase 18 的 PPPP=2 ~20GB/卡PP=4 ~10GB/卡)。

实时进度与重启续作指南见 docs/MOE_PROGRESS.md

1. 架构config.json已核对

num_hidden_layers=24, hidden=2880, head_dim=64≠hidden/heads, n_heads=64, n_kv_heads=8GQA n_rep=8, expert intermediate=2880, num_local_experts=32, num_experts_per_tok=4, vocab=201088, max_pos=131072, rope_theta=150000, sliding_window=128交替层layer_types, rms_norm_eps=1e-5, swiglu_limit=7.0, alpha=1.702, tie_embeddings=false。 量化:MXFP4,仅 expert MLPgate_up/down 的 _blocks+_scales attn/router/embed/lm_head 为 BF16。

2. 参考数学HF transformers modeling_gpt_oss.py,逐字核对)

RMSNorm — 标准fp32 算 varianceeps=1e-5

RouterGptOssTopKRoutersoftmax 在 topk 之后,含 bias

logits  = x @ W_router^T + b_router          # [T, 32]
top_val, idx = topk(logits, k=4, dim=-1)     # [T, 4]
top_val = softmax(top_val, dim=-1)           # 仅对选中的 4 个归一化
scores  = zeros[T,32].scatter(1, idx, top_val)

ExpertsGptOssExpertsfused gate_upinterleavedclamped(up+1)·glu

alpha=1.702; limit=7.0
gate_up = x @ gate_up_proj[e] + gate_up_proj_bias[e]   # [.., 2*dim]
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] + down_proj_bias[e]
out  = Σ_{e∈top4} scores[t,e] * y_e

Attentioneager_attention_forward带 sinks

scaling = head_dim**-0.5 = 64**-0.5q/k/v/o 都有 bias
RoPE(theta=150000) on q,krepeat_kv(n_rep=8)
attn = (q @ k^T) * scaling + causal_mask        # 滑窗层叠加 banded(window=128)
sinks = module.sinks[head]                       # 每 head 一个标量
combined = cat([attn, sinks broadcast], dim=-1)  # 多一列
combined -= combined.max(-1, keepdim)            # 数值稳定
probs = softmax(combined, -1)
scores = probs[..., :-1]                          # 丢掉 sink 列 => 概率不归一到 1
o = (scores @ v) -> merge heads -> @Wo + bo

sinks 等价于 softmax 分母多了 exp(sink)——可学习的"不注意"通道。 交替 sliding windowconfig layer_types 标明哪些层 window=128其余全注意力。

与 Qwen3 的新增点MoE FFN、MXFP4 反量化、attention sinkssoftmax 多一列再丢)、 交替 sliding window、q/k/v/o bias、head_dim=64、clamped (up+1)*glu、rope_theta=150000。

实测张量布局layer 0已用 tools/mxfp4_probe.py 核对)

self_attn.q_proj.weight [4096,2880] +bias[4096]   # 64 heads*64
self_attn.k_proj.weight [512,2880]  +bias[512]    # 8 kv*64
self_attn.v_proj.weight [512,2880]  +bias[512]
self_attn.o_proj.weight [2880,4096] +bias[2880]
self_attn.sinks [64]                               # 每 q-head 一个标量BF16
input_layernorm.weight [2880]; post_attention_layernorm.weight [2880]
mlp.router.weight [32,2880] +bias[32]
mlp.experts.gate_up_proj_blocks [32,5760,90,16] U8 + _scales [32,5760,90] U8 + _bias[32,5760] BF16
mlp.experts.down_proj_blocks    [32,2880,90,16] U8 + _scales [32,2880,90] U8 + _bias[32,2880] BF16
# 全局: model.embed_tokens.weight, model.norm.weight, lm_head.weight (BF16)

MXFP4 打包:[..., nblk=90, 16] U8每 16 字节 = 32 个 FP4 码(低 nibble=偶 idx高 nibble=奇 idx 每 block 一个 E8M0 scale90*32 = 2880 = 输入(hidden)维。即 gate_up 每 expert 权重逻辑 shape [5760 out, 2880 in]已转置存储:行=out列=in与 HF nn.Linear 一致 y=x·Wᵀ)。

RoPErotate_half非 interleave

dim = head_dim = 64; base = rope_theta = 150000
inv_freq = 1 / base^(arange(0,64,2)/64)        # 32 项
freqs = pos ⊗ inv_freq                          # [S, 32]cos/sin = cos(freqs)/sin(freqs)  (不 doubling)
# 应用: x=[.., 64], first=x[:32], second=x[32:]
#   out_first  = first*cos - second*sin
#   out_second = second*cos + first*sin

⚠️ 与 Qwen3 的 RoPE kernelinterleave不同 —— gptoss 走 rotate_half。需单独处理。

Decoder layerpre-norm 残差,结构同 Qwen3

h = x + attn(input_norm(x))            # attn 含 sinks/bias/滑窗
out = h + moe(post_norm(h))            # moe = router + top4 experts 加权和

最终:logits = lm_head(norm(h_last))。无 q_norm/k_norm与 Qwen3 不同gptoss 没有)。

3. MXFP4 反量化expert 权重)

expert 张量名:model.layers.{i}.mlp.experts.gate_up_proj_blocks/_scales...down_proj_blocks/_scalesbias 为 BF16。MXFP4每 32 元素一 block 共享一个 E8M0(8-bit 指数) scale每元素 4-bit FP4(E2M1)。反量化 val = fp4_lut[code] * 2^(e8m0 - 127)P19.1 先用 Python(numpy) 反量化并与 HF 一层 数值对照block 方向 / LUT / gate_up interleave再写进 Rust loader。

4. 路线(正确优先)

  1. P19.1 Python 侦查 + MXFP4 反量化验证(不依赖 GPU
  2. P19.2 config.rs 加 MoE 字段Qwen3 路径不变)。
  3. P19.3 gptoss.rsdenseattn+sinks+bias+滑窗 / norm / lm_head+ MoE FFN (正确优先:逐 token top-4 gather→clamped SwiGLU→加权和MXFP4 在 from_weights 反量化为 BF16。验收prefill logits 与 HF BF16 容差内一致top-1 一致)。
  4. P19.4 接 PPexperts 随层切),--pp 端到端PP=2/4 与 PP=1 等价。
  5. P19.5 llama.cpp 对比(升级 submodule 到支持 gpt-oss 的版本 + 取/转 GGUF 跑 AIME 2025 + GSM8K复用 tools/bench + summarize_fullq.py

5. 风险

  • MXFP4 格式细节必须逐字对 → Python 反量化兜底。
  • attention sinks + 交替滑窗:现有 flash/paged kernel 未必支持 → 正确优先版本先走朴素 attention显式 mask + sink 列)。
  • llama.cpp pinned b9371 早于 gpt-oss约 2025-08→ 需升级 submodule有连锁影响。
  • 性能MoE 正确优先版本(逐 expert gather/scatter会慢先对再快。
  • 环境huggingface.co 被墙,需经代理 + hf-mirror 下载(见 MOE_PROGRESS.md §2

6. 不在本阶段范围

GPU 原生 MXFP4 + 按需反量化 kernel先全 BF16高性能 grouped-GEMM / expert parallel TP×MoE单卡运行需 MXFP4-native