系識別:從圖像分割到場景理解的完整落地指南)
做計(jì)算機(jī)視覺落地的人大概都有這種感覺分割模型把畫面分得越細(xì)越暴露一個(gè)尷尬——模型“看”到了但沒“想”明白。SAM這類分割模型確實(shí)能把物體輪廓處理得非常漂亮但它始終不會回答“這個(gè)杯子和這張桌子是什么關(guān)系”“那個(gè)人的手和杯子有沒有接觸”“鍵盤是不是被貓踩著”這類問題。RelateAnything這個(gè)工作的定位非常直接在SAM完成分割之后再加一層對象關(guān)系識別把零散的mask升級成結(jié)構(gòu)化的場景理解——輸出類似“人-抱著-貓”“杯子-在-桌子上”這樣的關(guān)系三元組。這篇文章我會從設(shè)計(jì)思路、核心實(shí)現(xiàn)、復(fù)現(xiàn)流程到踩坑調(diào)優(yōu)完整講一遍適合正在做場景理解、關(guān)系檢測、機(jī)器人操作感知以及想把SAM落地成“會看圖說話”產(chǎn)品的同學(xué)參考。整條鏈路拆開看并不復(fù)雜但每個(gè)環(huán)節(jié)都有不少工程細(xì)節(jié)值得認(rèn)真處理。1. 項(xiàng)目定位為什么分割之后還要識別關(guān)系1.1 SAM的上限和邊界在哪先對齊一下SAM到底做了什么事。給定一張圖SAM可以輸出高質(zhì)量的分割掩碼每個(gè)物體一個(gè)mask甚至不需要任何訓(xùn)練樣本就能在任意圖像上工作。這個(gè)能力確實(shí)很強(qiáng)但它有兩個(gè)明顯的邊界第一SAM不給mask分配語義標(biāo)簽它不知道這個(gè)mask是一只貓還是一把椅子第二即便我們通過外部分類器或CLIP給每個(gè)mask打上了“貓”“杯子”“桌子”的標(biāo)簽SAM依然不知道這些物體之間是什么關(guān)系。這兩者的差別其實(shí)就是“看到”和“理解”的差別。我經(jīng)常拿裁縫打比方SAM像是特別熟練的裁剪師傅把整塊布料按輪廓剪成一片片裁片裁得非常精準(zhǔn)但他不會告訴你這一片是袖子、那一片是前襟更不會告訴你它們該怎么縫合。關(guān)系識別要做的就是把這些裁片組合成一張完整的版型圖——既要認(rèn)出每片是什么還要知道它們之間的連接方式。實(shí)際項(xiàng)目里這個(gè)差距會非常直觀。比如給一張桌面場景圖SAM能干凈利落地把杯子、筆記本、鼠標(biāo)、桌面都割出來可是下游機(jī)器人想要抓取“杯子旁邊的筆”就需要知道“筆在杯子的左邊”或“筆和杯子相鄰”這樣的空間關(guān)系再比如做圖像檢索時(shí)想找“戴著圍巾的人”單純靠類別標(biāo)簽也檢索不出來因?yàn)椤按鳌北旧硎且粋€(gè)跨物體的關(guān)系屬性。這就是RelateAnything要補(bǔ)的位。1.2 兩階段解耦為什么是性價(jià)比更高的路線目標(biāo)明確了接下來是方案選擇。早期做視覺關(guān)系檢測Visual Relationship Detection或者場景圖生成Scene Graph Generation通常是把“物體檢測關(guān)系分類”塞在一個(gè)模型里端到端訓(xùn)練。這樣做的理論完備性沒問題但落地時(shí)很痛苦檢測分支和關(guān)系分支共享特征為了關(guān)系任務(wù)犧牲檢測精度同時(shí)關(guān)系標(biāo)注數(shù)據(jù)又貴又少模型很容易過擬合。RelateAnything這一類思路最舒服的地方在于把任務(wù)拆成兩個(gè)階段。第一階段只負(fù)責(zé)分割這個(gè)能力SAM已經(jīng)做到很通用直接拿來用第二階段只管關(guān)系推理輸入是第一階段產(chǎn)生的實(shí)例輸出是關(guān)系。兩個(gè)階段之間的耦合極小你可以凍結(jié)SAM專門優(yōu)化關(guān)系模塊也可以隨時(shí)用更強(qiáng)的分割模型替換掉SAM關(guān)系模塊基本不用動。我用一個(gè)詞總結(jié)這種架構(gòu)的好處解耦。分割和關(guān)系推理本質(zhì)上是兩種不同復(fù)雜度的問題。分割更多依賴底層視覺特征邊緣、紋理、形狀就能支撐而關(guān)系推理需要更大范圍的上下文和一定的常識能力比如看到“人”和“馬”要知道這是“騎”而不是“站在一起”這需要模型理解空間姿態(tài)、交互語義。硬要把兩件事放同一個(gè)模型里互相干擾不如讓強(qiáng)項(xiàng)各司其職。2. 核心實(shí)現(xiàn)拆解從mask到關(guān)系三元組2.1 關(guān)系預(yù)測的輸出形式在深入實(shí)現(xiàn)之前先明確關(guān)系預(yù)測到底輸出什么。最經(jīng)典的結(jié)構(gòu)是關(guān)系三元組(主語subject, 謂語predicate, 賓語object)。比如“人騎在馬上”就可以表達(dá)為(“人”, “騎”, “馬”)。RelateAnything在圖像層面的輸出就是一系列這樣的三元組同時(shí)把每個(gè)主語和賓語對應(yīng)的mask或邊界框也帶出來方便可視化或供下游使用。這里有一個(gè)容易忽略的細(xì)節(jié)一個(gè)物體可以同時(shí)參與多個(gè)關(guān)系三元組。比如“人”和“杯子”可以是“拿著”的關(guān)系和“椅子”可以是“坐在”的關(guān)系和“桌子”可以是“靠著”的關(guān)系。因此關(guān)系識別模塊的輸出不應(yīng)該是一個(gè)全局的單一標(biāo)簽而是要對圖像中的多組候選對象對pair逐一判斷。這也是為什么工程上經(jīng)常要先枚舉候選對再對每個(gè)候選對做分類。2.2 分割結(jié)果如何變成候選實(shí)例既然要枚舉候選對第一步就是把SAM的輸出整理成干凈、可用的實(shí)例列表。SAM的自動分割模式SamAutomaticMaskGenerator默認(rèn)會輸出一大摞mask在一張中等復(fù)雜的圖上生成一兩百個(gè)mask都很正常。這些mask里有很多質(zhì)量不穩(wěn)定的小碎片直接拿去配對會帶來大量無效計(jì)算。我的習(xí)慣是按三個(gè)條件過濾面積過濾mask像素占比過小的直接丟棄比如小于畫面1%的碎片除非你的業(yè)務(wù)關(guān)心小物體。穩(wěn)定性過濾在生成mask時(shí)SAM會給出一個(gè)stability_score代表這個(gè)mask的穩(wěn)定程度。低于閾值的mask通常邊界飄忽容易被關(guān)系模塊帶偏。重疊過濾SAM默認(rèn)輸出可能包含多個(gè)高度重疊的mask可以通過box_nms_thresh做非極大值抑制或者自己按IoU去重。過濾完成后每個(gè)實(shí)例通常保留三樣?xùn)|西mask本身、mask的外接矩形、從mask推導(dǎo)的類別標(biāo)簽如果關(guān)系模塊需要語義標(biāo)簽。這里我不建議直接用外接矩形摳圖丟給后續(xù)模型因?yàn)榫匦螀^(qū)域會混入大量背景尤其是細(xì)長物體矩形裁剪一半都是干擾。更穩(wěn)妥的做法是拿mask做掩碼摳圖背景位置填灰值或零值再統(tǒng)一縮放到固定尺寸。2.3 關(guān)系判斷的幾條技術(shù)路線把兩個(gè)實(shí)例的區(qū)域特征準(zhǔn)備好之后就要回答“它們是什么關(guān)系”。目前比較常見的判斷方式有三條路RelateAnything整體思路上會傾向于把前兩種結(jié)合使用。第一是純幾何先驗(yàn)。利用兩個(gè)mask的坐標(biāo)和形狀信息計(jì)算中心點(diǎn)距離、重疊度、相對位置角度、面積比等特征然后對“左、右、上、下、內(nèi)部、旁邊”這類空間關(guān)系做規(guī)則判斷。這個(gè)方式快、可解釋性強(qiáng)但只能覆蓋空間關(guān)系沒法理解“騎、抱、戴、支撐”這類需要語義推理的動作關(guān)系。第二是基于視覺-語言模型VLM或?qū)Ρ饶P偷姆绞?。把兩個(gè)實(shí)例區(qū)域拼在一起配合一個(gè)提示問題讓多模態(tài)模型輸出關(guān)系。比如把“人”的區(qū)域和“馬”的區(qū)域同時(shí)給到模型問“這個(gè)物體和這個(gè)物體是什么關(guān)系”模型回答“騎”。這條路線上限高能理解開放語義關(guān)系但計(jì)算量也大而且如果不做微調(diào)直接上通用模型的回答可能不夠穩(wěn)定。第三是圖神經(jīng)網(wǎng)絡(luò)或Transformer關(guān)系頭。把所有實(shí)例當(dāng)作節(jié)點(diǎn)通過消息傳遞機(jī)制在整張圖范圍內(nèi)學(xué)習(xí)節(jié)點(diǎn)之間邊的類別。這種路線對全局上下文建模更完整但需要足量的關(guān)系標(biāo)注數(shù)據(jù)來訓(xùn)練關(guān)系頭工程復(fù)雜度也更高。RelateAnything這類工作通常不會把幾何先驗(yàn)丟掉。實(shí)際部署時(shí)我發(fā)現(xiàn)一個(gè)很有效的做法先用幾何規(guī)則篩掉空間上根本不相關(guān)的組合比如兩個(gè)物體相隔半個(gè)屏幕、mask完全沒有鄰近區(qū)域基本不可能是“拿著”“踩著”“放在上面”這種關(guān)系直接跳過剩下空間上接近的候選對再交給語義關(guān)系分類器。這樣既保住精度又減少了大批量無效語義計(jì)算。2.4 關(guān)系標(biāo)簽體系怎么定關(guān)系標(biāo)簽的設(shè)計(jì)直接影響數(shù)據(jù)成本和模型上限。我建議至少區(qū)分三類關(guān)系大類典型標(biāo)簽適用情況空間關(guān)系左、右、上、下、旁邊、內(nèi)部、遠(yuǎn)處場景布局相關(guān)容易標(biāo)注規(guī)則可覆蓋動作/支撐關(guān)系拿著、抱著、騎、穿、戴、放在、支撐、靠著人與物體、物體與物體之間的交互需要語義理解部分/從屬關(guān)系屬于、部分、包含組件與整體比如“瓶蓋屬于瓶子”注意兩點(diǎn)第一動作關(guān)系通常伴隨著空間鄰近但“在人旁邊”和“被人拿著”是不同的必須讓模型學(xué)會區(qū)分第二真實(shí)場景里絕大多數(shù)候選對之間是“無關(guān)”關(guān)系如果分類器沒有“無關(guān)”這一類所有候選對都會得到某個(gè)低置信度關(guān)系標(biāo)簽誤報(bào)率會很高。標(biāo)簽體系里一定要留一個(gè)“無關(guān)/背景”類再配合置信度閾值才能控住誤報(bào)。3. 實(shí)操復(fù)現(xiàn)從環(huán)境搭建到跑通一次完整推理3.1 環(huán)境準(zhǔn)備與依賴選擇我假定你手里有一張普通NVIDIA顯卡顯存8G以上就能把流程完整跑起來。基礎(chǔ)環(huán)境是Python 3.9、PyTorch 2.x、torchvision。分割部分用segment-anything官方庫權(quán)重文件我建議直接下sam_vit_h_4b8939.pth雖然體積大一點(diǎn)約2.5GB但分割質(zhì)量是最好的顯存緊張可以換sam_vit_l或sam_vit_b效果差距在復(fù)雜場景會比較明顯。關(guān)系識別模塊如果走VLM或CLIP路線需要額外安裝多模態(tài)模型依賴并下載對應(yīng)的權(quán)重。這一步要提醒的是權(quán)重下載是完整流程最容易卡殼的地方建議先把所有權(quán)重文件下載到本地固定目錄再寫代碼不然真到跑推理的時(shí)候才發(fā)現(xiàn)某個(gè)權(quán)重缺失調(diào)試體驗(yàn)會非常糟糕。3.2 推理主流程逐步拆解整體流程我拆成六個(gè)步驟讀圖并做基本預(yù)處理包括縮放、顏色空間校正。用SAM自動分割生成全圖mask列表。按面積、穩(wěn)定性、重疊度過濾mask得到候選實(shí)例集合。枚舉候選實(shí)例兩兩組合先用幾何先驗(yàn)做粗篩。對剩余候選對提取mask區(qū)域特征送入關(guān)系分類模塊。匯總關(guān)系三元組置信度低于閾值的丟棄輸出結(jié)構(gòu)化結(jié)果。偽代碼大致長這樣import torch from segment_anything import build_sam, SamAutomaticMaskGenerator sam build_sam(checkpointsam_vit_h_4b8939.pth) generator SamAutomaticMaskGenerator( sam, points_per_side32, pred_iou_thresh0.88, stability_score_thresh0.90, box_nms_thresh0.7, ) image load_image(demo.jpg) masks generator.generate(image) candidates filter_masks(masks, min_area0.01) relations [] for i in range(len(candidates)): for j in range(i 1, len(candidates)): if not geometric_prior(candidates[i], candidates[j]): continue relation predict_relation(candidates[i], candidates[j], image) if relation.confidence 0.6: relations.append(relation) output format_triplets(relations)從代碼順序上也能看出來真正的關(guān)系語義分類只是內(nèi)層循環(huán)里的一小步更大的工作量花在候選過濾和幾何粗篩上。這里points_per_side控制自動采樣的點(diǎn)數(shù)點(diǎn)數(shù)越多分割越細(xì)但速度也會明顯變慢。我實(shí)測下來32是一個(gè)比較均衡的值16會讓小物體漏得多64則單張圖經(jīng)常跑到幾秒到十幾秒。3.3 輸出結(jié)構(gòu)化結(jié)果的落地細(xì)節(jié)關(guān)系三元組的輸出格式建議直接用JSON每個(gè)三元組包含主語、賓語、關(guān)系、置信度和對應(yīng)的bbox/mask索引方便后續(xù)可視化或接入業(yè)務(wù)邏輯。一個(gè)典型的輸出片段{ relations: [ { subject: 3, object: 5, predicate: on top of, confidence: 0.93 }, { subject: 2, object: 7, predicate: holding, confidence: 0.87 } ] }可視化時(shí)把每個(gè)參與關(guān)系的mask用不同顏色疊加畫在原圖上然后用箭頭或連線連接主語和賓語旁邊標(biāo)注關(guān)系標(biāo)簽。這一步看起來簡單但作用很大只有把結(jié)果可視化你才能一眼看出關(guān)系模塊的失誤模式到底在哪——是空間方向判斷反了還是兩個(gè)物體本來就離得近但沒動作關(guān)系被誤判成了“拿著”。4. 實(shí)戰(zhàn)中的常見問題與調(diào)優(yōu)技巧4.1 SAM自動分割數(shù)量膨脹怎么辦分割階段最容易遇到的問題是mask數(shù)量爆炸。一張街景圖可能分割出幾百個(gè)實(shí)例兩兩配對就是幾萬對關(guān)系模塊就算再快也會被拖垮。我從實(shí)際項(xiàng)目里總結(jié)出兩個(gè)處理原則。第一先過濾再配對。用面積和置信度把明顯低質(zhì)量的碎片全部清除寧可漏掉一些小物體也不能讓一堆垃圾實(shí)例參與關(guān)系計(jì)算。第二設(shè)置最大實(shí)例數(shù)。如果一個(gè)場景中候選實(shí)例超過比如80個(gè)優(yōu)先保留面積大、置信度高的實(shí)例其余丟棄。對絕大多數(shù)業(yè)務(wù)場景來說關(guān)注最顯眼的幾十個(gè)物體之間的核心關(guān)系已經(jīng)夠用了。4.2 遮擋和重疊導(dǎo)致的誤判分割階段最頭疼的是遮擋。兩個(gè)物體疊在一起時(shí)SAM可能把后面那個(gè)物體的mask搞得很不完整甚至只割出邊緣一圈。關(guān)系模塊拿到這種殘缺區(qū)域很容易把“騎在馬上的人”誤判成“站在馬旁邊的人”因?yàn)轳R的背部區(qū)域被人的mask蓋住了。我的應(yīng)對辦法是關(guān)系識別時(shí)不要只用mask內(nèi)的像素可以考慮把物體外接矩形擴(kuò)大一個(gè)比例比如擴(kuò)大10%讓模型看到周圍上下文。因?yàn)殛P(guān)系判斷很多時(shí)候依賴上下文線索——人和馬的相對位置、接觸面積、姿勢方向這些并不是只看前景區(qū)域就能看出來的。但擴(kuò)大的比例要控制擴(kuò)太多又會引入其他物體干擾。4.3 關(guān)系類別極端不平衡視覺關(guān)系中存在非常嚴(yán)重的標(biāo)簽不平衡問題?!芭赃叀薄案浇边@類弱空間關(guān)系占了絕大多數(shù)而“騎著”“穿著”“插著”這類有意義的動作關(guān)系占比極低。如果不做處理模型會傾向于把所有候選對都預(yù)測成“旁邊”看起來準(zhǔn)確率還行實(shí)際完全沒用。解決手段有三個(gè)層級。最基礎(chǔ)的是在損失函數(shù)中對稀有關(guān)系類別加大權(quán)重進(jìn)階一點(diǎn)是引入“無關(guān)”類讓模型只在置信度高的時(shí)候輸出具體關(guān)系否則輸出“無關(guān)”再進(jìn)階一點(diǎn)是調(diào)整推理閾值把輸出具體關(guān)系的置信度門檻調(diào)高寧缺毋濫。我在實(shí)際項(xiàng)目中通常把關(guān)系輸出的閾值設(shè)在0.6到0.7之間低于這個(gè)值默認(rèn)無關(guān)系誤報(bào)率能壓下來不少。4.4 推理速度優(yōu)化關(guān)系識別鏈路最吃時(shí)間的有兩處SAM分割和成對關(guān)系判斷。SAM分割本身可以通過換小模型、減少采樣密度、半精度推理來加速成對關(guān)系判斷則推薦兩個(gè)方法并行使用。第一是幾何粗篩前置這在前面已經(jīng)提過能篩掉一半以上空間上不相關(guān)的候選對。第二是批量推理把語義關(guān)系分類模塊改成批處理接口把幾百個(gè)候選對的特征打包成batch一次前向計(jì)算完而不是兩兩依次調(diào)用。這個(gè)優(yōu)化通常能帶來數(shù)倍的速度提升。如果顯存允許還可以把SAM和關(guān)系模塊都切到半精度FP16精度損失在可接受范圍內(nèi)速度則能進(jìn)一步改善。4.5 一個(gè)容易被忽略的工程問題坐標(biāo)對齊這個(gè)坑我認(rèn)為值得單獨(dú)拿出來說。在完整流程中圖像處理會涉及到縮放、裁剪、mask坐標(biāo)映射。SAM輸出的mask坐標(biāo)是在它內(nèi)部處理過的尺寸上的而關(guān)系分類模塊拿到的圖像可能又是另一個(gè)尺寸。如果坐標(biāo)映射沒有做對齊會出現(xiàn)“分割結(jié)果明明正確但關(guān)系模塊拿到的是錯誤區(qū)域的像素”這種情況。直接表現(xiàn)就是空間關(guān)系判斷全亂套。建議在任何一步變換圖像尺寸或坐標(biāo)時(shí)都先寫一個(gè)坐標(biāo)轉(zhuǎn)換函數(shù)并且每一步都保留同一套坐標(biāo)系定義。調(diào)試時(shí)最有效的手段是在可視化圖像上同時(shí)畫上SAM輸出的mask和按坐標(biāo)摳出來的區(qū)域圖兩張圖對得上再往下走。5. 應(yīng)用場景與擴(kuò)展方向5.1 機(jī)器人操作前的場景理解關(guān)系識別在機(jī)器人和具身智能領(lǐng)域的需求是最實(shí)際的。機(jī)器人要抓取一個(gè)物體只靠分割結(jié)果是不夠的它需要知道物體是放在桌面上還是被夾在其他物體之間是穩(wěn)固的還是會倒旁邊是否有障礙物。有了關(guān)系三元組機(jī)器人就能把“放在桌子上的杯子”“壓在書本下的手機(jī)”這類信息直接轉(zhuǎn)化為操作策略。比如抓取“壓在書本下的手機(jī)”之前先要規(guī)劃一個(gè)移開書本的步驟。這一步對視覺感知來說很關(guān)鍵。5.2 自動標(biāo)注與結(jié)構(gòu)化描述生成對數(shù)據(jù)生產(chǎn)團(tuán)隊(duì)來說RelateAnything這類能力可以批量生成帶關(guān)系標(biāo)注的結(jié)構(gòu)化描述。過去人工標(biāo)注“杯子在桌子上”這樣的關(guān)系要一張圖一張圖地標(biāo)周期長成本高現(xiàn)在可以先跑分割再跑關(guān)系識別自動產(chǎn)出候選三元組人工只需要做篩選修正。這個(gè)流程能把標(biāo)注效率提升不少而且三位一體的輸出格式天然適合訓(xùn)練下游場景圖模型。5.3 圖像編輯和生成場景的關(guān)系保持?jǐn)U散模型做圖像編輯時(shí)經(jīng)常遇到一個(gè)經(jīng)典問題把某個(gè)物體替換掉之后它和周圍物體之間的空間關(guān)系可能會崩壞明明在原圖“帽子在頭上”生成結(jié)果變成“帽子飄在一邊”。如果能預(yù)先用關(guān)系識別把關(guān)鍵的空間關(guān)系顯式抽出來再把這些信息作為條件或約束輸入到生成流程中關(guān)系保持會穩(wěn)定很多。這也是關(guān)系識別在未來生成式應(yīng)用里一個(gè)很有潛力的方向。5.4 與指代分割、視覺問答的聯(lián)動SAM本身支持交互式提示比如給定一個(gè)點(diǎn)或一個(gè)框分割出對應(yīng)物體。如果把RelateAnything的關(guān)系輸出作為提示就能實(shí)現(xiàn)“分割出那個(gè)人騎著的馬”這種指代式分割——先用關(guān)系識別鎖定“人騎馬”這個(gè)關(guān)系對再把“馬”的mask作為提示輸入SAM。這等于把關(guān)系識別和分割模型串成了一個(gè)閉環(huán)對交互式視覺助手來說非常自然。6. 一些心得和后續(xù)建議做了一段時(shí)間的分割關(guān)系識別落地我最大的體會是這類任務(wù)真正的難點(diǎn)往往不在模型選型而在工程細(xì)節(jié)。關(guān)系判斷的準(zhǔn)確率很大程度取決于候選實(shí)例的質(zhì)量而候選實(shí)例的質(zhì)量又取決于分割參數(shù)、過濾規(guī)則、坐標(biāo)對齊。很多人一上來就調(diào)關(guān)系分類模型的結(jié)構(gòu)折騰很久沒效果其實(shí)把SAM的閾值調(diào)準(zhǔn)、把過濾規(guī)則寫干凈精度已經(jīng)能漲不少。后續(xù)如果想再往前走我建議優(yōu)先關(guān)注兩個(gè)方向一個(gè)是把關(guān)系標(biāo)簽體系做成開放詞匯的也就是不限定固定關(guān)系列表而是通過視覺-語言模型直接生成自然語言關(guān)系描述這樣能覆蓋更多長尾關(guān)系另一個(gè)是在關(guān)系推理時(shí)引入時(shí)序信息視頻中的“拿起、放下、推”這類動作關(guān)系單幀很難判斷多幀信息會可靠得多。這條路線擴(kuò)展性很強(qiáng)做好了能直接對接很多實(shí)際業(yè)務(wù)。