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

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

全卷积网络(FCN)

1.全卷积神经网络介绍

FCN对图像进行像素级的分类,从而解决了语义级别的图像分割(semantic segmentation)问题。与经典的CNN在卷积层之后使用全连接层得到固定长度的特征向量进行分类(全联接层+softmax输出)不同,FCN可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的feature map进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。

在这里插入图片描述全卷积网络利用三种特殊技术

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

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

2.1x1卷积

卷积运算的输出是通过用滑动窗口来扫描输入的卷积核,以及执行元素相乘和求和来实现的。

在这里插入图片描述

一个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 = 1stride = 1return 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.转置卷积

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

在这里插入图片描述

有些人可能称它为反卷积,因为它撤销了前一个卷积,由于我们所做的只是调换前向传播和反向传播的顺序,这里的数学计算实际上和之前做的完全一样。因此,其可微性质保留了下来。而训练与之前的神经网络完全相同。

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

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

如果我们有2x2输入和3x3内核;使用“ SAME”填充,并且跨度为2,我们可以预期输出尺寸为4x4。下图给出了该过程的想法。在这里插入图片描述3x3加权内核(输入像素与3x3内核的乘积)由红色和蓝色正方形表示,它们之间的步幅为2。虚线正方形表示输出周围的填充。随着加权核的移动,步幅决定了输出的最终尺寸。这些参数的不同值将导致上采样输出的尺寸不同。

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

import tensorflow as tf
import numpy as npdef upsample(x):"""对x应用两倍upsample并返回结果"""output = tf.layers.conv2d_transpose(x, 3, [2, 2], (2, 2), padding='SAME')return outputx = 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.跳跃链接

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

在这里插入图片描述

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

更多相关:

  • printf()函数优点在于可以格式化输出 格式:   %['padding_character][-][width][.precision]type   所有的转换说明都是以%开始,如果想打印一个%符号,必须用%% ;   参数“'padding_character”是可选,它将被用来填充变量直至所指定的宽度,该参...

  • 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按GPLTGPLT....这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写)的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按GPLT的顺序打印,直到所有字符都被输出。 输入格式: 输入在一行中给出一个长度不超过10000的、仅...

  • 给定两个整数A和B,输出从A到B的所有整数以及这些数的和。 输入格式: 输入在一行中给出2个整数A和B,其中−100≤A≤B≤100,其间以空格分隔。 输出格式: 首先顺序输出从A到B的所有整数,每5个数字占一行,每个数字占5个字符宽度,向右对齐。最后在一行中按Sum = X的格式输出全部数字的和X。 输入样例:...

  • python面试题目 原文地址:https://www.usblog.cc/blog/post/justzhl/b5cc9a05c7d2 问题一:以下的代码的输出将是什么? 说出你的答案并解释。 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Parent(object):     x...

  • 文章目录搭建实验平台使用Unity的准备工作在PC端安装连接Hololens软件Hololens设置开始连接吧绘制显示PC端网页监控...

  •  将多次卷积和池化后的图像展开进行全连接,如下图所示。  全连接层需要把输入拉成一个列项向量          比如你的输入的feature map是2X2,那么就需要把这个feature map 拉成4X1的列向量,如果你的feature map 的channels是3,也就是你的输入是3X2X2,也就是相当于有了12个像素点,...

  •   注意,前情提示: 本代码基于《Node.js(nodejs)对本地JSON文件进行增、删、改、查操作(轻车熟路)》 传送门Node.js(nodejs)对本地JSON文件进行增、删、改、查操作(轻车熟路)_你挚爱的强哥❤给你发来1条消息❤-CSDN博客 首先安装 cnpm i nodejs-websocket 在/a...

  • Ubuntu 配置自带桌面共享 1、在setting>>shareing>>remote 选择on 如果用ubunutu直接远程连接的话已经可以了, 2、在ubuntu下使用系统自带的remmina连接 vnc类型 直接输入ip地址 3、如果在windows下面连接的话需要把加密选项关闭 内容: 安装dconf-edito...

  • 第四节 RabbitMQ在C#端的应用-客户端连接 原文:第四节 RabbitMQ在C#端的应用-客户端连接 版权声明:未经本人同意,不得转载该文章,谢谢 https://blog.csdn.net/phocus1/article/details/87357911 1.在VS2013中新建控制台程序,然后添加引用:.NE...