Fully Convolutional Networks for Semantic Segmentation

论文:https://arxiv.org/abs/1411.4038
代码:https://github.com/wkentaro/pytorch-fcn

语义分割 vs 实例分割 概念:https://blog.csdn.net/electech6/article/details/85317608

1 核心思想

FCN用全卷积的CNN完成图像的语义分割任务,主要的核心思想包括三部分:

  1. 将分类网络的全连接层变成卷积层,这样就可以处理任意尺寸的输入图像;
  2. 对网络的输出进行反卷积(转置卷积),保证输出和输入图像具有相同的尺寸;
  3. 新设计了包含skip结构的新网络,使用多层信息进行分割,提升了分割效果。

1.1 将全连接层变为卷积层

在这里插入图片描述图像分类是一个图像级的任务,目的是利用一个图像的全局信息进行类型判断。而图像的语义分割任务是一个像素级的任务,需要判断输入图像上每一个像素点的类别。在图像分类中,最后一般使用的是一个全连接层,其神经元数量就等于训练集的类别数,目的是将全连接层的输出经过softmax函数后判定样本属于每一个类别的概率。而图像分类任务,也是由于全连接层的存在,需要输入是固定尺寸的,对于大尺寸的输入图像,一般都是从其上裁剪不同的固定尺寸的子块进行前向运算得到分别结果。

如果将全连接层转换成卷积层,那么网络对输入图像的尺寸就不再有要求,对于大尺寸的输入图像,得到的输出就是一个heatmap,将这个heatmap进行综合就可以得到其分类结果。并且使用全卷积的操作还可以提高推理速度,如作者论文3.1部分所说,一个227 * 227的子块,前向推理的速度是1.2ms,而一个500 * 500的大图,在全卷积的网络上推理得到大小为10 * 10的输出,整个的推理耗时为22ms,对这张500 * 500的大图,使用子块进行推理总的耗时为10 * 10 *1.2 = 120ms,因此全卷积网络的推理比原始网络速度快了 12 / 22 = 5.4倍。

将全连接层换成卷积层也很好操作,只需要设置卷积层的kernel尺寸和输入的尺寸一致即可,如全连接层的输入为 7 * 7 * 128,7 * 7 是空间尺寸,128是channel数,输出为4096,原来的全连接层的操作是对输入进行flatten操作,将其变换为 7 * 7 *128 = 6272的一维向量,然后通过一个 4096 * 6272的全连接层,得到4096维的输出。那么只需要修改该全连接层为kernel大小为7 * 7 * 4096的卷积层就完成了变换,输出同样为 1 * 1 * 4096,这样得到了同样的输出,参数数量还大大减小了,有助于提升推理速度。

1.2 对输出进行反卷积

语义分割任务需要得到输入图像上每一个像素点的类别信息,因此需要输出的尺寸和输入的尺寸是一样的。而在CNN的前向计算过程中,一般都会进行下采样的操作,那么如何把下采样了之后的特征恢复到和输入同样大小的尺寸就是本节要关心的内容了 。

作者在3.2,3.3节共介绍了三种上采样的方式,分别是:

  1. shift-and-stitch
    说句实话,论文里的话说的其实很清楚,但就是看不懂,这篇博客https://www.jianshu.com/p/e534e2be5d7d解释的特别好,这里就不再重复了。
    作者认为这种方式的缺点是滤波器无法获取更加细粒度的输入信息(不是很理解)。
  2. 减小网络层的stride
    假设一个网络层(卷积层/池化层)的stride为s,后面跟着一个滤波器为 f i j f_{ij} fij的卷积层。如果将前面一层的stride变为s,那么得到的输出就将扩大s倍。但是,变化前后得到的卷积结果却是不一样的,因为原来的滤波器 f i j f_{ij} fij在stride=1时对应的输入的信息要比stride=s时对应的输入信息要少(输入信息是指这两个网络层前的输入)。因此,作者调整第二个网络层的滤波器范围为:
    在这里插入图片描述
    保证调整stride前后滤波器对应的输入信息是一致的。
    作者认为采用这种方式,不断地调整各层地输出,虽然可以得到和输入尺寸一致地输出,但是每次卷积计算地感受野会减小,不利于捕获全局信息,总体计算量会增大。
  3. 反卷积(转置卷积)
    作者最终采用的是反卷积的方式扩大输出的尺寸,好处是易于实现且能够通过反向传播训练反卷积的参数、计算的效率也比较高。反卷积的解释可以参考:https://blog.csdn.net/cdknight_happy/article/details/78898791的第四部分。

1.3 使用skip设计分割网络

作者首先将AlexNet、VGG和GoogleNet修改成全卷积网络,并微调预训练的分类模型得到分割模型。分割效果如下图所示,已经取得了在当时比较好的分割效果:
在这里插入图片描述作者设计了一个新的包含对尺度输入的分割网络,取得了更好的分割效果,网络结构如下图所示:
在这里插入图片描述上面的网络结构其实就是同时使用低层细粒度的特征和高层高语义的特征联合进行语义分割。分割的效果和性能指标如下所示:

