all lessons / computer_vision / 10 · segmentation lesson 10 / 19

Semantic, instance, and panoptic segmentation

语义、实例与全景分割

Lesson 09 localized objects with boxes and learned that a box is the coarsest possible answer to "where?" — it brackets an object but also swallows the background inside the rectangle. Some tasks (a tumor outline, a drivable-road mask, a green-screen matte) need a label at every pixel. That is a fundamentally harder output shape, and earning it forces us to confront a structural tension inside the CNN itself.

第 09 课用边界框定位物体,并让我们明白框是对"在哪里?"这个问题最粗糙的答案——它框住了一个物体,但也把矩形内的背景一起吞了进去。有些任务(勾勒肿瘤轮廓、可行驶道路掩码、绿幕抠像)需要在每一个像素上给出标签。这是一种本质上更难的输出形态,而要拿下它,就迫使我们直面 CNN 内部的一个结构性张力。

The plan计划
Five moves. (1) Name the three segmentation tasks precisely, because their outputs differ and so do their metrics. (2) Expose the core tension — deep features are semantic but coarse, shallow features are detailed but dumb — and show why dense prediction needs an encoder-decoder. (3) Build the decoder for real: the three ways to upsample (transposed conv, bilinear+conv, unpooling), U-Net skip connections, and atrous/dilated convolution as the alternative that keeps resolution without downsampling. (4) Derive the Dice loss and show with numbers why it beats cross-entropy under class imbalance; define mIoU and Panoptic Quality. (5) Arrive at SAM/SAM2 — promptable segmentation — which reframes the whole task. 五步走。(1) 精确地定义三种分割任务,因为它们的输出不同,度量指标也随之不同。(2) 揭示核心张力——深层特征语义强但粗糙,浅层特征细节多却"没脑子"——并说明为什么稠密预测需要编码器-解码器。(3) 动真格地搭建解码器:三种上采样方式(转置卷积、双线性+卷积、反池化)、U-Net 跳跃连接,以及作为替代方案、无需下采样即可保持分辨率的空洞/膨胀卷积。(4) 推导 Dice 损失,并用数字说明它为何在类别不平衡下胜过交叉熵;定义 mIoU 与全景质量(Panoptic Quality)。(5) 抵达 SAM/SAM2——可提示分割——它重新定义了整个任务。

1 · Three tasks, three output shapes

1 · 三种任务,三种输出形态

"Segmentation" is three problems wearing one name. They differ in what a pixel's label is allowed to mean.

"分割"是三个共用一个名字的问题。它们的区别在于一个像素的标签被允许表示什么含义。

Task任务Output per pixel每像素的输出Separates instances?区分实例吗?Example示例
SemanticOne class label (road, car, sky).一个类别标签(道路、汽车、天空)。No — two touching cars become one "car" blob.否——两辆相接的车会变成一整块"汽车"区域。Drivable-surface mask for a robot.机器人用的可行驶区域掩码。
InstanceA mask per detected object, with an identity.为每个检测到的物体给出一个带身份标识的掩码。Yes — car #1 and car #2 are distinct masks.是——1 号车和 2 号车是两个不同的掩码。Count and outline every cell in a microscope image.对显微图像中的每个细胞计数并勾勒轮廓。
PanopticEvery pixel gets both a class and (for countable "things") an instance id.每个像素同时获得一个类别,以及(对可数的"things"而言)一个实例 id。Yes for things; "stuff" (road, sky) is uncountable and just gets a class.对 things 是;"stuff"(道路、天空)不可数,只获得一个类别。Full scene parse for autonomous driving.自动驾驶所需的完整场景解析。

The vocabulary that makes panoptic precise: "things" are countable objects (people, cars) where instance identity matters; "stuff" is amorphous regions (sky, grass, road) where counting is meaningless. Semantic segmentation handles both but cannot separate two adjacent things; instance segmentation separates things but ignores stuff; panoptic unifies them — every pixel gets exactly one (class, id) pair with no overlaps and no gaps. Hold onto that "no overlaps, no gaps" constraint; it is what makes the panoptic metric in section 4 non-trivial.

让全景分割变精确的术语:"things"是可数的物体(人、汽车),其实例身份很重要;"stuff"是无固定形态的区域(天空、草地、道路),对它计数毫无意义。语义分割两者都能处理,但无法区分两个相邻的 thing;实例分割能区分 things,却忽略 stuff;全景分割把二者统一起来——每个像素恰好获得一个 (类别, id) 对,既无重叠也无空隙。记住"无重叠、无空隙"这个约束;正是它让第 4 节的全景度量变得不平凡。

