Semantic segmentation 要替每一個像素貼一個類別(人、路、天空),同一類的不同個體不分開。最笨的做法是每個像素切一小塊圖丟進分類 CNN,相鄰的小塊大部分重疊,卻一直重算。FCN 的做法是把分類網路最後的全連接層換成 1×1 卷積,整張圖一次算完,得到一張很小的「類別熱圖」。剩下的問題是:怎麼把這張小圖放大回原尺寸。下面是一維版本,8 格放大成 16 格左右。
用途:講義列了手機人像模式(前景和背景分開,背景模糊)、YouTube stories 換背景、虛擬化妝;自駕車和無人機的場景理解;替視力不好的人標出重要物體;讓機器人切出物體才能抓;醫療影像切腫瘤、牙齒蛀洞(ISBI 2015 牙科 X 光挑戰)。
資料集:講義提到 PASCAL VOC、COCO(同時有 semantic 和 instance 標註)、Cityscapes(街景)、ADE20K(類別多)。講義引用的觀察:PASCAL VOC 和 Cityscapes 類別少,難在邊界;ADE20K 難在語意和上下文。所以在一個資料集上有效的改動,換一個資料集常常沒用。
怎麼打分:把預測 mask 和真實 mask 當成兩個像素集合比重疊。
講義備註的例子:10×10 的小物體,預測偏了 5 個 FP、5 個 FN,IoU 0.905、Dice 0.95;偏到 10 和 10,IoU 0.818、Dice 0.90。IoU 比較嚴格,各類平均的 mIoU 是 PASCAL VOC、COCO 的標準;Dice 對小目標比較穩,醫療影像常用。訓練時常用 BCE + Dice loss:BCE 給平滑的逐像素梯度,Dice 不被大量背景像素淹沒。
難在哪:loss 是每個像素各算各的,所以問題變成「一個像素怎麼考慮到周圍的上下文」。講義整理成三件事:解析度(高解析度太貴,要縮小)、縮小後輸出又小又粗(要放大、要補細節)、物體有大有小(要多尺度)。
FCN 的三個改造:(1) 全連接層可以看成 kernel 蓋住整個輸入的卷積,改成 1×1 卷積後,任意大小的圖都能進來,輸出是一張粗的熱圖。(2) 網路內的放大(in-network upsampling):unpooling(直接複製,或只放在角落其他補 0)、max unpooling(用 switch 放回原位)、transpose convolution。(3) skip connections,見 SEC.02。
Transpose convolution:一般卷積 4×4 輸入、3×3 filter 得到 2×2;transpose 反過來把 2×2 變成 4×4。一維例子:輸入 [a, b]、kernel [x, y, z]、stride 2,輸出是 [ax, ay, az + bx, by, bz],重疊的那格相加。別名有 upconvolution、fractionally strided convolution、backward strided convolution;叫它 deconvolution 不好,因為訊號處理的 deconvolution 是卷積的反運算。另一種放大是直接 resize(雙線性內插)。DeconvNet 把 VGG-16 鏡像一份當 decoder:unpooling + transpose conv + ReLU。
深層知道「這是什麼」,但經過好幾次 pooling,位置已經很粗。淺層解析度高,知道「在哪裡」,卻不知道是什麼。FCN 的 skip connection 把兩者合起來:深層的預測放大 2 倍,加上淺層自己用 1×1 卷積做的預測,再放大。下面用一個有尖角的形狀真的算一次:先縮小 8 倍再放大回來,看邊界掉了多少,再打開 skip。
FCN 的三個版本:VGG 經過五次 pooling,pool5 只剩原圖的 1/32。FCN-32s 直接把 pool5 的預測放大 32 倍。FCN-16s 先把它放大 2 倍,加上 pool4 經過 1×1 卷積的預測,再放大 16 倍。FCN-8s 再多加 pool3,最精細。2 倍放大的 kernel 先用雙線性初始化,再跟著學。講義的數字:相對先前最好的 SDS,mean IoU 相對進步 30%,而且快 286 倍。實作細節:原作者在 conv1_1 加了 pad = 100,否則小於 192×192 的圖在放大前特徵圖就縮到 0 了,代價是引入很多雜訊。
SegNet:encoder 是 VGG,decoder 的 unpooling 不學,直接拿 encoder 記下的 max pooling 位置。優點是保留高頻資訊,而且只存位置、不存特徵,比 FCN 省記憶體;缺點是從低解析度放大時,會漏掉鄰近位置的資訊。
U-Net:對稱的收縮路徑和擴張路徑,前者抓上下文,後者精確定位。每一層的 skip 用拼接,讓 decoder 把 pooling 時丟掉的特徵學回來。它在很少資料下也能學(講義備註:EM stack 只有 30 張密集標註),所以在醫療影像很流行,拿下 ISBI 2015 的 CAD Caries 和 Cell tracking 兩項挑戰。分割做資料增強時,答案 mask 要跟著一起變形,這點和分類不同。
常見的分割網路長相(講義整理):下採樣路徑(從 AlexNet、VGG、ResNet 等分類網路遷移過來,抓語意)、上採樣路徑(恢復解析度)、skip(補細節)、多尺度偵測、可選的後處理(CRF)。編碼解碼型的代表有 SegNet、U-Net、Fully Convolutional DenseNets;特徵強化型的代表是 DeepLab 系列和 dilated convolutions。
Deep layer aggregation:講義的觀點是一般的 skip 都是線性的,而且只把最淺的層接過來。DLA 提出兩種更深的聚合:IDA 從最淺、最小的尺度開始,一層一層往深處合併不同解析度;HDA 用樹狀結構合併各個區塊和 channel 的特徵。兩者都和主幹網路無關,可以接在任何 backbone 上。
ResNeSt:另一個方向是不改 decoder、改 backbone。ResNeSt 把 attention 做到 group 層級(split attention),直接換掉 ResNet。講義引用的數字:DeepLabV3 + ResNeSt-269 在 ADE20K 單模型 47.60% mIoU;PSPNet 的 44.94% 保持了快三年;只換 backbone 就約有 3% 的提升。
另一條路是一開始就別縮那麼小。DeepLab 把 VGG 最後的 pooling stride 改成 1,特徵圖停在 28×28,不再縮到 7×7。但不縮小,每個 3×3 kernel 看到的範圍就變窄了。解法是 atrous(dilated)convolution:kernel 還是 9 個數字,但取樣點之間隔開 rate − 1 格。看到的範圍變大,參數一個也沒多。
DeepLab v1 的三個挑戰和對策:(1) pooling 和 strided convolution 讓解析度下降,用 atrous convolution;(2) 物體有多種大小,用 atrous spatial pyramid pooling(v2 才加入);(3) 網路對位置的不變性讓邊界不準,用 fully connected CRF。架構是 VGG-16,最後一個 block 的 max pooling stride 設 1,特徵圖保持 28×28;conv4、conv5 改用 atrous;最後用雙線性內插放大回原圖,再用 CRF 修邊界。CRF 的直覺:顏色差很多的相鄰像素(藍天和黑色飛機)多半屬於不同類,顏色接近的就拉成同類。後來的網路不再用 CRF:網路本身變好了,CRF 也不能端到端訓練,而且很慢。
DeepLab v2:backbone 換成 ResNet(這一步就直接提升效果),加入 ASPP(四個分支,rate 從 6 到 24),另外把圖縮成 1.0、0.75、0.5 三種大小各跑一次再融合(多尺度輸入,代價是慢三倍),後處理仍是 CRF。
DeepLab v3:每個卷積後加 Batch Norm;ASPP 改成一個 1×1 分支、三個 3×3 分支(output stride 16 時 rate 6、12、18,output stride 8 時加倍),再加一個整張圖 pooling 的分支補全域資訊;拿掉 CRF;測試時用 0.5 到 1.75 六種輸入大小加左右翻轉。
DeepLab v3+:v3 直接放大 8 或 16 倍太粗。v3+ 把 v3 當 encoder,再加 decoder:ASPP 的輸出先放大 4 倍,和 backbone 裡 stride 4 的淺層特徵(先用 1×1 卷積降 channel,免得淺層佔太重)拼接,經過 3×3 卷積,再放大 4 倍。backbone 換成改良的 Xception,用 atrous depthwise separable convolution 降低計算量。
KSAC(kernel-sharing atrous convolution):ASPP 的各分支互不共享,每支只學到某一種大小的物體,資料等於被切分,參數也隨分支數線性增加。KSAC 讓所有分支共用同一個 kernel,講義的數字:rate 6、12、18 時省下 62% 的參數;ASPP 加到 24 反而變差(可能過擬合),KSAC 仍然進步。
Transformer 進入分割:講義的比較是 CNN 參數很有效率,但處理全域資訊的能力不足,而全域上下文對分割很重要。Segmenter 把分割重新表述成序列到序列的問題:預訓練的 ViT 當 encoder,decoder(Mask Transformer)加入 K 個可學習的類別向量,和 patch 特徵做內積得到每一類的 mask,放大後取 argmax。SegFormer 解掉 ViT 直接拿來分割的問題:ViT 只輸出單一、低解析度的特徵圖,SegFormer 的 MiT encoder 分層輸出 1/4、1/8、1/16、1/32 四種解析度;ViT 的位置編碼在測試解析度不同時要插值、效果打折,SegFormer 乾脆不用位置編碼,改用重疊的 patch embedding,加上 Mix-FFN(在 feed-forward 裡放一個 3×3 depthwise conv 傳遞位置資訊);decoder 只有幾層 MLP。講義引用的數字:SegFormer-B0 只有 3.7M 參數,B5 是 87M,先前的 SETR 是 318M。Mask2Former 把 semantic、instance、panoptic 三種分割統一成 mask classification:固定數量的 query 直接輸出一組「mask + 類別」;核心是 masked attention,每個 query 只看自己預測的 mask 區域,收斂比較快,小物體和邊界也比較好。
Semantic segmentation 不分個體:畫面裡三隻狗是同一片「狗」。Instance segmentation 要把每一隻分開。Mask R-CNN 的做法是 Faster R-CNN(C4)先找框,再對每個框跑一個小 FCN 畫 mask。關鍵在「從框裡把特徵拿出來」這一步:框的座標是小數,特徵圖是一格一格的整數。RoIPool 四捨五入兩次;RoIAlign 一次都不捨入,用雙線性內插取值。
Mask R-CNN = Faster R-CNN + 每個 RoI 上的 FCN。在分類和框回歸之外多一個分支,對每個框輸出一張 binary mask(每個像素是不是這個物體),速度和 Faster R-CNN 差不多,也容易延伸到人體關鍵點。講義列的三個關鍵:(1) RoIPool 換成 RoIAlign,簡單但影響很大,mask 準確率提升 10% 到 50%;(2) mask 和類別解耦:每一類各輸出一張 m×m 的 mask(共 K 張),逐像素 sigmoid、平均 binary cross-entropy,只有正確類那張算 loss,類別之間不競爭;(3) mask 用 FCN 而不是全連接層,參數少、保留空間結構。總 loss = Lcls + Lbox + Lmask。輸出的 28×28 soft mask 用雙線性重新取樣回原圖座標,再用 0.5 切開。
消融實驗(講義引用):RoIAlign 對 RoIPool 在 stride 32、高 IoU 門檻時進步最大,而且不需要 dilation 或上採樣就有好的 box AP。每類 binary mask(sigmoid)比 multinomial mask(softmax)大幅領先:分類分支已經認出是「蘋果」,mask 分支不用再認一次。FCN mask head 比 MLP 多 2.1 分,因為 MLP 把位置資訊攪掉了。COCO 上用 ResNet-101 比當時最好的方法多 2 AP,每張約 200 ms;物體偵測本身也因 RoIAlign 和多任務訓練進步。
人體關鍵點:每個關鍵點當成一張只有一格是 1 的 mask,人體姿態是 17 張;在 56×56 的位置上做 softmax(56² 選 1)。
YOLACT / YOLACT++:one-stage 路線,把 mask 拆成兩條平行分支。Protonet 從 FPN 的 P3(夠深又夠高解析度)輸出 k 張整張圖共用的原型 mask(預設 32 張、138×138),卷積保留空間連貫性;預測頭替每個物體輸出 k 個係數,用 tanh 讓它可正可負,組合更多。最終 mask = sigmoid(原型 × 係數),一次矩陣乘法,再用框裁切。原型會自己學會切左右、上下、前景背景(zero padding 讓卷積知道離邊界多遠)。YOLACT++ 加上 Fast Mask Re-Scoring(預測 mask 的 IoU 當分數)、間隔放置的 deformable convolution、更多 anchor 比例,講義備註的數字是 29.8 提升到 34.1 mAP,速度維持約 33 fps。
Foundation model:在大量資料上(常用 self-supervised)訓練、能適應很多下游任務的大模型,2021 年由史丹佛的研究者推廣。特徵是泛化(zero-shot、few-shot)、資料量大、常是多模態、訓練成本極高但之後微調便宜。GPT、DALL-E 是例子,分割的代表是 SAM。
SAM(Segment Anything):可提示(promptable)的分割模型,三個部分。Image encoder 是用 MAE 預訓練的 ViT,最重,算一次就能重複用;prompt encoder 把點、框(稀疏提示,轉成位置 embedding)和粗 mask(密集提示,卷積下採樣後直接加到影像 embedding 上)編碼;mask decoder 很輕,參考 DETR 和 MaskFormer,用 focal loss + Dice loss 訓練,可以在瀏覽器的 CPU 上 50 ms 內跑完。一個點可能指襯衫也可能指整個人,所以一個提示輸出 3 張 mask(整體、部分、子部分),並預測各自的 IoU 來排序。資料靠三階段的 data engine(輔助手動、半自動、全自動標註),做出 SA-1B:1,100 萬張圖、10 億個 mask。講義列的限制:會錯過精細結構,有時產生小的斷開碎塊,邊界不如計算量更大的方法清楚。
SAM 2:擴展到影片,把單張圖看成只有一格的影片。核心是 streaming memory:每一格先過 image encoder,透過 memory attention 參考記憶庫,再由 mask decoder 輸出,結果經 memory encoder 寫回記憶庫,不用每格重新給提示。訓練資料是 SA-V。SAM 3(2025 年 11 月,2026 年 1 月推出 SAM 3.1):從「用點、框提示一個物體」變成「用概念詞提示一整類」(concept-based),輸入「紅色汽車」就找出所有符合的實例,並在影片裡追蹤;架構是 DETR 式的偵測器加一個 presence head(先判斷概念在不在,把「是什麼」和「在哪裡」分開),追蹤器沿用 SAM 2 的記憶。MobileSAM:把 ViT-H encoder 的知識蒸餾到輕量 encoder(解耦蒸餾,單張 GPU 不到一天),體積小 60 多倍、效果相當,每張約 10 ms(encoder 8 ms、decoder 2 ms)。
Foundation model 時代的分割:講義列的方向有通用分割模型、把 LLM 接進分割、可提示與語言驅動的分割、open-vocabulary segmentation、不用訓練的分割,以及用 diffusion model 當表示學習器(它在文字生圖時就隱約學會了物體分組)。
合流(講義第 132 頁):偵測(DETR)和分割(Mask2Former)最後都收斂到 query-based set prediction,甚至共用同一套 backbone。SAM 系列更進一步,把「訓練一個專用模型」換成「用 prompt 呼叫一個通用模型」。2026 年的實務是先試 SAM 3 或 open-vocabulary detector 能不能直接解決,只有資料分布特殊或延遲要求極嚴時,才投入資源訓練專用模型。
這個模組最常被講錯的幾句話。