简单思路
一、什么是语义分割?
任务定义:
对图像中的每一个像素进行分类,输出与图像同样大小的标签图,每个像素被分配一个语义类别(如 sky、cat、tree、grass)。
- 只关注“类别”,不区分实例。
- 所以如果图中有两只牛,语义分割只会把它们全都标成“cow”,不会区分“牛1”和“牛2”。
- 见图一中右下角牛的分割:只是一个统一的“Cow”区域。
二、最直接的思路:滑动窗口(Sliding Window)
对图中每个像素都提一个 patch,送入 CNN 得出中心像素的类别预测。
流程如下(图二):
- 从图像中提取每个像素周围的 patch;
- 把 patch 输入 CNN,预测中心像素的类别;
- 重复这个过程,逐个像素预测;
- 拼接得到整张图的分割结果。
问题:
- 极度低效! 重复计算同一片区域的卷积特征;
- 类似于早期的“慢速 R-CNN”;
- 只能作为概念启发,不实用。
三、主流方法:全卷积网络(Fully Convolutional Network, FCN)
用一个完全由卷积层组成的 CNN,直接对整张图进行像素级预测。
结构如下(图三):
- 输入图像大小为 3 \times H \times W;
- 使用一堆卷积层提取特征(没有 FC 层);
- 输出维度为 C \times H \times W,其中 C 是类别数;
- 对每个像素位置上那一列做 softmax,得到每个像素的类别分布;
- 使用每像素的交叉熵损失训练。
优点:
- 没有重复计算:高效;
- 结构简单、端到端训练;
- 空间结构不丢失,可输出与输入等大的预测图。
四、实际挑战(感受野 + 计算效率)
1. 感受野问题:
深层网络中一个像素的预测依赖的输入区域叫做“感受野”。
- 如果你仅堆叠 3 \times 3 的卷积,感受野增长很慢;
- 2 层堆叠 → 感受野 5×5;
- 3 层堆叠 → 感受野 7×7;
- 所以需要很多层才能看到足够大范围。
但语义分割很多决策需要“全局上下文”,太小的感受野会影响效果。
2. 高分辨率图像计算开销大:
- 为了精度,语义分割常对高分辨率图像操作;
- 但每个像素都要分类,计算成本非常高;
- 所以不能在原图尺度上全卷积堆太深。
3. :不考虑个体,只标类别,需要 instance segmentation 解决
五、后续发展(虽然 lecture未讲解)
- 为了解决感受野问题,后续提出:
- 空洞卷积(Dilated Convolution)
- U-Net、DeepLab 等架构使用 skip connection + 多尺度特征融合;
- 为了解决高分辨率计算,使用:
- Encoder-Decoder 架构;
- 图像下采样 + 上采样策略(如 Deconv、Upsample、Interpolation)。
下采样与上采样
下采样(如池化、步长卷积) 把特征图变小了,但最后要预测每个像素的类别,所以我们必须 把特征图恢复到原图尺寸,这就需要上采样。下面总结一下讲解内容:
一、下采样的作用
- 使用池化(Pooling)或步长卷积(Strided Convolution)将图像尺寸降低。
- 优点:
- 计算更快
- 感受野更大,利于理解上下文
二、上采样的几种方式
方法1:最近邻上采样(Nearest Neighbor Upsampling)
- 做法:直接把每个像素“复制”到多个位置,形成更大尺寸的图。
- 特点:简单快速,但容易产生锯齿感、不平滑。
方法2:双线性插值(Bilinear Interpolation)
- 做法:假设你要计算一个新像素的值,这个点落在原图中 4 个像素之间,就根据这四个点的值加权平均,权重由距离决定。
- 效果:平滑,图像质量较好。
- 公式含义:
意思是:从周围四个整数像素中,通过线性权重组合出浮点坐标的值。
- 适合:很多语义分割模型默认上采样方式。
方法3:三次插值(Bicubic Interpolation)
- 做法:基于 16 个邻居点(4×4)做高阶插值,比双线性更复杂。
- 特点:更平滑、效果更好,但计算更慢。
- 常用场景:图像缩放处理,网络中偶尔也用。
方法4:最大非池化(Max Unpooling)
- 原理:
- 下采样时用 最大池化,每个区域只保留最大值。
- 把最大值 出现的位置记录下来。
- 上采样时:把特征放回“原来的最大值位置”,其余位置补 0。
- 优点:保留位置信息,适合搭配最大池化使用。
三、选择上采样方式的经验法则
下采样方法 | 建议上采样方式 |
---|---|
平均池化 / 步长卷积 | 最近邻 / 双线性 / 三次插值 |
最大池化 | 最大非池化(Max Unpooling) |
总结:
下采样让我们计算更快、看得更远,上采样则把理解的结果还原到原图尺度。如何上采样,不止影响清晰度,也影响语义对齐和可学习性。
转置卷积(上采样)的机制
转置卷积(Transposed Convolution) 的详细解释,它是语义分割中最常用的可学习上采样方法之一。我们逐段拆解、结合图示来理解它的 动机、计算方式、命名由来与数学原理:
一、为什么需要转置卷积(Transposed Convolution)?
我们做语义分割,需要输出 和输入图像等大小的像素标签。但:
- CNN在下采样(通过池化、stride卷积)后会降低分辨率;
- 所以我们需要 上采样 操作,把低分辨率的特征图变回高分辨率;
- 像双线性插值/最近邻上采样不能学习,没参数可调。
这就引出了:转置卷积 = 有参数、可训练的上采样方法。
二、转置卷积是如何运作的?
举例
- 输入是一个 2×2 特征图,使用一个 3×3 的卷积核(filter),stride=2。
- 对每个输入位置:
- 拿该位置的值乘以整个卷积核;
- 将乘完的结果 平移到输出图的对应位置;
- 如果多个位置产生的输出区域有重叠,就把它们 加起来。
- 输出本来是 5×5,但为了得到 4×4,裁掉最上/最左的一行一列(称为 “修剪”)。
本质上,这个过程就是:每个输入点通过一个滑动窗口扩散出一个加权区域,多个区域之间重叠相加形成更大的输出图。
三、换个角度:从一维举例(图2)
输入是 [a, b],filter 是 [x, y, z]。
- a 点生成 [ax, ay, az]
- b 点生成 [bx, by, bz],偏移后加入到输出中,中间有重叠的位置相加(az + bx)
- 输出长度比输入大,实现了上采样。
这就是 1D 转置卷积最直观的过程。
四、为什么叫“转置”卷积?
普通卷积可表示成矩阵乘法:
- 卷积操作 = 乘以一个稀疏的矩阵 X
- 所以 y = X * a
转置卷积做的事:
- 就是用同一个矩阵的 转置 Xᵀ 去做乘法:
- y = Xᵀ * a
这就是为什么叫“transposed convolution”。
对应矩阵角度:转置卷积就是普通卷积的矩阵形式的转置
五、stride=1 和 stride>1 有啥不同?
当 stride = 1:
- 普通卷积和转置卷积是对称的操作(几乎等价,只是padding规则略有不同)。
当 stride > 1:
- 两者的稀疏模式完全不同;
- 转置卷积无法再表示为某种常规卷积;
- 这就体现出转置卷积的独特性:它不是普通卷积的简单反向操作,而是一种新的上采样方式。
六、转置卷积和卷积反向传播的关系
- 实际上,转置卷积的前向过程 = 普通卷积的反向传播过程;
- 所以它有一个合理的别名叫 “Backward Strided Convolution”。
这个对称性意味着,我们在反向传播中能自动计算梯度,非常适合用于训练。
总结一句话:
转置卷积是一种参数可学习的上采样方法,本质是普通卷积矩阵的转置操作,允许从低分辨率特征图恢复成高分辨率输出,常用于语义分割、图像生成等任务。
上采样对比
上采样方法 | 是否可学习 | 原理简介 | 优点 | 缺点 | 常见用途 |
---|---|---|---|---|---|
最近邻插值 | 否 | 将每个像素复制成更大的区域(如2x2) | 简单、速度快 | 产生块状伪影(aliasing)、不平滑 | 图像预处理、基础上采样 |
双线性插值 | 否 | 用周围4个像素加权平均,权重按距离线性分布 | 平滑、连续 | 不可学习、不能表达复杂模式 | 语义分割中常用作初步上采样 |
三次插值(bicubic) | 否 | 用周围16个像素构建三次函数,拟合出插值结果 | 更平滑、边缘过渡更自然 | 计算量更大、不适合GPU加速训练 | 图像编辑、resize 操作 |
Max Unpooling | 否 | 记录max pooling时的最大位置,将值放回原位,其余位置填0 | 能恢复空间结构、可与下采样对齐 | 仅适用于max pooling、稀疏、信息可能丢失 | 语义分割中的SegNet结构 |
Transposed Convolution | 是 | 用滤波器卷积输入,每次扩大后相加,等价于卷积矩阵的转置 | 可学习、能适应更复杂的上采样模式 | 可能引入“棋盘伪影”(checkerboard artifacts) | FCN、UNet、GAN(如DCGAN) |
Sub-pixel Convolution | 是 | 低分辨率特征图先卷积产生多个通道,再“重排”成高分辨率图(如ESPCN) | 学习性强,计算效率高 | 实现复杂,对框架支持要求高 | 超分辨率任务(SR)、轻量图像上采样任务 |
Learned Interpolation(可学习插值) | 是 | 模拟插值操作但让插值权重可学习 | 灵活度高,可控性强 | 参数多,训练要求高 | 高质量图像重建、高级图像合成任务 |
推荐使用建议:
- 想快速上采样但不需学习 → 选择最近邻或双线性;
- 想保持结构对应关系 → 选择 Max Unpooling;
- 想在训练中优化上采样质量 → 首选 Transposed Convolution;
- 做高端图像合成/超分辨率 → 尝试 Sub-pixel 或 Learned Interpolation。
语义分割 pipeline
语义分割的完整 Pipeline(以经典 FCN / DeepLab 为例)
1️输入图像
原始图像(比如 512 × 512 × 3)。
2️特征提取(Backbone 网络)
使用 CNN 提取图像的深层特征,常见的有:
- VGG / ResNet / MobileNet / EfficientNet
- 输出是低分辨率、高语义的特征图,例如 32 × 32 × 2048
3️空间维度恢复(上采样)
将低分辨率的特征图还原到原图大小,使用方法:
- 最近邻插值(Nearest Neighbor)
- 双线性插值(Bilinear Interpolation)
- 转置卷积(Transposed Convolution)
- 上采样 + 1×1 卷积(常用于 DeepLab)
4️融合细节(可选,提升边界准确性)
通过**跳跃连接(skip connections)**将低层的浅层特征与高层语义特征融合,常用于:
- FCN(比如 FCN-8s 用了浅层和中层特征)
- U-Net 使用大量跳跃连接
5️分类每个像素
对上采样后的特征图的每个像素点进行分类:
- 输出为 H × W × C,其中 C 是类别数
- 最后取 argmax 得到每个像素的类别标签
6️Loss & 训练
- 使用 Cross-Entropy Loss 逐像素训练
- 可加入 Dice Loss / IOU Loss 增强边界质量
整体流程总结成一句话:
输入图像 → CNN提特征 → 上采样恢复空间信息 → 融合低层细节 → 每个像素分类 → 输出语义标签图
举个例子(FCN):
- 输入一张猫图。
- ResNet 提出高语义的 7×7×2048 特征图。
- 用转置卷积上采样回 224×224×C。
- 对每个像素进行 softmax 分类(猫、狗、背景……)。
- 输出整张图的像素分类结果。
语义分割过度到实例分割
背景梳理
在前面我们已经讲了:
- 目标检测(Object Detection):识别每个物体实例,并用一个 边界框 包起来(如 Faster R-CNN);
- 语义分割(Semantic Segmentation):为 每个像素 分配一个类别,但 不区分实例,例如两只猫会被标成同一个类。
新的问题
语义分割虽然像素级别很精细,但:
- 它不知道这张图中有几只猫;
- 也不能区分哪一块像素属于哪只猫。
所以我们会想:有没有可能又有像素级的精度,又能分辨出“这是猫1”,“那是猫2”?
——这就引出了下一个任务:实例分割(Instance Segmentation)
Thing vs Stuff 的分类思想
作者解释,目标实例分割更适用于“事物(thing)”,不适合“东西(stuff)”,因为:
分类 | 含义 | 举例 |
---|---|---|
Thing(事物) | 可以明确数清楚有几个,是离散对象,可以识别每个单独实例 | 狗、猫、人、杯子 |
Stuff(东西) | 没有明确边界、数量不清楚、也无意义去数,属于“连续背景” | 天空、草地、海、水、路 |
因此:
- 实例分割适合处理 Thing 类别
- 而语义分割更适合处理 Stuff 类别
关键点总结
- 语义分割不能区分实例,只关心类别;
- 实例分割是下一步目标,它解决“谁是谁”的问题;
- 在做视觉任务时要先考虑:你面对的是 thing 还是 stuff?
实例分割(基于backbone 和RPN)
为什么需要实例分割?
- 语义分割:可以给每个像素贴标签,但不能区分同类中的不同物体(比如两只狗都标成“dog”,无法区分谁是谁)。
- 目标检测:可以检测不同实例(用边框框出来),但没有像素级的精细轮廓。
- 实例分割结合了两者优点:既能区分不同实例,又能输出精细的分割边界。
实例分割的完整 Pipeline(以 Mask R-CNN 为例)
输入图像
↓
[Backbone] → 特征图
↓
[RPN] → 一堆 RoI(目标区域)
↓
[RoI Align]
↓
┌────────────┬─────────────┬────────────┐
| 分类 Head │ 回归 Head │ Mask Head │
└────────────┴─────────────┴────────────┘
→ 类别 → 框位置 → 每个 RoI 的掩码
1️主干网络(Backbone)
- 用于提取图像的基础特征图
- 常用:ResNet + FPN(特征金字塔网络)
- 输出:多尺度的 feature map,用于后续任务模块共享使用
2️区域提案网络(Region Proposal Network, RPN)
- 在特征图上滑动窗口,预测**物体区域(RoI)**的候选框(bounding boxes)
- 输出一批 RoI proposals(比如 300 个)
3️RoI Align(区域对齐操作)
- 将每个 RoI 区域从主干特征图上裁剪出来,并进行尺寸统一(例如裁成 14×14×C)
4️多任务分支(任务 Head)
Head 类型 | 功能 | 输出 |
---|---|---|
分类头 | 对该 RoI 属于哪个类别分类 | C 类别分数 |
回归头 | 精修该 RoI 的边界框位置 | 4*C(每类对应 4 个坐标值) |
Mask 头 | 预测每个 RoI 的分割掩码 | C × 28×28(每类一个 28×28 的mask) |
5️损失函数联合训练
- 总 loss = 分类 loss + 回归 loss + mask loss(只对正样本 RoI 计算)
总结:
实例分割 = 通用主干网络 + RPN + 多任务 Head(分类 / 框 / mask)
每个 RoI 对应一次独立的预测。整个过程是端到端训练的。
训练方式
- 对每个候选框,训练:
- 分类(属于哪个类) → 分类损失
- 回归(精细调框的位置) → 回归损失
- 分割(前景 vs 背景) → 掩码损失
- 掩码的监督信号来源于训练图像中已知的实例分割标签(对每个框内对象有像素级标注)。
优点
- 结构清晰,利用 Faster R-CNN 结构,仅添加一个小分支就能做分割。
- 可用于物体检测中的“thing”类(即可区分实例的类别,如人、狗、猫等)。
- 预测的 mask 细节丰富,能精确覆盖对象轮廓。
限制
- 主要用于“thing”类,不能很好处理“stuff”类(比如天空、草地等无法实例化的类别)。
- 属于两阶段方法,速度上不如单阶段方法(如 YOLO)。
最新发展
- Justin提到最近(几周前)刚有一个 真正成功的单阶段实例分割方法 被提出,但还不够成熟。
一些更复杂的分割任务
1. Panoptic Segmentation(全景分割)
- 目标:同时完成语义分割和实例分割。
- 方法:为图像中的所有像素进行分类。
- 对“things”(可以数的物体,如人、牛等),不仅要标注类别,还要区分每一个实例。
- 对“stuff”(不可数背景,如天、树、草),只需标注类别,不分实例。
- 意义:语义分割不区分实例,实例分割不处理stuff,而全景分割解决两者统一的问题。
2. Human Keypoints(人体关键点检测)
- 目标:识别人身体上的关键部位坐标,用于估计姿态(pose)。
- 方法:
- 检测图像中的人 → 分配每人一个 17个关键点(如鼻子、肩膀、膝盖等)。
- 关键点位置以热图(heatmap)方式表示,可用CNN来回归这些热图。
- 扩展:通过为 Mask R-CNN 添加一个新 head 来实现关键点预测。
3. General Idea: Per-Region Heads
- 无论是掩膜预测(Mask)、关键点预测(Keypoint)、边界框分类回归,本质上都可以看作是在 Faster R-CNN 的框架中对每个 Region 添加一个 head。
- 统一结构:每个 region 通过 RoI Pooling → 提取 feature → 输入不同的 head 模块 → 输出对应类型的预测(分类/边框/掩膜/关键点等)。
4. Dense Captioning(密集图像描述)
- 目标:为图像中多个不同区域生成自然语言描述。
- 方法:
- 在每个检测区域上附加一个 语言模型(如 LSTM)→ 生成对应描述。
- 效果:不仅检测出图像中的对象,还能“说出”它是干什么的,比如“man sitting on a table”,“white laptop on table”。
5. 3D Shape Prediction(3D形状预测)
- 目标:不仅检测对象,还预测其完整的三维形状。
- 方法:
- 在物体检测器上再接一个 head,用于输出每个对象的 3D 表征(如体素、点云、网格)。
- 是正在研究的热门方向,下一讲会专门讲解如何处理 3D 网格。
总结:
对当前视觉任务家族的大盘点,从最初的目标检测、分割,到更丰富的目标理解(关键点、描述、三维重建)。核心思路是——只要在已有的检测网络中加入合适的“头部”结构,任何复杂的视觉任务都可以统一到一个 pipeline 里进行端到端学习。这个范式极大地提升了灵活性和性能,是现代计算机视觉的关键路线之一。
在已有的 目标检测网络(如 Faster R-CNN 或 Mask R-CNN)的基础上,我们可以通过添加不同功能的模块(head),来实现不同的计算机视觉任务。这些模块叫做“head”,每个 head 可以做不同的事,比如:
- 分类 head:判断是狗还是猫
- 边框 head:预测物体的边界框位置
- 掩膜 head:做实例分割
- 关键点 head:预测人体关节
- 语言生成 head:做图像描述
- 3D 预测 head:输出物体的三维结构
这些任务以前可能要分别设计不同的网络,现在只需要在**统一的主干网络(backbone)+ 提案网络(RPN)**基础上,添加一个或多个“任务模块(head)”即可。这就是:
把不同的视觉任务统一到一个 pipeline 里(共享前面的大部分网络),而不同任务的输出由不同的 head 控制。
这样就可以实现一个多任务学习系统,端到端训练,效率高、可扩展强。换句话说,只要检测网络搭好了,添加不同“脑袋”就能解决不同问题。