可提示的新範式
前面四篇我們都用舊方法建分割器:先選定一份固定的類別清單(道路、人、車……),蒐集標註好的影像,再訓練網路把每個像素塗成其中一種標籤。這做得很漂亮——直到有人要求一個你從未訓練過的類別,或是一個根本不屬於任何類別的物件。整條流程被你第一天就定下的標籤清單綁死了。最後這一篇,就是要掙脫這個枷鎖。我們要打造單一一個能「依需求」分割的模型:你給它一張影像,再加上一點點「你指的是什麼」的提示,它就回傳你要的那張遮罩。
這個提示就叫做 prompt(提示)。提示可以是在你想要的東西上點一下、在它周圍隨手框一個框、塗一張粗略的遮罩,或者——在最新的系統裡——一段文字像「那隻狗」。模型的任務不再是「把每個像素分到 K 個類別之一」,而是「給定這個提示,回傳使用者想要的遮罩」。這就是把 互動式分割 推到邏輯極致,它徹底改寫了規則:沒有固定的類別清單,分割變成一場對話,而不是一次性的預測。
如果這聽起來很熟悉,那是應該的。回到第 1 篇,我們認識了 GrabCut:你在物件周圍拖一個框,graph-cut 演算法就在框內找出前景。框就是一種提示。GrabCut 是本篇一切的精神祖先——同樣是「大概告訴我在哪,我給你精確的遮罩」——只不過它靠的是手工打造的色彩模型與圖優化,而不是深度網路。現在改變的是引擎:我們保留 GrabCut 可提示的精神,把它手工的機械換成一個看過十億張遮罩的模型。
本篇的計畫如下。首先我們重訪 經典互動式分割——介於 GrabCut 與今日之間、點選與塗鴉式的方法——讓你親身感受它們被什麼所侷限。接著我們打開 Segment Anything 模型(SAM):它的架構、它怎麼訓練、以及「一切」究竟是什麼意思。最後我們落到實務:如何用前幾篇那一模一樣的重疊度量來 評估 這些基礎分割模型,以及如何把 SAM 與其他模型 組合 起來得到帶標籤、有用的結果。讀完之後,你會有一條貫穿的線索,從經典的 影像分割 一路通到開放世界的最前沿。
經典的互動式分割
在深度學習之前,互動式分割其實早就能用,只是很費工。核心想法是 種子(seeds):使用者標出幾個他確定是前景的像素(比方說綠色筆畫),再標幾個確定是背景的(紅色筆畫),演算法就把這些標籤傳播到其他所有像素。GrabCut 用 graph cut 做這件事——它建一張圖,每個像素是一個節點,相鄰像素之間用邊相連,邊的權重在顏色相近時很高,種子則錨定一個最小割(min-cut),把前景從背景切開。多塗一筆、重新割一次,就得到更乾淨的遮罩。
一個近親是 隨機漫步(random walker) 家族。想像你站在一個未標記的像素上,在影像裡隨機漫步,而且你比較容易在顏色相近的像素之間移動。問一句「我最有可能先走到哪一個種子——前景的還是背景的?」就給這個像素一個柔性的機率。深在物件內部的像素幾乎一定先走到前景種子;隔著一條強邊界的像素則較可能先走到背景種子。這些方法優雅、而且完全不需要訓練資料,但它們只懂低層線索——顏色與局部對比。它們根本不知道「狗」是什麼,所以一隻趴在顏色相近沙發上的狗會讓它們嚴重混亂。
深度學習時代的 互動式分割 正好補上了這個盲點。訣竅妙在極其簡單:把使用者的點選變成額外的影像通道。對每一次點選,你畫一張和影像同樣大小的小地圖——一張距離圖,或一個以點選為中心、隨距離衰退的高斯「熱」斑——正面(前景)點選一個通道、負面(背景)點選一個通道。你把這些通道疊到 RGB 影像上,再把整包送進一個分割網路(一個 U-Net 風格的編碼器–解碼器,就跟第 2 篇一樣)。網路現在同時看到圖片和使用者指的位置,而且它已經從資料學會了物件長什麼樣。
魔法在於 使用者在迴圈中(user-in-the-loop) 的這個迴圈。模型預測一張遮罩;你瞄一眼;哪裡漏出去了(抓到一點背景)你就補一個負面點選,哪裡漏掉了(留了個洞)你就補一個正面點選;模型把這些新通道折進去再修一次。每一次修正點選都等於告訴網路「你這裡錯了」,下一張遮罩就更貼近你要的。三、四下點選通常就能得到一個乾淨的物件——遠少於用手一筆筆塗。
Segment Anything 模型(SAM)
Segment Anything 模型(由 Meta AI 於 2023 年發表)最好理解成三個合作的部件,配上一條指導原則:昂貴的工作只做一次,讓提示變便宜。 當你在 SAM 工具裡打開一張影像,它會短暫思考一下,然後就讓你到處點、即時得到遮罩。那份俐落不是運氣——是架構使然。我們一個一個來看這三個部件。
第一部分——重量級的影像編碼器。 這是一個 Vision Transformer(ViT),就是你在 transformer 學程裡認識過的那個架構。它每張影像只跑一次,也是最花成本的步驟。回想一下 ViT 怎麼運作:它把影像切成一格格固定大小的小區塊(patch),把每個區塊攤平成一個向量(一個「token」),再讓這些 token 透過自注意力(self-attention)彼此交談,使每個 token 都被影像其餘部分所影響。輸出是一格密集的特徵向量網格——一份豐富的 影像嵌入(image embedding),編碼了整張圖「什麼在哪裡」。因為這份嵌入只算一次並被快取,之後每一次點選都能免費重用它。
一張照片上疊著規則的格線,每個格子都被標示為獨立的區塊,並成為一個輸入 token。
transformer 之所以是這裡對的引擎,原因在 注意力(attention)——每個區塊 token 都能注意到其他所有 token,所以狗耳朵上的區塊會被狗尾巴上的區塊影響,即使它們相隔很遠。這種全域觸及,正是 SAM 把物件理解成一個連通整體、而非一袋局部紋理的方式;這恰恰是第 3 篇我們用空洞卷積與 ASPP 苦苦對付的長距離脈絡問題。注意力把那些技巧費力近似的東西,免費給了它。
影像上的一張熱圖,顯示一個查詢區塊如何強烈地注意到屬於同一物件的其他區塊。
第二部分——輕量的提示編碼器。 這個小模組把你的提示轉成少數幾個嵌入向量,這些向量活在與影像特徵相同的空間裡。一個點變成一個位置嵌入加上一個微小的、學來的標籤,說明它是「前景」或「背景」;一個框變成兩個角點嵌入;一張粗略的輸入遮罩則被降採樣後直接加到影像嵌入上。不管你給它什麼,提示編碼器都很便宜——和影像編碼器相比,它幾乎不費什麼力。
第三部分——快速的遮罩解碼器。 影像嵌入與提示嵌入就在這裡相遇。一個小型 transformer 解碼器讓提示 token 與影像特徵來回注意幾次,再把結果上採樣成全解析度的遮罩。整個解碼跑在 毫秒 等級,這正是重點所在:一旦重量級的編碼做完,每一次點選就以 提示 → 解碼器 → 遮罩 的路徑快到能在你游標下感覺像即時。這個切分——編碼一次、每個提示各解碼一次——就是把一個龐大的 ViT 變成一個人可以互動把玩之物的關鍵。
# The encode-once, prompt-many pattern that makes SAM feel interactive
predictor.set_image(image) # heavy: runs the ViT encoder ONCE, caches embedding
# Now every prompt is cheap — reuses the cached image embedding:
masks, iou_scores, _ = predictor.predict(
point_coords=[[420, 360]], # one click (x, y)
point_labels=[1], # 1 = foreground, 0 = background
multimask_output=True, # return several candidate masks (see next section)
)
# Add a correction click and predict again — still milliseconds, no re-encode:
masks, iou_scores, _ = predictor.predict(
point_coords=[[420, 360], [500, 410]],
point_labels=[1, 0], # second click marks background to remove a leak
multimask_output=True,
)SAM 如何訓練,以及「一切」是什麼意思
SAM 之所以是 基礎模型(foundation model) 而不只是又一個分割器,關鍵在它所學內容的規模與通用性。要學會「一切」,你得幾乎看過所有東西——而當時沒有任何現成資料集大到接近這個程度。於是 SAM 團隊用一個聰明的 資料引擎(data engine) 自己造了一個:一個讓模型與資料集彼此自我拉抬的飛輪。
- 輔助階段:人類標註者點選物件,一個初版、較弱的 SAM 提出遮罩;人類只需修正錯誤,而不是從零描邊——標註快得多。
- 重新訓練:把這些新的、更高品質的遮罩餵回去重新訓練 SAM,讓它更會提出遮罩。
- 半自動階段:進步後的 SAM 自動標出它有把握的物件;人類只標它漏掉的東西,把力氣花在刀口上。
- 全自動階段:此時已夠強,SAM 被一格密集的點陣提示,自己為所有東西生成遮罩,規模衝到 1,100 萬張影像、超過十億張遮罩——這就是 SA-1B 資料集。
第二項要素是 可提示的預訓練(promptable pretraining)。SAM 的訓練任務不是預測一組固定類別,而是:「給定一張影像和任意提示,預測正確的遮罩。」因為訓練時的提示是多樣且隨機的,要做得好的唯一辦法,就是真正理解各式各樣東西的物件結構——沒有「車長這樣」這種死記的捷徑。這個前置任務(pretext task)逼出通用性,回報則是強大的 零樣本遷移(zero-shot transfer):把 SAM 對準一張它訓練時從沒見過的顯微影像、衛星照、或水下場景,它仍能合理地分割,且不需微調。「一切」的意思是:任何你能用提示指出的物件,在大致任何影像裡。
SAM 正面迎擊一個誠實的難題:歧義(ambiguity)。假設你在一個人的襯衫上點一下。你指的是襯衫?整個人?還是一起站著的那群人?單獨一次點選真的分辨不出來——這三者都是有效的遮罩。SAM 不去賭其中一個,而是 一次預測好幾張遮罩(通常三張,大致對應到 部位/物件/場景 三個層級的詮釋),並為每一張附上一個 信心分數,讓你——或周邊的系統——留下你真正想要的那一張。
那個信心就是 預測 IoU(predicted IoU):在每張遮罩旁,解碼器輸出一個數字,是 SAM 自己對「這張遮罩與真實物件重疊得多好」的估計——也就是對我們從第 2 篇用到現在的那個 IoU 度量的一個猜測。它不是對正確性的完美衡量(畢竟是模型自己批改自己的作業),但它誠實又好用:系統可以自動留下預測 IoU 最高的那張遮罩,或把三張排序讓使用者挑。同樣是重疊的想法,現在向內轉成了模型對自己的評估。
評估與組合基礎分割模型
關於 SAM 最重要的實務事實是:它是 與類別無關的(class-agnostic)。它給你一張漂亮精確的遮罩,卻從不告訴你那東西「是什麼」——沒有標籤、沒有類別。對於有人類在迴圈裡的工具(「我點的,我知道那是狗」),這沒問題。但對於一條需要帶標籤分割的自動流程,光有 SAM 只是答案的一半。另一半來自把 SAM 與一個提供「意義」的模型 組合 起來。
這個組合模式簡單又強大:讓另一個模型產生提示。一個普通的物件偵測器輸出帶類別名的框(「狗」,框在這);把每個框當成框提示餵給 SAM,你就得到帶標籤、像素級精準的遮罩——偵測的「是什麼」嫁給了 SAM 的「精確在哪」。更棒的是,一個 開放詞彙(open-vocabulary) 的文字定位模型,能接收一句話像「那隻狗」並為它找出一個框,即使是任何偵測器都沒訓練過的類別;把它接進 SAM,你就有了文字驅動的分割。這正是 Grounded-SAM 這類系統背後的配方。你也可以反過來跑:拿一張 SAM 遮罩,把它裁出來,交給一個影像分類器去命名。
# Grounded-SAM-style composition: text -> boxes -> SAM masks -> labelled segmentation
boxes, labels = grounding_model.detect(image, text_prompt="dog. frisbee.")
predictor.set_image(image) # one-time SAM encode
labelled_masks = []
for box, label in zip(boxes, labels):
mask, iou, _ = predictor.predict(box=box, multimask_output=False)
labelled_masks.append((label, mask)) # SAM gives 'where', detector gave 'what'
# Now each mask carries a name -> a full semantic/instance segmentation, zero-shot.我們怎麼知道這些好不好?用的是貫穿整個學程、一模一樣的重疊度量。零樣本與否、最前沿與否,一張預測遮罩仍然是依它與真實標註重疊得多好來評分。交集除以聯集(IoU)量的是重疊比例;第 2 篇的 Dice 係數 量的本質上是同一種一致性,但偏重交集;而當任務是「實例加背景物質」時,第 4 篇的 全景品質(PQ)把辨識與遮罩準確度合成一個數字。到了最前沿,度量沒有變——變的只是模型。
一條精確率–召回率曲線,說明偽陽性與偽陰性之間的取捨。
領域現況與下一步該往哪走
退一步看整段旅程,因為它說的是一個彼此相連的故事。我們從 經典 的 影像分割 開始:閾值法依強度切分像素,分水嶺法把影像淹沒以找邊界,超像素把相似的鄰居分群,而 GrabCut 讓一個框提示驅動一次 graph cut。這些都很聰明,卻對「意義」一無所知——它們懂顏色與邊緣,不懂物件。
接著深度學習登場。FCN、編碼器–解碼器、與 U-Net(第 2 篇)教網路為每個像素預測一個密集標籤,並透過結合粗略語意與精細細節來恢復銳利的邊界。DeepLab 的空洞卷積、ASPP、與 PSPNet(第 3 篇)解決了脈絡問題——同時以多種尺度看一個物件而不失解析度——CRF 則把邊緣修乾淨。Mask R-CNN 與全景分割(第 4 篇)加上了分辨個別物件實例的能力,再用 全景分割 把「things(可數物件)」與「stuff(背景物質)」統一成整個場景一致的標註。
最後是本篇:SAM 與可提示的基礎模型 打破了最後一個束縛——固定的類別清單。透過把重量級編碼只做一次、每個提示便宜地解碼,透過用資料引擎飛輪在十億張遮罩上訓練,並透過以多張遮罩與預測 IoU 信心擁抱歧義,SAM 把 互動式分割 變成了開放世界、依需求的分割。這條弧線從手工打造的色彩模型,一路走到一個幾乎能分割任何你指得出之物的模型。
前沿接下來往哪走?有四個方向大開。影片與時間一致的分割:跨影格追蹤「同一張」遮罩,使它不閃爍、不互換身分——SAM 2 已經把可提示的想法延伸到影片。3D 與點雲分割:從光達掃描與深度資料中為機器人與 AR 雕出意義,這時「像素」變成空間中的點。即時與裝置端的效率:把重量級 ViT 編碼器蒸餾,讓可提示分割能跑在手機或機器人上,而不是伺服器。以及 開放詞彙、文字驅動的遮罩:把迴圈閉合,讓你只要「說」要分割什麼就得到帶標籤的遮罩,中間不需要偵測器。
你已經走完 影像分割 的整條路——從依強度為像素上色,到提示一個模型去分割任何影像中的任何東西。前沿不是終點線;它是發射台。挑一個讓你興奮的方向——影片、3D、效率、或語言驅動的遮罩——抓一個預訓練好的基礎模型,用你現在已掌握的度量誠實地衡量,然後去分割一個還沒人分割過的東西吧。