文章目录

  • 前言
  • 网络结构
    • 残差结构
    • ResNet网络结构
    • BottleNeck结构
  • 代码详解
  • 实战

前言

2015年,何凯明为了降低网络训练难度,解决梯度消失的问题,提出了残差网络(Residual Network,ResNet),在2015年的ImageNet大规模视觉识别竞赛(ImageNet Large Scale Visual Recognition Challenge, ILSVRC)中获得了图像分类和物体识别的优胜。
论文地址:Deep Residual Learning for Image Recognition

网络结构

残差结构

Residual net(残差网络)︰将靠前若干层的某一层数据输出直接跳过多层引入到后面数据层的输入部分。
残差神经单元∶假定某段神经网络的输入是x,期望输出是H(x),如果我们直接将输入x传到输出作为初始结果,那么我们需要学习的目标就是F(x)= H(x) - x,这就是一个残差神经单元,相当于将学习目标改变了,不再是学习一个完整的输出H(x),只是输出和输入的差别H(x) - x,即残差。

ResNet网络结构

整体架构

下图中展示了 18 层、34 层、50 层、101 层、152 层框架细节

在ResNet类中网络数据的流向:

(1)数据进入网络后先经过输入部分(conv1, bn1, relu, maxpool);

(2)然后进入中间卷积部分(layer1, layer2, layer3, layer4);

(3)最后数据经过一个平均池化和全连接层(avgpool, fc)输出得到结果;

具体来说,resnet18和其他res系列网络的差异主要在于layer1~layer4,其他的部件都是相似的

BottleNeck结构


左图是ResNet18/34 BottleNeck
残差结构的主分支是由两层3x3的卷积层组成,而残差结构右侧的连接线是shortcut分支也称捷径分支(注意为了让主分支上的输出矩阵能够与我们捷径分支上的输出矩阵进行相加,必须保证这两个输出特征矩阵有相同的shape

右图是ResNet50/101/152 BottleNeck
第一个是1x1的卷积层用来压缩channel维度,第二个是3x3的卷积层,第三个是1x1的卷积层用来还原channel维度(注意主分支上第一层卷积层和第二次卷积层所使用的卷积核个数是相同的,第三次是第一层的4倍)

代码详解

导库

import argparseimport numpy as np
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as Data
import matplotlib.pyplot as plt
from PIL import Image
import torch.nn as nn
import torch.nn.functional as F
import torch.nn as nn
import torch.nn.functional as F

卷积运算
计算公式

def conv3x3(in_planes, out_planes, stride=1):"""3x3 convolution with padding"""'''nn.Conv2d 二维卷积的实现in_planes:输入的四维张量[N, C, H, W]中的C了,即输入张量的channels数out_planes:期望的四维输出张量的channels数kernel_size:卷积核的大小stride:步长padding:图像填充'''return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,padding=1, bias=False)

残差层

class Bottleneck(nn.Module):expansion = 4def __init__(self, inplanes, planes, stride=1, downsample=None):super(Bottleneck, self).__init__()self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False)self.bn1 = nn.BatchNorm2d(planes)self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride,padding=1, bias=False)self.bn2 = nn.BatchNorm2d(planes)self.conv3 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)self.bn3 = nn.BatchNorm2d(planes * 4)# inplace=True,用输出的数据覆盖输入的数据;节省空间,此时两者共用内存self.relu = nn.ReLU(inplace=True)self.downsample = downsampleself.stride = stridedef forward(self, x):residual = x # 先用变量保存初始张量out = self.conv1(x)print("1:",out.shape)out = self.bn1(out) #作用:卷积层之后总会添加BatchNorm2d进行数据的归一化处理print("2:",out.shape)out = self.relu(out)print("3:",out.shape)out = self.conv2(out)print("4:",out.shape)out = self.bn2(out)print("5:",out.shape)out = self.relu(out)print("6:",out.shape)out = self.conv3(out)print("7:",out.shape)out = self.bn3(out)print("8:",out.shape)if self.downsample is not None:residual = self.downsample(x)print("now:",residual.shape)out += residualprint("9:",out.shape)out = self.relu(out)print("10:",out.shape)return out

简单查看一下数据流动

net=Bottleneck(256,64)
# [N, C, H, W]
print(net(torch.rand([1,256,480,480])).shape)

ResNet整体框架