2 · Why dense prediction needs an encoder-decoder

2 · 为什么稠密预测需要编码器-解码器

Recall the receptive-field story from lesson 06: stacking strided convolutions and pooling grows each unit's receptive field, so deep layers "see" a large region and can answer what is this? But that same downsampling throws away spatial resolution. A 224×224 image after a typical backbone is a 7×7 feature grid — each cell summarizes a ~32×32 image patch. That grid is rich in semantics (it knows "dog") and useless for boundaries (it cannot tell you which of the 1024 pixels in that patch are dog vs grass).

回忆第 06 课关于感受野的故事:堆叠带步幅的卷积和池化会增大每个单元的感受野,于是深层"看"到一大片区域,能够回答这是什么?但同样的下采样也丢弃了空间分辨率。一张 224×224 的图像经过典型骨干网络后是一个 7×7 的特征网格——每个格子概括了一个约 32×32 的图像块。这个网格语义丰富(它知道"狗"),却对边界毫无用处(它无法告诉你那个块里的 1024 个像素中哪些是狗、哪些是草)。

This is the central tension of dense prediction, and it is worth stating as a law:

这是稠密预测的核心张力,值得当作一条定律来陈述:

The depth/resolution trade深度/分辨率的权衡
Deep = semantics, shallow = detail. Going deeper trades spatial resolution for semantic abstraction. Classification only needs the semantics, so it happily collapses to a 7×7 grid and then a single vector. Segmentation needs the semantics and the original resolution back. You cannot get both from one layer — you must combine a deep layer (knows the class) with shallow layers (know the edges). 深层=语义,浅层=细节。越往深走,就是用空间分辨率去换取语义抽象。分类只需要语义,所以它乐得坍缩成一个 7×7 网格、再变成一个单一向量。分割则既需要语义,需要把原始分辨率找回来。你无法从单一层里同时得到两者——你必须把一个深层(知道类别)与若干浅层(知道边缘)结合起来。

2.5 · Interactive · downsample → upsample → skip

2.5 · 交互 · 下采样 → 上采样 → 跳跃连接

This is the lesson's central claim made tangible: downsampling averages a crisp boundary into blocky cells, bilinear upsampling can only smear those blocks back out (it cannot invent the lost edge), and a skip connection hands the decoder the original sharp boundary that downsampling destroyed. Raise the downsample factor and watch the boundary error climb; toggle the skip and watch it collapse.

这是把本课的核心论断变得可触摸:下采样把一条锐利的边界平均成块状的格子,双线性上采样只能把这些块重新涂抹开(它无法凭空造出丢失的边缘),而跳跃连接把下采样所摧毁的那条原始锐利边界交还给解码器。调大下采样倍率,看边界误差攀升;切换跳跃连接,看它骤然坍塌。

Why downsampling loses boundaries — and how skips give them back
为什么下采样会丢失边界——以及跳跃连接如何把它们找回来
Left: the original crisp shape on a fine grid. Middle: downsampled to a coarse grid (deep/semantic, low-res), then bilinearly upsampled — a blurred boundary. Right: the decoder output. With skip OFF it is just the blurry upsample; with skip ON the shallow-layer boundary is overlaid, recovering the crisp edge. The KPI counts boundary pixels the upsample gets wrong vs the ground truth.
左:细网格上的原始锐利形状。中:下采样到粗网格(深层/语义、低分辨率),再做双线性上采样——一条模糊的边界。右:解码器输出。跳跃连接 OFF 时它只是模糊的上采样;跳跃连接 ON 时叠加了浅层边界,锐利边缘得以恢复。KPI 统计上采样相对真值判错的边界像素数。
Downsample factor
下采样倍率
×8
Coarse grid
粗网格
Boundary error (px)
边界误差(像素)
Skip
跳跃连接
OFF
Show the core JS查看核心代码
// downsample: average each f×f block → coarse cell (the "deep, low-res" map)
function downsample(mask, N, f){
  const M = N / f, coarse = [];
  for (let r=0;r<M;r++){ coarse[r]=[];
    for (let c=0;c<M;c++){ let s=0;
      for (let dr=0;dr<f;dr++) for (let dc=0;dc<f;dc++) s += mask[r*f+dr][c*f+dc];
      coarse[r][c] = s/(f*f);                 // fractional → boundary detail is gone
    }}
  return coarse;
}
// bilinear upsample back to N×N: smears blocks, cannot recreate the lost edge
function bilinear(coarse, M, N){ /* distance-weighted avg of 4 nearest coarse cells */ }
// skip ON → decoder uses the ORIGINAL fine mask's boundary, which the coarse map lost

