为什么resnet的输入是一定的?

因为resnet最后有一个全连接层。正是因为这个全连接层导致了输入的图像的大小必须是固定的。

输入为固定的大小有什么局限性?

原始的resnet在imagenet数据集上都会将图像缩放成224×224的大小,但这么做会有一些局限性:

(1)当目标对象占据图像中的位置很小时,对图像进行缩放将导致图像中的对象进一步缩小,图像可能不会正确被分类

(2)当图像不是正方形或对象不位于图像的中心处,缩放将导致图像变形

(3)如果使用滑动窗口法去寻找目标对象,这种操作是昂贵的

如何修改resnet使其适应不同大小的输入?

(1)自定义一个自己网络类,但是需要继承models.ResNet

(2)将自适应平均池化替换成普通的平均池化

(3)将全连接层替换成卷积层

相关代码:

import torch
import torch.nn as nn
from torchvision import models
import torchvision.transforms as transforms
from torch.hub import load_state_dict_from_url from PIL import Image
import cv2
import numpy as np
from matplotlib import pyplot as plt class FullyConvolutionalResnet18(models.ResNet):
def __init__(self, num_classes=1000, pretrained=False, **kwargs): # Start with standard resnet18 defined here
super().__init__(block = models.resnet.BasicBlock, layers = [2, 2, 2, 2], num_classes = num_classes, **kwargs)
if pretrained:
state_dict = load_state_dict_from_url( models.resnet.model_urls["resnet18"], progress=True)
self.load_state_dict(state_dict) # Replace AdaptiveAvgPool2d with standard AvgPool2d
self.avgpool = nn.AvgPool2d((7, 7)) # Convert the original fc layer to a convolutional layer.
self.last_conv = torch.nn.Conv2d( in_channels = self.fc.in_features, out_channels = num_classes, kernel_size = 1)
self.last_conv.weight.data.copy_( self.fc.weight.data.view ( *self.fc.weight.data.shape, 1, 1))
self.last_conv.bias.data.copy_ (self.fc.bias.data) # Reimplementing forward pass.
def _forward_impl(self, x):
# Standard forward for resnet18
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x) x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x) # Notice, there is no forward pass
# through the original fully connected layer.
# Instead, we forward pass through the last conv layer
x = self.last_conv(x)
return x

需要注意的是我们将全连接层的参数拷贝到自己定义的卷积层中去了。

看一下网络结构,主要是关注网络的最后:

我们将self.avgpool替换成了AvgPool2d,而全连接层虽然还在网络中,但是在前向传播时我们并没有用到 。

现在我们有这么一张图像:

图像大小为:(387, 1024, 3)。而且目标对象骆驼是位于图像的右下角的。

我们就以这张图片看一下是怎么使用的。

with open('imagenet_classes.txt') as f:
labels = [line.strip() for line in f.readlines()] # Read image
original_image = cv2.imread('camel.jpg')# Convert original image to RGB format
image = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB) # Transform input image
# 1. Convert to Tensor
# 2. Subtract mean
# 3. Divide by standard deviation transform = transforms.Compose([
transforms.ToTensor(), #Convert image to tensor.
transforms.Normalize(
mean=[0.485, 0.456, 0.406], # Subtract mean
std=[0.229, 0.224, 0.225] # Divide by standard deviation
)]) image = transform(image)
image = image.unsqueeze(0)
# Load modified resnet18 model with pretrained ImageNet weights
model = fcresnet18.FullyConvolutionalResnet18(pretrained=True).eval()
print(model)
with torch.no_grad():
# Perform inference.
# Instead of a 1x1000 vector, we will get a
# 1x1000xnxm output ( i.e. a probabibility map
# of size n x m for each 1000 class,
# where n and m depend on the size of the image.)
preds = model(image)
preds = torch.softmax(preds, dim=1) print('Response map shape : ', preds.shape) # Find the class with the maximum score in the n x m output map
pred, class_idx = torch.max(preds, dim=1)
print(class_idx) row_max, row_idx = torch.max(pred, dim=1)
col_max, col_idx = torch.max(row_max, dim=1)
predicted_class = class_idx[0, row_idx[0, col_idx], col_idx] # Print top predicted class
print('Predicted Class : ', labels[predicted_class], predicted_class)