class ResNet(nn.Module):''''''def __init__(self, block, layers, num_classes, grayscale):self.inplanes = 64# 判断是否是灰度图if grayscale:in_dim = 1else:in_dim = 3super(ResNet, self).__init__()self.conv1 = nn.Conv2d(in_dim, 64, kernel_size=7, stride=2, padding=3,bias=False)self.bn1 = nn.BatchNorm2d(64)self.relu = nn.ReLU(inplace=True)# 最大池化层self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)# 构建每一个阶段self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2)self.layer3 = self._make_layer(block, 256, layers[2], stride=2)self.layer4 = self._make_layer(block, 512, layers[3], stride=2)self.avgpool = nn.AvgPool2d(7, stride=1)self.fc = nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):n = m.kernel_size[0] * m.kernel_size[1] * m.out_channelsm.weight.data.normal_(0, (2. / n)**.5)elif isinstance(m, nn.BatchNorm2d):m.weight.data.fill_(1)m.bias.data.zero_()def _make_layer(self, block, planes, blocks, stride=1):downsample = None#保持输出通道数量一致if stride != 1 or self.inplanes != planes * block.expansion:downsample = nn.Sequential(nn.Conv2d(self.inplanes, planes * block.expansion,kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(planes * block.expansion),)layers = []layers.append(block(self.inplanes, planes, stride, downsample))self.inplanes = planes * block.expansionfor i in range(1, blocks):layers.append(block(self.inplanes, planes))return nn.Sequential(*layers)def forward(self, x):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)# because MNIST is already 1x1 here:# disable avg pooling# x = self.avgpool(x)x = x.view(x.size(0), -1)logits = self.fc(x)probas = F.softmax(logits, dim=1)return logits, probas
'''
该份代码适合resnet50之后的,resnet18请看实战部分代码
resnet50:layers = [3, 4, 6, 3]
resnet101:layers=[3, 4, 23, 3]
resnet152:layers=[3, 8, 36, 3]
'''
model = ResNet(block=Bottleneck, layers=[3, 4, 6, 3],num_classes=10,grayscale=True)
print(model)

实战

ResNet18识别MNIST数据集


import argparseimport numpy as np
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as Data
import matplotlib.pyplot as plt
from PIL import Image
import torch.nn as nn
import torch.nn.functional as F
import torch.nn as nn
import torch.nn.functional as Fclass ResidualBlock(nn.Module):def __init__(self, inchannel, outchannel, stride=1):super(ResidualBlock, self).__init__()self.left = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel, outchannel, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(outchannel))self.shortcut = nn.Sequential()if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(outchannel))def forward(self, x):out = self.left(x)out += self.shortcut(x)out = F.relu(out)return outclass ResNet(nn.Module):def __init__(self, ResidualBlock, num_classes=10):super(ResNet, self).__init__()self.inchannel = 16self.conv1 = nn.Sequential(nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(16),nn.ReLU(),)self.layer1 = self.make_layer(ResidualBlock, 16,  2, stride=1)self.layer2 = self.make_layer(ResidualBlock, 32, 2, stride=2)self.layer3 = self.make_layer(ResidualBlock, 64, 2, stride=2)self.layer4 = self.make_layer(ResidualBlock, 128, 2, stride=2)self.fc = nn.Linear(128, num_classes)def make_layer(self, block, channels, num_blocks, stride):strides = [stride] + [1] * (num_blocks - 1)   #strides=[1,1]layers = []for stride in strides:layers.append(block(self.inchannel, channels, stride))self.inchannel = channelsreturn nn.Sequential(*layers)def forward(self, x):out = self.conv1(x)out = self.layer1(out)out = self.layer2(out)out = self.layer3(out)out = self.layer4(out)out = F.avg_pool2d(out, 4)out = out.view(out.size(0), -1)out = self.fc(out)return out
def ResNet18():return ResNet(ResidualBlock)lr = 0.1
Epoch = 1
Batch_size = 128
# device = torch.device("cuda" if torch.cuda.is_available() else "cpu")if __name__ == "__main__":train_dataset = torchvision.datasets.MNIST(root='./MNIST',train=True,download=True,transform=torchvision.transforms.ToTensor())test_dataset = torchvision.datasets.MNIST(root='./MNISt',train=False,download=True,transform=torchvision.transforms.ToTensor())# define train loadertrain_loader = Data.DataLoader(dataset=train_dataset,shuffle=True,batch_size=Batch_size)test_loader = Data.DataLoader(dataset=test_dataset,shuffle=True,batch_size=Batch_size)test_x = torch.unsqueeze(test_dataset.data, dim=1).type(torch.Tensor)test_y = test_dataset.targetsnet = ResNet18()
#     net.to(device)# print(net)#查看网络结构opt = torch.optim.SGD(net.parameters(), lr=lr)loss_fun = nn.CrossEntropyLoss()a = []ac_list = []cnt=1for epoch in range(Epoch):for i,(x, y) in enumerate(train_loader):
#             cnt+=1
#             if cnt>200:
#                 breakoutput = net(x)loss = loss_fun(output, y)opt.zero_grad()loss.backward()opt.step()            if i % 100 == 0:a.append(i)test_output = torch.max(net(test_x), dim=1)[1]loss = loss_fun(net(test_x), test_y).item()accuracy = torch.sum(torch.eq(test_y, test_output)).item() / test_y.numpy().sizeac_list.append(accuracy)print('Epoch:', Epoch, '|loss%.4f' % loss, '|accuracy%.4f' % accuracy)print('real value', test_y[: 10].numpy())print('train value', torch.max(net(test_x)[: 10], dim=1)[1].numpy())plt.plot(a, ac_list, color='r')plt.show()