So a segmentation network has two halves. The encoder (a classification backbone like a ResNet) downsamples, building semantics while shedding resolution. The decoder reverses that: it upsamples the coarse semantic map back to full image resolution, pulling in detail from shallow encoder layers as it goes. The Fully Convolutional Network (FCN, Long et al. 2015) was the first to make this explicit: replace the classifier's final fully-connected layers with convolutions and an upsampling stage, so the network maps an image to a same-shape label map.

所以一个分割网络有两半。编码器(像 ResNet 这样的分类骨干网络)做下采样,一边构建语义、一边丢弃分辨率。解码器则将其逆转:它把粗糙的语义图上采样回完整的图像分辨率,一路从浅层编码器特征中拉入细节。全卷积网络(FCN,Long 等,2015)第一个把这一点讲明白:用卷积和一个上采样阶段替换分类器最后的全连接层,让网络把一张图像映射到一张同形状的标签图。

input H×W H/2 H/4 H/8 semantics ENCODER — downsample, gain semantics, lose resolution → H/4 H/2 mask H×W ← DECODER — upsample, recover detail skip: copy shallow detail forward ↑×2 decoder = the part lesson 09 never had

3 · Building the decoder for real

3 · 动真格地搭建解码器

"Upsample" is the word that hides all the work. A 7×7 grid has 49 cells; the output needs ~50,000 pixels. Where do the in-between values come from? There are three mechanisms, and knowing the difference is a common interview probe.

"上采样"这个词把所有的活儿都藏了起来。一个 7×7 网格有 49 个格子;而输出需要约 50,000 个像素。中间那些值从哪儿来?有三种机制,弄清它们的区别是常见的面试考点。

3a · Transposed convolution (learned upsampling)

3a · 转置卷积(可学习的上采样)

A normal (strided) convolution maps many input pixels to one output. A transposed convolution — sometimes loosely called "deconvolution," though it does not invert anything — runs that mapping backward: it takes each input value, multiplies it by a learned kernel, and scatters the result into a larger output grid, summing where scatters overlap. With stride 2 it doubles spatial size. The kernel weights are learned, so the network learns how to upsample rather than using a fixed rule.

一次普通的(带步幅的)卷积把许多输入像素映射到一个输出。转置卷积——有时被含糊地称作"反卷积(deconvolution)",尽管它并不还原任何东西——把这个映射反着跑:它取每个输入值,乘以一个可学习的卷积核,再把结果散布到一个更大的输出网格上,在散布重叠处求和。步幅为 2 时它把空间尺寸翻倍。卷积核权重是学出来的,所以网络学的是如何上采样,而不是套用一条固定规则。

The cost: where strided scatters overlap unevenly, you get the infamous checkerboard artifact — a grid of brighter/darker squares, because some output pixels receive more kernel contributions than others (e.g. a 3×3 kernel at stride 2, in 2-D, gives some output pixels 1 kernel tap and others up to 4). The fix is either a kernel size divisible by the stride, or the next option.

代价是:当带步幅的散布不均匀地重叠时,你会得到臭名昭著的棋盘格伪影——一片明暗相间的方格,因为有些输出像素接收到的卷积核贡献比别的多(例如二维下步幅为 2 的 3×3 卷积核,会让有些输出像素只接收 1 个核抽头、有些则多达 4 个)。修复办法要么让卷积核尺寸能被步幅整除,要么改用下一个方案。

A 1-D worked example makes the uneven overlap concrete. Take a length-2 input [a, b], a stride-2 transposed conv with a width-3 kernel [k0, k1, k2]. Each input value is scattered into the output, centered on its strided position, then overlaps are summed. Input a sits at output position 0 and paints positions {−1, 0, 1}; input b sits at output position 2 and paints {1, 2, 3}:

一个一维的算例把不均匀的重叠讲具体。取一个长度为 2 的输入 [a, b],一个步幅为 2、核宽为 3 的转置卷积 [k0, k1, k2]。每个输入值都以其带步幅的位置为中心散布到输出上,然后重叠处求和。输入 a 位于输出位置 0,涂到位置 {−1, 0, 1};输入 b 位于输出位置 2,涂到 {1, 2, 3}:

