3、语义分割核心概念:逐像素分类、感受野、上采样与下采样
好,咱们进入正题。语义分割听起来高大上,但核心就四个词:逐像素分类、感受野、上采样、下采样。把这四个东西搞明白,你基本就拿到语义分割的入场券了。
3.1 逐像素分类:给每个像素发一张“身份证”
语义分割最朴素的想法是什么?说白了,就是给图像里的每一个像素,都打上一个类别标签。
你想想看,一张道路场景图,里面有车、有人、有路、有树。传统分类任务,整张图只输出一个类别——“这是一张道路场景图”。但语义分割不一样,它要回答:“这个像素是路,这个像素是车,这个像素是人……”
我刚开始接触这个任务时,觉得这简直是在“像素级地抠图”。没错,就是这种感觉。每个像素都要被分配一个类别,比如:
- 像素值 (120, 80, 200) → 类别:天空
- 像素值 (45, 60, 30) → 类别:树木
- 像素值 (200, 200, 200) → 类别:路面
所以,语义分割的输出不是一张普通图片,而是一张与输入尺寸相同的“标签图”。每个位置的值,代表该像素所属的类别编号。
核心要点:语义分割 = 对每个像素做一次分类。输入是 H×W×3 的 RGB 图像,输出是 H×W×C 的概率图(C 是类别数),最后取 argmax 得到每个像素的最终标签。
3.2 感受野:网络能看到多大的“视野”
接下来聊感受野。这个概念,我当年学的时候绕了好一阵子。
简单说,感受野就是卷积神经网络中,某个特征图上的一个点,对应到原始输入图像上的区域大小。
举个例子:
- 一个 3×3 的卷积,感受野就是 3×3 的区域。
- 再叠一个 3×3 的卷积,感受野就变成了 5×5。
- 再叠一个,变成 7×7。
为什么会这样?因为每一层都在“看”上一层输出的一个局部区域,层层叠加,视野就变大了。
我在项目中遇到过一个问题:用浅层网络做道路分割,结果远处的车辆根本识别不出来。为什么?因为感受野太小,网络只能看到近处的局部纹理,看不到远处的整体轮廓。
我的经验:对于道路场景理解,感受野至少要覆盖到 100×100 像素以上,才能有效识别远处的车辆和行人。否则,模型容易“近视”。
感受野的计算公式也不复杂:
# 感受野递推公式
# l_out = (l_in - 1) * stride + kernel_size
# 其中 l_in 是上一层的感受野,l_out 是当前层的感受野
def compute_rf(layers):
rf = 1
for k, s in layers:
rf = (rf - 1) * s + k
return rf
# 示例:三层 3x3 卷积,步长均为 1
layers = [(3,1), (3,1), (3,1)]
print(compute_rf(layers)) # 输出 7
3.3 下采样:压缩信息,提取高层语义
下采样,说白了就是把图像变小。常用的方式有:
- 最大池化(Max Pooling):取局部区域的最大值,保留最显著的特征。
- 平均池化(Average Pooling):取局部区域的平均值,平滑特征。
- 步长卷积(Strided Convolution):用大于 1 的步长直接卷积,同时完成特征提取和尺寸缩小。
为什么要下采样?两个原因:
- 减少计算量:图像变小了,后续卷积的计算量指数级下降。
- 增大感受野:下采样后,同样大小的卷积核,对应的原始图像区域更大。
我曾经犯过一个错误:为了追求精度,把下采样次数减少,结果模型训练了三天三夜还没收敛。嗯,这里要注意:下采样是必要的,但下采样次数太多,会丢失细节信息。道路场景中,车道线的边缘、行人的轮廓,这些细节一旦丢失,分割结果就会很粗糙。
避坑指南:我曾经在一个项目中,把下采样从 4 次增加到 6 次,结果 mIoU 反而下降了 3 个点。原因就是细节丢失太严重。后来我改用空洞卷积(Dilated Convolution)来增大感受野,同时保持分辨率,效果才提上来。
3.4 上采样:把特征图“放大”回原图尺寸
下采样把图变小了,但最终输出要和输入一样大啊。怎么办?上采样。
上采样就是把小图变大的过程。常用的方法有三种:
| 方法 | 原理 | 特点 |
|---|---|---|
| 双线性插值 | 根据周围像素加权平均计算新像素值 | 简单、无参数、速度快,但不够灵活 |
| 转置卷积 | 可学习的上采样,通过卷积核的转置运算实现 | 有参数、可学习,但容易产生棋盘格效应 |
| 反池化 | 记录池化时的位置,上采样时把值放回原位 | 保留位置信息,但其他位置补零,比较稀疏 |
我个人习惯用双线性插值 + 转置卷积的组合。先用双线性插值把尺寸大致恢复,再用转置卷积微调细节。这样既保证了速度,又保留了学习能力。
你想想看,如果只用双线性插值,边缘会模糊;如果只用转置卷积,又容易出棋盘格。两者结合,效果往往更好。
关键理解:下采样和上采样是一对“搭档”。下采样提取高层语义,上采样恢复空间细节。语义分割的经典架构 U-Net、DeepLab 等,本质上都是在平衡这两者之间的关系。
3.5 四个概念的关系:一张图说清楚
最后,我用一个简单的流程把四个概念串起来:
输入图像 (H×W×3)
↓
[下采样 × N] → 特征图变小,感受野变大,语义信息增强
↓
[卷积层] → 提取高层特征
↓
[上采样 × N] → 特征图恢复原尺寸,逐像素分类
↓
输出标签图 (H×W×1)
这个流程,就是语义分割最基础的编码器-解码器结构。编码器负责下采样,解码器负责上采样。中间通过跳跃连接(Skip Connection)来融合浅层细节和深层语义。
好了,这一章的核心概念就这些。下一章,我会带你手撕一个最简单的语义分割网络,看看代码里这些概念是怎么落地的。