在上一篇文章中,我们精读了 CLIP。

CLIP 的核心思想是:使用大规模图文对训练图像编码器和文本编码器,让图像和文本进入同一个语义空间。它让 ViT 不再只是一个图像分类 backbone,而是成为多模态模型中的视觉编码器。

这一篇我们继续看另一个非常重要的视觉基础模型:SAM,Segment Anything Model。

如果说 CLIP 让 ViT 学会了“图像和语言如何对齐”,那么 SAM 则让 ViT 进入了另一个更加基础的视觉任务:图像分割。

SAM 对应的论文是 Segment Anything,由 Alexander Kirillov、Nikhila Ravi、Ross Girshick 等人提出,发表于 ICCV 2023。论文提出了一个新的分割任务、一个可提示的分割模型,以及一个超大规模分割数据集 SA-1B;SA-1B 包含约 1100 万张图像和超过 10 亿个 mask。

一、为什么需要 SAM?

在 SAM 之前,图像分割已经有很多成熟任务,例如语义分割、实例分割、全景分割和交互式分割。语义分割关注的是:

每个像素属于哪个类别?

例如:

person、car、road、sky、building

实例分割关注的是:

每个目标实例在哪里?

例如同一张图中有三个人,实例分割不仅要知道这些像素属于 person,还要区分 person 1、person 2、person 3。

交互式分割关注的是:用户给一个点或框,模型分割用户想要的目标。

这些任务都很重要,但传统分割模型通常存在一个问题:任务和数据集绑定得很紧。

例如,一个在 COCO 上训练的实例分割模型,通常只能识别 COCO 定义好的类别;一个医学图像分割模型,往往需要专门的医学标注数据;一个遥感图像分割模型,也通常需要专门针对遥感场景重新训练。

SAM 想解决的问题更大:

能不能训练一个通用分割模型,让它面对新图像、新类别、新场景时,也能根据用户提示分割目标?

这就是 SAM 名字中 Segment Anything 的含义:不是只分割固定类别,而是尽可能分割任意图像中的任意对象。

二、SAM 的核心思想:Promptable Segmentation

SAM 最关键的概念是:

Promptable Segmentation

也就是 可提示分割

这里的 prompt 和 NLP 中的 prompt 思想类似。对于大语言模型,我们可以输入一段提示,让模型完成不同任务;对于 SAM,我们可以输入点、框、粗略 mask 等提示,让模型分割对应区域。

SAM 的任务可以抽象为一个函数:

                                                M = f_\theta(I, P)

其中:

I 表示输入图像;
P 表示用户提供的 prompt;
M 表示模型输出的分割 mask;

f_\theta表示 SAM 模型。

也就是说,SAM 不是简单地做:

                                             I \rightarrow M

而是做:

                                           (I, P) \rightarrow M

这一点非常重要。传统分割模型通常只输入图像,然后输出预定义类别的分割结果。SAM 则把用户提示也作为输入,因此它可以根据不同 prompt 输出不同 mask。例如同一张图像中有一只狗和一辆车:

点在狗身上 → 输出狗的 mask
框住汽车 → 输出汽车的 mask
给一个粗略 mask → 输出更精细的 mask

因此,SAM 的核心不是“自动识别所有类别”,而是:根据提示分割用户指定的区域。

论文明确指出,SAM 被设计并训练为 promptable 模型,因此可以 zero-shot 迁移到新的图像分布和任务。

三、SAM 与传统分割模型的区别

可以用一个表格来理解 SAM 和传统分割模型的区别。

对比维度 传统语义 / 实例分割 SAM
输入 图像 图像 + prompt
输出 固定类别 mask 或实例 mask prompt 对应的 mask
类别空间 通常固定 不依赖固定类别
是否需要任务微调 通常需要 可以 zero-shot 使用
交互方式 较弱 支持点、框、mask 等提示
核心目标 针对特定任务分割 构建通用可提示分割模型

因此,SAM 不是简单地替代 DeepLab、Mask R-CNN 或 Mask2Former。它更像是把分割任务重新定义成:

