Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)

全卷积网络(FCN)

1.全卷积神经网络介绍

FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。
Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)_第1张图片全卷积网络利用三种特殊技术

  1. 以一层层卷积层替代全连接层(1x1卷积
  2. 利用转置卷积层进行上采样
  3. 跳跃连接

全卷积网络在结构上通常有编码器(Encoder)和解码器(Decoder)组成。编码器是一系列卷积层,如VGG和ResNet。编码器的目标是从图像中提取特征,解码器放大编码器的输出,使它和原来的图像大小相同。

2.1x1卷积

卷积运算的输出是通过用滑动窗口来扫描输入的卷积核,以及执行元素相乘和求和来实现的。
Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)_第2张图片
一个1x1的卷积本质上是与一组维数的滤波器进行卷积:

  • 1x1xfilter_size (HxWxD),
  • stride = 1,
  • padding为全零填充(‘SAME‘)。

1x1卷积有助于降低该层的维数。相同大小的全连接层会产生相同数量的特征。然而,用卷积层替换全连接层有一个额外的好处,在(测试模型期间,可以将任何大小的图像输入到训练好的网络中。

import numpy as np
import tensorflow as tf


# 自定义初始化,默认设置种子为0
def custom_init(shape, dtype=tf.float32, partition_info=None, seed=0):
    return tf.random_normal(shape, dtype=dtype, seed=seed)


# TODO:使用“tf.layers。conv2d '来重现' tf.layers. density '的结果。
# 设置 `kernel_size` 和 `stride`.
def conv_1x1(x, num_outputs):
    kernel_size = 1
    stride = 1
    return tf.layers.conv2d(x, num_outputs, kernel_size, stride, weights_initializer=custom_init)


num_outputs = 2
x = tf.constant(np.random.randn(1, 2, 2, 1), dtype=tf.float32)
# 如果>的秩是2,则输入张量被‘tf. layer’压扁,然后再把它重塑回原来的秩作为输出
dense_out = tf.layers.dense(x, num_outputs, weights_initializer=custom_init)
conv_out = conv_1x1(x, num_outputs)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())

    a = sess.run(dense_out)
    b = sess.run(conv_out)
    print("Dense Output =", a)
    print("Conv 1x1 Output =", b)

    print("Same output? =", np.allclose(a, b, atol=1.e-5))

但是我发现并不能运行,程序报如下错误:

TypeError: conv2d() got an unexpected keyword argument 'weights_initializer'

经查询之后,tf.layers.densetf.layers.conv2d中并没有相关参数,于是将weights_initializer=custom_init更改为kernel_initializer=custom_init,程序正确输出如下:

Dense Output = [[[[-0.07399321  0.3901071 ]
   [-0.21965009  1.1580395 ]]

  [[ 0.29445606 -1.5524316 ]
   [-0.7433553   3.9191186 ]]]]
Conv 1x1 Output = [[[[-0.07399321  0.3901071 ]
   [-0.21965009  1.1580395 ]]

  [[ 0.29445606 -1.5524316 ]
   [-0.7433553   3.9191186 ]]]]
Same output? = True

1x1卷积函数注释:
tf.layers.conv2d(x, num_outputs, 1, 1, kernel_initializer=custom_init)

  • num_outputs 定义输出通道或内核的数量
  • 第三个参数是内核大小,即1
  • 第四个参数是步幅,我们将其设置为1
  • kernel_initializer=custom_init:我们使用自定义初始化程序,因此dense层和卷积层中的权重相同。

这就是保留空间信息的矩阵乘法运算。

3.转置卷积

我们可以使用转置卷积来创建全卷积网络的解码器。转置卷积本质上是一个反向卷积 ,其中前向和反向传播被调换。因此,我们称它为转置卷积

Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)_第3张图片
有些人可能称它为反卷积,因为它撤销了前一个卷积,由于我们所做的只是调换前向传播和反向传播的顺序,这里的数学计算实际上和之前做的完全一样。因此,其可微性质保留了下来。而训练与之前的神经网络完全相同。

转置卷积有助于将上一层上采样到所需的分辨率或尺寸。假设有一个3x3的输入,并且希望将其上采样到所需的6x6尺寸。该过程涉及将输入的每个像素与内核或过滤器相乘。如果此过滤器的大小为5x5,则此操作的输出将为大小为5x5的加权内核。然后,该加权内核定义您的输出层。

但是,该过程的上采样部分由步幅和填充定义。在TensorFlow中,使用tf.layers.conv2d_transpose,跨度为2和“ SAME”填充将导致输出尺寸为6x6。

如果我们有2x2输入和3x3内核;使用“ SAME”填充,并且跨度为2,我们可以预期输出尺寸为4x4。下图给出了该过程的想法。Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)_第4张图片3x3加权内核(输入像素与3x3内核的乘积)由红色和蓝色正方形表示,它们之间的步幅为2。虚线正方形表示输出周围的填充。随着加权核的移动,步幅决定了输出的最终尺寸。这些参数的不同值将导致上采样输出的尺寸不同。

在TensorFlow中,API tf.layers.conv2d_transpose用于创建转置的卷积层,程序如下:

import tensorflow as tf
import numpy as np


def upsample(x):
    """
    对x应用两倍upsample并返回结果
    """
    output = tf.layers.conv2d_transpose(x, 3, [2, 2], (2, 2), padding='SAME')
    return output


x = tf.constant(np.random.randn(1, 4, 4, 3), dtype=tf.float32)
conv = upsample(x)

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    result = sess.run(conv)

    print('Input Shape: {}'.format(x.get_shape()))
    print('Output Shape: {}'.format(result.shape))

输出如下:

Input Shape: (1, 4, 4, 3)
Output Shape: (1, 8, 8, 3)

程序注释
使用tf.layers.conv2d_transpose(x, 3, (2, 2), (2, 2))上采样。

  • 第二个参数3是内核/输出通道的数量。
  • 第三个参数是内核大小(2,2)。注意,内核大小也可以是(1,1),输出形状也可以是相同的。但是,如果将其更改为(3,3),请注意形状将为(9,9),至少使用“VALID”填充。
  • 第四个参数,步幅数,是我们如何从高到宽从(4, 4)到(8, 8)。如果这是一个常规的卷积,则输出的高度和宽度将为(2, 2)。

4.跳跃链接

全卷积网络使用的第三种特殊技术,是跳跃连接。一般来说 卷积或编码的一个缺点是,当你仔细观察某张图片,会导致范围缩小,从而失去了更大的图片。因此 即使我们要将编码器的输出解码回原始图像尺寸,一些信息却已经丢失。
Udacity机器人软件工程师课程笔记(二十九) - 全卷积网络(FCN)_第5张图片

跳跃连接是轻松保留信息的一种方式。跳跃连接工作的方式是使一层的输出与一个非相邻层连接。这里使用按元素添加操作 ,将来自编码器的池化层的输出与当前层的输出相结合,最终的结果连接到下一层。这些跳跃连接使网络可以使用来自多分辨率的信息,因此网络能够做出更精确的分割决策。

你可能感兴趣的:(机器人软件工程,tensorflow,神经网络,机器学习,深度学习,卷积)