FCN

来源:https://blog.csdn.net/zhyj3038/article/details/71195262

1 实例分割,

对图像中的每一个像素点进行分类,同种物体的不同实例也用不同的类标进行标注,中间是实例分割,右图是语义分割.

2FCN与CNN区别

3CNN中Alexnet的代码:https://blog.csdn.net/akadiao/article/details/78592290?locationNum=10&fps=1

def AlexNet(images, classNum=None, dropoutrate=None):
    parameters = []

    # 卷积层1
    conv1, parameters = convLayer(images, name='conv1', kh=11, kw=11, n_out=64, dh=4, dw=4, p=parameters)
    # 添加LRN层和最大池化层
    # 对conv1进行LRN处理
    lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1')
    # 对lrn1进行最大池化处理,池化尺寸3*3,步长2*2,padding模式选VALID即取样不能超过边框
    pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')
    # 打印出结果pool1的结构
    print pool1.op.name, ' ', pool1.get_shape().as_list()

    # 卷积层2
    conv2, parameters = convLayer(pool1, name='conv2', kh=5, kw=5, n_out=192, dh=1, dw=1, p=parameters)
    # LRN处理
    lrn2 = tf.nn.lrn(conv2, 4, bias=1.0,alpha=0.001/9, beta=0.75, name='lrn2')
    # 最大池化处理
    pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')
    # 打印出结果pool2的结构
    print pool2.op.name, ' ', pool2.get_shape().as_list()

    # 卷积层3
    conv3, parameters = convLayer(pool2, name='conv3', kh=3, kw=3, n_out=384, dh=1, dw=1, p=parameters)

    # 卷积层4
    conv4, parameters = convLayer(conv3, name='conv4', kh=3, kw=3, n_out=256, dh=1, dw=1, p=parameters)

    # 卷积层5
    conv5, parameters = convLayer(conv4, name='conv5', kh=3, kw=3, n_out=256, dh=1, dw=1, p=parameters)
    pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool5')
    print pool5.op.name, ' ', pool5.get_shape().as_list()

    fc_in = tf.reshape(pool5, [-1, 256*6*6])
    fc6 = fcLayer(fc_in, 256*6*6, 4096, True, 'fc6')
    dropout6 = tf.nn.dropout(fc6, dropoutrate)

    fc7 = fcLayer(dropout6, 4096, 4096,True, 'fc7')
    dropout7 = tf.nn.dropout(fc7, dropoutrate)

    fc8 = fcLayer(dropout7, 4096, classNum, True, 'fc8')

    return pool5, parameters

FCN网络结构的部分代码,将三个全连接换成了卷积层:
with tf.variable_scope("inference"):
    image_net = vgg_net(weights, processed_image)
    conv_final_layer = image_net["conv5_3"]

    pool5 = utils.max_pool_2x2(conv_final_layer)

    W6 = utils.weight_variable([7, 7, 512, 4096], name="W6")
    b6 = utils.bias_variable([4096], name="b6")
    conv6 = utils.conv2d_basic(pool5, W6, b6)
    relu6 = tf.nn.relu(conv6, name="relu6")
    if FLAGS.debug:
        utils.add_activation_summary(relu6)
        #根据给出的keep_prob参数,将输入tensor x按比例输出。
    relu_dropout6 = tf.nn.dropout(relu6, keep_prob=keep_prob)

    W7 = utils.weight_variable([1, 1, 4096, 4096], name="W7")#图片的尺寸变成1*1,卷积核的数量4096,输出的尺寸就是1*1*4096,最后额
    b7 = utils.bias_variable([4096], name="b7")
    conv7 = utils.conv2d_basic(relu_dropout6, W7, b7)
    relu7 = tf.nn.relu(conv7, name="relu7")
    if FLAGS.debug:
        utils.add_activation_summary(relu7)
    relu_dropout7 = tf.nn.dropout(relu7, keep_prob=keep_prob)

    W8 = utils.weight_variable([1, 1, 4096, NUM_OF_CLASSESS], name="W8")
    b8 = utils.bias_variable([NUM_OF_CLASSESS], name="b8")
    conv8 = utils.conv2d_basic(relu_dropout7, W8, b8)
    # annotation_pred1 = tf.argmax(conv8, dimension=3, name="prediction1")

    # now to upscale to actual image size 现在升级为实际的图像大小
    deconv_shape1 = image_net["pool4"].get_shape()
    W_t1 = utils.weight_variable([4, 4, deconv_shape1[3].value, NUM_OF_CLASSESS], name="W_t1")
    b_t1 = utils.bias_variable([deconv_shape1[3].value], name="b_t1")
    conv_t1 = utils.conv2d_transpose_strided(conv8, W_t1, b_t1, output_shape=tf.shape(image_net["pool4"]))
    fuse_1 = tf.add(conv_t1, image_net["pool4"], name="fuse_1")

    deconv_shape2 = image_net["pool3"].get_shape()
    W_t2 = utils.weight_variable([4, 4, deconv_shape2[3].value, deconv_shape1[3].value], name="W_t2")
    b_t2 = utils.bias_variable([deconv_shape2[3].value], name="b_t2")
    conv_t2 = utils.conv2d_transpose_strided(fuse_1, W_t2, b_t2, output_shape=tf.shape(image_net["pool3"]))
    fuse_2 = tf.add(conv_t2, image_net["pool3"], name="fuse_2")

    shape = tf.shape(image)
    deconv_shape3 = tf.stack([shape[0], shape[1], shape[2], NUM_OF_CLASSESS])
    W_t3 = utils.weight_variable([16, 16, NUM_OF_CLASSESS, deconv_shape2[3].value], name="W_t3")
    b_t3 = utils.bias_variable([NUM_OF_CLASSESS], name="b_t3")
    conv_t3 = utils.conv2d_transpose_strided(fuse_2, W_t3, b_t3, output_shape=deconv_shape3, stride=8)

    annotation_pred = tf.argmax(conv_t3, dimension=3, name="prediction")