out[0] = a·k1   (1 tap)
out[1] = a·k2 + b·k0   (2 taps — overlap)
out[2] = b·k1   (1 tap)
out[3] = b·k2   (1 tap)

The positions that fall between two strided centers receive two kernel taps; the others receive only one. Even with a constant input (a = b) and constant kernel, the output alternates 1·k vs 2·k — a periodic bright/dark ripple with no signal behind it. In 2-D this overlap count is the product of the per-axis counts, so a stride-2 3×3 kernel gives output pixels 1, 2, or 4 taps → the familiar checkerboard tiling. A kernel width that is a multiple of the stride makes every output position receive the same tap count, which is why "kernel divisible by stride" removes the artifact.

落在两个带步幅中心之间的位置接收两个核抽头;其余位置只接收一个。即便输入恒定(a = b)、核也恒定,输出仍会在 1·k 与 2·k 之间交替——一圈周期性的明暗涟漪,其背后并无任何信号。在二维下,这个重叠计数是各轴计数的乘积,所以步幅为 2 的 3×3 卷积核会让输出像素获得 1、2 或 4 个抽头 → 就是那熟悉的棋盘格铺排。让核宽是步幅的整数倍,就能使每个输出位置接收到相同的抽头数,这正是"核能被步幅整除"消除该伪影的原因。

3b · Bilinear upsample + convolution (the modern default)

3b · 双线性上采样 + 卷积(现代默认做法)

Separate the two jobs. First resize the feature map with a fixed, parameter-free interpolation — bilinear: each new pixel is a distance-weighted average of its four nearest source pixels. This is smooth and artifact-free but blurry and learns nothing. Then apply an ordinary 3×3 convolution to sharpen and let the network add learnable structure on top of the smooth resize. "Resize-then-conv" avoids checkerboards and is the standard decoder upsampling step today.

把两件事分开。先用一种固定、无参数的插值——双线性——缩放特征图:每个新像素是它最近的四个源像素的距离加权平均。这平滑、无伪影,却模糊、且什么都学不到。然后施加一次普通的 3×3 卷积来锐化,让网络在平滑缩放之上叠加可学习的结构。"先缩放再卷积(resize-then-conv)"避开了棋盘格,是当今标准的解码器上采样步骤。

3c · Unpooling (remember where the max was)

3c · 反池化(记住最大值在哪里)

If the encoder used max-pooling, it threw away everything except the maximum in each window — and crucially, where that maximum was. Max-unpooling records those argmax positions on the way down and, on the way up, places each value back at its remembered location, zeroing the rest. This restores sharp boundaries cheaply (SegNet used it) because boundary pixels are exactly the high-response locations pooling kept.

如果编码器用了最大池化,它就丢弃了每个窗口里除最大值以外的一切——而关键在于,也丢弃了那个最大值在哪里最大反池化(max-unpooling)在下行时记录这些 argmax 位置,在上行时把每个值放回它被记住的位置,其余置零。这以很低的代价恢复了锐利的边界(SegNet 用了它),因为边界像素恰恰就是池化所保留的那些高响应位置。

Upsampler上采样方式Learns?可学习?Artifact risk伪影风险Use it when何时使用
Transposed conv转置卷积Yes (kernel)是(卷积核)Checkerboard if size not divisible by stride核尺寸不能被步幅整除时会出现棋盘格You want a fully-learned decoder and control the kernel/stride.你想要一个完全可学习的解码器,并能掌控核/步幅。
Bilinear + conv双线性 + 卷积Conv only仅卷积部分None (smooth)无(平滑)Default. Robust, simple, used in DeepLabv3+, most modern decoders.默认选择。鲁棒、简单,DeepLabv3+ 及大多数现代解码器都在用。
Max-unpooling最大反池化NoSparse/blocky output输出稀疏/块状Encoder used max-pool and boundary sharpness is the priority.编码器用了最大池化,且边界锐度是首要目标。

3d · U-Net skip connections — give the decoder back what the encoder discarded

3d · U-Net 跳跃连接——把编码器丢掉的东西还给解码器