在这里插入图片描述

1.4 loss计算

语义分割任务是对每一个像素点都计算分类loss,然后将这些loss值求和作为整幅图像的loss值。

patchwise training is loss sampling
包含全连接层的网络,因为需要输入尺寸是固定的,所以很多时候都是从图像上随机裁剪固定大小的patch组成batch进行训练,由于patches可能高度重叠,所以需要一些sampling方法对选取一些patches作为训练集,一方面平衡类别,另一方面解决空间相关性等问题。但是作者这里对全卷积网络,使用的是整幅图像进行训练,包含了很多的像素点的关联关系,但是作者认为可以通过对损失进行加权解决类别不均衡的问题,还可以通过对损失进行采样解决图像块间的空间关联问题(损失采样就是随机忽略最后一层的某些神经元,因此计算损失的时候也就相当于忽略了某些图像子块)。

2 实验结果

2.1 语义分割的度量标准

参考自:https://blog.csdn.net/majinlei121/article/details/78965435

在这里插入图片描述
在这里插入图片描述度量代码:

import numpy as np


def _fast_hist(label_true, label_pred, n_class):
    mask = (label_true >= 0) & (label_true < n_class)
    hist = np.bincount(
        n_class * label_true[mask].astype(int) +
        label_pred[mask], minlength=n_class ** 2).reshape(n_class, n_class)
    return hist


def label_accuracy_score(label_trues, label_preds, n_class):
    """Returns accuracy score evaluation result.

      - overall accuracy
      - mean accuracy
      - mean IU
      - fwavacc
    """
    hist = np.zeros((n_class, n_class))
    for lt, lp in zip(label_trues, label_preds):
        hist += _fast_hist(lt.flatten(), lp.flatten(), n_class)
    acc = np.diag(hist).sum() / hist.sum()
    with np.errstate(divide='ignore', invalid='ignore'):
        acc_cls = np.diag(hist) / hist.sum(axis=1)
    acc_cls = np.nanmean(acc_cls)
    with np.errstate(divide='ignore', invalid='ignore'):
        iu = np.diag(hist) / (
            hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist)
        )
    mean_iu = np.nanmean(iu)
    freq = hist.sum(axis=1) / hist.sum()
    fwavacc = (freq[freq > 0] * iu[freq > 0]).sum()
    return acc, acc_cls, mean_iu, fwavacc

2.2 在不同数据集上的分割效果

在这里插入图片描述在这里插入图片描述在这里插入图片描述

3 FCN的优缺点

FCN 的优势在于:

  • 可以接受任意大小的输入图像(没有全连接层);
  • 更加高效,避免了使用邻域带来的重复计算和空间浪费的问题。

其不足也很突出:

  • 得到的结果还不够精细 。进行8倍上采样虽然比32倍的效果好了很多,但是上采样的结果还是比较模糊和平滑,对图像中的细节不敏感。
  • FCN是对各个像素进行分类,没有充分考虑像素与像素之间的关系。忽略了在通常的基于像素分类的分割方法中使用的空间规整(spatial regularization)步骤,缺乏空间一致性。

4 训练代码细节

  • 分割任务,label是每个像素点的类别信息,无法对label进行缩放处理,输入图像尺寸又不完全相同,训练时的解决办法是设置batchsize=1,即按照各图像的尺寸进行训练。有的帖子给出了对输入图像和label使用相同尺寸的矩形框裁剪进行大batch的训练,也是一种解决办法;
  • 反卷积层,使用的是称为bilinear weight的方法进行权重的初始化,就是越靠近卷积核的中心位置,其权重值越大,具体的初始化代码如下所示:
# https://github.com/shelhamer/fcn.berkeleyvision.org/blob/master/surgery.py
def get_upsampling_weight(in_channels, out_channels, kernel_size):
    """Make a 2D bilinear kernel suitable for upsampling"""
    factor = (kernel_size + 1) // 2
    if kernel_size % 2 == 1:
        center = factor - 1
    else:
        center = factor - 0.5
    og = np.ogrid[:kernel_size, :kernel_size]
    filt = (1 - abs(og[0] - center) / factor) * \
           (1 - abs(og[1] - center) / factor)
    weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size),
                      dtype=np.float64)
    weight[range(in_channels), range(out_channels), :, :] = filt
    return torch.from_numpy(weight).float()
  • 反卷积处理之后,并无法完全保证输出的尺寸和输入的尺寸完全一致,因此这里是从反卷积的输出上裁剪和输入尺寸一致的子块来保证输入和输出的尺寸一致;
  • 第一个卷积层的padding为100,因此才能从卷积池化结果中进行反卷积恢复出大于原图尺寸的输出;
  • 使用预训练的分类模型初始化分割模型的卷积层的权重,分割模型中由全连接层变换得到的卷积层的权重就由原来的全连接层的权重进行初始化,反卷积层不参与训练过程。整个的训练是对分类模型的微调,学习率设置的相当小。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值