给定图像和提示,输出与提示对应的有效 mask。

这也是它接近视觉基础模型的原因。

四、SAM 的整体结构

SAM 的整体结构可以分为三部分:

模块 作用
Image Encoder 使用 ViT 编码图像,生成图像 embedding
Prompt Encoder 编码点、框、mask 等提示信息
Mask Decoder 融合图像 embedding 和 prompt embedding,输出 mask

官方代码中,SAM 模型本身也由这三个核心组件构成:ImageEncoderViTPromptEncoderMaskDecoder

整体流程可以写成:

输入图像
  ↓
Image Encoder(ViT)
  ↓
图像 embedding

输入 prompt:点 / 框 / mask
  ↓
Prompt Encoder
  ↓
prompt embedding

图像 embedding + prompt embedding
  ↓
Mask Decoder
  ↓
输出 mask

这三个模块的分工非常清楚。

Image Encoder 负责“看懂图像”。

Prompt Encoder 负责“理解用户想分割哪里”。

Mask Decoder 负责“根据图像内容和用户意图生成分割结果”。

五、Image Encoder:ViT 如何成为 SAM 的视觉编码器?

SAM 中最重的模块是 Image Encoder。

论文补充材料中说明,SAM 使用的是经过 MAE 预训练的 ViT,并进行少量改造以适配高分辨率输入。默认强模型使用 ViT-H/16,输入图像通常被缩放和 padding 到 1024×1024,输出是 16 倍下采样的图像 embedding,也就是 64×64 的空间特征图。随后通过卷积将通道维度变为 256。

也就是说,SAM 的图像编码过程可以简化为:

                                                        I \in \mathbb{R}^{1024 \times 1024 \times 3}

经过 ViT 编码后得到:

                                                       E_I \in \mathbb{R}^{64 \times 64 \times 256}

其中:

1024×1024 是输入图像分辨率;
64×64 是图像 embedding 的空间尺寸;
256 是 embedding 通道数;
下采样倍率为 16。

为什么是 16 倍下采样?

因为 ViT-H/16 中 patch size 为 16×16。一张 1024×1024 图像被切成:

                                                       \frac{1024}{16} \times \frac{1024}{16} = 64 \times 64

个 patch。每个 patch 经过 ViT 编码后,对应图像 embedding 中的一个空间位置。所以 SAM 中 ViT 的作用可以理解为:把高分辨率图像转换成一个包含全局语义和空间结构的 dense feature map。

这和 CLIP 中的 ViT 不同。CLIP 中的 ViT 输出的是一个全局图像 embedding,用于和文本 embedding 对齐。SAM 中的 ViT 输出的是一个空间特征图,用于后续生成像素级 mask。

可以这样对比:

模型 ViT 的作用 输出形式
CLIP 图文对齐 全局图像向量
MAE 自监督重建 patch-level 表示
DINO 自蒸馏表征学习 语义特征 / 注意力图
SAM 分割基础模型图像编码器 dense image embedding

这也是本篇文章的核心:ViT 在 SAM 中不再只是分类器,而是成为通用分割系统中的图像编码器。

六、为什么 Image Encoder 可以只运行一次?

SAM 的一个重要设计是:

Image Encoder 很重,但每张图像只需要运行一次。
Mask Decoder 很轻,可以针对不同 prompt 多次运行。

这点非常关键。

用户在同一张图像上可能会点很多次、框很多次、修改提示很多次。如果每次 prompt 改变都重新跑一遍 ViT-H,那么交互速度会非常慢。

SAM 的做法是:

第一步:对图像运行一次 Image Encoder,得到图像 embedding。
第二步:用户每次输入 prompt,只运行 Prompt Encoder + Mask Decoder。

论文补充材料也说明,虽然 image encoder 的计算量很高,但它只需要对每张图像计算一次,而不是对每个 prompt 计算一次;轻量 mask decoder 的计算量很小,适合交互式使用。

因此,在实际部署中可以先缓存图像 embedding:

                                E_I = \text{ImageEncoder}(I)

