4.8 实例分割Mask R-CNN与SAM

1.从语义分割再往前走一步

说起来,前面几章我们已经把卷积网络(3.3 节讲过的CNN)能做的事过了一遍。检测那边告诉我们物体在哪里(4.3 节讲了Faster R-CNN、YOLO和DETR),语义分割那边告诉我们每个像素属于哪一类。这两件事其实都已经挺难了,可真实世界里有些活儿,光知道这些还不够。

我们看一个具体场景。小明拍了张照片,画面里三只猫挤在沙发上。语义分割会把所有猫的像素都标成同一个颜色,因为对它来说猫就是猫,分不出哪只是哪只。可要是小明想给其中一只换背景,或者想数清楚到底有几只猫,语义分割就帮不上忙了。这时候需要的就是实例分割,它不光要知道每个像素属于哪一类,还要把同一类里不同的个体分开,把三只猫各自标出来。

所以实例分割的位置大概介于检测和语义分割之间。检测给出的是框(一个矩形把物体圈住),语义分割给出的是整张图的像素标签图,实例分割给出的是每个物体各自的精确轮廓,每个个体单独一份。顺便提一句,还有一个更全的概念叫全景分割(panoptic segmentation),它把这两件事合到一起,既给每个可数物体一份单独的轮廓,又给背景里那些不可数类别(天空、草地、道路这些)一个整体的像素标签。这一篇我们主要讲实例分割这一支。

2.Mask R-CNN:在检测的基础上多抠一层轮廓

要做实例分割,最经典的一条路就是Mask R-CNN。它是在Faster R-CNN(4.3 节讲过的两阶段检测器)的基础上,再加一个小分支出来的。

我们先回顾一下Faster R-CNN在做什么。它先用一个叫RPN(区域提议网络)的模块,从图像里挑出一批可能含有物体的候选框,再对每个候选框预测类别和位置。整个过程给出来的是框,框里是什么、框在哪里,都有了。

Mask R-CNN的想法很直接,就是在这个基础上,对每个候选框再多预测一份掩码。掩码是什么意思呢,就是一张和候选框同样大小的二值图,每一个位置标明它属不属于这个物体。我们用 MM 表示这份掩码,MM 里的每个位置 Mi,jM_{i,j}iijj 分别表示行号和列号)只取 0011 两个值,11 代表这个像素属于当前这个物体,00 代表不属于。这样每个物体就有了一份自己的精确轮廓,不再只是一个矩形框。

这里有个细节特别值得一说,叫RoI Align。Faster R-CNN里有个叫RoI Pooling的操作,它要把任意大小的候选框切成固定大小的网格,方便后面接全连接层。可是它取整的时候会带来一点位置误差,对检测这种只看框的任务影响不大,但到了掩码这一级,一个像素的偏差就能让轮廓边缘对不齐。Mask R-CNN特地用双线性插值(一种根据周围四个像素做加权平均的方法)替代了取整,把每个采样点的位置都算得更精细。说穿了,就是为了让抠出来的轮廓不再抖抖歪歪。

总的看下来,Mask R-CNN一次预测三样东西,分别是类别、框的位置、还有掩码。它训练的时候这三样一起学,预测的时候一起出,损失函数也是三部分加起来的。模型不算小,FLOPs(浮点运算次数,反映计算量大小)大概在几百G这个量级(G表示十亿),手机端实时跑挺吃力的,但在服务器端做高精度分割,它一直是被反复比较的标杆。

我之前看过一本讲自动驾驶的科普书,里头提到Mask R-CNN用来抠车辆和行人的轮廓,做得特别细致,连后视镜边缘和行人的背包带都能勾出来。这种精度,光靠一个矩形框是远远不够的。

3.SAM:一个能听提示的分割大模型

讲完Mask R-CNN,我们跳到2023年。Meta(前Facebook)那一年放出了一个叫SAM(Segment Anything Model)的模型,它的思路和Mask R-CNN差别很大。

SAM最大的特点是可提示(promptable)。什么叫提示呢,就是你可以给它一个点、一个框、一段文字,它就能根据这个提示把对应物体的掩码分割出来。你点一下画面里某只猫,它就把这只猫的完整轮廓抠出来。你画一个松松的框把狗圈进去,它就把框里那条狗精确标出来。这和传统分割模型那种给我一张图、模型自己决定分割什么的做法,路数完全不一样。

SAM的整体结构分三块。第一块是图像编码器(image encoder),用一个大型的ViT(视觉Transformer,后面讲注意力那篇会细说)把图像抽成一组特征。第二块是提示编码器(prompt encoder),把你给的点、框、文字这些提示也变成特征。第三块叫掩码解码器(mask decoder),用轻量化的注意力把图像特征和提示特征凑到一起,输出几份候选掩码。我们用 EIE_I 表示图像特征,EPE_P 表示提示特征,FF 表示最终输出的掩码,那么这个流程大致可以写成 F=f(EI,EP)F = f(E_I, E_P),其中 ff 表示掩码解码器这个函数。

训练SAM用的是一个超大规模的数据集,叫SA-1B,里面有十亿级别的掩码标注。这么大的规模,是靠一个半自动的流程搭起来的,先有一个辅助模型自动标注,再人工把关,一点点把数据撑大。Meta还做了一个很有意思的工程,把数据飞轮转起来,模型越强标注质量越高,标注越多模型又越强,这样一圈圈滚下去,规模就起来了。

