NEWSIO+ 教育系列01 自己訓練一個 AI02 從照片還原 3D03 深度學習
主線 C · 模組 C5動手做 // 四個實驗台

每個像素
貼標籤

分類網路最後把圖縮小了,怎麼把答案「放大」回每個像素?SAM 又是怎麼做到「什麼都能切」?

分類只要回答一句「這是貓」。分割要回答幾十萬句:這個像素是貓、這個是沙發、這個是地板。麻煩在於分類網路為了看得廣、算得快,一路把圖縮小,最後只剩一小塊。這個模組講四個把答案放大回去的工具:會學的放大、把淺層細節接回來、不縮小也看得廣、從框裡對齊地拿特徵。最後看 SAM 怎麼把「訓練一個分割模型」變成「下一個提示」。

SEC.01 / DENSE PREDICTION

圖被縮小了,答案要怎麼放大回去

Semantic segmentation 要替每一個像素貼一個類別(人、路、天空),同一類的不同個體不分開。最笨的做法是每個像素切一小塊圖丟進分類 CNN,相鄰的小塊大部分重疊,卻一直重算。FCN 的做法是把分類網路最後的全連接層換成 1×1 卷積,整張圖一次算完,得到一張很小的「類別熱圖」。剩下的問題是:怎麼把這張小圖放大回原尺寸。下面是一維版本,8 格放大成 16 格左右。

PROBE A / 放大回去動手做 // 改 kernel,一步一步加上去
輸出長度17 格
每格收到幾份貢獻7 格兩份、10 格一份
輸入全是 1 時,中段輸出的起伏1
現在這一步8 個輸入都加上去了
kernel
Transpose convolution 的每個輸入格,把自己乘上 kernel 的三個數字,貼到輸出的三格上;stride 2 代表輸入往右一格,貼的位置往右兩格。三格寬、間隔兩格,所以每隔一格就有兩份貢獻疊在一起(下方標 2 的格子),另一格只有一份。輸入全是 1、kernel 是 [1, 1, 1] 時,輸出變成 1、2、1、2…,這就是棋盤效應,二維時看起來像格子紋。把 kernel 改成 [0.5, 1, 0.5](雙線性內插的形狀),兩個半份加起來剛好一份,起伏消失;FCN 就是用雙線性內插初始化這個 kernel,再讓它跟著訓練。切到 max unpooling:它不學任何東西,只用 encoder 做 max pooling 時記下的位置(switch,上排橘框)把值放回原位,其他格補 0。訊號和 kernel 都是示意用的數字。
放大也可以學Transpose convolution 是一種會學的放大:kernel 的數字和其他權重一樣由 backprop 調整。它不是 convolution 的反運算。
重疊不均就有格紋kernel 寬度不是 stride 的整數倍時,有些輸出格拿到的貢獻比別人多。kernel 沒學好,輸出就帶著規則的格子紋。
記住位置比較省Max unpooling 只要存每個池化窗裡最大值在哪,不用存整張特徵圖。SegNet 就是這樣做。
細看 · 用途、資料集、怎麼打分、FCN 的三個改造

用途:講義列了手機人像模式(前景和背景分開,背景模糊)、YouTube stories 換背景、虛擬化妝;自駕車和無人機的場景理解;替視力不好的人標出重要物體;讓機器人切出物體才能抓;醫療影像切腫瘤、牙齒蛀洞(ISBI 2015 牙科 X 光挑戰)。

資料集:講義提到 PASCAL VOC、COCO(同時有 semantic 和 instance 標註)、Cityscapes(街景)、ADE20K(類別多)。講義引用的觀察:PASCAL VOC 和 Cityscapes 類別少,難在邊界;ADE20K 難在語意和上下文。所以在一個資料集上有效的改動,換一個資料集常常沒用。

怎麼打分:把預測 mask 和真實 mask 當成兩個像素集合比重疊。

IoU = TP / (TP + FP + FN)  Dice = 2TP / (2TP + FP + FN) = 2·IoU / (1 + IoU) (TP 兩邊都說是前景,FP 只有預測說是,FN 只有答案說是)

