一张 32GB 的 RTX 5090,跑通了 Qwen3.5-122B-A10B 的文本推理。压缩后的文本权重和缓冲都留在 GPU 上,没有往 CPU 卸载;固定短请求测试中,NVML used 显存的采样峰值是 30.64 GiB。

装进显卡之后,还得看它回答得怎么样。四组开发评测里,每组通过数都比对应 BF16 对照少了 1~2 题。看总分差距不大,逐题对照却发现,变化比这个数字多。

这篇记录的是 Bonsai 风格实验中的 R122-native4095:它怎么放进单卡、实际占多少显存,以及哪些检查还没有通过。数据来自截至 2026 年 10 月 10 日的公开实验记录。

这 122B 是怎么放进去的

模型是 MoE,总参数约 122B,每个 token 激活约 10B。虽然计算时只选部分专家,权重仍然需要存下来。这次没有靠 CPU 权重卸载换取显存空间。

实验把专家权重压到三值,dense、embedding 等部分使用 4bit,保留其余必要参数。随后冻结低比特基座,只训练 rank-8 的低秩补偿,尝试修回压缩带来的误差。48 层一共训练 192 个 FP32 补偿张量,在一张 B300 上做了 4096 次参数更新。5090 负责推理,训练发生在 B300 上。

三值编码有个容易理解的小例子:五个取值只有 -1、0、+1 的编码,一共 3^5 = 243 种组合,能装进一个字节。只算编码,相当于每个权重 1.6bit。但尺度、保留权重、补偿和缓存都要额外占空间,整个模型并不是按这个数字存储。

推理时,runtime 保留打包权重,只把选中的专家权重展开成 BF16 后计算。这样省下了存储空间,也留下了解包开销。这版还没有用融合的三值矩阵乘内核,具体的补偿位置与舍入顺序见方法说明。

权重放下了,显存还剩多少

部署记录里的 CUDA 模型持久存储约 28.10 GiB。运行 HTTP 服务以后,还会有缓存、临时计算和运行时开销,NVML used 的已采样峰值到了 30.64 GiB。驱动另报告约 0.484 GiB 的 reserved,不能漏掉,也不能重复算进 used。

这组数据对应四个闭环客户端,输入不超过 128 token,输出上限 64,KV 缓存 896 MiB、总上下文配置 1536。57,036 个采样点里,最少剩余空间约 740 MiB。短请求能运行,余量并不宽裕。

如果你也在估算部署显存,需要给运行开销留位置,不能只按 28.10 GiB 准备机器。这里没有同协议的 BF16 推理显存基线,所以只报告实测占用,不计算压缩倍数。原始字节数和测量条件在HTTP 四轮记录里。

总分少一两题,具体答对的题却变了

四组开发评测都与同一轮匹配的 122B BF16 结果比较。表里是通过数 / 总题数:

开发评测BF16 对照量化加补偿
HumanEval135 / 164134 / 164
CMMLU 子集163 / 201161 / 201
GSM8K 子集190 / 200189 / 200
中文与格式48 / 4846 / 48

最容易被总分遮住的是逐题变化。CMMLU 看起来只少了两题,实际有 12 道原来答对的题变错了,另有 10 道原来答错的题变对了。HumanEval 则是丢了 8 道、新增 7 道。净差很小,不等于只有一两道题发生变化,也不足以判断真实任务上的损失。

这些是开发过程中的评测,包含子集和 48 题小面板,独立保留测试没有打开。候选有两道不支持的 HumanEval 样本,仍计在 164 的分母里。验证集上的蒸馏目标下降了,导出重载和数值检查也通过了;四组质量分数仍低于对应基线,这版没有通过原定质量验收。逐题统计保留在训练与质量记录。

35B 那版也测了,上下文会吃掉显存余量

另一个产物是 Qwen3.5-35B-A3B 的 R35-8192:冻结低比特基座,训练 rank-16 补偿。它与上面的 122B 产物不同,也不是教学仓库根目录的 35B 全专家 QAT 训练结果。

在同样型号的 5090 上,固定解码 64 步,把输入从 128 token 拉到 16K,记录如下:

输入 token 数PyTorch 分配峰值
12814.85 GiB
51214.89 GiB
409615.61 GiB
819216.46 GiB
1638418.26 GiB

128 token 到 16K,分配峰值多了约 3.41 GiB。只看最短输入,会漏掉这部分需求。

这里测的是 PyTorch allocated,包含缓存初始化和 CUDA Graph 录制,与前面的 NVML used 口径不同。每档测一次,即使遇到 EOS 也继续执行到 64 步。这是占用测试,长上下文的回答质量和 16GB 显卡能否运行都需要另测。35B 原始记录保留了各档条件。

解包更快了,整条请求却没有更快

把 122B 接到 runtime 后,也试过调整解包 kernel 的启动参数。单独测时,部分三值形状快了约 5%,一个 4bit 形状快了约 14%。算式和布局没变,解包出的 BF16 元素也逐一比对相同。

但把候选实现接回整模型,结果没有跟着改善。四个新进程按原版、候选、候选、原版的顺序测试,每轮同样的 12 条混合请求。原版每轮请求总耗时平均 158.31 秒,候选是 158.83 秒;48 次请求的 2,552 个输出 token ID 全部一致。

这轮没测到端到端收益,我就没有换掉默认实现。局部提速的数据保留在微基准记录,是否采用则依据整模型对照。至于局部收益为什么没有留下来,这组结果还不足以解释。

想继续看代码,可以从哪里开始

本轮研究已暂停,代码、检查点和实验记录保留。除了上面的质量门槛,后续 HTTP 实验也还有严格输出一致性问题,具体状态见结果说明。

如果你想先理解三值权重怎么存,站内有一个纯 Python 编码小实验,普通电脑就能跑。仓库也公开了实现和测量记录。122B 推理则需要另外取得指定 packed 权重;仓库没有发布模型权重,完整训练与评分复现流程也还没齐。

本文使用 AI 工具辅助整理。实验设置和测试结果见正文。

相关原始记录 ↗讨论这个问题 ↗