FCN

本文主要记录个人在学习FCN算法(Fully Convolutional Network)的个人理解。

摘要

FCN(Fully Convolutional Network)1是深度学习在图像语义分割领域的奠基作,后来几乎所有图像语义分割的模型都有着FCN的影子。

FCN脱胎于CNN,CNN通常在卷积层后连接一系列全连接层,最终输出一个结果,通常是分类问题中各个类别的概率。FCN则一般通过反卷积层对卷积输出作上采样,使它恢复到输入图像的大小尺寸,这样就实现了对每一个像素进行分类预测。与CNN相比,FCN有一个显著的特点就是输入图像尺寸不需要裁剪成指定大小,卷积和反卷积都不依赖图像的整体尺寸(CNN的全连接层需要尺寸固定才能确定权重weight)。

理论背景介绍

卷积

数学定义

数学上两函数的卷积计算公式可以表示为: 在信号处理/机器学习相关应用中,更常用的是离散卷积,其计算公式:

在机器学习中,两个输入函数x和h分别代表输入和待学习的权重。分析离散卷积的计算公式,我们发现,离散卷积的计算过程可以表示为如下几个步骤:

  1. 两列数组第一个保持正向,同时将第二列数组反向。
  2. 将反向后的第二列数组与正向的第一列数组每一项一一对应,使得每一项两数组的下标之和均为n(x(i)与h(n-i)对应)。
  3. 将对应好的两数列每一项(x(i)与h(n-i))相乘,再将所以的乘积求和作为求得数列y的第项y(n)。
  4. 将第二列数组向后平移一位,使得下标之和为n+1。重复上述步骤,直到计算完每一个n的取值。

将卷积的思想应用在机器学习中,x(n)是卷积层的输入,h(n)是待学习的参数,由于h(n)是未知的待学习的,所以省去了将数组反向的步骤,学习出来的直接是反向后的数组序列。更高维度卷积与一维类似,每个维度的实现都和第一个维度相似。下图2是一个输入大小为核大小为的二维卷积示例,输入中蓝色四个元素与卷积核对应每一项的乘积求和作为输出矩阵左上角第一个输出,接下来向右向下滑动卷积核,得到输出矩阵其余位置的输出计算结果。

卷积层参数

为了计算方便,机器学习的卷积层存在着一些参数,对应着各种不同结构的待学习矩阵h(n)参数。这里以pytorch中一维卷积层Conv1d3使用的参数为例,简单介绍一下各个参数的意义:

in_channels & out_channels

in_channels和out_channels指输入输出通道数,一般的彩色图片通道数为3(RGB三个通道)。对于多通道卷积的计算,只是对一般卷积运算的引申,原卷积层每一个变量x(i)变成一个长度为in_channels的矩阵,待学习矩阵每一个变量h(i)变成形为out_channels*in_channels的二阶矩阵,这样每一项相乘就变成了矩阵相乘,得到的矩阵积大小也就变成了out_channels。

这是一个二维卷积输入输出通道数的示意图4,输入通道为3,输出通道为2,卷积核大小为,即总共的卷积核。

实际计算中,矩阵维度往往不止序列长度、通道数这两个维度,一般还有一个批处理大小batch size维度,这个维度是同时进行计算的数据个数,若batch_size=N,则N笔数据放在同一个矩阵中一同计算出最终结果,这样操作是为了充分利用GPU并行计算资源以加快机器学习的训练速度。

kernel_size

kernel_size指卷积核的大小,即待学习参数矩阵只有kernel_size个位置有值,其余位置都是0,所以我们只需要滑动大小为kernel_size的框来学习计算。以上是一维情形,对于二维的情况,卷积核一般就是一个二维正方形矩阵,大小为kernel_size*kernel_size。

stride

stride一般指的是步幅,也即卷积和每次滑动的格点数,一般默认为1。

padding

padding一般指填充,是将输入矩阵的最外侧边界填充元素(通常填充的是0),这样可以帮助我们调整输出矩阵的长宽大小(不填充输出矩阵会变小)。

dilation

dilation参数一般对应的是膨胀卷积,即参与卷积运算的不是相邻的输入原胞数据,而是有一定的间隔,具体可以参考下面这个图5的示例(dilation=1)。从另一个角度来看,这个的卷积核其实可以看成的卷积和,只是第2/4列为0。

groups

groups一般是分组卷积,也即将卷积计算分成多组来进行。

参考图示6操作,输入特征按照通道数分成g组,则每一个输入的特征尺寸为,卷积核尺寸为,每一组的输出尺寸为,将g组输出拼接即得到最终尺寸为的输出。显然,相对于一般的卷积,分组卷积会减少计算量。

