Qwenputer:在 Qwen3.5 权重中编译出可运行的 RISC-V CPU
Qwenputer: A functioning, deterministic RISC-V CPU inside Qwen3.5
作者 Divyansh Agrawal 发布 Qwenputer,在 Qwen3.5 4B 的权重中编译进一个 32 位 RISC-V CPU(RV32IM 加 Zba 扩展),用模型的 Gated DeltaNet 层状态充当 RAM,无需外部运行时或工具调用即可在前向传播中执行算术、C 和 MicroPython 代码。
编译权重
一个原版 Qwen 3.5 4B,其权重中编译进了一颗 RISC-V CPU,并运行在它自己的 Gated DeltaNet 层上作为 RAM。
难以置信这不是硅基
Qwenputer 是一个相当有趣的实验性模型。它是一个原版 Qwen 3.5 4B,只增加了一样东西:一颗嵌入在权重中的 RISC-V CPU,它把模型自身的 Gated DeltaNet 注意力层当作 RAM 使用。(想了解 GDN 的工作原理,这里有一份便捷指南。)这个“CPU”完全在模型的前向传播中运行,可以执行算术运算、C 和 MicroPython 代码,完全不需要任何外部运行时或工具调用。
需要明确的是,它不是一个插件、工具或沙箱。这篇博客将详细解释这个模型如何工作、模型如何利用 GDN 层作为 RAM,以及我如何还嵌入了一个实验性的模型内文件存储。
我还基于 Qwenputer 写了两篇后续文章:关于混合模型如何学会将推理隐藏在 RNN 状态中并规避 CoT 监控,以及像这样的编译权重如何作为模型自身无法篡改的内置监控工具。

你可以从 Hugging Face 或 ModelScope 获取 Qwenputer,代码在 GitHub 上。
Qwen3.5 内部的 RNN 可以充当精确的 RAM
Qwen3-Next 和 Qwen 3.5(Qwenputer 所基于的模型)使用了一个相当巧妙的技巧。模型中 4 个注意力层里有 3 个使用 Gated DeltaNet——一种线性 RNN——作为注意力机制(更多内容见此)。每个头维护一个状态矩阵,delta 规则负责更新它。
现在,我们的有趣实验就从这里开始。我发现,通过将门控设为 1,11 每个 Gated DeltaNet 头维护一个状态矩阵,即它的记忆。在每个 token 处,在写入任何新内容之前,它会将整个状态乘以门控——一个 0 到 1 之间的数。这就是 RNN 遗忘的方式:门控为 0.9 时,每个 token 都会让存储的所有内容略微衰减;门控为 1 则意味着任何内容都不会衰减。将写入强度(beta)设为 1,22 在门控之后,头会将状态的一个方向推向一个新值,写入强度 beta 决定推多远。beta 为 1 意味着该方向上的旧值被完全替换,而不是混合。并使用 one-hot 键编码,33 one-hot 键是一个只有一个 1 的零向量,因此它恰好指向状态的一行,就像内存地址指向一个单元一样。我们执行的每次写入都将恰好覆盖一行,而每个 one-hot 查询都能检索到精确的值。这意味着 GDN 层本身可以完全像 RAM 一样工作,且成本远低于使用普通注意力作为 RAM,因为无论对话多长,GDN 头的状态大小保持不变,而注意力的 KV 缓存则不然。