return tf.expand_dims(annotation_pred, dim=3), conv_t3
#对conv8此时进行32倍的上采样可以得到原图大小,这个时候得到的结果就是叫做FCN-32s. 
#在FCN-32s的基础上进行fine tuning,把pool4层和conv8的2倍上采样结果相加之后进行一个16倍的上采样,得到的结果是FCN-16s.如conv_t1
#FCN-16s的基础上进行fine tuning,把pool3层和2倍上采样的pool4层和4倍上采样的conv8层加起来,进行一个8倍的上采样,得到的结果就是FCN-8s

4 FCN的优点和缺点:

FCN的优点,能够end-to-end, pixels-to-pixels,而且相比于传统的基于cnn做分割的网络更加高效,因为避免了由于使用像素块而带来的重复存储和计算卷积的问题。

FCN的缺点也很明显,首先是训练比较麻烦,需要训练三次才能够得到FCN-8s,而且得到的结果还是不精细,对图像的细节不够敏感,这是因为在进行decode,也就是恢复原图像大小的过程时,输入上采样层的label map太稀疏,而且上采样过程就是一个简单的deconvolution. 

其次是对各个像素进行分类,没有考虑到像素之间的关系.忽略了在通常的基于像素分类的分割方法中使用的空间规整步骤,缺乏空间一致性.

5 FCN的实施过程中的跳跃连接的理解

  这个结构的作用就在于优化结果,因为如果将全卷积之后的结果直接上采样得到的结果是很粗糙的,所以作者将不同池化层的结果进行上采样之后来优化输出。具体结构如下:


6 U-net:

和FCN相比,结构上比较大的改动在上采样阶段,上采样层也包括了很多层的特征.

还有一个比FCN好的地方在于,Unet只需要一次训练,FCN需要三次训练.



### FCN (Fully Convolutional Networks) 概述 全卷积神经网络(FCN, Fully Convolutional Networks)是一种专门设计用于解决像素级语义分割问题的深度学习框架[^2]。它摒弃了传统卷积神经网络中的全连接层,转而使用全卷积操作来实现任意尺寸输入图像的处理,并生成对应尺寸的输出。这一特性使得FCN在网络结构上更加灵活,在推理和学习过程中表现出更高的效率。 #### 使用场景 FCN的主要应用场景集中在需要高精度像素级标注的任务中,例如: - **语义分割**:对整幅图像中的每个像素分配一个类别标签,广泛应用于自动驾驶、医学影像分析等领域[^1]。 - **实例分割**:不仅识别物体类别,还需要区分同一类别的不同个体。 - **目标检测**:尽管不是其主要用途,但可以通过结合其他模块(如区域建议网络RPN),间接支持目标检测任务。 #### 实现方式 ##### 1. 替换全连接层为卷积层 传统的CNN通常包含若干卷积层之后接几个全连接层来进行最终分类或回归计算。而在FCN的设计理念下,所有的全连接层都被替换成了等效功能的卷积层。这样做的好处是可以让模型适应各种分辨率的输入数据而不改变参数数量。 例如,对于VGG16这样的经典分类网络,可以将其最后三个全连接层转换成相应的卷积核大小分别为7×7、1×1以及另一组1×1的卷积层[^3]。 ```python import torch.nn as nn class VGG16_FCN(nn.Module): def __init__(self): super(VGG16_FCN, self).__init__() # 原始VGG部分省略... self.fc_conv_1 = nn.Conv2d(512, 4096, kernel_size=7) self.fc_conv_2 = nn.Conv2d(4096, 4096, kernel_size=1) self.score_fr = nn.Conv2d(4096, n_classes, kernel_size=1) def forward(self, x): ... x = F.relu(self.fc_conv_1(x)) x = F.dropout(x, p=0.5, training=self.training) x = F.relu(self.fc_conv_2(x)) x = F.dropout(x, p=0.5, training=self.training) out = self.score_fr(x) return out ``` ##### 2. 上采样与跳跃连接 为了恢复因多次池化操作丢失的空间信息,FCN引入了反卷积(Deconvolution/Transposed Convolution)机制进行逐层放大特征图尺度。此外,通过融合来自更浅层次富含位置细节的信息(即所谓的“跳跃连接”),进一步提升了边界清晰度及整体准确性。 具体来说,FCN8s版本综合利用了pool3、pool4两个中间阶段的结果与最终预测值相加后再做双线性插值得到更高分辨率的地图。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值