池化

池化层是另一种经常用在图像处理中的网络结构。核心在于将图像数据进行压缩减少数据量,常见的做法有最大池化、平均池化等。下图是最大池化的一个简单示例。

反卷积

先前的卷积、池化等操作会使输出尺寸变小,在FCN的图像语义分割领域中,需要输入输出图像大小一致,这时我们就需要一种扩大图像尺寸实现小分辨率到大分辨率的操作,这种操作被称为上采样(Upsample),反卷积就是常见的一种上采样实现算法。

下图7是一个输入大小为,卷积核大小为,stride=1的卷积示例。

第一步的计算由蓝色标出,输入矩阵第一个元素与卷积核相乘得到第一部分输出,随后平移输入得到后续几个结果,相加后得到最终的输出结果。仔细观察整个计算过程,发现整个运算类似卷积运算的反运算(正向卷积运算的输出变成反向的输入,正向的输入变成反向的输出),但这个转置卷积/反卷积只能对矩阵的形状做逆向,函数值不能逆向求得。

上述示例没有考虑步幅(stride=1),若步幅为2,则运算过程如下所示:

整个反卷积/转置卷积的计算其实仍然可以利用矩阵变换来得到,反卷积其实是一种特殊的卷积。在进行卷积计算之前,需要对卷积核进行上下左右翻转(二维),在外围以及行列之间(取决于步幅stride)进行填充补0。下面是上面两个示例的反卷积计算细节。

对于机器学习过程中的反卷积,卷积核的参数是需要学习的,所以不必关注其反转运算,直接对输入进行补0后进行卷积运算即可,下图是一个卷积核移动计算示例。

算法模型细节

fcn算法的结构可以简单表示为以下结构,先通过一个卷积神经网络(一般是CNN的变种)提取信息,再通过反卷积(转置卷积)将图片变换回原先的输入大小,这样就得到了与原输入大小相同的输出,即每一个像素点属于哪一个分类的概率信息。

此图是原始论文中FCN算法实现的架构细节,模型前半部分和一般的CNN结构类似,交替使用卷积层和池化层,卷积层因先做了扩边处理(外围一圈填充0)故处理完大小不变,池化层使图像长宽均缩短为原先的1/2,这一部分总共经历5层池化层后图像长宽尺寸均减小到原先的1/32,对此输出直接上采样就得到FCN-32的输出(第一个预测结果,精度略差),这里的上采样就是反卷积运算,主要通过间隔补零来改变大小。

FCN-16和FCN-8的计算思想类似,只是把最后一个预测卷积层的输入修改了一下大小,以增加输入信息。FCN-16的作法,首先将conv7层的输出进行双线性插值使得长宽变为2倍,插值后与pool4池化层的输入相加,得到的结果作为最终卷积层的输入,输入大小是原图尺寸的1/16故此方法一般称为FCN-16。FCN-8的方法类似,最终的卷积层输入由三部分组成:将conv7的输出插值使得长宽变为4倍、将pool4的输出插值使得长宽变为1倍以及pool3的输出,这三部分大小都是原图的1/8,所有此方法记为FCN-8。

在Pytorch中使用

torch vision包内有FCN的pytorch实现代码8,可以直接通过引用来使用(可根据具体情况使用预训练好的参数,有resnet50和resnet101两种基础框架backbone)。

1
2
import torch
model = torch.hub.load('pytorch/vision', 'fcn_resnet50', pretrained=True)

当然,如果是想把此算法用在其他的问题上,一般需要对网络的一些层进行微调(比如预测的输出种类数与预训练的类别数不同,需要改变最后的输出类别数)。


  1. Fully Convolutional Networks for Semantic Segmentation (arxiv.org)

  2. 6.2. 图像卷积 — 动手学深度学习 2.0.0-beta0 documentation (d2l.ai)

  3. Conv1d — PyTorch 1.11.0 documentation

  4. 6.4. 多输入多输出通道 — 动手学深度学习 2.0.0-beta0 documentation (d2l.ai)

  5. GitHub - vdumoulin/conv_arithmetic: A technical report on convolution arithmetic in the context of deep learning

  6. A Tutorial on Filter Groups (Grouped Convolution) - A Shallow Blog about Deep Learning (yani.ai)

  7. 13.10. 转置卷积 — 动手学深度学习 2.0.0-beta0 documentation (d2l.ai)

  8. FCN | PyTorch

正在加载今日诗词....