delta 规则充当记忆这一想法并不新鲜:Schlag 等人(2021)已经证明,带有 delta 规则的线性注意力可以作为一种联想记忆,也就是“快速权重”。44 Imanol Schlag、Kazuki Irie 和 Jürgen Schmidhuber,"Linear Transformers Are Secretly Fast Weight Programmers",ICML 2021。 这里的新意在于把它做得精确且可寻址,放进一个生产级模型里,并且足够可靠,能在它上面运行一整个 CPU。
这就是整个模型所建立的基础。就目前而言,默认的 Qwen 3.5 并不会在 GDN 中保留精确值。通过这一修改,我们解锁了这个实验的下一部分。
把一台计算机编译进层里
既然我们现在有了一个功能完整、廉价又快速的 RAM,就可以着手把一台能工作的 CPU 编译进模型权重里。幸运的是,这不需要训练:编译器直接设置每一个权重。你把 CPU 描述成一个电路,也就是一长串小步骤,比如“读取这个寄存器”“把这两个数相加”或“若为零则跳转”,编译器会把每一步变成 transformer 层的一部分:负责查表的注意力头、负责算术的前馈单元,以及持有 RAM 的 DeltaNet 头。随后,当模型解码时,CPU 就在运行。
每条指令会变成几个生成的 token:一个指明要写入的内存行(ADDR),下一个携带值(VAL),还有一个 STEP token 结束该指令。这些 token 就是 CPU 的执行轨迹,所以当 Qwen 生成它们时,CPU 实际上正在运行。
CPU
这个 CPU 是一个 32 位 RISC-V 核心(RV32IM,外加 Zba 扩展和一个用于大数运算的小型 bignum 单元),它占据 Qwen 32 层前面的 11 层。就目前而言,它通过了官方的 riscv-tests,其程序与 qemu 一致,并且已经自由运行了 1800 万条指令而无一错误。55 自由运行意味着 CPU 在整个运行过程中依据自己的输出持续运行,步骤之间没有参考机器来纠正它。
这个 CPU 在前向传播中也能相当好地运行 C 和 MicroPython 代码(虽然有点慢)。它还不是 Linux 级别的芯片,但它实现了完整的 RV32IM 指令集。
Qwen 如何使用它
Qwen 3.5 4B 能够相当好地使用这个 CPU,无需任何后训练。CPU 完全在内部使用,不通过任何外部工具调用来调用。
为了让模型可靠地使用它,我决定走省事的路线:借助模型已经训练得很好的工具调用格式,Qwen 向 CPU 发送指令并读回结果。不使用任何外部工具调用或运行时。 一旦我们的 CPU 收到指令,计算机层就会运行,然后写入工具响应。我们使用一个名为 RvChat 的小型驱动把固件加载到 GDN 状态中,这使轨迹 token 不会进入 Qwen 的层。
Qwen 的原始权重保持原样。计算机权重被添加在其上,位于额外的维度和层中。
它有效吗?
我想,在读完我这一通没完没了的唠叨之后,你心中最主要的问题会是:这真的有效吗?

