Porting the Moebius 0.2B image inpainting model to run in the browser with Claude Code

Simon Willison

使用 Claude Code 将 Moebius 0.2B 图像修复模型移植到浏览器中运行

原文由 Simon Willison 发布,订阅该博客

今天早上在 Hacker News 上我看到了Moebius: 0.2B Lightweight Image Inpainting Framework with 10B-Level Performance,介绍了一个小巧却高效的图像修复模型——你可以标记图像中需要移除的区域,模型会自动想象并填补空白。已发布的模型需要 PyTorch 和 NVIDIA CUDA,但由于它标称只有 0.2B 参数,我决定尝试用 WebGPU 让它在浏览器中运行。简而言之:我成功了,你可以在simonw.github.io/moebius-web/体验 demo。详情请继续往下看。

成品展示

以下是成品工具的视频演示:

你可以在其中打开任意图片(非正方形图片会自动添加黑边),涂抹需要移除的区域,点击 “Run inpaint” 按钮,然后等待模型施展魔法。

并行的 Agent 支线项目

我今天的主线项目是为 Datasette 落地一个重大功能:创建和修改表格的界面,作为上周发布的插入和编辑行功能的后续。

我当时在 Codex Desktop 中进行这项工作(这是这个 PR),经常会遇到 5 到 10 分钟的空档,只能干等它完成一次中等规模的重构或为界面改动做最后的收尾。

(关于编码 Agent 有件有趣的事:问题越难,你在等它们运算完成时反而越有时间去分心!)

于是我决定在终端窗口中启动 Claude Code,看看能把 Moebius 移植到网页上做到什么程度。

用 Agent 做前期调研来启动项目

我的第一步是在 Claude.ai 上向普通的 Claude 询问这个项目的可行性,Claude.ai 具备从 GitHub 克隆仓库的能力:

Clone https://github.com/hustvl/Moebius/ and tell me if they published the code and weights to run this model anywhere

(当时我还没发现权重文件的链接,它藏在“News”栏目里。)

接着:

For Moebius what are the options for running it right now - Python and NVIDIA CUDA only or other options too?

还有:

Muse on the feasibility of porting it to Transformers.js or similar and running it in a browser

我喜欢让模型“muse on X”,这是我发现的表达想让它帮我思考某个问题、又不给出具体目标的最简短方式。

这是那次对话的记录。我把最后一条回答复制出来,保存为research.md,供之后的 Claude Code 阅读。

Claude 建议使用基于 WebGPU 后端的 ONNX Runtime Web——也就是我之前提到的 Transformers.js 库更底层的那一层。

这足以说服我,值得放手让 Claude Code 去试试,看它能做到什么程度。

我通常会这样启动项目:尽可能多地收集编码 Agent 可能需要的各种信息。由于本来也没指望这个项目真的能跑通,所以所有操作都是在我的 /tmp 文件夹里完成的:

cd /tmp
mkdir Moebius
cd Moebius
# Grab the Moebius python code
git clone https://github.com/hustvl/Moebius
# And the model weights (Claude figured this out):
GIT_LFS_SKIP_SMUDGE=0 git clone \
  https://huggingface.co/hustvl/Moebius Moebius-weights
# Finally a couple of libraries we might use:
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
# Copy in that research.md from earlier
git add research.md
git commit -m "Initial research by Claude Opus 4.8"

我在 /tmp/Moebius 文件夹——也就是所有准备好的研究材料的上一级目录——中启动了一个 claude 实例,并输入了提示:

Read ./moebius-web/research.md - your goal is to port this model to ONNX and WebGPU so we can run it directly in a browser, with a simple UI

在它开始工作后,我又追加了这样一条后续指令(保留了原文的拼写错误):

Bulid this in /tmp/Moebius/moebius-web and commit early and often, also maintain a notes.md file in there with notes about what you figure out along the way - also start by writing out a plan.md in there and update that plan as oy work too

我经常会让 Agent 像这样记录笔记——最终的结果往往很有意思,无论是对我自己,还是对之后接手同一项目的下一个 Agent 会话都是如此。这是项目结束时那个notes.md 文件的样子。

我让它跑起来后就回到了自己的主项目,时不时回来看看 Claude 的进展。当看起来它可能已经做出能跑的东西时,我输入了:

Tell me what URL I can visit in my own browser to try this

然后我在 Chrome 里试了一下,并把出现的错误(以及错误截图)粘贴回了 Claude Code。