講義備註的例子: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。

SEC.02 / SKIP CONNECTIONS

放大回來的邊界是糊的:把淺層的細節接回來

深層知道「這是什麼」,但經過好幾次 pooling,位置已經很粗。淺層解析度高,知道「在哪裡」,卻不知道是什麼。FCN 的 skip connection 把兩者合起來:深層的預測放大 2 倍,加上淺層自己用 1×1 卷積做的預測,再放大。下面用一個有尖角的形狀真的算一次:先縮小 8 倍再放大回來,看邊界掉了多少,再打開 skip。

PROBE B / 縮小再放大,邊界剩多少動手做 // 開關 skip,換形狀
預測對的前景多切了(FP)漏掉了(FN)
沒有 skip:IoU0
目前設定:IoU0
邊界帶 IoU(真實邊界 3 像素內)0
錯的像素(沒有 skip → 目前)0
左邊只用最深層的預測(8 倍時每 8×8 像素只剩一個數字),雙線性放大回 128×128 再用 0.5 切開:尖角被磨圓,細天線整根消失。右邊把只縮小 2 倍那層的預測放大後加上去,再判斷一次,尖角回來了。IoU 是交集除以聯集;「邊界帶」只算真實邊界 3 像素內的像素,因為錯誤幾乎都在那裡,整張圖的 IoU 會被大片正確的內部稀釋。這裡為了看得見,直接把 mask 縮小來代替各層的預測;真的網路裡每一層的預測是學出來的。形狀、解析度、權重都是示意。
深層管「什麼」,淺層管「哪裡」Skip 讓網路同時學語意和位置,而且是端到端一起學。
相加或拼接FCN 把兩層的預測逐點相加;U-Net 在 channel 維度拼接,讓 decoder 自己學要用多少淺層細節。
越多 skip 越細講義的對照:FCN-32s 沒有 skip、16s 一條、8s 兩條,輸出一個比一個接近真實答案。
細看 · FCN-32s / 16s / 8s、SegNet、U-Net、更深的聚合

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% 的提升。

SEC.03 / ATROUS CONVOLUTION

不縮小也能看得廣:在 kernel 裡挖洞

另一條路是一開始就別縮那麼小。DeepLab 把 VGG 最後的 pooling stride 改成 1,特徵圖停在 28×28,不再縮到 7×7。但不縮小,每個 3×3 kernel 看到的範圍就變窄了。解法是 atrous(dilated)convolution:kernel 還是 9 個數字,但取樣點之間隔開 rate − 1 格。看到的範圍變大,參數一個也沒多。

PROBE C / 挖洞的 kernel 與 ASPP動手做 // 拉 rate,疊兩層,看 ASPP

一個 3×3 kernel 27×27 的特徵圖

ASPP 幾個 rate 並排

感受野3 × 3
參數量(每對輸入、輸出 channel)9
一般 kernel 要看到同樣範圍需要9
範圍內真的被看到的格子9 / 9
ASPP 各分支感受野與參數0
拉 rate:9 個橘點散開,虛線框(感受野)從 3×3 長到 13×13,參數一直是 9;一般 kernel 要看到同樣範圍需要 (2r + 1)² 個。打開「疊兩層」:感受野變成 4r + 1,但 rate 大於 1 時被看到的格子一格一格跳開,中間有洞,這就是講義說的 gridding effect。右邊是 ASPP:同一張特徵圖上平行跑幾個不同 rate 的 3×3 分支加一個 1×1 分支,小 rate 管小物體、大 rate 管大物體,結果接起來。勾 KSAC 讓分支共用一組 kernel,參數不再隨分支數成長。這裡的參數量只算 kernel 本身,是示意。
感受野不必靠縮小Pooling 讓感受野變大,代價是位置資訊。Atrous 不降解析度也能變大,計算量不變。
一個 rate 只配一種大小物體有大有小,所以 ASPP 同時用好幾個 rate,像同時戴好幾副不同度數的眼鏡。
Transformer 天生看得廣Self-attention 讓每個位置都能看整張圖,所以 SegFormer 的 decoder 只要幾層 MLP,不需要 ASPP。
細看 · DeepLab v1 到 v3+、KSAC、Transformer 分割

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 區域,收斂比較快,小物體和邊界也比較好。