在 DeepMind 数据集上:
- div_big:Qwenputer 得分 97.5%,而给 Qwen 3.5 4B 配备 Python 工具为 95%,基础 Qwen 为 72.5%。
- mul_big:Qwenputer 得分 97.5%,而使用 Python 工具时为 100%,基础版 Qwen 为 55%。
- mul_div_multiple_longer:Qwenputer 得分 100%,而使用 Python 工具时为 97.5%,基础版 Qwen 为 90%。
- add_or_sub_big:Qwenputer 得分 97.5%,与使用 Python 工具时相同,基础版 Qwen 为 87.5%。
在 NUPA 的长整数运算上,Qwenputer 从 48.6% 提升到 100%,与给 Qwen 配备 Python 工具时相同。
CPU 打印出的数字也是精确的:我将它约 230,000 个输出位置与参考机器进行了比对,没有发现任何不匹配。而且给 Qwen 加装计算机并不会改变它的说话方式:它的普通文本与基础版 Qwen 的差异在噪声范围内(KL 6e-4,噪声底为 5e-4)。
硬盘?就这经济形势?
既然在语言模型里塞一个 CPU 显然还不够离谱,我决定明智的做法是再给它一个文件系统。
既然我们已经有可用的 RAM,这基本上是小菜一碟。在模型侧,它通过内部的 put / append / get / list / delete 操作暴露出来。再次强调,没有实际的工具调用。当 Qwen“保存”文件时,编译后的层会将其名称哈希到 DeltaNet 状态中维护的目录的 1,024 个槽位之一,并将文本本身存储在其他头中,每行八个 token,串联在一起。如果两个名称最终落在同一个槽位,它就会检查下一个。再次找到文件大约需要两三步,而不是扫描,它最多可容纳 512 个文件,约合 53,000 个 token 的文本。
有趣的是,这相当持久。你可以在对话结束时保存状态,将其加载到下一个对话中,文件仍然在那里。使用这个文件系统,Qwen 在对话之间发生变化的事实上正确率达到 90%,而粘贴旧对话时为 30%,并且与给 Qwen 配备由 Python 运行的内存工具相当(83.3% 对 78.9%)。这个功能仍处于实验阶段,其代码位于 rv-store 分支,模型位于 Hugging Face 和 ModelScope。
局限与下一步
Qwenputer 很大程度上是一个实验,因此存在一些局限。额外的层和更宽的隐藏维度使检查点达到 18.8 GB,大约是基础版 Qwen 的两倍,尽管其中大部分是零填充。它还需要一个小型驱动程序(RvChat)来启动固件并将跟踪 token 排除在 Qwen 的层之外,因此它还不能通过普通的 generate() 调用运行。而且它有点慢,每条 CPU 指令要花费几个解码 token,因此大型计算需要几秒而不是几毫秒。我用于基准测试的批处理执行器在 L4 上每秒运行约 3,200 个 CPU token。
接下来,我希望 Qwen 编写 Python 并在其自身权重内的 MicroPython 上运行,通过一个与 calc 工作方式相同的 python() 工具。让 CPU 更快是另一个大目标。
超越算术
对 RNN 层的精确控制对算术之外的许多用途都很有用。它也使 Qwenputer 成为 AI 安全研究的绝佳试验台,我写了两篇后续文章:像 Qwen 3.5 这样的混合模型如何学会将推理隐藏在 RNN 状态中并逃避 CoT 监控,以及编译权重如何作为可监控性工具,比如模型无法关闭的权重内监控器,或用于可解释性的答案密钥。
试一试
如果你想自己动手玩一玩,这个模型可以在单张 24 GB 显卡上运行。你需要 uv 和 Hugging Face CLI。你不需要工具服务器或 Python 沙箱来做计算:CPU 会在模型内部完成这些计算。
git clone https://github.com/divagr18/qwenputer && cd qwenputer/experimental/riscv && uv sync --extra hf
hf download divagr1925/Qwen3.5-4B-Qwenputer --local-dir qwenputer-rv
uv run python -m chipformer.rv.chat qwenputer-rv --firmware tests/data/rv/calc-bridge.elf \
--ask "Use calc: what is 123456789 * 987654321?"一切都是开放的:GitHub 上的代码,以及 Hugging Face(Qwenputer、Filesystem)和 ModelScope(Qwenputer、Filesystem)上的两个模型。
如果你在工作中使用它,可以这样引用:
@software{agrawal2026qwenputer,
author = {Agrawal, Divyansh},
title = {{Qwenputer}: a computer compiled into the weights of a pretrained language model},
year = {2026},
url = {https://github.com/divagr18/qwenputer}
}注释
- 每个 Gated DeltaNet 头都维护一个状态矩阵,也就是它的记忆。在每个 token 上,在写入任何新内容之前,它会把整个状态乘以门控值,一个介于 0 和 1 之间的数。RNN 就是这样遗忘的:门控值为 0.9 时,每处理一个 token,已存储的内容都会略微衰减;门控值为 1 则意味着永不衰减。↩
- 经过门控之后,该头会把状态的某一个方向朝新值微调,写入强度 beta 决定调整幅度。beta 为 1 意味着该方向上的旧值被完全替换,而不是混合。↩
- one-hot 键是一个只有一个 1、其余全为 0 的向量,因此它恰好指向状态中的某一行,就像内存地址指向某一个存储单元一样。↩
- Imanol Schlag、Kazuki Irie 和 Jürgen Schmidhuber,"Linear Transformers Are Secretly Fast Weight Programmers",ICML 2021。↩
- 自由运行意味着 CPU 在整个运行过程中持续基于自己的输出继续推进,步骤之间没有参考机器来纠正它。↩
来源:Hacker News · divagr.com