全卷积网络Fully Convolutional Networks (FCN)实战
全卷积网络Fully Convolutional Networks (FCN)实战
使用图像中的每个像素进行类别预测的语义分割。全卷积网络(FCN)使用卷积神经网络将图像像素转换为像素类别。与之前介绍的卷积神经网络不同,FCN通过转置卷积层将中间层特征映射的高度和宽度转换回输入图像的大小,使得预测结果在空间维度(高度和宽度)与输入图像一一对应。给定空间维度上的位置,信道维度的输出将是对应于该位置的像素的类别预测。
将首先导入实验所需的包或模块,然后解释转置卷积层。
%matplotlib inline
from d2l import mxnet as d2l
from mxnet import gluon, image, init, np, npx
from mxnet.gluon import nn
npx.set_np()
1. Constructing a Model
展示了一个完全卷积网络模型的最基本设计。如图1所示,全卷积网络首先利用卷积神经网络提取图像特征,然后通过1×1卷积层将通道数转换为类别数,最后利用转置卷积层将特征映射的高度和宽度转换为输入图像的大小。模型输出与输入图像具有相同的高度和宽度,并且在空间位置上具有一对一的对应关系。最终输出信道包含对应空间位置的像素的类别预测。

Fig. 1. Fully convolutional network.
下面,我们使用在ImageNet数据集上预先训练的ResNet-18模型来提取图像特征,并将网络实例记录为pretrained_net。如您所见,模型成员变量特性的最后两层是全局最大池化层GlobalAvgPool2D和示例展平层Flatten。输出模块包含用于输出的完全连接层。完全卷积网络不需要这些层。
pretrained_net = gluon.model_zoo.vision.resnet18_v2(pretrained=True)
pretrained_net.features[-4:], pretrained_net.output
(HybridSequential(
(0): BatchNorm(axis=1, eps=1e-05, momentum=0.9, fix_gamma=False, use_global_stats=False, in_channels=512)
(1): Activation(relu)
(2): GlobalAvgPool2D(size=(1, 1), stride=(1, 1), padding=(0, 0), ceil_mode=True, global_pool=True, pool_type=avg, layout=NCHW)
(3): Flatten
),
Dense(512 -> 1000, linear))
接下来,我们创建完全卷积的网络实例网。它复制了除预训练后的神经网络实例成员变量特征和预训练后得到的模型参数外的所有神经层。
net = nn.HybridSequential()
for layer in pretrained_net.features[:-2]:
net.add(layer)
当输入的高度和宽度分别为320和480时,网络的正向计算将使输入的高度和宽度减小到原来的1/32,即10和15。
X = np.random.uniform(size=(1, 3, 320, 480))
net(X).shape
(1, 512, 10, 15)
通过1×1卷积层将输出通道数转换为Pascal VOC2012(21)的类别数。最后,我们需要将特征图的高度和宽度放大32倍,以将它们变回输入图像的高度和宽度。依据卷积层输出形状的计算方法。因为(320−64+16×2+32)/32=10和(480−64+16×2+32)/32=15,我们构造了一个跨距为32的转置卷积层,并将卷积核的高度和宽度设置为64,填充为16。如果s的宽度和高度都是整数,那么s/2的卷积因子很难被放大。
num_classes = 21
net.add(nn.Conv2D(num_classes, kernel_size=1),
nn.Conv2DTranspose(
num_classes, kernel_size=64, padding=16, strides=32))
1.2. Initializing the Transposed Convolution Layer
转置卷积层可以放大特征地图。在图像处理中,有时需要放大图像,即上采样。上采样的方法有很多种,最常用的方法是双线性插值。简单地说,为了在坐标(x,y)处获得输出图像的像素,首先将坐标映射到输入图像的坐标。
这可以根据三个输入的大小与输出大小的比率来实现。映射值x′还有y′ 通常是实数。然后,我们找到离坐标最近的四个像素(x′,y′)在输入图像上。最后,输出图像在坐标(x,y)处的像素,根据输入图像上的这四个像素及其与(x′,y′)的相对距离计算。双线性插值的上采样可以通过使用以下双线性核函数构造的卷积核的转置卷积层来实现。由于篇幅的限制,我们只给出了双线性_核函数的实现,而不讨论算法的原理。
def bilinear_kernel(in_channels, out_channels, kernel_size):
factor = (kernel_size + 1) // 2
if kernel_size % 2 == 1:
center = factor - 1
else:
center = factor - 0.5
og = (np.arange(kernel_size).reshape(-1, 1),
np.arange(kernel_size).reshape(1, -1))
filt = (1 - np.abs(og[0] - center) / factor) * \
(1 - np.abs(og[1] - center) / factor)
weight = np.zeros((in_channels, out_channels, kernel_size, kernel_size))
weight[range(in_channels), range(out_channels), :, :] = filt
return np.array(weight)
将实验双线性插值上采样实现转置卷积层。构造一个转置卷积层,将输入的高度和宽度放大2倍,并用双线性_核函数初始化其卷积核。
conv_trans = nn.Conv2DTranspose(3, kernel_size=4, padding=1, strides=2)
conv_trans.initialize(init.Constant(bilinear_kernel(3, 3, 4)))
读取图像X并将上采样结果记录为Y。为了打印图像,需要调整通道尺寸的位置。
img = image.imread('../img/catdog.jpg')
X = np.expand_dims(img.astype('float32').transpose(2, 0, 1), axis=0) / 255
Y = conv_trans(X)
out_img = Y[0].transpose(1, 2, 0)
如您所见,转置卷积层将图像的高度和宽度都放大了2倍。值得一提的是,除了坐标比例尺不同外,双线性插值放大后的图像与原始图像看起来一样。
d2l.set_figsize((3.5, 2.5))
print('input image shape:', img.shape)
d2l.plt.imshow(img.asnumpy());
print('output image shape:', out_img.shape)
d2l.plt.imshow(out_img.asnumpy());
input image shape: (561, 728, 3)
output image shape: (1122, 1456, 3)