SEC.04 / INSTANCE & FOUNDATION MODELS

分出每一個個體,再到什麼都能切

Semantic segmentation 不分個體:畫面裡三隻狗是同一片「狗」。Instance segmentation 要把每一隻分開。Mask R-CNN 的做法是 Faster R-CNN(C4)先找框,再對每個框跑一個小 FCN 畫 mask。關鍵在「從框裡把特徵拿出來」這一步:框的座標是小數,特徵圖是一格一格的整數。RoIPool 四捨五入兩次;RoIAlign 一次都不捨入,用雙線性內插取值。

PROBE D / RoIPool 對不齊,RoIAlign 對得齊動手做 // 拖 RoI,換 stride
真正的 RoI 與 binRoIPool 拿去算的格子RoIAlign 取樣點
RoI 在特徵圖上的座標 → RoIPool 取整後0
RoIPool 的 bin 最多偏掉(原圖像素)0
RoIPool 和參考值的平均差0
RoIAlign 和參考值的平均差0
每個 bin:Pool / Align / 參考
0
橘色實線是真正的 RoI(圖是 256×256 像素),虛線是它切出的 bin。藍色格子是 RoIPool 實際拿去算的特徵格:先把框的邊界除以 stride 四捨五入到整數格,切 bin 時又取整一次,所以和橘框對不齊。橘點是 RoIAlign 的取樣點:每個 bin 4 個點,位置不取整,值由周圍 4 格雙線性內插。「參考值」是在每個 bin 裡密密取 256 個點算出來的。把 stride 換成 32,格子變粗,RoIPool 偏得更多;講義的消融實驗也是 stride 越大,RoIAlign 的好處越明顯。彙整切到 max 時,每個 bin 只有 4 個取樣點,可能剛好錯過峰值,RoIAlign 的數值誤差也會變大,但位置仍然是對齊的;論文說 max 或 average 都可以。講義的例子:原圖 15 像素對到特徵圖是 15 × 25/128 ≈ 2.93 格,RoIPool 取成 2 格。特徵圖數值是示意。
分類不怕小偏移,mask 怕框歪一點,分類還是對的;但 mask 要對到像素,差半格就整片位移。
每類一張「是或不是」Mask 分支替每一類各畫一張 binary mask,用 sigmoid;屬於哪一類交給分類分支。比每個像素用 softmax 搶類別好很多。
SAM:訓練換成提示點一下、畫個框,或打一段文字,同一個模型就切出來。不用為每個任務重新訓練。
細看 · Mask R-CNN、YOLACT、SAM 家族、合流

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 能不能直接解決,只有資料分布特殊或延遲要求極嚴時,才投入資源訓練專用模型。

SEC.05 / MISCONCEPTIONS

常見誤解

這個模組最常被講錯的幾句話。

RoIAlign 只是 RoIPool 的小改進。消融實驗顯示 RoIAlign 對小物體和高 IoU 門檻的影響很大,是 Mask R-CNN 能做像素級 mask 的前提,不是可有可無的細節。
分數(mIoU、mAP)越高的模型,在所有場景都比較好。這些分數是跨類別、跨門檻的平均,可能蓋掉小物體或某一類的嚴重失敗。實務上要按類別、按物體大小分開看。
Transpose convolution 就是把 convolution 反過來,所以叫 deconvolution。它不是反運算,是一種會學的放大卷積。Deconvolution 在訊號處理是另一件事,講義直接說這個名字不好。
SAM 什麼都能切,以後不用再訓練分割模型。SAM 會錯過精細結構、邊界不夠清楚。講義的建議是先試 SAM 3 這類通用模型;資料分布特殊或延遲要求極嚴時,還是要訓練專用模型。