SAM最让人意外的地方,是它对没见过的东西也能分。比如你拿一个它在训练时几乎没见过的医学影像或者工业零件图,给它点一下,它照样能把目标抠出来。这种能力叫零样本(zero-shot)分割。我记得有个做病理切片的朋友跟我说,他们团队没训练过任何模型,直接拿SAM点了几下,居然把细胞轮廓分得相当像样,省下了大量人工标注的功夫。说起来,这种通用性在分割领域是头一回见。

4.从专用到通用:分割的基础模型这条路

SAM的意义,说起来比它本身的功能更值得关注。

在它之前,做分割的常见思路是这样的。你想分医学影像,就去标一批医学数据,训一个专门模型。你想分遥感图,再去标一批遥感数据,训另一个专门模型。每一个任务都得从头来一遍,数据成本和算力成本都不低。这种模式可以叫一个任务一个模型。

SAM把这件事翻转过来了。它用一个超大规模的通用模型,直接面对各种各样的分割任务,不再为每个任务单独训练。这个思路其实就是视觉基础模型(foundation model,大规模预训练之后能适配很多任务的通用模型)的做法,和自然语言里BERT、GPT那一路是一脉相承的。SAM一出,分割领域的研究节奏明显变了,很多人不再执着于刷某个特定数据集的分数,转而研究怎么把通用模型往自己的场景里适配。

当然通用也有代价。SAM在特定任务上的精度,有时候比不过专门训练过的小模型,毕竟它没有专门学过那些领域细节。所以现在大家常用的折中办法,是拿SAM当预训练底座,再在自己的数据上微调一下(fine-tune,用自家数据继续训练使模型更贴合特定任务),既保留了通用性,又照顾了精度。这种思路我们在3.10 节讲训练稳定性的时候提过类似的味道,工具是要看场景的,没有一套配方走天下。

5.在哪里能见到它们

实例分割的实际用处,说起来面很广。

图像编辑这边,最常见的就是一键抠图。手机修图软件里那个智能选择功能,背后多半就是实例分割或者它的轻量化版本。小张前阵子帮妈妈处理一张老照片,想把她从集体照里单独抠出来,用了带SAM的修图工具,点一下就选好了,省得自己拿着套索工具一笔一笔描。

医疗这边,实例分割用来勾画器官和病灶。一张CT图里,肺、肝、肾这些器官各自要分开标出来,肿瘤也得一个一个数清楚,这正好是实例分割擅长的事。语义分割在这上面就不够用,因为左肺右肺它当成一类。

遥感这边,从卫星图里数飞机、数船、数储油罐,每一架每一艘都要单独标出来。我看过一篇讲港口监测的报告,里面用实例分割把停泊的船只一艘一艘分开,再统计数量,效率比人工数高了一个数量级。

工业这边,流水线上的零件检测也用得上。机械手要抓一个零件,光知道这一堆零件在哪个大区域是不够的,还得知道每一个具体在哪里、朝向如何,这也得靠实例分割给出各自的轮廓。

6.收个尾

这一篇我们走了一条从精到通的路。Mask R-CNN是精的代表,它在检测的基础上加一个掩码分支,把每个物体的轮廓抠得清清楚楚。SAM是通的代表,它靠超大规模数据和可提示的设计,把分割从专用模型带到了基础模型这条路。两者更多时候是互补的关系,一个吃精度,一个吃通用。下一篇我们继续在视觉这条线上往前走,到时候见。

练习

Q1. 实例分割和语义分割有什么区别?画面里三只猫,两者分别怎么标?

语义分割只区分类别不区分同类个体,画面里三只猫它都标成"猫"这一类,三只糊成一团。实例分割不光知道每个像素属于哪一类,还把同一类里不同的个体分开,会把三只猫各自标出来(第一只、第二只、第三只),每只单独一份轮廓。所以实例分割的位置介于检测(给框)和语义分割(给整张像素标签图)之间,给的是每个物体各自的精确轮廓。

Q2. Mask R-CNN 在 Faster R-CNN 基础上加了什么?它一次预测哪三样东西?RoI Align 又为什么要替掉 RoI Pooling?

Mask R-CNN 在 Faster R-CNN 基础上对每个候选框多预测一份掩码(二值图,每个位置标明属不属于这个物体)。它一次预测三样:类别、框的位置、掩码,损失也是这三部分加起来一起学。RoI Align 要替掉 RoI Pooling 是因为后者取整会带来位置误差,对只看框的检测影响不大,但到掩码这一级一个像素的偏差就让轮廓边缘对不齐;RoI Align 用双线性插值把每个采样点位置算得更精细,抠出来的轮廓不再抖抖歪歪。

Q3. SAM 为什么能对没见过的医学影像、工业零件图也分得好?这种能力叫什么?

因为 SAM 用了超大规模数据集 SA-1B(十亿级掩码,靠半自动流程搭起来)训练,结构上可提示(点、框、文字都能当提示),把分割做成了通用基础模型。它学到的是通用的分割能力,不再为每个任务单独训练,所以对训练时几乎没见过的领域也能分,这种能力叫零样本(zero-shot)分割。当然在特定任务上精度有时比不过专门训练的小模型,常用折中是拿它当预训练底座再微调。

相关标签
深度学习实例分割Mask R-CNNSAM