(1)FCN简介
全卷积网络是Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图像语义分割的一种框架,是深度学习用于语义分割领域的开山之作。FCN将传统CNN后面的全连接层换成了卷积层,这样网络的输出将是热力图而非类别;同时,为解决卷积和池化导致图像尺寸的变小,使用上采样方式对图像尺寸进行恢复
(2)FCN的创新点
①使用了卷积层替代原来经典CNN里面的全连接层,让图片的输入尺寸不受限制。
②使用了反卷积层对输出的feature map进行上采样,实现了pixel to pixel的语义分割
③结合不同深度层结果的skip结构,确保鲁棒性和精确性
(3)一些FCN的细节
①FCN的三个模型架构
前情提示:VGG16的网络结构图
FCN-32
①图片在经过VGG16的主干网络提取特征以后,图片下采样了32倍
②再经过padding为3的FC6和FC7以后,feature map的长和宽没有变,通道数变成了4096
③在经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)
④最后经过使用双线性插值初始化参数的反卷积层,对feature map直接上采样32倍,与原图片一样尺寸,通道数为num-class的结果。
FCN-16
①图片在经过VGG16的主干网络提取特征以后,图片下采样了32倍
②再经过padding为3的FC6和FC7以后,feature map的长和宽没有变,通道数变成了4096
③在经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)的卷积层
④最后经过使用双线性插值初始化参数的反卷积层,对feature map直接上采样2倍
⑤与此同时,把原图片在经过VGG16的主干网络的Maxpool4的feature map单独拿出来,此时可见下采样了16倍,再经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)的卷积层
⑥把①-④得到的结果和⑤得到的结果直接相加,作为输入输入到反卷积层里面,上采样16倍后得到与原图片一样尺寸,通道数为num-class的结果。
FCN-8
①图片在经过VGG16的主干网络提取特征以后,图片下采样了32倍
②再经过padding为3的FC6和FC7以后,feature map的长和宽没有变,通道数变成了4096
③在经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)的卷积层
④最后经过使用双线性插值初始化参数的反卷积层,对feature map直接上采样2倍
⑤与此同时,把原图片在经过VGG16的主干网络的Maxpool4的feature map单独拿出来,此时可见下采样了16倍,再经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)的卷积层
⑥把①-④得到的结果和⑤得到的结果直接相加,作为输入输入到反卷积层里面,上采样2倍后得到一个下采样8倍的feature map。
⑦与此同时,把原图片在经过VGG16的主干网络的Maxpool3的feature map单独拿出来,此时可见下采样了8倍,再经过卷积核大小为1,通道数为num-class(数据集包括背景的类别个数)的卷积层.
⑧把①-⑥得到的结果和⑦得到的结果直接相加,作为输入输入到反卷积层里面,上采样8倍后得到与原图片一样尺寸,通道数为num-class的结果。
②FCN的一些实现细节
(1)最后得到的与原图片一样尺寸,通道数为num-class的feature map里面一个像素就对应21个数(根据数据集而定),经过softmax处理以后就是代表着每一个像素属于某个类别的概率。
(2)据说原作者在几个反卷积层上面直接就冻结了参数,也就是说这几个反卷积层我们可以直接将其看作是双线性插值的上采样操作就行。
(3)pytorch官方就有对FCN的实现,不过使用的主干网络是Resnet,下列是代码:
from torchvision.io.image import read_image
from torchvision.models.segmentation import fcn_resnet50,FCN_ResNet50_Weights
from torchvision.transforms.functional import to_pil_image
img=read_image("D:/dog_07.jpg")
# Step 1: 下载训练好的模型权重文件
weights = FCN_ResNet50_Weights.DEFAULT
model = fcn_resnet50(weights=weights)
model.eval() #这一句作用是把Dropout,BN层这些东西打开
# Step 2: 权重文件里面有自己的transform
preprocess = weights.transforms()
# Step 3: 这一步就是单纯把输入维度从[a,b,c]变成[1,a,b,c](pytorch很喜欢这样)
batch = preprocess(img).unsqueeze(0)
# Step 4: 预测图片并且可视化结果
prediction = model(batch)["out"]
normalized_masks = prediction.softmax(dim=1)
class_to_idx = {cls: idx for (idx, cls) in enumerate(weights.meta["categories"])}
mask = normalized_masks[0, class_to_idx["dog"]]
to_pil_image(mask).show()
结果如下所示:
(4)feature map在经过核为7*7,stride为1的卷积核的时候使用的是padding为3,那么根据下列公式可以计算出feature map的尺寸是不会发生改变的:
(5)pytorch对FCN的实现用上了膨胀卷积,膨胀卷积(空洞卷积)就是在进行卷积操作的时候不用原图的连续数字进行计算而是采用相邻的数字进行计算,具体图示如下:
采用这种卷积所带来的作用就是可以增大感受域,但是连续采用这种卷积又会发生一种叫做gridding effect问题:就是说随着膨胀卷积层的叠加,感受域增加了但是由于空洞卷积的机制原因会丢失很多像素的信息,如下图所示(PS:dilated_ratio=1的时候空洞卷积就退化成普通的卷积)
(5)卷积操作的平移不变性:卷积:简单地说,图像经过平移,相应的特征图上的表达也是平移的。下图只是一个为了说明这个问题的例子。输入图像的左下角有一个人脸,经过卷积,人脸的特征(眼睛,鼻子)也位于特征图的左下角。假如人脸特征在图像的左上角,那么卷积后对应的特征也在特征图的左上角。
(6)coarse output:指原图像经过处理(例如backbone)以后所得到的输出(例如下采样之后的图像或者经过CNN分类网络之后得到的classfication score)
(7)FCN所采用的上采样方法
①反卷积是一种特殊的正向卷积,先按照一定的比例通过补 来扩大输入图像的尺寸,接着旋转卷积核,再进行正向卷积,具体的操作步骤如下:
②双线性插值:双线性插值是用原图像中4个点计算新图像中1个点,效果略逊于双三次插值,速度比双三次插值快,属于一种平衡美,在很多框架中属于默认算法。
单线性插值的算法如下:
双线性插值就是使用了3次单线性插值,下图展示的是先x后y,也可以先y后x,效果一样
③FCN中的反卷积操作的填充时并不是进行填充0,往往需要进行插值等方式来填充,可以用双线性插值来达到效果,FCN中论文说了这一层中的deconvolution filter不需要是固定的(例如,双线性上采样),但可以学习。一堆反卷积层和激活函数甚至可以学习一个非线性的上采样,在实验中,论文作者发现in-network upsampling对于图像分割任务这样的dense prediction任务是快速和有效的。