之后每次用户输入不同 prompt:

                                 \begin{aligned} M_1 &= \text{MaskDecoder}(E_I, P_1) \\ M_2 &= \text{MaskDecoder}(E_I, P_2) \\ M_3 &= \text{MaskDecoder}(E_I, P_3) \end{aligned}

这样就能实现较快的交互式分割。

七、Prompt Encoder:点、框、mask 如何变成 embedding?

SAM 支持多种 prompt。

常见 prompt 包括:

point prompt:点提示
box prompt:框提示
mask prompt:掩码提示

论文中还讨论了 text prompt,但 SAM 主体和实际常用版本主要围绕几何 prompt 展开,即点、框和 mask。

Prompt Encoder 的任务是:

把不同形式的提示统一编码成模型可以理解的 embedding。

SAM 将 prompt 分成两类:

prompt 类型 例子 编码形式
sparse prompt 点、框、文本 稀疏向量 embedding
dense prompt mask 与图像空间对应的 dense embedding

点提示通常包含两部分信息:

点的位置:这个点在哪里?
点的类型:这个点是前景点还是背景点?

例如:

positive point:用户点在目标内部
negative point:用户点在非目标区域

对于一个点 p=(x,y),SAM 会使用位置编码表示它的空间位置,再加上一个可学习的类型 embedding,表示它是前景点还是背景点。论文补充材料说明,点由位置编码与“前景/背景”可学习 embedding 相加表示。

可以写成:

                          e_{\text{point}} = PE(x, y) + e_{\text{type}}

一个 box 可以由左上角和右下角两个点表示:

                             B = (x_1, y_1, x_2, y_2)

SAM 会把 box 编码成两个 corner embedding:

                           \begin{aligned} e_{\text{top-left}} &= PE(x_1, y_1) + e_{\text{top-left-type}} \\ e_{\text{bottom-right}} &= PE(x_2, y_2) + e_{\text{bottom-right-type}} \end{aligned}

论文补充材料说明,box 被表示为一对 embedding:左上角位置编码加上“左上角”类型 embedding,右下角位置编码加上“右下角”类型 embedding。这样,模型不仅知道框的位置,还知道哪个点是左上角、哪个点是右下角。

点和框只是少量坐标,因此可以编码成稀疏 token;mask 本身是一个二维区域,与图像空间对应,因此需要编码成 dense embedding。设输入 mask prompt 为:

                                           P_{\text{mask}} \in \mathbb{R}^{H \times W}

SAM 会用卷积网络对 mask 进行下采样和通道映射,使其变成与 image embedding 相同空间尺度和通道维度的 dense embedding。论文补充材料中说明,mask prompt 会先以比输入图像低 4 倍的分辨率输入,再经过两层 stride-2 卷积继续下采样,最后用 1×1卷积映射到 256 维,并与 image embedding 相加。

                                E_P^{\text{dense}} = \text{ConvEncoder}(P_{\text{mask}})

可以抽象写成:

                                  E_I' = E_I + E_P^{\text{dense}}

如果没有 mask prompt,模型会使用一个可学习的 “no mask” embedding 加到每个图像 embedding 位置上。

八、Mask Decoder:如何根据提示生成 mask?

SAM 的 Mask Decoder 是一个轻量级 decoder。它接收两类输入:

image embedding:来自 ViT 图像编码器
prompt embedding:来自 Prompt Encoder

输出:

一个或多个分割 mask
对应的 mask 质量预测分数

论文补充材料中描述,SAM 的 mask decoder 是一个两层 transformer decoder,它通过 self-attention 和 cross-attention 更新 prompt tokens 与 image embedding,最后通过动态 mask prediction head 生成 mask。

可以把 Mask Decoder 理解成:

                                 M = D(E_I, E_P)

其中:

D 是 mask decoder;
E_I是图像 embedding;
E_P是 prompt embedding;
M 是输出 mask。

为什么需要 Transformer decoder?

因为 SAM 需要解决的是 prompt-guided segmentation。也就是说,模型不仅要看图像,还要理解 prompt 和图像区域之间的关系。例如用户给一个点:

这个点附近是什么目标?
这个点属于前景还是背景?
这个点所在区域的边界在哪里?

