520KB 内存跑世界模型:微控制器上的生成实验
tinyworldformer 在 Raspberry Pi Pico 2W 上运行游戏画面生成与动作决策网络,无需 GPU 或云端连接。项目通过 Int8 量化、Transformer 块循环复用及双核分工压缩资源占用,并提供终端试玩入口。
AI 深度解读
6 美元、520 KB RAM,能跑一个逐帧生成游戏画面的模型吗?tinyworldformer 给出了一个小规模实例:它在 Raspberry Pi Pico 2W 上,用一个模型预测下一帧,再用另一个模型决定按哪个键,全程不依赖 GPU、云端或联网。
看点不是画面有多漂亮,而是作者如何把生成模型压进微控制器:重复使用同一组权重、把生成器量化成 int8,并分别安排推理和设备交互。不过,「能运行」距离「运行得多好」还有几项指标需要补齐。
生成器负责造世界,另一个网络负责玩
tinyworldformer 的生成器接收当前画面和玩家动作,预测下一帧及奖励;动作网络则读取画面,决定下一步操作。两者接起来,就形成一个循环:动作推动画面变化,新画面再推动下一次决策。
这里的「世界模型」,可以理解为一个学过游戏变化规律的预测器。它从真实游戏的录像中学习,但在生成会话开始后,不会每帧调用真实游戏引擎校正。动作网络看到的是预测画面,生成器收到的是动作。
这也提出了一个比单帧好看更难的问题:模型连续预测后,球、障碍物和敌人还能不能遵守规则?
作者为此准备了五个用 numpy 编写的真实游戏,作为逐帧比较的参照。原文列出的数据如下,MACs 表示乘加运算次数。
| 游戏 | 画面尺寸 | 生成器参数 | 每帧 MACs | 完全准确帧比例 |
|---|---|---|---|---|
| Breakout | 20×16 | 26k | 4.3M | 99.99% |
| Doom-lite | 32×32 | 55k | 4.2M | 83.6% |
| Flappy Bird | 24×24 | 46k | 2.0M | 100% |
| Pong | 20×32 | 46k | 2.3M | 99.45% |
| Pac-Man lite | 48×48 | 131k | 8.2M | 86.4% |
这个准确率的判定很严格:一帧内每个格子都对上,才算该帧正确。一个阴影像素出错,也会让整帧计为失败。
因此,83.6% 不能直接解释成「游戏有 16.4% 的时间不能玩」。反过来,它也不能证明游戏状态始终正确。球的位置、碰撞和奖励是否准确,需要另外检查。
520 KB 靠三种取舍省下来
首先,画面不会按每个格子直接展开成长序列。Breakout 使用 2×2 的分块,Doom-lite、Flappy Bird 和 Pong 使用 4×4,Pac-Man lite 使用 6×6,得到每款游戏 36 到 80 个 token。
每个 token 组合了格子内容、格子在分块内的位置、分块位置和动作信息。分块内不同位置使用不同的嵌入表,是为了保留排列关系,避免只知道出现过什么,却不知道它们在哪里。
其次,生成器使用循环 Transformer。普通的多层结构需要保存多组权重,例如八个不同的块;这里保存一个包含注意力和 ReLU 前馈网络的块,连续执行 1 到 4 次,每次执行前加入原始输入。
共享权重省的是参数存储,重复计算仍然要付出时间。
训练时,每一轮输出都与真实下一帧比较,让中间结果也能用于预测。部署时便可以调整轮数:原文称一轮可能留下明显伪影,四轮能修正大部分错误,Pico 2W 选择两轮。它没有给出各轮的具体帧率和准确率对照。
第三步是量化。生成器使用 int8,单个权重从 float32 的 4 字节降到 1 字节。下面的原文示例展示了缩放、取整和重建过程,并非完整部署实现:
import numpy as np
pesos = np.array([0.42, -0.87, 1.35, -1.90], dtype=np.float32)
escala = np.max(np.abs(pesos)) / 127
cuantizados = np.round(pesos / escala).astype(np.int8)
reconstruidos = cuantizados.astype(np.float32) * escala
print(cuantizados)
print(np.round(reconstruidos, 4))
关键是 escala 那一行,它决定浮点值如何映射到整数。输出整数为 [28, -58, 90, -127],重建结果为 [0.4189, -0.8678, 1.3465, -1.9],能直接看到舍入误差。
动作网络只有 9,000 到 16,000 个参数,保留 float32。作者认为它已经足够小,继续压缩的收益有限,而动作误差会影响决策。不过,原文没有提供动作网络量化前后的对照结果。
RP2350 的双核也各有分工:核心 1 执行两个网络,核心 0 处理 USB 和屏幕。这不是两个模型各占一个核心,而是把推理与设备交互分开。
本地跑起来的,首先是真实游戏
原文提供的入门命令,可以在 Linux 或 macOS 安装 uv、克隆仓库并启动游戏:
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/SheepSeb/tinyworldformer
cd tinyworldformer
uv run python -m tinyworldformer.games.play --game pacman
要看清最后一行的 games.play:它打开的是 numpy 编写的真实游戏窗口,不是量化模型生成的世界。--game 还接受 breakout、doomlite、flappy 和 pong。uv 会根据 pyproject.toml 获取依赖。
Windows 的安装步骤改为 PowerShell 命令 powershell -c "irm https://astral.sh/uv/install.ps1 | iex",之后沿用相同的克隆和运行步骤。
先玩真实版本有实际价值:你需要先知道模型在模仿什么,才能判断它生成的球路、碰撞或敌人移动是否合理。对应实现位于 src/tinyworldformer/games/。
但原文没有给出下载训练权重、启动模型推理、编译 Rust 固件和烧录设备的完整流程,不能把这几行命令当成端侧部署教程。训练使用的具体硬件也没有交代;离线推理不等于在微控制器上训练生成器。
仓库还包含一个流式强化学习代理,能够在模型生成的世界中学习,而不只是执行固定策略。设备上的任意按钮可以把控制权交回用户。不过,原文没有提供它的学习速度、稳定性或与真实环境训练的比较数据。
我的判断
我认可这个项目的实验方法:先限定一个小世界,保留可执行的真实规则,再压缩预测模型,最后逐帧核对。与只展示一段生成视频相比,它提供了更清楚的验证入口。
它证明了特定小型游戏的生成推理可以进入微控制器,尚不足以证明通用游戏生成已经可用。
几个限制需要一起看。
第一,原文称其能「实时」运行,却没有提供 FPS、单帧延迟、峰值 RAM、固件体积和实测功耗。520 KB 是设备 RAM 容量,不能据此判断模型运行后还剩多少空间。注意力中间结果、屏幕缓冲和程序运行也要占内存。
第二,准确率的统计条件没有说清,包括测试样本量、连续生成长度,以及表中结果是否对应设备上的两轮 int8 推理。Doom-lite 的说明还出现了「768 个格子」,与表中的 32×32 不一致,原文未解释原因。
第三,不能把 Pac-Man lite 的较低准确率直接归因于量化。缺少同一模型量化前后的对照,游戏复杂度、模型容量和量化误差各自贡献多少,无法从跨游戏数据中拆出来。
原文关于 FPU 的表述也应谨慎:FPU 支持浮点计算,可能帮助动作网络及浮点运算部分;它不能单独证明 int8 推理效率,更不能证明每次注意力运算前都必须完整反量化。具体执行路径需要看实现。
与 GPU 或云端生成相比,这套方案的优势是离线和较低的硬件门槛,代价是任务范围很窄。与传统 TinyML 分类任务相比,它多验证了连续画面生成的可能性。至于「低几个数量级的能耗」,原文没有测量数据,应视为定性判断。
能拿来做什么
- 想理解世界模型,可以先运行
uv run python -m tinyworldformer.games.play --game breakout,再阅读对应 numpy 实现,梳理状态、动作、下一帧和奖励之间的关系。 - 想复现压缩效果,应固定同一款游戏,分别比较 1、2、4 轮和量化前后的结果,同时记录延迟、内存与游戏状态错误,避免只看画面是否顺眼。
- 做嵌入式控制实验,可以参考它把生成器与动作网络分开的方式,探索用小模型模拟传感器环境;但这仍是原文提出的迁移方向,没有现成效果保证。
- 做前端或 Node 工具时,更适合从可视化验证入手:展示真实帧与预测帧的差异、动作和奖励记录。原文没有提供浏览器或 Node 推理接口,需要另做适配。