说明:imagenet_classes.txt中是标签信息。在数据增强时,并没有将图像重新调整大小。用opencv读取的图片的格式为BGR,我们需要将其转换为pytorch的格式:RGB。同时需要使用unsqueeze(0)增加一个维度,变成[batchsize,channel,height,width]。看一下avgpool和last_conv的输出的维度:

我们使用torchsummary库来进行每一层输出的查看:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
from torchsummary import summary
summary(model, (3, 387, 1024))

结果:

----------------------------------------------------------------
Layer (type) Output Shape Param #
================================================================
Conv2d-1 [-1, 64, 194, 512] 9,408
BatchNorm2d-2 [-1, 64, 194, 512] 128
ReLU-3 [-1, 64, 194, 512] 0
MaxPool2d-4 [-1, 64, 97, 256] 0
Conv2d-5 [-1, 64, 97, 256] 36,864
BatchNorm2d-6 [-1, 64, 97, 256] 128
ReLU-7 [-1, 64, 97, 256] 0
Conv2d-8 [-1, 64, 97, 256] 36,864
BatchNorm2d-9 [-1, 64, 97, 256] 128
ReLU-10 [-1, 64, 97, 256] 0
BasicBlock-11 [-1, 64, 97, 256] 0
Conv2d-12 [-1, 64, 97, 256] 36,864
BatchNorm2d-13 [-1, 64, 97, 256] 128
ReLU-14 [-1, 64, 97, 256] 0
Conv2d-15 [-1, 64, 97, 256] 36,864
BatchNorm2d-16 [-1, 64, 97, 256] 128
ReLU-17 [-1, 64, 97, 256] 0
BasicBlock-18 [-1, 64, 97, 256] 0
Conv2d-19 [-1, 128, 49, 128] 73,728
BatchNorm2d-20 [-1, 128, 49, 128] 256
ReLU-21 [-1, 128, 49, 128] 0
Conv2d-22 [-1, 128, 49, 128] 147,456
BatchNorm2d-23 [-1, 128, 49, 128] 256
Conv2d-24 [-1, 128, 49, 128] 8,192
BatchNorm2d-25 [-1, 128, 49, 128] 256
ReLU-26 [-1, 128, 49, 128] 0
BasicBlock-27 [-1, 128, 49, 128] 0
Conv2d-28 [-1, 128, 49, 128] 147,456
BatchNorm2d-29 [-1, 128, 49, 128] 256
ReLU-30 [-1, 128, 49, 128] 0
Conv2d-31 [-1, 128, 49, 128] 147,456
BatchNorm2d-32 [-1, 128, 49, 128] 256
ReLU-33 [-1, 128, 49, 128] 0
BasicBlock-34 [-1, 128, 49, 128] 0
Conv2d-35 [-1, 256, 25, 64] 294,912
BatchNorm2d-36 [-1, 256, 25, 64] 512
ReLU-37 [-1, 256, 25, 64] 0
Conv2d-38 [-1, 256, 25, 64] 589,824
BatchNorm2d-39 [-1, 256, 25, 64] 512
Conv2d-40 [-1, 256, 25, 64] 32,768
BatchNorm2d-41 [-1, 256, 25, 64] 512
ReLU-42 [-1, 256, 25, 64] 0
BasicBlock-43 [-1, 256, 25, 64] 0
Conv2d-44 [-1, 256, 25, 64] 589,824
BatchNorm2d-45 [-1, 256, 25, 64] 512
ReLU-46 [-1, 256, 25, 64] 0
Conv2d-47 [-1, 256, 25, 64] 589,824
BatchNorm2d-48 [-1, 256, 25, 64] 512
ReLU-49 [-1, 256, 25, 64] 0
BasicBlock-50 [-1, 256, 25, 64] 0
Conv2d-51 [-1, 512, 13, 32] 1,179,648
BatchNorm2d-52 [-1, 512, 13, 32] 1,024
ReLU-53 [-1, 512, 13, 32] 0
Conv2d-54 [-1, 512, 13, 32] 2,359,296
BatchNorm2d-55 [-1, 512, 13, 32] 1,024
Conv2d-56 [-1, 512, 13, 32] 131,072
BatchNorm2d-57 [-1, 512, 13, 32] 1,024
ReLU-58 [-1, 512, 13, 32] 0
BasicBlock-59 [-1, 512, 13, 32] 0
Conv2d-60 [-1, 512, 13, 32] 2,359,296
BatchNorm2d-61 [-1, 512, 13, 32] 1,024
ReLU-62 [-1, 512, 13, 32] 0
Conv2d-63 [-1, 512, 13, 32] 2,359,296
BatchNorm2d-64 [-1, 512, 13, 32] 1,024
ReLU-65 [-1, 512, 13, 32] 0
BasicBlock-66 [-1, 512, 13, 32] 0
AvgPool2d-67 [-1, 512, 1, 4] 0
Conv2d-68 [-1, 1000, 1, 4] 513,000

