【深度学习笔记】计算机视觉——FCN(全卷积网络

全卷积网络

sec_fcn

如 :numref:sec_semantic_segmentation中所介绍的那样,语义分割是对图像中的每个像素分类。
全卷积网络(fully convolutional network,FCN)采用卷积神经网络实现了从图像像素到像素类别的变换 :cite:Long.Shelhamer.Darrell.2015
与我们之前在图像分类或目标检测部分介绍的卷积神经网络不同,全卷积网络将中间层特征图的高和宽变换回输入图像的尺寸:这是通过在 :numref:sec_transposed_conv中引入的转置卷积(transposed convolution)实现的。
因此,输出的类别预测与输入图像在像素级别上具有一一对应关系:通道维的输出即该位置对应像素的类别预测。

%matplotlib inline
import torch
import torchvision
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

构造模型

下面我们了解一下全卷积网络模型最基本的设计。
如 :numref:fig_fcn所示,全卷积网络先使用卷积神经网络抽取图像特征,然后通过 1 × 1 1\times 1 1×1卷积层将通道数变换为类别个数,最后在 :numref:sec_transposed_conv中通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。
因此,模型输出与输入图像的高和宽相同,且最终输出通道包含了该空间位置像素的类别预测。

在这里插入图片描述

🏷fig_fcn

下面,我们[使用在ImageNet数据集上预训练的ResNet-18模型来提取图像特征],并将该网络记为pretrained_net
ResNet-18模型的最后几层包括全局平均汇聚层和全连接层,然而全卷积网络中不需要它们。

pretrained_net = torchvision.models.resnet18(pretrained=True)
list(pretrained_net.children())[-3:]
Downloading: "https://download.pytorch.org/models/resnet18-f37072fd.pth" to /home/ci/.cache/torch/hub/checkpoints/resnet18-f37072fd.pth0%|          | 0.00/44.7M [00:00<?, ?B/s][Sequential((0): BasicBlock((conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(downsample): Sequential((0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)))(1): BasicBlock((conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))),AdaptiveAvgPool2d(output_size=(1, 1)),Linear(in_features=512, out_features=1000, bias=True)]

接下来,我们[创建一个全卷积网络net]。
它复制了ResNet-18中大部分的预训练层,除了最后的全局平均汇聚层和最接近输出的全连接层。

net = nn.Sequential(*list(pretrained_net.children())[:-2])

给定高度为320和宽度为480的输入,net的前向传播将输入的高和宽减小至原来的 1 / 32 1/32 1/32,即10和15。

X = torch.rand(size=(1, 3, 320, 480))
net(X).shape
torch.Size([1, 512, 10, 15])

接下来[使用 1 × 1 1\times1 1×1卷积层将输出通道数转换为Pascal VOC2012数据集的类数(21类)。]
最后需要(将特征图的高度和宽度增加32倍),从而将其变回输入图像的高和宽。
回想一下 :numref:sec_padding中卷积层输出形状的计算方法:
由于 ( 320 − 64 + 16 × 2 + 32 ) / 32 = 10 (320-64+16\times2+32)/32=10 (32064+16×2+32)/32=10 ( 480 − 64 + 16 × 2 + 32 ) / 32 = 15 (480-64+16\times2+32)/32=15 (48064+16×2+32)/32=15,我们构造一个步幅为 32 32 32的转置卷积层,并将卷积核的高和宽设为 64 64 64,填充为 16 16 16
我们可以看到如果步幅为 s s s,填充为 s / 2 s/2 s/2(假设 s / 2 s/2 s/2是整数)且卷积核的高和宽为 2 s 2s 2s,转置卷积核会将输入的高和宽分别放大 s s s倍。

num_classes = 21
net.add_module('final_conv', nn.Conv2d(512, num_classes, kernel_size=1))
net.add_module('transpose_conv', nn.ConvTranspose2d(num_classes, num_classes,kernel_size=64, padding=16, stride=32))

[初始化转置卷积层]

在图像处理中,我们有时需要将图像放大,即上采样(upsampling)。
双线性插值(bilinear interpolation)
是常用的上采样方法之一,它也经常用于初始化转置卷积层。

为了解释双线性插值,假设给定输入图像,我们想要计算上采样输出图像上的每个像素。

  1. 将输出图像的坐标 ( x , y ) (x,y) (x,y)映射到输入图像的坐标 ( x ′ , y ′ ) (x',y') (x,y)上。
    例如,根据输入与输出的尺寸之比来映射。
    请注意,映射后的 x ′ x′ x y ′ y′ y是实数。
  2. 在输入图像上找到离坐标 ( x ′ , y ′ ) (x',y') (x,y)最近的4个像素。
  3. 输出图像在坐标 ( x , y ) (x,y) (x,y)上的像素依据输入图像上这4个像素及其与 ( x ′ , y ′ ) (x',y') (x,y)的相对距离来计算。

双线性插值的上采样可以通过转置卷积层实现,内核由以下bilinear_kernel函数构造。
限于篇幅,我们只给出bilinear_kernel函数的实现,不讨论算法的原理。

def bilinear_kernel(in_channels, out_channels, kernel_size):factor = (kernel_size + 1) // 2if kernel_size % 2 == 1:center = factor - 1else:center = factor - 0.5og = (torch.arange(kernel_size).reshape(-1, 1),torch.arange(kernel_size).reshape(1, -1))filt = (1 - torch.abs(og[0] - center) / factor) * \(1 - torch.abs(og[1] - center) / factor)weight = torch.zeros((in_channels, out_channels,kernel_size, kernel_size))weight[range(in_channels), range(out_channels), :, :] = filtreturn weight

让我们用[双线性插值的上采样实验]它由转置卷积层实现。
我们构造一个将输入的高和宽放大2倍的转置卷积层,并将其卷积核用bilinear_kernel函数初始化。

conv_trans = nn.ConvTranspose2d(3, 3, kernel_size=4, padding=1, stride=2,bias=False)
conv_trans.weight.data.copy_(bilinear_kernel(3, 3, 4));

读取图像X,将上采样的结果记作Y。为了打印图像,我们需要调整通道维的位置。

img = torchvision.transforms.ToTensor()(d2l.Image.open('../img/catdog.jpg'))
X = img.unsqueeze(0)
Y = conv_trans(X)
out_img = Y[0].permute(1, 2, 0).detach()

可以看到,转置卷积层将图像的高和宽分别放大了2倍。
除了坐标刻度不同,双线性插值放大的图像和在 :numref:sec_bbox中打印出的原图看上去没什么两样。

d2l.set_figsize()
print('input image shape:', img.permute(1, 2, 0).shape)
d2l.plt.imshow(img.permute(1, 2, 0));
print('output image shape:', out_img.shape)
d2l.plt.imshow(out_img);
input image shape: torch.Size([561, 728, 3])
output image shape: torch.Size([1122, 1456, 3])

在这里插入图片描述

全卷积网络[用双线性插值的上采样初始化转置卷积层。对于 1 × 1 1\times 1 1×1卷积层,我们使用Xavier初始化参数。]

W = bilinear_kernel(num_classes, num_classes, 64)
net.transpose_conv.weight.data.copy_(W);

[读取数据集]

我们用 :numref:sec_semantic_segmentation中介绍的语义分割读取数据集。
指定随机裁剪的输出图像的形状为 320 × 480 320\times 480 320×480:高和宽都可以被 32 32 32整除。

batch_size, crop_size = 32, (320, 480)
train_iter, test_iter = d2l.load_data_voc(batch_size, crop_size)
read 1114 examples
read 1078 examples

[训练]

现在我们可以训练全卷积网络了。
这里的损失函数和准确率计算与图像分类中的并没有本质上的不同,因为我们使用转置卷积层的通道来预测像素的类别,所以需要在损失计算中指定通道维。
此外,模型基于每个像素的预测类别是否正确来计算准确率。

def loss(inputs, targets):return F.cross_entropy(inputs, targets, reduction='none').mean(1).mean(1)num_epochs, lr, wd, devices = 5, 0.001, 1e-3, d2l.try_all_gpus()
trainer = torch.optim.SGD(net.parameters(), lr=lr, weight_decay=wd)
d2l.train_ch13(net, train_iter, test_iter, loss, trainer, num_epochs, devices)
loss 0.443, train acc 0.863, test acc 0.848
254.0 examples/sec on [device(type='cuda', index=0), device(type='cuda', index=1)]

在这里插入图片描述

[预测]

在预测时,我们需要将输入图像在各个通道做标准化,并转成卷积神经网络所需要的四维输入格式。

def predict(img):X = test_iter.dataset.normalize_image(img).unsqueeze(0)pred = net(X.to(devices[0])).argmax(dim=1)return pred.reshape(pred.shape[1], pred.shape[2])

为了[可视化预测的类别]给每个像素,我们将预测类别映射回它们在数据集中的标注颜色。

def label2image(pred):colormap = torch.tensor(d2l.VOC_COLORMAP, device=devices[0])X = pred.long()return colormap[X, :]

测试数据集中的图像大小和形状各异。
由于模型使用了步幅为32的转置卷积层,因此当输入图像的高或宽无法被32整除时,转置卷积层输出的高或宽会与输入图像的尺寸有偏差。
为了解决这个问题,我们可以在图像中截取多块高和宽为32的整数倍的矩形区域,并分别对这些区域中的像素做前向传播。
请注意,这些区域的并集需要完整覆盖输入图像。
当一个像素被多个区域所覆盖时,它在不同区域前向传播中转置卷积层输出的平均值可以作为softmax运算的输入,从而预测类别。

为简单起见,我们只读取几张较大的测试图像,并从图像的左上角开始截取形状为 320 × 480 320\times480 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, 320, 480)X = torchvision.transforms.functional.crop(test_images[i], *crop_rect)pred = label2image(predict(X))imgs += [X.permute(1,2,0), pred.cpu(),torchvision.transforms.functional.crop(test_labels[i], *crop_rect).permute(1,2,0)]
d2l.show_images(imgs[::3] + imgs[1::3] + imgs[2::3], 3, n, scale=2);


在这里插入图片描述

小结

  • 全卷积网络先使用卷积神经网络抽取图像特征,然后通过 1 × 1 1\times 1 1×1卷积层将通道数变换为类别个数,最后通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。
  • 在全卷积网络中,我们可以将转置卷积层初始化为双线性插值的上采样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/725727.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

校招中的“熟悉linux操作系统”一般是指达到什么程度?

校招中的“熟悉linux操作系统”一般是指达到什么程度&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&am…

归并排序总结

1.归并排序 归并排序的步骤如下&#xff1a; ①枚举中点&#xff0c;将区间分为左右两段&#xff1b; ②对左右两段区间分别排序&#xff1b; 这个过程以递归的方式进行。 ③合并两段区间。 是一个模拟的过程。用两个指针分别指向左右区间&#xff0c;判断当前哪个数小&…

基于机器学习的垃圾分类

1绪论 1.1问题背景 垃圾分类有减少环境污染、节省土地资源、再生资源的利用、提高民众价值观念等的好处&#xff0c;在倡导绿色生活&#xff0c;注重环境保护的今天&#xff0c;正确的垃圾分类和处理对我们的生态环境显得尤为重要。 在国外很多国家&#xff0c;经过了几十年…

VTK的编译和部署,配合c++和visual studio2022,VTK开发环境的配置

1.下载 在官网选择最新的版本 Download | VTK 下载之后进行解压&#xff0c;然后再里面创建build目录&#xff0c;方便后面使用cmake进行编译 2.对源码进行编译 打卡Cmake&#xff0c;如图操作 可以看到点击configure之后&#xff0c;cmake对我们的代码在进行处理 处理完成之…

基于SpringBoot+Vue+ElementUI+Mybatis前后端分离管理系统超详细教程(二)

学习后端CRUD操作 书接上文&#xff0c;我们学习了前后端分离项目的基础环境配置和用户管理模块的前后端基础搭建&#xff0c;以下链接是上一节教程内容详细步骤&#xff0c;友友们可以跟着步骤实操。本节课程我们在前面项目的基础上接着学习后端CRUD操作&#xff0c;真正打通数…

阿里云一键登录(号码认证服务)

前言 用户登录原来的登录方式如下 1. 手机号验证码 2. 账号密码 运营觉得操作过于复杂, 因此想引入阿里自动登录的逻辑, 也就是号码认证服务,所以才有了这篇问文章 注: 本文只是记录Java端的实现, app端的请自行查询文档实现 官方资料 文档 : 什么是号码认证服务_号码认证服务(…

SpringBoot中集成LiteFlow(轻量、快速、稳定可编排的组件式规则引擎)实现复杂业务解耦、动态编排、高可扩展

场景 在业务开发中&#xff0c;经常遇到一些串行或者并行的业务流程问题&#xff0c;而业务之间不必存在相关性。 使用策略和模板模式的结合可以解决这个问题&#xff0c;但是使用编码的方式会使得文件太多, 在业务的部分环节可以这样操作&#xff0c;在项目角度就无法一眼洞…

【洛谷 P9240】[蓝桥杯 2023 省 B] 冶炼金属 题解(二分答案)

[蓝桥杯 2023 省 B] 冶炼金属 题目描述 小蓝有一个神奇的炉子用于将普通金属 O 冶炼成为一种特殊金属 X。这个炉子有一个称作转换率的属性 V V V&#xff0c; V V V 是一个正整数&#xff0c;这意味着消耗 V V V 个普通金属 O 恰好可以冶炼出一个特殊金属 X&#xff0c;当普…

产业园区如何实现数字化运营管理?

​在数字化浪潮席卷全球的今天&#xff0c;产业园区正经历着前所未有的变革&#xff0c;数字化运营管理成为各个园区转型升级的发力方向&#xff0c;它不仅能够提升园区的运营管理效率&#xff0c;还能够帮助园区提高服务效能、实现精准招商、增强决策效率&#xff0c;从而全面…

Redis实战—商户查询缓存

本博客为个人学习笔记&#xff0c;学习网站&#xff1a;黑马程序员Redis入门到实战 实战篇之商户查询缓存 目录 什么是缓存 添加Redis缓存 缓存更新策略 数据库缓存不一致解决方案 案例&#xff1a;给查询商铺的缓存添加超时剔除和主动更新策略 缓存穿透 案例&#xff1…

奇富科技:大数据任务从诊断到自愈的实践之路

一、为什么要做诊断引擎 毓数平台是奇富科技公司自主研发的一站式大数据管理、开发、分析平台&#xff0c;覆盖大数据资产管理、数据开发及任务调度、自助分析及可视化、统一指标管理等多个数据生命周期流程&#xff0c;让用户使用数据的同时&#xff0c;挖掘数据最大的价值。…

打造高效、安全的交易平台:开发流程与关键要素解析

在数字化时代&#xff0c;大宗商品交易平台开发/搭建已成为连接买家与卖家的桥梁&#xff0c;为无数企业和个人提供了便捷、高效的交易机会。然而&#xff0c;随着市场的竞争日益激烈&#xff0c;如何打造一个既符合用户需求又具备竞争力的交易平台&#xff0c;成为了众多开发者…

文件上传{session文件包含以及条件竞争、图片文件渲染绕过(gif、png、jpg)}

session文件包含以及条件竞争 条件&#xff1a; 知道session文件存储在哪里 一般的默认位置&#xff1a; /var/lib/php/sess_PHPSESSID /var/lib/php/sessions/sess_PHPSESSID /tmp/sess_PHPSESSID /tmp/sessions/sess_PHPSESSID ####在没做过设置的情况下一般都是存储在/var…

解决WordPress更新插件或者更新版本报WordPress 需要访问您网页服务器的权限的问题

文章目录 前言一、原因二、解决步骤总结 前言 当对WordPress的插件或者版本进行更新时报错&#xff1a;要执行请求的操作&#xff0c;WordPress 需要访问您网页服务器的权限。 请输入您的 FTP 登录凭据以继续。 如果您忘记了您的登录凭据&#xff08;如用户名、密码&#xff09…

光线追踪7 - 抗锯齿(Antialiasing)

目前为止&#xff0c;如果你放大渲染出的图像&#xff0c;可能会注意到图像边缘的明显“阶梯状”效果。这种阶梯效果通常被称为“走样”或“锯齿”。当真实相机拍摄图片时&#xff0c;边缘通常没有锯齿&#xff0c;因为边缘像素是一些前景和一些背景的混合。请考虑&#xff0c;…

5. 链接和加载(linker and loader)

链接和加载(linker and loader)&#xff1a; linker即链接器&#xff0c;它负责将多个.c编译生成的.o文件&#xff0c;链接成一个可执行文件或者是库文件&#xff1b; loader即加载器&#xff0c;它原本的功能很单一只是将可执行文件的段拷贝到编译确定的内存地址即可&#x…

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目

作者推荐 视频算法专题 本文涉及知识点 树上倍增 树 图论 并集查找 换根法 深度优先 割点 LeetCode3067. 在带权树网络中统计可连接服务器对数目 给你一棵无根带权树&#xff0c;树中总共有 n 个节点&#xff0c;分别表示 n 个服务器&#xff0c;服务器从 0 到 n - 1 编号…

Java | 在消息对话框中显示文本

首先需要导入JOptionPane类&#xff0c;JOptionPane类属于Swing组件中的一种&#xff0c;其导入方式如下&#xff1a; import javax.swing.JOptionPane;可以使用JOptionPane的showMessageDialog方法显示消息文本。 参数格式&#xff1a; JOptionPane.showMessageDialog(paren…

【C语言】指针详细解读2

1.const 修饰指针 1.1 const修饰变量 变量是可以修改的&#xff0c;如果把变量的地址交给⼀个指针变量&#xff0c;通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制&#xff0c;不能被修改&#xff0c;怎么做呢&#xff1f;这就是const的作⽤。 …