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 讓它在瀏覽器裡跑起來。TL;DR:我成功了,你可以在 simonw.github.io/moebius-web/ 試玩 demo。詳細過程請繼續往下看。

完成品

以下是完成品的影片示範:

你可以在裡面開啟任何圖片(非正方形的圖片會加上黑邊),塗選要移除的區域,點擊「Run inpaint」按鈕,然後等待模型施展魔法。

同步進行的 Agent 支線專案

我今天的主要專案是在 Datasette 落地一項重大功能:用於建立與修改資料表的 UI,作為我上週釋出的新增與編輯資料列功能的後續。

當時我是用 Codex Desktop 在做這個(這是該 PR),常常得花上 5 到 10 分鐘轉著手指發呆,等它完成一次中等規模的重構,或為 UI 的改動加上最後的修飾。

(寫程式的 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 建議使用 ONNX Runtime Web 搭配 WebGPU 後端——也就是在我原本提議的 Transformers.js 函式庫更底層的那一層。

這就足以說服我,值得放手讓 Claude Code 去試試看能做到什麼程度。

我通常會這樣啟動專案:盡可能先蒐集 coding 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 CLI 工具,所以我在 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 檔案,所以我想用 subagent 來處理,就可以避免把頂層 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. 運算圖(computation graph)——由節點組成的有向圖,每個節點都是一個運算子(operator)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 進行翻譯

留言