在完全卷积网络中,我们初始化转置卷积层以进行上采样双线性插值。对于1×1卷积层,我们使用Xavier进行随机初始化。
W = bilinear_kernel(num_classes, num_classes, 64)
net[-1].initialize(init.Constant(W))
net[-2].initialize(init=init.Xavier())
1.3. Reading the Dataset
将随机裁剪的输出图像的形状指定为320×480,所以高度和宽度都可以被32整除。
batch_size, crop_size = 32, (320, 480)
train_iter, test_iter = d2l.load_data_voc(batch_size, crop_size)
Downloading ../data/VOCtrainval_11-May-2012.tar from http://d2l-data.s3-accelerate.amazonaws.com/VOCtrainval_11-May-2012.tar...
read 1114 examples
read 1078 examples
1.4. Training
可以开始训练模型了。这里的损失函数和精度计算与图像分类中使用的损失函数和精度计算没有实质性的不同。因为我们使用转置卷积层的通道来预测像素类别,所以在SoftmaxCrossEntropyLoss中指定了axis=1(通道尺寸)选项。此外,该模型还根据每个像素的预测类别是否正确来计算精度。
num_epochs, lr, wd, ctx = 5, 0.1, 1e-3, d2l.try_all_gpus()
loss = gluon.loss.SoftmaxCrossEntropyLoss(axis=1)
net.collect_params().reset_ctx(ctx)
trainer = gluon.Trainer(net.collect_params(), 'sgd',
{'learning_rate': lr, 'wd': wd})
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, ctx)
loss 0.331, train acc 0.890, test acc 0.841
290.4 examples/sec on [gpu(0), gpu(1)]

