语义分割的目的是将图像中的每个像素映射至一个目标类。样例如下:
上图中所有车辆被标记为相同的颜色,每个目标类都分别进行了分割,相比于分类,分割是一个更加复杂的问题。
我们使用全卷积网络(FCNs)对图像进行分割,全卷积网络首次在 这篇文章
我会介绍两个重要的技巧,使你能够将用于图像分类的预训练 CNN 转换为 FCN 来执行图像分割。
将全连接层(FC)转换为卷积层(CONV)
FC 和 CONV 层唯一的不同在于 CONV 层中的神经元仅仅和输入中的局部区域相连接,CONV 块中的很多神经元共享参数。然而,这两个层中的神经元仍然要计算点积,所以它们的函数形式是相同的。因此,在 FC 和 CONV 层之间进行转换是可行的。
假设你的卷积层输出大小为7x7x512,紧跟一个有 4096 个神经元的 FC 层,则对于一个单独的输入图像来说,FC 层的输出就为 1x4096。
你可以将 FC 层替换为 CONV 层,使用 7x7 的卷积核、补零、步长为1、输出深度为 4096。你可以快速地算出输出仅为 1x1x4096,与 FC 层的输出相同。
FC 层到 CONV 层转换的实用方法
让我们考虑一个网络架构,输入图像大小为 224x224x3,然后使用一系列卷积、池化和全连接层来减小图像,最后送入大小为 1000 的激活层,即 1000 个目标类别的分类分数。
通过上面的架构你可以发现 Conv5 层的输出大小为 7x7x512,然后是两个各有 4096 个神经元的 FC 层。上述架构对输入进行下采样,空间尺寸减小了 2⁵ 倍,使得第五层输出的空间大小为 224/2/2/2/2/2 = 7。
如上文所描述,我们可以将这3个 FC 层转换为 CONV 层:
- 第一个 FC 层可以使用 7x7 卷积核使其输出为 [1x1x4096]
- 第二个 FC 层可以使用 1x1 卷积核使其输出为 [1x1x4096]
- 最后一个 FC 层可以使用 1x1 卷积核使其输出为 [1x1x1000]
例如,如果一个 224x224 的图像能够得到 [7x7x512] 的中间快,即大小除以32,那么输入一张 384x384 的图像可以得到 [12x12x512] 的中间块,因为 384/32=12。紧接着的3个 CONV 层使得最后的输出为 [6x6x1000],因为(12-7)/1+1=6。注意到我们得到的不是一个 [1x1x1000] 的向量类别分数,而是 [6x6x1000] 的数组类别分数。
你怎么解释每张图像 6x6x1000 的输出?你可以认为每个 6x6 的分片是 1000 个目标类别下采样的热图!!!你可以使用插值算法,调整下采样热图的大小至输入图像的大小。
原始图像和重叠的热图。图片来源:
你可以将热图重叠在原始图像上来观察图像中目标的位置。如上图所示,你可以看到一个类别为“狗”的热图。这是这个的 代码实现。
从粗糙的热图到像素级的分割
尽管是一个好的开始,但是上述方法生成的热图太过粗糙,我们需要一个有效的方法对粗糙的热图进行上采样。就是后面所说的反卷积层
反卷积是正常卷积的逆操作,即反卷积输出的大小是卷积的输入大小,保持了和卷积相兼容的连接模式。
下图是反卷积的例子,将输入上采样2倍。
输入为 2x2,输出为 4x4,通过对输入补零实现。这是只是上采样的一种方法,还有其他反卷积方法,这有一个不错的 教程。
你可以使用这些反卷积层将粗糙的热图上采样至原始输入的大小,然后你就可以使用这个神经网络进行端到端的训练,来生成一个像素级的分割图。训练标记数据是什么样的呢?
训练数据——输入
如果你想使用这样一个网络对上图分割道路区域,标签就看起来像下面这张图。
训练数据——标签
你需要预处理标签图,这样每个像素值就和目标类数量相同,你可以使用softmax交叉熵损失函数来训练网络。
这是一些我的网络的输出样例。代码:Github