================================================================
Total params: 11,689,512
Trainable params: 11,689,512
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 4.54
Forward/backward pass size (MB): 501.42
Params size (MB): 44.59
Estimated Total Size (MB): 550.55
----------------------------------------------------------------

最后是看一下预测的结果:

Response map shape :  torch.Size([1, 1000, 1, 4])
tensor([[[978, 980, 970, 354]]])
Predicted Class : Arabian camel, dromedary, Camelus dromedarius tensor([354])

与imagenet_classes.txt中对应(索引下标是从0开始的)

可视化关注点:

from google.colab.patches import cv2_imshow
# Find the n x m score map for the predicted class
score_map = preds[0, predicted_class, :, :].cpu().numpy()
score_map = score_map[0] # Resize score map to the original image size
score_map = cv2.resize(score_map, (original_image.shape[1], original_image.shape[0])) # Binarize score map
_, score_map_for_contours = cv2.threshold(score_map, 0.25, 1, type=cv2.THRESH_BINARY)
score_map_for_contours = score_map_for_contours.astype(np.uint8).copy() # Find the countour of the binary blob
contours, _ = cv2.findContours(score_map_for_contours, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE) # Find bounding box around the object.
rect = cv2.boundingRect(contours[0])
# Apply score map as a mask to original image
score_map = score_map - np.min(score_map[:])
score_map = score_map / np.max(score_map[:])
score_map = cv2.cvtColor(score_map, cv2.COLOR_GRAY2BGR)
masked_image = (original_image * score_map).astype(np.uint8) # Display bounding box
cv2.rectangle(masked_image, rect[:2], (rect[0] + rect[2], rect[1] + rect[3]), (0, 0, 255), 2) # Display images
#cv2.imshow("Original Image", original_image)
#cv2.imshow("activations_and_bbox", masked_image)
cv2_imshow(original_image)
cv2_imshow(masked_image)
cv2.waitKey(0)

在谷歌colab中ipynb要使用:from google.colab.patches import cv2_imshow

而不能使用opencv自带的cv2.show()
结果:

参考:https://www.learnopencv.com/cnn-receptive-field-computation-using-backprop/?ck_subscriber_id=503149816