Upsampling alone cannot invent detail it never had: a 7×7 semantic map blown up to full size is just a blurry colored blob. The U-Net insight (Ronneberger et al. 2015, originally for biomedical images) is to copy each encoder feature map across to the matching-resolution decoder stage and concatenate it. The decoder layer at resolution H/4 receives both the upsampled-semantic features (knows "this region is liver") and the original H/4 encoder features (knows "the edge is here"). It fuses class and boundary in one place. This is why U-Net dominates medical segmentation, where boundaries are the whole point and training sets are tiny.

仅靠上采样无法凭空造出它从未拥有的细节:把一个 7×7 的语义图放大到全尺寸,只是一团模糊的彩色斑块。U-Net 的洞见(Ronneberger 等,2015,最初用于生物医学图像)是把每一层编码器特征图复制到分辨率相匹配的解码器阶段并做拼接。分辨率为 H/4 的解码器层同时接收到上采样后的语义特征(知道"这块区域是肝脏")原始的 H/4 编码器特征(知道"边缘在这里")。它在同一个地方融合了类别与边界。这正是 U-Net 在医学分割中占据主导地位的原因——那里边界就是全部意义所在,而训练集又非常小。

Why skips, not just a deeper decoder为什么用跳跃连接,而不只是更深的解码器
Skip connections are not an optimization trick like residuals — they carry information that downsampling destroyed. No amount of decoder depth can reconstruct the exact pixel where a boundary sat once that pixel was averaged into a 32×32 cell. The shallow feature map still has it; the skip simply hands it over. 跳跃连接不是像残差那样的优化技巧——它们携带的是被下采样摧毁的信息。一旦某个边界所在的像素被平均进一个 32×32 的格子里,再深的解码器也无法重建出它究竟落在哪个像素上。而浅层特征图里还保留着它;跳跃连接只是把它直接交出来。

3e · Atrous / dilated convolution — keep resolution instead of recovering it

3e · 空洞/膨胀卷积——保住分辨率,而不是把它找回来

A different philosophy: what if we never downsampled so aggressively in the first place? A dilated (atrous, French for "with holes") convolution inserts gaps between kernel taps. A 3×3 kernel with dilation rate r=2 samples input pixels at offsets {−2, 0, +2} in each axis — it covers a 5×5 region but still uses only 9 weights. Tie this directly to lesson 06's receptive-field math: dilation multiplies the effective kernel extent by r, so receptive field grows without stride and without losing resolution.

另一种哲学:如果我们一开始就不那么激进地下采样呢?膨胀(atrous,法语意为"带孔")卷积在卷积核抽头之间插入间隙。一个膨胀率 r=2 的 3×3 卷积核,在每个轴上以偏移 {−2, 0, +2} 采样输入像素——它覆盖了一个 5×5 的区域,却仍然只用 9 个权重。把这直接接到第 06 课的感受野公式上:膨胀把有效核尺寸乘以 r,于是感受野在用步幅、丢分辨率的情况下增长。

effective kernel size = k + (k − 1)(r − 1)    ⟹    3 + (2)(r−1) = 3, 5, 7, … for r = 1, 2, 3

DeepLab stacks dilated convs and adds ASPP (Atrous Spatial Pyramid Pooling): several parallel dilated convs at different rates, capturing context at multiple scales without ever dropping below, say, H/8 resolution. The trade vs U-Net: dilation keeps a large feature map throughout (more memory, more compute) but needs less decoder; skips downsample then reconstruct (cheaper feature maps, more decoder machinery). Both reach full resolution; they just pay for it differently.

DeepLab 堆叠膨胀卷积,并加入 ASPP(空洞空间金字塔池化,Atrous Spatial Pyramid Pooling):若干条不同膨胀率的并行膨胀卷积,在多个尺度上捕获上下文,同时分辨率始终不掉到比如 H/8 以下。与 U-Net 相比的权衡:膨胀卷积自始至终保持一张大的特征图(更多内存、更多计算),但需要的解码器更少;跳跃连接则是先下采样再重建(特征图更省,但解码器机构更多)。两者都能到达完整分辨率,只是付出的代价方式不同。

For instance segmentation the dominant pattern is different again: Mask R-CNN takes the lesson-09 detector (boxes + classes), and for each detected box adds a small fully-convolutional mask head that predicts a binary mask inside that box. Detection localizes; the mask head refines to pixels. That is why instance segmentation inherits all of detection's machinery (anchors/NMS or set prediction) plus a per-region decoder.

