pytorch resnet50_用Pytorch手工实现ResNet50
《吴恩达深度学习课程》第四课第二周的作业是:使用Keras和Tensorflow编写ResNet50,用程序实现题目中描述的网络结构。由于程序填空提供了不少示例,做完后仍感觉理解不透彻,又使用Pytorch实现了一遍。
ResNet50包含49个卷积层和1个全连接层,属于较大型的网络,实现起来略有难度。对于理解数据流、卷积层、残差、瓶颈层,以及对大型网络的编写和调试都有很大帮助。
使用的数据仍是第四课第二周中的手势图片识别,题目说明、Keras例程和相关数据可从以下网址下载:https://blog.csdn.net/u013733326/article/details/80250818
Keras ResNet50程序填空的代码可从以下网址下载:https://github.com/Kulbear/deep-learning-coursera/blob/master/Convolutional%20Neural%20Networks/Residual%20Networks%20-%20v1.ipynb
Torch官方版本的ResNet实现可从以下网址下载(网络结构细节略有不同):https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py
网络结构
ResNet网络结构如下图所示:
代码
下面使用约100行代码实现了ResNet50网络类(可缩减至80行左右),另外100行代码用于处理数据,训练和预测。
准备数据:
import mathimport numpy as npimport h5pyimport matplotlib.pyplot as pltimport scipyfrom PIL import Imagefrom scipy import ndimageimport torchimport torch.nn as nnfrom cnn_utils import *from torch import nn,optimfrom torch.utils.data import DataLoader,Datasetfrom torchvision import transforms%matplotlib inlinenp.random.seed(1)torch.manual_seed(1)batch_size = 24learning_rate = 0.009num_epocher = 100X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = load_dataset()X_train = X_train_orig/255.X_test = X_test_orig/255.class MyData(Dataset): #继承Datasetdef __init__(self, data, y, transform=None): #__init__是初始化该类的一些基础参数 self.transform = transform #变换 self.data = data self.y = ydef __len__(self):#返回整个数据集的大小return len(self.data)def __getitem__(self,index):#根据索引index返回dataset[index] sample = self.data[index]if self.transform: sample = self.transform(sample)#对样本进行变换return sample, self.y[index] #返回该样本train_dataset = MyData(X_train, Y_train_orig[0], transform=transforms.ToTensor())test_dataset = MyData(X_test, Y_test_orig[0], transform=transforms.ToTensor())train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True)test_loader = DataLoader(test_dataset,batch_size=batch_size,shuffle=False)
实现ResNet
class ConvBlock(nn.Module): def __init__(self, in_channel, f, filters, s):super(ConvBlock,self).__init__() F1, F2, F3 = filtersself.stage = nn.Sequential( nn.Conv2d(in_channel,F1,1,stride=s, padding=0, bias=False), nn.BatchNorm2d(F1), nn.ReLU(True), nn.Conv2d(F1,F2,f,stride=1, padding=True, bias=False), nn.BatchNorm2d(F2), nn.ReLU(True), nn.Conv2d(F2,F3,1,stride=1, padding=0, bias=False), nn.BatchNorm2d(F3), )self.shortcut_1 = nn.Conv2d(in_channel, F3, 1, stride=s, padding=0, bias=False)self.batch_1 = nn.BatchNorm2d(F3)self.relu_1 = nn.ReLU(True) def forward(self, X): X_shortcut = self.shortcut_1(X) X_shortcut = self.batch_1(X_shortcut) X = self.stage(X) X = X + X_shortcut X = self.relu_1(X)return Xclass IndentityBlock(nn.Module): def __init__(self, in_channel, f, filters):super(IndentityBlock,self).__init__() F1, F2, F3 = filtersself.stage = nn.Sequential( nn.Conv2d(in_channel,F1,1,stride=1, padding=0, bias=False), nn.BatchNorm2d(F1), nn.ReLU(True), nn.Conv2d(F1,F2,f,stride=1, padding=True, bias=False), nn.BatchNorm2d(F2), nn.ReLU(True), nn.Conv2d(F2,F3,1,stride=1, padding=0, bias=False), nn.BatchNorm2d(F3), )self.relu_1 = nn.ReLU(True) def forward(self, X): X_shortcut = X X = self.stage(X) X = X + X_shortcut X = self.relu_1(X)return Xclass ResModel(nn.Module): def __init__(self, n_class):super(ResModel,self).__init__()self.stage1 = nn.Sequential( nn.Conv2d(3,64,7,stride=2, padding=3, bias=False), nn.BatchNorm2d(64), nn.ReLU(True), nn.MaxPool2d(3,2,padding=1), )self.stage2 = nn.Sequential( ConvBlock(64, f=3, filters=[64, 64, 256], s=1), IndentityBlock(256, 3, [64, 64, 256]), IndentityBlock(256, 3, [64, 64, 256]), )self.stage3 = nn.Sequential( ConvBlock(256, f=3, filters=[128, 128, 512], s=2), IndentityBlock(512, 3, [128, 128, 512]), IndentityBlock(512, 3, [128, 128, 512]), IndentityBlock(512, 3, [128, 128, 512]), )self.stage4 = nn.Sequential( ConvBlock(512, f=3, filters=[256, 256, 1024], s=2), IndentityBlock(1024, 3, [256, 256, 1024]), IndentityBlock(1024, 3, [256, 256, 1024]), IndentityBlock(1024, 3, [256, 256, 1024]), IndentityBlock(1024, 3, [256, 256, 1024]), IndentityBlock(1024, 3, [256, 256, 1024]), )self.stage5 = nn.Sequential( ConvBlock(1024, f=3, filters=[512, 512, 2048], s=2), IndentityBlock(2048, 3, [512, 512, 2048]), IndentityBlock(2048, 3, [512, 512, 2048]), )self.pool = nn.AvgPool2d(2,2,padding=1)self.fc = nn.Sequential( nn.Linear(8192,n_class) ) def forward(self, X):out = self.stage1(X)out = self.stage2(out)out = self.stage3(out)out = self.stage4(out)out = self.stage5(out)out = self.pool(out)out = out.view(out.size(0),8192)out = self.fc(out)return out
训练和预测
device = 'cuda'def test(): model.eval() #需要说明是否模型测试 eval_loss = 0 eval_acc = 0for data in test_loader: img,label = data img = img.float().to(device) label = label.long().to(device)out = model(img) #前向算法 loss = criterion(out,label) #计算loss eval_loss += loss.item() * label.size(0) #total loss _,pred = torch.max(out,1) #预测结果 num_correct = (pred == label).sum() #正确结果 eval_acc += num_correct.item() #正确结果总数 print('Test Loss:{:.6f},Acc: {:.6f}' .format(eval_loss/ (len(test_dataset)),eval_acc * 1.0/(len(test_dataset))))model = ResModel(6)model = model.to(device)criterion = nn.CrossEntropyLoss()optimizer = optim.SGD(model.parameters(), lr=learning_rate, momentum=0.8)#开始训练for epoch in range(num_epocher): model.train() running_loss = 0.0 running_acc = 0.0for i,data in enumerate(train_loader,1): img,label = data img = img.float().to(device) label = label.long().to(device)#前向传播out = model(img) loss = criterion(out,label) #loss running_loss += loss.item() * label.size(0) _,pred = torch.max(out,1) #预测结果 num_correct = (pred == label).sum() #正确结果的数量 running_acc += num_correct.item() #正确结果的总数 optimizer.zero_grad() #梯度清零 loss.backward() #后向传播计算梯度 optimizer.step() #利用梯度更新W,b参数#打印一个循环后,训练集合上的loss和正确率if (epoch+1) % 1 == 0: print('Train{} epoch, Loss: {:.6f},Acc: {:.6f}'.format(epoch+1,running_loss / (len(train_dataset)), running_acc / (len(train_dataset)))) test()
实验1000张图片作为训练集,120张图片作为测试集,在使用GPU的情况下几分钟即可完成100次迭代,使用CPU两三个小时也能训练完成,训练好的模型约100M左右,在测试集准确率基本稳定在97.5%。对比简单的网络结构,ResNet50可以较短的时间内达到较好的效果。
瓶颈层
使用Pytorch实现ResNet时,需要注意卷积层间的对接,比如在第二层conv2.x中有3个Block,Block内部3层通道输出分别是64,64,256,于是有64->64->256,较容易理解;而第二层的3个Block之间,需要将256再转回64,在第二层内部,通道变化是64->64->256->64->64->256->64->64->256。
其数据流变化如下图所示:
block中的三个卷积层:第一层,卷积核1x1用于实现通道数转换,第二层3x3实现特征提取,第三层将通道数转换成目标大小。
不同的块内结构是Resnet50与Resnet34的主要区别:
不同的结构,在Block块数相同,且参数规模相似的情况下,Resnet34提取512个特征(输出通道数),而ResNet50能提取2048个特征。从论文中可以看到同结构的对比效果。
论文地址:https://arxiv.org/pdf/1512.03385.pdf
在图像处理中卷积核是四维的,其大小为:卷积核长x卷积核宽x输入通道数x输出通道数。在数据处理后期通道数越来越大,因此左图中的结构在层数多,输出特征多的情况下,参数将变得非常庞大;而右图限制了3x3卷积处理的通道数,1x1的卷积操作运算量又比较小,有效地解决了这一问题。
调试方法
搭建大型网络时,数据在网络中逐层处理,常出现相邻层之间数据接口不匹配的问题。在本例中可对照官方版本的ResNet结构排查问题,使用下面程序可打印出torchvision中ResNet的网络结构。
import torchvisionresnet50 = torchvision.models.resnet.ResNet(torchvision.models.resnet.Bottleneck,[3, 4, 6, 3],1000)res_layer1 = torch.nn.Sequential(resnet50.conv1, resnet50.maxpool, resnet50.layer1)img = torch.rand((2, 3, 224, 224)) # 生成图片print(res_layer1(img).shape) # 查看第一层输出数据的形状print(res_layer1) # 查看第一层网络结构
pytorch resnet50_用Pytorch手工实现ResNet50相关推荐
- Pytorch学习 - Task6 PyTorch常见的损失函数和优化器使用
Pytorch学习 - Task6 PyTorch常见的损失函数和优化器使用 官方参考链接 1. 损失函数 (1)BCELoss 二分类 计算公式 小例子: (2) BCEWithLogitsLoss ...
- PyTorch学习记录——PyTorch生态
Pytorch的强大并不仅局限于自身的易用性,更在于开源社区围绕PyTorch所产生的一系列工具包(一般是Python package)和程序,这些优秀的工具包极大地方便了PyTorch在特定领域的使 ...
- Pytorch学习 - Task5 PyTorch卷积层原理和使用
Pytorch学习 - Task5 PyTorch卷积层原理和使用 1. 卷积层 (1)介绍 (torch.nn下的) 1) class torch.nn.Conv1d() 一维卷积层 2) clas ...
- 【pytorch速成】Pytorch图像分类从模型自定义到测试
文章首发于微信公众号<与有三学AI> [pytorch速成]Pytorch图像分类从模型自定义到测试 前面已跟大家介绍了Caffe和TensorFlow,链接如下. [caffe速成]ca ...
- python pytorch fft_看PyTorch源代码的心路历程
1. 起因 曾经碰到过别人的模型prelu在内部的推理引擎算出的结果与其在原始框架PyTorch中不一致的情况,虽然理论上大家实现的都是一个算法,但是从参数上看,因为经过了模型转换,中间做了一些调整. ...
- pytorch forward_【Pytorch部署】TorchScript
TorchScript是什么? TorchScript - PyTorch master documentationpytorch.org TorchScript是一种从PyTorch代码创建可序列 ...
- PyTorch系列 (二): pytorch数据读取自制数据集并
PyTorch系列 (二): pytorch数据读取 PyTorch 1: How to use data in pytorch Posted by WangW on February 1, 2019 ...
- PyTorch入门(一)--PyTorch基础
PyTorch基础 1. PyTorch与TensorFlow的区别 2. PyTorch基本数学形式 3. 关于Tensor 1. PyTorch与TensorFlow的区别 PyTorch和Ten ...
- python pytorch语音识别_PyTorch通过ASR实现语音到文本端的模型以及pytorch语音识别(speech) - pytorch中文网...
ASR,英文的全称是Automated Speech Recognition,即自动语音识别技术,它是一种将人的语音转换为文本的技术.今天我们主要了解pytorch实现语音到文本的端到端模型. spe ...
最新文章
- 只给测试集不给训练集,要怎么做自己的物体检测器?
- 解决Transformer固有缺陷:复旦大学等提出线性复杂度SOFT
- centos打架django + mysql环境
- 设计模式 笔记 命令模式 Command
- 比特币开发者:BSC可能会超越以太坊成为顶级智能合约平台
- Android内部类监听和多对象监听方法
- Boost Asio Work类
- 在Windows系统下,Nginx反向代理缓存配置
- Operation,Tensor, Variable
- Bookshelf 2 POJ - 3628(01背包||DFS)
- 无水印的html5制作软件,ScreenToGif-开源、简单、无水印的gif动图制作软件
- ELK-使用nxlog+filebeat采集不同类型的日志输出到logstash
- 图片标签格式的转换:TXT转为XML,PNG转为JPG格式
- 微信小程序—那些安全验证,滑块验证、输入验证码、拼图验证
- 耳机驱动调试(插拔检测与按键检测)
- 快速入门:Slurm资源管理与作业调度系统
- 致终将火爆的NFC——ISO14443 TypeA(二)
- # 0003:fill的游戏
- 产品推荐丨边缘视觉处理AI模组
- 英语不好,能不能学会编程?
热门文章
- PHP_递归实现无限级分类
- 静态的html页面想要设置使用浏览器缓存
- Struts2的输入验证(三)-短路验证与非字段验证
- 地图旋转_如何使用万能地图下载器计算七参数
- 间接寻址级别不同_单片机指令系统与寻址方式
- pythonfor循环例句_Python for 循环语句
- matlab中bitget函数用法_MATLAB基础函数命令
- linux查看系统版本_Win8系统查看directx版本的操作方法是什么?
- 川大计算机专业导师冯子亮,问问川大计算机研究生招收的小专业有哪些
- c语言编译器储存有什么用,C编译器怎么样对内存划分和使用