1.5. Prediction
在预测过程中,需要对每个通道的输入图像进行标准化,并将其转换为卷积神经网络所需的四维输入格式。
def predict(img):
X = test_iter._dataset.normalize_image(img)
X = np.expand_dims(X.transpose(2, 0, 1), axis=0)
pred = net(X.as_in_ctx(ctx[0])).argmax(axis=1)
return pred.reshape(pred.shape[1], pred.shape[2])
为了可视化每个像素的预测类别,我们将预测的类别映射回其在数据集中的标记颜色。
def label2image(pred):
colormap = np.array(d2l.VOC_COLORMAP, ctx=ctx[0], dtype='uint8')
X = pred.astype('int32')
return colormap[X, :]
测试数据集中图像的大小和形状各不相同。由于该模型使用了一个跨距为32的转置卷积层,当输入图像的高度或宽度不能被32整除时,转置卷积层输出的高度或宽度会偏离输入图像的大小。为了解决这个问题,我们可以在图像中裁剪多个高宽为32的整数倍的矩形区域,然后对这些区域中的像素进行正演计算。合并时,这些区域必须完全覆盖输入图像。当一个像素被多个区域覆盖时,不同区域的前向计算中输出的转置卷积层的平均值可以用作softmax操作的输入,以预测类别。
为了简单起见,我们只读取一些大的测试图像并裁剪出一个320×480形状的区域
从图像的左上角开始。仅此区域用于预测。对于输入图像,首先打印裁剪区域,然后打印预测结果,最后打印标签类别。
voc_dir = d2l.download_extract('voc2012', 'VOCdevkit/VOC2012')
test_images, test_labels = d2l.read_voc_images(voc_dir, False)
n, imgs = 4, []
for i in range(n):
crop_rect = (0, 0, 480, 320)
X = image.fixed_crop(test_images[i], *crop_rect)
pred = label2image(predict(X))
imgs += [X, pred, image.fixed_crop(test_labels[i], *crop_rect)]
d2l.show_images(imgs[::3] + imgs[1::3] + imgs[2::3], 3, n, scale=2);