import argparseimport numpy as np
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
import torch.utils.data as Data
import matplotlib.pyplot as plt
from PIL import Image
import torch.nn as nn
import torch.nn.functional as F
import torch.nn as nn
import torch.nn.functional as Fclass ResidualBlock(nn.Module):def __init__(self, inchannel, outchannel, stride=1):super(ResidualBlock, self).__init__()self.left = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=3, stride=stride, padding=1, bias=False),nn.BatchNorm2d(outchannel),nn.ReLU(inplace=True),nn.Conv2d(outchannel, outchannel, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(outchannel))self.shortcut = nn.Sequential()if stride != 1 or inchannel != outchannel:self.shortcut = nn.Sequential(nn.Conv2d(inchannel, outchannel, kernel_size=1, stride=stride, bias=False),nn.BatchNorm2d(outchannel))def forward(self, x):out = self.left(x)out += self.shortcut(x)out = F.relu(out)return outclass ResNet(nn.Module):def __init__(self, ResidualBlock, num_classes=10):super(ResNet, self).__init__()self.inchannel = 16self.conv1 = nn.Sequential(nn.Conv2d(1, 16, kernel_size=3, stride=1, padding=1, bias=False),nn.BatchNorm2d(16),nn.ReLU(),)self.layer1 = self.make_layer(ResidualBlock, 16,  2, stride=1)self.layer2 = self.make_layer(ResidualBlock, 32, 2, stride=2)self.layer3 = self.make_layer(ResidualBlock, 64, 2, stride=2)self.layer4 = self.make_layer(ResidualBlock, 128, 2, stride=2)self.fc = nn.Linear(128, num_classes)def make_layer(self, block, channels, num_blocks, stride):strides = [stride] + [1] * (num_blocks - 1)   #strides=[1,1]layers = []for stride in strides:layers.append(block(self.inchannel, channels, stride))self.inchannel = channelsreturn nn.Sequential(*layers)def forward(self, x):out = self.conv1(x)out = self.layer1(out)out = self.layer2(out)out = self.layer3(out)out = self.layer4(out)out = F.avg_pool2d(out, 4)out = out.view(out.size(0), -1)out = self.fc(out)return out
def ResNet18():return ResNet(ResidualBlock)lr = 0.1
Epoch = 1
Batch_size = 20if __name__ == "__main__":train_dataset = torchvision.datasets.MNIST(root='./MNIST',train=True,download=True,transform=torchvision.transforms.ToTensor())test_dataset = torchvision.datasets.MNIST(root='./MNISt',train=False,download=True,transform=torchvision.transforms.ToTensor())# define train loadertrain_loader = Data.DataLoader(dataset=train_dataset,shuffle=True,batch_size=Batch_size)test_loader = Data.DataLoader(dataset=test_dataset,shuffle=True,batch_size=Batch_size)net=ResNet18()print(len(test_loader))PATH ="./my_net.pth"net.load_state_dict(torch.load(PATH))test_correct = 0test_total = 0net.eval()for x, y in test_loader:y_pred = net(x)y_pred = torch.argmax(y_pred, dim=1)test_correct += (y_pred==y).sum().item()test_total += y.size(0)epoch_test_acc = test_correct / test_totalprint(epoch_test_acc)

测试结果:

参考文献
ResNet及其变种的结构梳理、有效性分析与代码解读(PyTorch)

深度学习原理与框架-卷积网络细节-网络设计技巧 1. 3个3 * 3替换7 * 7卷积核 2. 1 * 1 和 3 * 3 替换 3 * 3卷积核

ResNet网络详解及Pytorch代码实现