这样来回几轮之后,我们得到了一个看起来能用的版本!是时候把它发布到网上,让其他人也能使用了。

How would we publish this to Hugging Face such that the model weights were on there and the HTML demo would show up in Hugging Face spaces?

Claude Code 知道如何使用 hf 命令行工具,所以我在 Hugging Face 上创建了一个模型仓库,然后创建了一个可以写入该仓库的 token,并把它放进了 /tmp/Moebius/token.txt 文件中,供 Claude 使用。

它帮我把 1.24GB 转换后的 ONNX 权重发布到了 huggingface.co/simonw/Moebius-ONNX

我之前见过其他 demo 从 Hugging Face 将权重加载到浏览器中,所以我知道这是可行的。我决定把自己的前端代码托管在 GitHub Pages 上,于是我说:

I want to publish the moebius-web folder to GitHub, minus the large files (so maybe minus the models/ folder), such that when I turn on GitHub Pages for that repo navigating to https://simonw.github.io/moebius-web/ serves the UI

告诉它最终的 URL 很重要,这样它在构建 demo 时就能修正其中的链接,确保部署到生产环境后能正常工作。

在主项目之余又经过几轮迭代,我们终于得到了一个可用的、已部署的版本!

不过……每次刷新页面,它似乎都会重新下载约 1.3GB 的模型权重。浏览器缓存对这个项目来说显然非常重要!

anything clever we can do with serviceworkers or similar to help cache this stuff? It seems to reload every time, I am concerned that there might be something weird about the way HF redirects work that mean we don't benefit from browser caching

我知道 Transformers.js 项目可以妥善处理这个问题,所以我抓了一份 Whisper Web demo 的副本,放进 /tmp/Moebius/whisper-web,然后说:

look in /tmp/Moebius/whisper-web (with a subagent) and see how they do this

那个项目全是经过混淆的、构建后的 JavaScript 文件,所以我觉得用一个子 Agent 去分析,可以避免把顶层会话剩下的 token 上下文都耗在解读这些文件上。

Claude 发现它使用的是 caches.open("transformers-cache")——也就是 CacheStorage API——并把它加入到了我们的项目中

我已经分享了这个项目的完整 Claude Code 对话记录(使用我的 claude-code-transcripts 工具发布)。

我从中学到了什么?

这绝对算得上是 vibe coding(氛围编程):我一行代码都没看,只负责测试、提出一些小的功能改进建议(比如为大文件下载添加进度条),以及给模型指出我希望它如何运作的示例方向。

由于我完全没有写代码,关于底层技术——WebGPU、ONNX 以及 Moebius 模型本身——我学到的东西非常有限。

和这类项目通常的情况一样,我学到的最重要的东西是关于什么是可能的

  • Claude Opus 4.8 能够将 PyTorch 模型转换为 ONNX,将结果发布到 Hugging Face,然后构建出一个可以加载并执行该模型的网页应用和界面。
  • Chrome、Firefox 和 Safari 现在都已经能够运行这类模型——我在这三个浏览器中都试过了。
  • CacheStorage API 可以处理约 1.3GB 的模型文件。
  • ……这意味着我们可以把图像修复作为纯客户端网页应用的一个功能!(如果用户能接受 1.3GB 的下载量的话。)

我觉得自己或许应该再多了解一下这个项目。于是我打开 Claude.ai 并输入:

Clone https://github.com/simonw/moebius-web/ and use it to teach me all about the model and ONNX and the process of converting a model to ONNX and WebGPU and basically everything I'd need to know in order to fully understand this repo

这是对话记录以及它创建的 understanding.md Markdown 文件,我现在已经把它添加到了 GitHub 仓库中。我觉得其中对 ONNX 的解释尤其有启发:

ONNX(Open Neural Network Exchange)是一种可移植的、与框架无关的神经网络文件格式。一个 .onnx 文件本质上是两样东西打包在一起:

  1. 计算图——由节点组成的有向图,其中每个节点都是一个算子ConvMatMulAddEinsumSoftmaxGatherResize 等),通过在它们之间流动的命名张量连接在一起。这就是前向传播的“配方”。
  2. 权重——学习得到的参数张量(卷积核、嵌入表等),作为初始化器存储在同一张图中。

关键在于,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 艺术图,展示了整个模型流水线是如何组合在一起的。

本文章由 muse-spark-1.2-contributor 进行翻译

评论