实例分割,主流范式又不一样:Mask R-CNN 拿来第 09 课的检测器(边界框 + 类别),并为每个检测到的框加上一个小型的全卷积掩码头,在该框内预测一个二值掩码。检测负责定位;掩码头把结果细化到像素。这正是实例分割为何会继承检测的全套机构(锚框/NMS 或集合预测)再加上一个逐区域解码器的原因。

4 · Losses and metrics under class imbalance

4 · 类别不平衡下的损失与度量

The natural loss is per-pixel cross-entropy (lesson 08): treat each pixel as an independent classification. It works, but it has a fatal weakness on the tasks segmentation is often used for. Consider a 256×256 medical image where a tumor occupies 500 pixels out of 65,536 — 0.76% foreground. A model that predicts "background everywhere" gets 99.24% pixel accuracy and near-zero cross-entropy, while being clinically worthless. Cross-entropy averages over pixels, so the 65,000 easy background pixels drown out the 500 that matter.

最自然的损失是逐像素交叉熵(第 08 课):把每个像素当作一次独立的分类。它能用,但在分割经常服务的那类任务上有一个致命弱点。设想一张 256×256 的医学图像,肿瘤在 65,536 个像素中只占 500 个——前景占 0.76%。一个"到处都预测背景"的模型能拿到 99.24% 的像素精度和近乎零的交叉熵,却在临床上毫无价值。交叉熵在像素上取平均,于是那 65,000 个容易的背景像素把真正重要的那 500 个淹没了。

Dice loss — optimize overlap, not per-pixel correctness

Dice 损失——优化重叠,而非逐像素的对错

The Dice coefficient measures overlap between predicted mask P and ground truth G directly, as a ratio that ignores the size of the background:

Dice 系数直接度量预测掩码 P 与真值 G 之间的重叠,是一个忽略背景大小的比值:

Dice = 2 |P ∩ G| / (|P| + |G|)    ⟹    Dice loss = 1 − Dice

In a network we need this differentiable, so we use soft probabilities pi ∈ [0,1] and one-hot targets gi, with a small ε for numerical safety:

在网络中我们需要它可微,所以改用概率 pi ∈ [0,1] 和独热目标 gi,并加上一个小的 ε 以保证数值安全:

Dice = (2 ∑i pi gi + ε) / (∑i pi + ∑i gi + ε)

Worked numbers, the imbalanced tumor. Take a toy 10-pixel image, 2 pixels are tumor (g = 1), 8 are background. A lazy model outputs p=0.1 everywhere.

算个具体数,不平衡的肿瘤。取一张玩具般的 10 像素图像,2 个像素是肿瘤(g = 1),8 个是背景。一个偷懒的模型到处都输出 p=0.1。

That is the whole reason Dice (and its cousins: Tversky, which weights false positives vs false negatives asymmetrically; focal Dice; or a Dice+CE sum) wins on imbalanced segmentation. In practice people often combine Dice (overlap-aware, imbalance-robust) with cross-entropy (smoother gradients, calibrated per-pixel confidence) and get the best of both.

这就是 Dice(以及它的一众亲戚:非对称加权假正例与假负例的 Tversky;focal Dice;或 Dice+CE 之和)在不平衡分割上胜出的全部原因。实践中人们常把 Dice(关注重叠、对不平衡鲁棒)与交叉熵(梯度更平滑、逐像素置信度经过校准)组合起来,兼取两者之长。

The metrics: mIoU and Panoptic Quality

度量指标:mIoU 与全景质量

IoU (Intersection-over-Union, lesson 09) reused per-class: for class c, IoU = (pixels correctly labeled c) / (pixels predicted c OR truly c) = TP / (TP + FP + FN) in pixels. One line of arithmetic: a class with TP = 40 correctly-labeled pixels, FP = 10 over-predicted, and FN = 10 missed scores IoU = 40 / (40 + 10 + 10) = 40/60 ≈ 0.667. mIoU (mean IoU) averages this over all classes — and because it averages over classes, a rare class counts as much as a common one, which is exactly what you want and why mIoU is the standard semantic-segmentation metric.

IoU(交并比,第 09 课)按类复用:对类别 c,IoU =(被正确标为 c 的像素)/(预测为 c 或真实为 c 的像素)= 以像素计的 TP / (TP + FP + FN)。一行算术:某个类有 TP = 40 个正确标注的像素、FP = 10 个多预测的、FN = 10 个漏掉的,得分 IoU = 40 / (40 + 10 + 10) = 40/60 ≈ 0.667mIoU(平均 IoU)在所有类别上对此取平均——而由于它是在类别上取平均,一个稀有类和一个常见类的权重一样重,这恰恰是你想要的,也是 mIoU 成为语义分割标准指标的原因。

