全卷积网络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 通 ...
随机推荐
- 如何利用C++的time头文件获取系统时间
C++提供了time.h头文件进行时间编辑操作,可以把时间格式化进tm结构体,方便使用.MFC框架中的ctime类就是基于time.h封装的. 代码样例: #include <iostream& ...
- UVA11100旅行(大包装小包,问最少多少个包)
题意: 有n个包裹,每个包裹的大小都给出了,然后小的包裹可以装到大的包裹里,问最少装成多少个包裹,装成最小的包裹后还有一个限制就是让包裹数最多的那个包裹最少,就是说尽量都平均分,比如有6 ...
- XCTF-web_python_template_injection
web_python_template_injection 这里涉及到flask的ssti漏洞(服务端模板注入). 简单点说就是,在使用flask/jinja2的模板渲染函数render_templa ...
- Android 面试必备 - 系统、App、Activity 启动过程“一锅端”
Android 系统启动过程 从系统层看: linux 系统层 Android系统服务层 Zygote 从开机启动到Home Launcher: 启动bootloader (小程序:初始化硬件) 加载 ...
- springboot开发浅谈 2021/05/11
学习了这么久,本人希望有时间能分享一下,这才写下这篇浅谈,谈谈软件,散散心情. 这是本人的博客园账号,欢迎关注,一起学习. 一开始学习springboot,看了好多网站,搜了好多课程.零零落落学了一些 ...
- SpringBoot+MyBatis练手项目笔记汇总
以下是我在练习SpringBoot+MyBatis训练时候个人一些笔记汇总(可以点击跳转),献丑了,网上很多大佬的文章都比我写的详细,一些好的文章,我会将贴到各个内容中. 1. 插入数据返回id和内部 ...
- .NET Core 中依赖注入框架详解 Autofac
本文将通过演示一个Console应用程序和一个ASP.NET Core Web应用程序来说明依赖注入框架Autofac是如何使用的 Autofac相比.NET Core原生的注入方式提供了强大的功能, ...
- Etcd中Raft日志复制的实现
Raft state of log commitIndex : A log entry is committed once the leader that created the entry has ...
- uboot1: 启动流程和移植框架
目录 0 环境 1 移植框架 3 执行流程 3.0 链接地址 3.1 start.S, 入口 3.2 __main 3.3 board_init_f()和init_sequence_f[] 3.4 r ...
- jquery的入口函数 和 js和jq的转化
先引入jq包 然后<script type="text/javascript"> $(function{ 获取标签:$('#box2') jq转为js:$('#box2 ...