全卷积网络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 通 ...
随机推荐
- 内网穿透工具FRP的使用
目录 FRP 使用FRP建立隧道 服务端 客户端
- Supervisord远程命令执行漏洞(CVE-2017-11610)
目录 Supervisor 漏洞复现 修复建议 Supervisor Supervisor是使用Python 开发的进程管理程序,一般使用命令行进行管理,当然也能通过web接口图形化管理服务.在配置了 ...
- 【JavaScript】JS从入门到深入(复习查漏向
[JavaScript]JS从入门到深入(复习查漏向 pre 精细得学过一遍JS后才发现,原来之前CTF中有些nodejs的题目以及一些游戏题的payload就变得很好理解了. 基础知识 ECMASc ...
- Android LayoutInflater.inflate源码解析
一年多以前看过源码,感觉了解比较透彻了,长时间不经大脑思考,靠曾经总结的经验使用inflate方法,突然发现不知道什么时候忘记其中的原理了,上网查了一些资料,还各有不同,反而把我搞糊涂了,还是自己看源 ...
- SpringBoot JPA + 分页 + 单元测试SpringBoot JPA条件查询
application.properties 新增数据库链接必须的参数 spring.jpa.properties.hibernate.hbm2ddl.auto=update 表示会自动更新表结构,所 ...
- python主线程捕获子线程异常
python内置threading.Thread类创建的子线程抛出的异常无法在主线程捕获,可以对该类进行优化,为子线程添加exit code属性,主线程通过获取子线程的返回状态,来判断子线程中是否发生 ...
- Python JWT 介绍
Python JWT 介绍 目录 Python JWT 介绍 1. JWT 介绍 2. JWT 创建 token 2.1 JWT 生成原理 2.2 JWT 校验 token 原理 3. 代码实现 4. ...
- java基础——if和Swith的应用
顺序结构 java的基本结构就是顺序结构,除非特别说明,否则就按照一句一句的执行 它是任何一个算法都离不开的基本算法结构 选择结构 if单选择 if双选择 if多选择 嵌套的if结构 switch多选 ...
- 《我常用的股票投资工具与网站》v2.0
<我常用的股票投资工具与网站>v2.0 王大海 职业投资,抽空做一点分享. 661 人赞同了该文章 "少年你好,想不到你竟有如此因缘际会看到这里.我看你骨骼精奇,定是万中无一的交 ...
- 通过format学习,python的内部方法是面象对象的-python面向对象
1.常用的形式 s ="{0} {0} qqq {0} xxx {1}".format('dog','cat')print(s) 结果:dog dog qqq dog xxx ca ...