ResNet网络模型相关推荐

  1. 卷积神经网络之ResNet网络模型学习

    Deep Residual Learning for Image Recognition 微软亚洲研究院的何凯明等人 论文地址 https://arxiv.org/pdf/1512.03385v1.p ...

  2. Halcon 深度学习自定义网络模型-ResNet通用网络产生器

    Halcon 深度学习自定义网络模型-ResNet通用网络产生器 备注: 版本要求:halcon21.05++ Python下的ResNet网络模型源码: import torch import to ...

  3. 深度学习目标检测 RCNN F-RCNN SPP yolo-v1 v2 v3 残差网络ResNet MobileNet SqueezeNet ShuffleNet

    深度学习目标检测--结构变化顺序是RCNN->SPP->Fast RCNN->Faster RCNN->YOLO->SSD->YOLO2->Mask RCNN ...

  4. Python 卷积神经网络 ResNet的基本编写方法

    ResNet(Residual Network)是由微软亚洲研究院提出的深度卷积神经网络,它在2015年的ImageNet挑战赛上取得了第一名的好成绩.ResNet最大的特点是使用了残差学习,可以解决 ...

  5. Pytorch下微调网络模型(迁移学习)进行图像分类

    Pytorch下微调网络模型进行图像分类 利用ImageNet下的预训练权重采用迁移学习策略,能够实现模型快速训练,提高图像分类性能.下面以vgg和resnet网络模型为例,微调最后的分类层进行分类. ...

  6. resNet_model—定义残差网络模型

    文章目录 resnet_model.py resnet_main.py cifar_input.py resnet_model.py """ResNet model.Re ...

  7. Resnet的在指静脉识别应用与改进

    一.<基于改进残差网络的指静脉识别算法>_易芮 2020.5.20 由于指静脉具有区分性的特征在于其细节特征,为了减少网络训练过程中的信息丢失,在网络中采用改进的大卷积层以及BottleN ...

  8. NFNet的学习笔记

    1 引言 NFNet是目前CNN-backbone中精度最高的模型: 2 主要贡献 NFNet的主要创新点如下所示: NFNet AGC 3 论文译读 论文链接:High-Performance La ...

  9. AI 智能皮影机器人

    AI 智能皮影机器人 项目演示视频 项目背景 创意来源 皮影戏(Shadow Puppets),又称"影子戏"或"灯影戏",作为我国的第一批世界非物质文化遗产, ...

  10. 【Pytorch】基于卷积神经网络实现的面部表情识别

    作者:何翔 学院:计算机学院 学号:04191315 班级:软件1903 转载请标注本文链接: https://blog.csdn.net/HXBest/article/details/1219812 ...

最新文章

  1. GO!自制一款【不丑】的名片
  2. qemu使用实例和常用参数
  3. 单页面axios_Axios封装之取消重复请求和接口缓存
  4. 计算机网络安全管理协议,河西学院校园网络安全管理协议
  5. 欧几里得算法以及推论
  6. mblog Mtons博客系统
  7. ReportViewer教程(15)-矩阵报表-1
  8. tinypng 批量处理插件_分享六款逆天的Excel插件,高效处理数据必备!低调使用...
  9. 2013 Multi-University Training Contest 1
  10. 将Alfred与BetterZip整合,提高你的Mac工作效率
  11. Shadowsocks错误:端口已被占用1090的错误解决办法
  12. 织梦dedecms调用热门搜索关键词的方法
  13. window7修改屏幕旋转快捷键
  14. python数据可视化方法和库
  15. 倒序输出字符串,如输入i love you,则输出you love i
  16. 苏州驾考科目三考试攻略
  17. 最详细matlab 2018a安装教程步骤.
  18. IPPICV: Download failed: 6;“Couldn‘t resolve host name“
  19. 以后你们就要给张一鸣还“花呗”了
  20. 魏文王问扁鹊的注释_扁鹊见魏文王是怎么回事 魏文王问扁鹊的典故故事

热门文章

  1. 小米首页html代码,小米首页小功能案例.html
  2. rs429-HI3282-HI3182笔记
  3. 单例模式(java代码实现)
  4. Ryzen平台下内存超频与内存时序
  5. Android App的破解技术有哪些?如何防止反编译?
  6. 【转】知识图谱构建全过程
  7. 社区版PyCharm安装并创建Django项目
  8. win10 linux 无法下载,更新win10后不能安装ubuntu的解决方法
  9. Java快递物流运输管理系统源码
  10. python批量图片转pdf,将TIFF图像批量转换为PDF ImageMagick Python