Panoptic Quality (PQ) scores the unified task. You match each predicted segment to a ground-truth segment when their IoU > 0.5 (this threshold guarantees a unique match). Then:

全景质量(PQ)为统一任务打分。当一个预测片段与一个真值片段的 IoU > 0.5 时就将二者匹配(这个阈值保证了匹配是唯一的)。然后:

PQ = [ ∑(p,g)∈TP IoU(p,g) ] / ( |TP| + ½|FP| + ½|FN| ) = SQ × RQ

This factors cleanly into two interpretable parts: SQ (Segmentation Quality) = average IoU over matched segments — how good are the masks you got right? — and RQ (Recognition Quality) = |TP| / (|TP| + ½|FP| + ½|FN|), which is the F1 score over segments — did you find the right segments at all? A model can have high SQ (crisp masks) but low RQ (misses many objects), or vice versa, and PQ exposes which.

它干净地分解为两个可解释的部分:SQ(分割质量,Segmentation Quality)= 匹配片段上的平均 IoU——你做对的那些掩码有多好?——以及 RQ(识别质量,Recognition Quality)= |TP| / (|TP| + ½|FP| + ½|FN|),也就是片段层面的 F1 分数——你究竟有没有找到正确的片段?一个模型可以有高 SQ(掩码锐利)却低 RQ(漏掉很多物体),或反之,而 PQ 会把是哪一种暴露出来。

Worked numbers. Suppose a scene gives 2 true-positive segments matched at IoU 0.8 and 0.6, plus 1 false positive (a predicted segment matching nothing) and 1 false negative (a real segment we missed). Then SQ = (0.8 + 0.6)/2 = 0.70 (average IoU over the matched segments) and RQ = |TP| / (|TP| + ½|FP| + ½|FN|) = 2 / (2 + 0.5 + 0.5) = 2/3 ≈ 0.667. So PQ = SQ × RQ = 0.70 × 0.667 ≈ 0.467. Check it against the direct one-line formula: PQ = (∑TP IoU) / (|TP| + ½|FP| + ½|FN|) = (0.8 + 0.6) / (2 + 0.5 + 0.5) = 1.4 / 3 ≈ 0.467 — the two routes agree, which is exactly the PQ = SQ × RQ factorization made arithmetic.

算个具体数。假设一个场景给出 2 个真正例片段,匹配 IoU 分别为 0.80.6,外加 1 个假正例(一个匹配不到任何东西的预测片段)和 1 个假负例(一个我们漏掉的真实片段)。那么 SQ = (0.8 + 0.6)/2 = 0.70(匹配片段上的平均 IoU),RQ = |TP| / (|TP| + ½|FP| + ½|FN|) = 2 / (2 + 0.5 + 0.5) = 2/3 ≈ 0.667。于是 PQ = SQ × RQ = 0.70 × 0.667 ≈ 0.467。用那条直接的一行公式核对一下:PQ = (∑TP IoU) / (|TP| + ½|FP| + ½|FN|) = (0.8 + 0.6) / (2 + 0.5 + 0.5) = 1.4 / 3 ≈ 0.467——两条路径结果一致,这正是 PQ = SQ × RQ 这个分解在算术上的体现。

Trap陷阱
Good mIoU can still fail the product if errors land on thin structures, boundaries, tiny defects, rare classes, or safety-critical slices — mIoU averages those away. Always slice the metric by region type and size before declaring victory, and add a boundary metric (e.g. boundary-IoU) when contours are the deliverable. 如果误差落在细长结构、边界、微小缺陷、稀有类别或安全攸关的切片上,漂亮的 mIoU 仍可能让产品翻车——mIoU 会把这些平均掉。宣布胜利之前,务必按区域类型和大小对指标做切片分析,当轮廓才是交付目标时,再加一个边界指标(例如 boundary-IoU)。

5 · Promptable segmentation: SAM and SAM2

5 · 可提示分割:SAM 与 SAM2

Everything above assumes a fixed, closed set of classes baked in at training time. The Segment Anything Model (SAM, Meta 2023) reframes the task: instead of "label every pixel with one of N classes," it answers "given a prompt — a click, a box, a rough mask — return the mask of the thing being pointed at," for any object, class-agnostic. The architecture has three parts that map cleanly onto this lesson:

以上一切都假设有一组在训练时就固化好的、封闭的类别。Segment Anything Model(SAM,Meta 2023)重新定义了任务:它回答的不是"用 N 个类别之一给每个像素打标签",而是"给定一个提示——一次点击、一个框、一个粗略掩码——返回被指向的那个东西的掩码",面向任意物体、与类别无关。它的架构有三个部分,恰好能干净地对应到本课:

The split matters for product design: encode the image once, then interact in real time as the user clicks. SAM was trained on ~1B masks via a data engine (model proposes, humans correct, model retrains). SAM2 (2024) extends this to video by adding a memory bank so a single click propagates a mask across frames — which is precisely the identity-over-time problem the next lesson tackles. Promptable models also change the annotation workflow: they become fast interactive labelers and generic mask proposers, shifting the engineering question from "train a closed-set head" to "manage prompt ambiguity, mask-proposal quality, and how much human correction remains."

这种拆分对产品设计很重要:图像只编码一次,随后随着用户点击进行实时交互。SAM 通过一个数据引擎(模型提议、人工修正、模型再训练)在约 10 亿个掩码上训练。SAM2(2024)通过加入一个记忆库把它扩展到视频,使得单次点击就能把一个掩码在各帧之间传播——这正是下一课要攻克的"跨时间保持身份"的问题。可提示模型还改变了标注工作流:它们成为快速的交互式标注器和通用的掩码提议器,把工程问题从"训练一个封闭集的头"转变为"管理提示的歧义、掩码提议的质量,以及还需要多少人工修正"。

Where this points next

这将指向何处

A mask answers "which pixels?" beautifully, but it is still a static, per-frame, unstructured answer. It does not tell you the 17 joints of a person and how they articulate, and — critically — when SAM2 had to propagate a mask across video frames, it needed something masks alone do not provide: a notion of the same object over time. Lesson 11 turns to sparse structured outputs: keypoints and pose (predicting specific landmarks as heatmaps, decoded with soft-argmax), then tracking — detecting objects per frame and stitching them into persistent identities with motion models and data association. And it will end by noticing that associating identity by appearance is, at bottom, a distance-in-an-embedding-space problem — which is lesson 12.

掩码把"是哪些像素?"回答得很漂亮,但它仍然是一个静态、逐帧、无结构的答案。它不会告诉你一个人的 17 个关节以及它们如何联动,而且——关键在于——当 SAM2 不得不把一个掩码在视频各帧间传播时,它需要一样掩码本身并不提供的东西:跨时间的同一个物体这一概念。第 11 课转向稀疏结构化输出:关键点与姿态(把特定地标预测成热力图,用 soft-argmax 解码),然后是跟踪——逐帧检测物体,并用运动模型和数据关联把它们缝合成持续的身份。它最后会注意到,按外观来关联身份,本质上是一个"嵌入空间中的距离"问题——这就是第 12 课。

Takeaway要点
Dense prediction needs both halves of the network because deep features carry semantics and shallow features carry detail, and no single layer has both. The decoder — built from transposed conv, bilinear+conv, or unpooling — upsamples the coarse semantic map, and skip connections hand back the boundary detail downsampling destroyed; dilated convolution is the alternative that grows receptive field without losing resolution. On the imbalanced tasks segmentation often serves, Dice loss beats cross-entropy because it scores overlap as a whole and cannot be diluted by a huge correct background. Report mIoU (averaged over classes) for semantic and PQ = SQ × RQ for panoptic, and slice by region. SAM/SAM2 reframes the task as promptable, class-agnostic mask prediction via an image encoder + prompt encoder + fast mask decoder. 稠密预测需要网络的两半,因为深层特征承载语义、浅层特征承载细节,没有哪一层能兼具两者。解码器——由转置卷积、双线性+卷积或反池化搭成——把粗糙的语义图上采样,而跳跃连接把下采样摧毁的边界细节交还回来;膨胀卷积则是那个在不丢分辨率的前提下增大感受野的替代方案。在分割常服务的不平衡任务上,Dice 损失胜过交叉熵,因为它把重叠作为整体来打分,不会被一大片正确的背景稀释。语义分割报告 mIoU(在类别上取平均),全景分割报告 PQ = SQ × RQ,并按区域做切片。SAM/SAM2 把任务重新定义为可提示、与类别无关的掩码预测,经由图像编码器 + 提示编码器 + 快速掩码解码器实现。

Interview prompts

面试题