这些问题需要 prompt token 和 image embedding 之间进行交互。Transformer decoder 的 cross-attention 很适合做这件事:

prompt tokens 作为 query
image embedding 作为 key/value
从图像中查找与 prompt 相关的区域

论文补充材料中说明,每个 decoder layer 包含四步:tokens 上的 self-attention、tokens 到 image embedding 的 cross-attention、token MLP,以及 image embedding 到 tokens 的 cross-attention。可以简化理解为:

prompt token 先彼此交流
prompt token 再去图像特征中寻找相关区域
图像特征再反过来吸收 prompt 信息
最后输出 mask


2. 为什么 decoder 是轻量的?

因为 SAM 的 Image Encoder 已经提取了强大的图像表示。Mask Decoder 不需要重新理解整张图像,它只需要根据 prompt 从图像 embedding 中取出目标区域。所以 SAM 的计算设计是:

重编码器 + 轻解码器

这和 MAE 中的“重 encoder + 轻 decoder”有点相似,但目的不同。MAE 的轻 decoder 用于重建像素。SAM 的轻 decoder 用于根据 prompt 生成 mask。

九、SAM 的损失函数如何理解?

SAM 的训练目标主要包括两部分:

mask prediction loss
IoU prediction loss

可以写成:

\mathcal{L} = \mathcal{L}_{mask} + \lambda \mathcal{L}_{iou}

其中:

$ \mathcal{L}_{mask} $用于监督分割 mask;

$ \mathcal{L}_{iou} $用于监督模型预测 mask 质量;

\lambda是权重系数

论文补充材料说明,SAM 使用 focal loss 和 dice loss 的线性组合监督 mask prediction,并用均方误差训练 IoU prediction head。

1. Focal Loss

分割任务本质上可以看作每个像素的二分类:

属于目标区域:1
不属于目标区域:0

普通二分类交叉熵可以写成:

\mathcal{L}_{BCE} = - [y \log(p) + (1 - y) \log(1 - p)]

Focal Loss 是对 BCE 的改进,主要用于处理前景/背景不平衡问题。一个常见形式为:

\mathcal{L}_{focal} = -\alpha y (1 - p)^\gamma \log(p) - (1 - \alpha)(1 - y) p^\gamma \log(1 - p)

其中:

\alpha用于平衡正负样本;
\gamma 用于降低容易样本的权重;
当一个像素已经很容易分类时,它对 loss 的贡献会变小;
当一个像素很难分类时,它对 loss 的贡献会更大。

为什么分割需要 Focal Loss?

因为一张图像中背景像素通常远多于前景像素。如果直接使用 BCE,模型可能会被大量容易背景像素主导。Focal Loss 可以让模型更关注难分像素和目标区域边界。

2. Dice Loss

Dice 系数常用于衡量两个区域的重叠程度。给定预测 mask MMM 和真实 mask YYY,Dice 系数为:

                                  Dice(M, Y) = \frac{2|M \cap Y|}{|M| + |Y|}

如果使用概率形式,可以写成:

                             Dice(M, Y) = \frac{2 \sum_i p_i y_i + \epsilon}{\sum_i p_i + \sum_i y_i + \epsilon}

其中:

  • p_i是第 i个像素的预测概率;
  • y_i是第 i个像素的真实标签;
  • ϵ是为了避免分母为 0 的平滑项。

Dice Loss 通常定义为:

\mathcal{L}_{dice} = 1 - Dice(M, Y)

Dice Loss 关注的是预测区域和真实区域的整体重叠程度,因此非常适合分割任务。

3. Mask Loss

SAM 的 mask loss 可以概括为:

\mathcal{L}_{mask} = \mathcal{L}_{focal} + \beta \mathcal{L}_{dice}

论文补充材料中给出的具体设定是 focal loss 和 dice loss 按 20:1 的比例组合。

也就是:

\mathcal{L}_{mask} = 20 \cdot \mathcal{L}_{focal} + 1 \cdot \mathcal{L}_{dice}

直观理解:

Focal Loss 负责像素级二分类,尤其关注难分像素;
Dice Loss 负责整体区域重叠,帮助 mask 形状更加合理。

4. IoU Prediction Loss

SAM 不仅输出 mask,还会输出每个 mask 的质量预测分数。真实 IoU 可以定义为:

                      IoU(M, Y) = \frac{|M \cap Y|}{|M \cup Y|}

其中:

M 是预测 mask;
Y 是真实 mask;

M∩Y 表示交集;
M∪Y 表示并集。

模型预测的 IoU 分数记为:

                              \hat{q}

真实 IoU 记为:

                          q = IoU(M, Y)

则 IoU prediction loss 可以写成均方误差:

                            \mathcal{L}_{iou} = (\hat{q} - q)^2

如果有多个 mask,则对多个 mask 的 IoU prediction 求和或平均。这个质量分数在推理时很有用,因为 SAM 可能输出多个 mask,需要根据预测 IoU 选择更可靠的结果。

十、SAM 的训练过程:模拟交互式分割

SAM 不是简单地给图像和 ground-truth mask 训练一次。

它会模拟真实交互式分割过程。论文补充材料中描述,训练时会先随机选择一个前景点或 bounding box 作为初始 prompt,然后模型预测 mask;之后从预测错误区域中采样新的点,作为下一轮交互提示。这个过程会迭代多轮,并且会把上一轮预测的 mask logits 作为下一轮的 mask prompt。

可以简化为:

第 1 轮:给一个点或框 → 预测 mask
第 2 轮:在错误区域加一个点 → 修正 mask
第 3 轮:继续在错误区域加点 → 进一步修正
...

用公式表示:

第 t 轮输入为:

              (I, P_t, M_{t-1})

模型输出:

           M_t = f_\theta(I, P_t, M_{t-1})

其中:

P_t是第 t 轮新增 prompt;
M_{t-1}是上一轮预测结果;
M_t是当前轮预测 mask。

这使得 SAM 学会了一个重要能力:用户不断给出提示时,模型可以逐步修正分割结果。这也是 SAM 适合交互式标注平台的原因。

十一、SA-1B 数据集:SAM 为什么能成为基础模型?

SAM 的另一个核心贡献是数据集:SA-1B。

SA-1B 包含约 1100 万张图像和超过 10 亿个 mask,是当时规模非常大的分割数据集。论文和官方仓库都说明,SAM 在 1100 万图像和 11 亿 mask 上训练,并展示了较强的 zero-shot 分割能力。为什么数据集这么重要?

因为分割标注比分类标注昂贵得多。

分类标注只需要给一张图一个标签:

dog
car
person

而分割标注需要画出精细区域:

每个目标的边界在哪里?
每个像素属于目标还是背景?
多个目标之间如何区分?

这使得大规模分割数据很难构建。SAM 采用了 data engine 思路,也就是模型和数据互相促进:

模型辅助标注 → 得到更多 mask → 用更多数据训练更强模型 → 再辅助标注更多数据

Meta 官方博客也强调,构建分割基础模型的难点之一是没有像图像、视频、文本那样天然大量存在的分割数据,因此 SAM 使用模型参与的数据引擎来扩展分割标注。

可以把 SA-1B 的构建过程理解为三个阶段:

1. 人工辅助标注阶段

早期模型还不够强,需要人工标注员使用交互式工具辅助生成 mask。

这时模型的作用是提高标注效率。

2. 半自动标注阶段

随着模型变强,系统可以自动生成一部分 mask,人工主要负责检查和修正。

这时模型和人工共同完成数据构建。

3. 全自动标注阶段

模型进一步变强后,可以自动在大量图像上生成候选 masks,并通过过滤策略保留高质量结果。

论文补充材料中也给出了自动 mask 生成的细节,例如在图像上采样规则网格点、使用多尺度 crop、根据 predicted IoU 和 stability score 过滤结果,并用 NMS 去除重复 mask。

因此,SAM 的成功不是单纯靠模型结构,而是依赖三者结合:可提示任务定义 + 强 ViT 图像编码器 + 超大规模数据引擎

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