【pytorch】改造resnet为全卷积神经网络以适应不同大小的输入的更多相关文章

  1. 全卷积神经网络FCN详解(附带Tensorflow详解代码实现)

    一.导论 在图像语义分割领域,困扰了计算机科学家很多年的一个问题则是我们如何才能将我们感兴趣的对象和不感兴趣的对象分别分割开来呢?比如我们有一只小猫的图片,怎样才能够通过计算机自己对图像进行识别达到将 ...

  2. 全卷积神经网络FCN

    卷积神经网络CNN(YannLecun,1998年)通过构建多层的卷积层自动提取图像上的特征,一般来说,排在前边较浅的卷积层采用较小的感知域,可以学习到图像的一些局部的特征(如纹理特征),排在后边较深 ...

  3. 全卷积神经网络FCN理解

    论文地址:https://people.eecs.berkeley.edu/~jonlong/long_shelhamer_fcn.pdf 这篇论文使用全卷积神经网络来做语义上的图像分割,开创了这一领 ...

  4. pytorch实战(7)-----卷积神经网络

    一.卷积: 卷积在 pytorch 中有两种方式: [实际使用中基本都使用 nn.Conv2d() 这种形式] 一种是 torch.nn.Conv2d(), 一种是 torch.nn.function ...

  5. Pytorch修改ResNet模型全连接层进行直接训练

    之前在用预训练的ResNet的模型进行迁移训练时,是固定除最后一层的前面层权重,然后把全连接层输出改为自己需要的数目,进行最后一层的训练,那么现在假如想要只是把 最后一层的输出改一下,不需要加载前面层 ...

  6. 基于区域的全卷积神经网络(R-FCN)简介

    在 Faster R-CNN 中,检测器使用了多个全连接层进行预测.如果有 2000 个 ROI,那么成本非常高. feature_maps = process(image)ROIs = region ...

  7. 卷积神经网络概念及使用 PyTorch 简单实现

    卷积神经网络 卷积神经网络(CNN)是深度学习的代表算法之一 .具有表征学习能力,能够按其阶层结构对输入信息进行平移不变分类,因此也被称为“平移不变人工神经网络”.随着深度学习理论的提出和数值计算设备 ...

  8. 手写数字识别 卷积神经网络 Pytorch框架实现

    MNIST 手写数字识别 卷积神经网络 Pytorch框架 谨此纪念刚入门的我在卷积神经网络上面的摸爬滚打 说明 下面代码是使用pytorch来实现的LeNet,可以正常运行测试,自己添加了一些注释, ...

  9. PyTorch基础——使用卷积神经网络识别手写数字

    一.介绍 实验内容 内容包括用 PyTorch 来实现一个卷积神经网络,从而实现手写数字识别任务. 除此之外,还对卷积神经网络的卷积核.特征图等进行了分析,引出了过滤器的概念,并简单示了卷积神经网络的 ...

随机推荐

  1. Web渗透基础小总结

    Web渗透框架概述 主要组成: 1. web语言代码(脚本) 2. web程序 3. 数据库程序 Web语言常见几大类 1. HTML:超文本标记语言,标准通用编辑语言下的一个应用 2. PHP:超文 ...

  2. 基于arduino的红外传感系统

    一.作品背景 在这个科技飞速发展的时代,物联网已经成为了我们身边必不可少的技术模块,我这次课程设计做的是一个基于arduino+树莓派+OneNet的红外报警系统,它主要通过识别人或者动物的运动来判断 ...

  3. Angular 从入坑到挖坑 - 表单控件概览

    一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...

  4. Windows下利用virtualenvwrapper指定python版本创建虚拟环境

    默认已安装virtualenvwrapper 一.添加环境变量(可选) 在系统环境变量中添加 WORKON_HOME ,用来指定新建的虚拟环境的存储位置,如过未添加,默认位置为 %USERPROFIL ...

  5. FSBPM流程引擎(002)之表单+自定义流程挂载到引擎

    本章节介绍如何将实际业务的表单和自定义流程挂载到FSBPM流程引擎上. 首先进入引擎交互界面: 点击创建:->出差申请 然后根据实际的业务输入对应的数据项即可,比如[姓名,部门,开始时间,结束时 ...

  6. JVM02——JVM运行时内存

    在上一篇文章中,我们介绍了 JVM 的内存区域,本文我们将继续围绕 JVM 展开话题,介绍 JVM 运行时内存.关注我的公众号「Java面典」了解更多 Java 相关知识点. Java 堆从 GC 的 ...

  7. MySQL 【优化宝典】

    概述 为什么要优化 系统的吞吐量瓶颈往往出现在数据库的访问速度上 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢 数据是存放在磁盘上的,读写速度无法和内存相比 如何优化 设计数据库 ...

  8. C++ 文件操作 FILE*

    #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> //编程题:往文件里写入字母表的26个字母. //要求:如果字母对应编码值 是奇数则写 ...

  9. Robotutor Scratch3.0 在线编程平台上线!

    终于,Scratch3.0在线编程平台上线了,不容易阿! 欢迎试用 https://scratch.robotutor.cn 欢迎交流,WeChat ID: iamlinweidong

  10. [项目分享]JSP+Servlet+JDBC实现的云端汽修后台管理系统

    本文存在视频版本,请知悉 项目简介 项目来源于:https://gitee.com/chenlinSir/CloudDemo-servlet 难度等级:简单 基于JSP+Servlet+Jdbc的云端 ...