用Claude Code将Moebius 0.2B图像修复模型移植到浏览器运行
Porting the Moebius 0.2B image inpainting model to run in the browser with Claude Code
Simonw 成功将 Moebius(0.2B 参数的轻量级图像修复模型)通过 ONNX Runtime Web 和 WebGPU 移植到浏览器中运行。该模型由 hustvl 发布,支持用户标记图像中要移除的区域并自动填充内容。Simonw 使用 Claude Code(基于 Claude Opus 4.8)完成从 PyTorch 模型到 ONNX 格式的转换、权重发布至 Hugging Face(1.24GB),并构建了基于 GitHub Pages 的前端 demo(simonw.github.io/moebius-web/)。项目利用 CacheStorage API 缓存模型文件,支持 Chrome、Firefox 和 Safari 浏览器。
今天早上在 Hacker News 上,我看到了 Moebius: 0.2B Lightweight Image Inpainting Framework with 10B-Level Performance,它描述了一个小巧但高效的 inpainting(图像修复)模型——你可以标记图像中要移除的区域,模型会想象出应该填充该空间的内容。发布的模型需要 PyTorch 和 NVIDIA CUDA,但由于它自称是 0.2B 参数,我决定尝试让它通过 WebGPU 在浏览器中运行。长话短说:我成功了,你可以在 simonw.github.io/moebius-web/ 尝试这个 demo。继续阅读以了解详情。
完成的工具
以下是该工具的演示视频:
你可以打开任意图像(非正方形图像会被加黑边),高亮要移除的区域,点击 "Run inpaint" 按钮,然后等待模型施展它的魔法。
一个并行的 agent 副项目
我今天的首要任务是在 Datasette 中落地一个主要功能:创建和修改表的 UI,作为我上周发布的插入和编辑行功能的后续。我正通过 Codex Desktop 处理这个功能(这里是 PR),经常发现自己要花 5-10 分钟干等着它完成一个中等规模的代码重构或为 UI 改动做最后的润色。(关于编码 agent 的一个有趣之处在于,问题越难,你在等待它们完成繁重工作时就越容易分心!)
所以我决定在终端窗口中启动 Claude Code,看看我能在将 Moebius 移植到 Web 上走多远。
一些 agent 式的研究来启动项目
我的第一步是向普通的 Claude 询问这个项目的可行性。在 Claude.ai 中,它能够从 GitHub 克隆仓库:
克隆 https://github.com/hustvl/Moebius/ 并告诉我他们是否发布了运行这个模型的代码和权重(我还没找到权重的链接,它藏在 "News" 部分)。
然后:
对于 Moebius,现在有哪些运行选项——只有 Python 和 NVIDIA CUDA,还是有其他选项?
接着:
思考一下将其移植到 Transformers.js 或类似工具并在浏览器中运行的可行性
我喜欢让模型“思考 X”,这是我发现的最简洁的方式,来表达我希望它们为我思考一个问题,而不给它们一个具体目标。这是那个聊天的记录。我复制了最后一个答案,保存为 research.md,供 Claude Code 稍后阅读。
Claude 建议在 WebGPU 后端上使用 ONNX Runtime Web——这是我建议的 Transformers.js 库之下的那一层。这足以让我相信值得让 Claude Code 放手一搏,看看它能走多远。
收集资料
我通常这样开始项目:尽可能多地收集编码 agent 可能需要的资料。由于我没想到这个项目真的能成功,我在 /tmp 文件夹中完成了所有操作:
cd /tmp
mkdir Moebius
cd Moebius
# 获取 Moebius Python 代码
git clone https://github.com/hustvl/Moebius
# 以及模型权重(Claude 搞清楚了这一点):
GIT_LFS_SKIP_SMUDGE=0 git clone \
https://huggingface.co/hustvl/Moebius Moebius-weights
# 最后是一些我们可能用到的库:
git clone https://github.com/huggingface/transformers.js
git clone https://github.com/microsoft/onnxruntime
启动 Claude Code
我为项目的其余部分创建了一个目录,并在其中运行了 git init,这样 Claude 就可以开始提交代码笔记:
mkdir /tmp/Moebius/moebius-web
cd /tmp/Moebius/moebius-web
git init
# 复制之前的 research.md
git add research.md
git commit -m "Initial research by Claude Opus 4.8"
我在 /tmp/Moebius 文件夹中启动了一个 claude 实例,这是我为它准备的所有研究材料的上层目录。我提示:
阅读 ./moebius-web/research.md - 你的目标是将这个模型移植到 ONNX 和 WebGPU,这样我们就可以直接在浏览器中运行它,并带有一个简单的 UI
当它开始工作时,我补充了以下内容(包含拼写错误):
在 /tmp/Moebius/moebius-web 中构建这个项目,并频繁提交,同时在那里维护一个 notes.md 文件,记录你沿途发现的内容——另外,先在那里写一个 plan.md,并在你工作时更新那个计划
我经常要求 agent 保留这样的笔记——最终结果通常很有趣,无论对我自己还是对下一个接触同一项目的 agent 会话都是如此。这是项目结束时 notes.md 文件的样子。
我启动了它,然后回到我的主要项目,偶尔检查一下 Claude 的进展。当它看起来可能有一些能用的东西时,我提示:
告诉我可以在自己的浏览器中访问哪个 URL 来尝试这个
然后我在 Chrome 中尝试了它,并将一些错误(以及错误截图)粘贴回 Claude Code。经过几轮这样的迭代,我们得到了一个似乎能用的东西!
是时候把它放到网上了
这样其他人就可以使用它。我们如何将其发布到 Hugging Face,使得模型权重在那里,并且 HTML demo 会显示在 Hugging Face Spaces 中?Claude Code 知道如何使用 hf CLI 工具,所以我在 Hugging Face 上创建了一个模型仓库,然后创建了一个可以写入该仓库的 token,并将其放入 /tmp/Moebius/token.txt 文件中,以便 Claude 可以使用它。它为我将 1.24GB 的转换后 ONNX 权重发布到了 huggingface.co/simonw/Moebius-ONNX。
我之前见过其他 demo 从 Hugging Face 将权重加载到浏览器中,所以我知道这是可行的。我决定在 GitHub Pages 上托管我自己的前端代码,所以我说:
我想将 moebius-web 文件夹发布到 GitHub,不包括大文件(所以可能不包括 models/ 文件夹),这样当我为那个仓库启用 GitHub Pages 时,导航到 https://simonw.github.io/moebius-web/ 就能提供 UI
告诉它最终的 URL 很重要,以防它需要修复它正在构建的 demo 中的 URL,以便它们能在生产环境中正常工作。
迭代与部署
经过几轮迭代,在我处理主要项目的间隙,我们得到了一个可以工作、已部署的版本!
除了……每次我重新加载页面时,它似乎都要下载约 1.3GB 的模型权重。浏览器缓存对此似乎非常重要!
有什么巧妙的方法可以用 serviceworker 或类似的东西来帮助缓存这些东西吗?它似乎每次都会重新加载,我担心 Hugging Face 的重定向方式可能有些奇怪,导致我们无法从浏览器缓存中受益
我知道 Transformers.js 项目可以正确处理这个问题,所以我复制了一份 Whisper Web demo,将其放入 /tmp/Moebius/whisper-web 中,并说:
查看 /tmp/Moebius/whisper-web(使用子 agent),看看他们是怎么做的
那个项目完全是混淆过的、构建好的 JavaScript 文件,所以我认为使用子 agent 可以避免消耗我剩余的顶级 token 上下文来解读那些文件。Claude 发现它使用了 caches.open("transformers-cache")——CacheStorage API——并将其添加到了我们的项目中。
我已经分享了该项目的完整 Claude Code 记录(使用我的 claude-code-transcripts 工具发布)。
我从这一切中学到了什么?
这绝对算得上是“氛围编码”:我没有看项目中的一行代码,我的输入仅限于测试、建议小的功能改进(比如大文件下载的进度条),以及向模型指出我希望事情如何运作的示例方向。
由于我没有写任何代码,我对底层技术——WebGPU、ONNX 和 Moebius 模型本身——的了解非常有限。正如这类项目通常的情况一样,我学到的最重要的东西是关于什么是可能的:
- Claude Opus 4.8 能够将 PyTorch 模型转换为 ONNX,将结果发布到 Hugging Face,然后构建一个可以加载并执行该模型的 Web 应用程序和界面。
- Chrome、Firefox 和 Safari 现在都能够运行这类模型——我在三者中都试过了。
- CacheStorage API 可以处理约 1.3GB 的模型文件。
- ……这意味着我们可以将 inpainting 作为纯客户端 Web 应用的一个功能!(如果我们的用户能忍受 1.3GB 的下载量的话。)
我觉得我应该试着多了解一点我的项目。我启动了 Claude.ai 并提示:
克隆 https://github.com/simonw/moebius-web/ 并用它来教我关于模型、ONNX、将模型转换为 ONNX 和 WebGPU 的过程,以及基本上我需要知道的一切,才能完全理解这个仓库
这是记录和它创建的 understanding.md Markdown 文件,我现在已经将其添加到 GitHub 仓库中。我发现对 ONNX 的解释特别有启发性:
ONNX(Open Neural Network Exchange,开放神经网络交换格式)是一种可移植、框架无关的神经网络文件格式。一个 .onnx 文件本质上是两样东西捆绑在一起:
- 计算图——一个有向图,由节点组成,每个节点是一个算子(
Conv、MatMul、Add、Einsum、Softmax、Gather、Resize……),通过它们之间流动的命名张量连接起来。这是前向传播的“配方”。- 权重——学习到的参数张量(卷积核、嵌入表等),作为初始化器存储在同一张图中。
关键的是,ONNX 抽象地描述了要计算什么,而没有说明如何或在什么硬件上计算。算子集由 opset 编号(此仓库使用 opset 18)进行版本控制,它精确定义了哪些算子存在以及它们的语义是什么。
事实证明 PyTorch 有内置的导出到 ONNX 的机制,如 export_onnx.py 中所示:
torch.onnx.export(
dec, (lat,), dec_path,
opset_version=args.opset,
input_names=["latent"],
output_names=["image"],
dynamic_axes={"latent": {0: "B"}, "image": {0: "B"}},
)
Claude 还包含了一个方便的词汇表和一个略有瑕疵的 ASCII 艺术图,展示了模型管道是如何组合在一起的。
标签:browsers, transformers-js, webgl, vibe-coding, coding-agents, claude-code, onnx