1.6. Summary
- The fully convolutional network first uses the convolutional neural network to extract image features, then transforms the number of channels into the number of categories through the 1×1 convolution layer, and finally transforms the height and width of the feature map to the size of the input image by using the transposed convolution layer to output the category of each pixel.
- In a fully convolutional network, we initialize the transposed convolution layer for upsampled bilinear interpolation.
全卷积网络Fully Convolutional Networks (FCN)实战的更多相关文章
- 深度学习方法(十三):卷积神经网络结构变化——可变形卷积网络deformable convolutional networks
上一篇我们介绍了:深度学习方法(十二):卷积神经网络结构变化--Spatial Transformer Networks,STN创造性地在CNN结构中装入了一个可学习的仿射变换,目的是增加CNN的旋转 ...
- 全卷积网络 FCN 详解
背景 CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体,在2015年之前还是一个世界难题.神经网络大神Jonathan Long发表了<Fully Convolutional N ...
- 语义分割--全卷积网络FCN详解
语义分割--全卷积网络FCN详解 1.FCN概述 CNN做图像分类甚至做目标检测的效果已经被证明并广泛应用,图像语义分割本质上也可以认为是稠密的目标识别(需要预测每个像素点的类别). 传统的基于C ...
- 全卷积网络(FCN)实战:使用FCN实现语义分割
摘要:FCN对图像进行像素级的分类,从而解决了语义级别的图像分割问题. 本文分享自华为云社区<全卷积网络(FCN)实战:使用FCN实现语义分割>,作者: AI浩. FCN对图像进行像素级的 ...
- 全卷积网络FCN详解
http://www.cnblogs.com/gujianhan/p/6030639.html CNN能够对图片进行分类,可是怎么样才能识别图片中特定部分的物体? (图像语义分割) FCN(Fully ...
- 论文阅读笔记六:FCN:Fully Convolutional Networks for Semantic Segmentation(CVPR2015)
今天来看一看一个比较经典的语义分割网络,那就是FCN,全称如题,原英文论文网址:https://people.eecs.berkeley.edu/~jonlong/long_shelhamer_fcn ...
- 全卷积网络FCN
全卷积网络FCN fcn是深度学习用于图像分割的鼻祖.后续的很多网络结构都是在此基础上演进而来. 图像分割即像素级别的分类. 语义分割的基本框架: 前端fcn(以及在此基础上的segnet,decon ...
- 全卷积网络(FCN)与图像分割
最近在做物体检测,也用到了全卷积网络,来此学习一波. 这篇文章写了很好,有利于入门,在此记录一下: http://blog.csdn.net/taigw/article/details/5140144 ...
- 论文学习:Fully Convolutional Networks for Semantic Segmentation
发表于2015年这篇<Fully Convolutional Networks for Semantic Segmentation>在图像语义分割领域举足轻重. 1 CNN 与 FCN 通 ...
随机推荐
- POJ2391 Floyd+离散化+二分+DINIC
题意: 有n个猪圈,每个猪圈里面都有一定数量的猪(可能大于当前猪圈的数量),每个猪圈都有自己的容量,猪圈与猪圈之间给出了距离,然后突然下雨了,问多久之后所有的猪都能进圈. 思路: ...
- POJ1094查分约束,判断关系是否唯一
题意: 给你一些a<b的关系,然后有三组询问. 1 当前这组之后如果能确定这n个数的大小关系,那么就输出关系 2 当前时候出现bug,就是和前面如果冲突,那么就不行 3 最后的答案是 ...
- CVE-2013-2551:Internet Explore VML COALineDashStyleArray 整数溢出漏洞简单调试分析
0x01 2013 Pwn2Own 黑客大赛 在 Pwn2Own 的黑客大赛上,来自法国的 VUPEN 安全团队再一次利用 0day 漏洞攻破 Windows8 环境下的 IE10 浏览器,这一次问题 ...
- Intel汇编语言程序设计学习-第四章 数据传送、寻址和算术运算-下
4.3 和数据相关的操作符和伪指令 操作符和伪指令并非机器可执行的指令,相反,它们是由汇编器进行解释的.开发者可以使用一系列的MASM操作符或伪指令获取数据的地址以及大小等特征信息: OFFSET操 ...
- Day004 Scanner对象
Scanner对象 作用 我们可以通过Scanner类来获取用户的输入 所属包 java.util.Scanner java 工具类下 基本语法 Scannner s=new Scanner( ...
- 解决Failed to execute goal se.eris:notnull-instrumenter-maven-plugin:0.6.8
https://blog.csdn.net/fanrenxiang/article/details/80864908 github拉起来的项目,jdk是11,而我电脑上的jdk是1.8.原因是jdk版 ...
- JavaWeb——MySQL多表
内容索引 1. 多表查询 2. 事务 3. DCL 多表查询: * 查询语法: select 列名列表 from 表名列表 where.... * 准备sql # 创建部门表 CREATE TABLE ...
- VS2017报错 由#define后的分号引发的【“ 应输入“)】
其实并不是第十行分号出现了问题,而是由于在宏定义后面加了分号,修改成这样即可 一开始竟然没看出来--甚至以为是VS中出现"宏可以转换为constexpr"问题--下次要仔细--
- 用户对象/GDI对象/内核对象
对象的分类 Windows的对象可以分为三种,分别是用户对象,GDI对象和内核对象.系统使用用户对象支持窗口管理,使用GDI对象支持图形,并使用内核对象支持内存管理,进程执行和进程间通信(IPC) . ...
- 【敏杰开发】Beta阶段项目展示
[敏杰开发]Beta阶段项目展示 项目相关地址汇总 线上地址:http://roadmap.imcoming.top 前端仓库:https://github.com/MinJieDev/Roadmap ...