残差网络ResNet

resnet是何凯明大神在2015年提出的.并且获得了当年的ImageNet比赛的冠军. 残差网络具有里程碑的意义,为以后的网络设计提出了一个新的思路.

googlenet的思路是加宽每一个layer,resnet的思路是加深layer.

论文地址:https://arxiv.org/abs/1512.03385

论文里指出,随着网络深度的增加,模型表现并没有更好,即所谓的网络退化.注意,不是过拟合,而是更深层的网络即便是train error也比浅层网络更高.



这说明,深层网络没有学习到合理的参数.

然后,大神就开始开脑洞了,提出了残差结构,也叫shortcut connection:



以前学习的是F(x)(就是每一层的映射关系,输入x,输出F(x)),现在学的是F(x)-x,那为啥学习F(x)-x就更容易呢?

关于残差网络为何有效的分析,参考:https://zhuanlan.zhihu.com/p/80226180

目前并没有一个统一的结论,我比较倾向于模型集成这个说法.

残差网络就可以被看作是一系列路径集合组装而成的一个集成模型,其中不同的路径包含了不同的网络层子集。Andreas Veit等人展开了几组实验(Lesion study),在测试时,删去残差网络的部分网络层(即丢弃一部分路径)、或交换某些网络模块的顺序(改变网络的结构,丢弃一部分路径的同时引入新路径)。实验结果表明,网络的表现与正确网络路径数平滑相关(在路径变化时,网络表现没有剧烈变化),这表明残差网络展开后的路径具有一定的独立性和冗余性,使得残差网络表现得像一个集成模型(ensemble)

模型结构

大神的思路咱跟不上,管他娘的为啥有效呢,深度学习里的玄学事情还少吗,这种问题留给科学家去研究吧. 咱们用深度学习是来做产品的,实际提高生产力的.

我们来看看resnet模型结构.



实现残差结构

按照论文里的34-layer这个来实现.

仔细看上面两个图可知,残差块用的卷积核为kernel_size=3.模型的conv3_1,conv4_1,conv5_1之前做了宽高减半的downsample.conv2_x是通过maxpool(stride=2)完成的下采样.其余的是通过conv2d(stride=2)完成的.

resnet采取了和vgg类似的堆叠结构,只不过vgg堆叠的是连续卷积核,resnet堆叠的是连续残差块.和vgg一样,越往后面的层,channel相较于前面的layer翻倍,h,w减半.

代码不是一蹴而就的,具体如何一步步实现可以去看github提交的history.

残差块的实现注意两点

  • 第一个卷积需要传入stride完成下采样
  • 卷积后改变了输入shape的话,为了完成相加的操作,需要对输入做1x1卷积
class Residual(nn.Module):
def __init__(self,in_channels,out_channels,stride=1):
super(Residual,self).__init__()
self.stride = stride
self.conv1 = nn.Conv2d(in_channels,out_channels,kernel_size=3,stride=stride,padding=1)
self.bn1 = nn.BatchNorm2d(out_channels)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(out_channels,out_channels,kernel_size=3,padding=1)
self.bn2 = nn.BatchNorm2d(out_channels) # x卷积后shape发生改变,比如:x:[1,64,56,56] --> [1,128,28,28],则需要1x1卷积改变x
if in_channels != out_channels:
self.conv1x1 = nn.Conv2d(in_channels,out_channels,kernel_size=1,stride=stride)
else:
self.conv1x1 = None def forward(self,x):
# print(x.shape)
o1 = self.relu(self.bn1(self.conv1(x)))
# print(o1.shape)
o2 = self.bn2(self.conv2(o1))
# print(o2.shape) if self.conv1x1:
x = self.conv1x1(x) out = self.relu(o2 + x)
return out

在卷积层完成特征提取后, 每张图可以得到512个7x7的feature map.做全局平均池化后得到512个feature.再传入全连接层做特征的线性组合得到num_classes个类别.

我们来实现34-layer的resnet

class ResNet(nn.Module):
def __init__(self,in_channels,num_classes):
super(ResNet,self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(in_channels,64,kernel_size=7,stride=2,padding=3),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
) self.conv2 = nn.Sequential(
nn.MaxPool2d(kernel_size=3,stride=2,padding=1),
Residual(64,64),
Residual(64,64),
Residual(64,64),
) self.conv3 = nn.Sequential(
Residual(64,128,stride=2),
Residual(128,128),
Residual(128,128),
Residual(128,128),
Residual(128,128),
) self.conv4 = nn.Sequential(
Residual(128,256,stride=2),
Residual(256,256),
Residual(256,256),
Residual(256,256),
Residual(256,256),
Residual(256,256),
) self.conv5 = nn.Sequential(
Residual(256,512,stride=2),
Residual(512,512),
Residual(512,512),
) # self.avg_pool = nn.AvgPool2d(kernel_size=7)
self.avg_pool = nn.AdaptiveAvgPool2d(1) #代替AvgPool2d以适应不同size的输入
self.fc = nn.Linear(512,num_classes) def forward(self,x):
out = self.conv1(x)
out = self.conv2(out)
out = self.conv3(out)
out = self.conv4(out)
out = self.conv5(out) out = self.avg_pool(out)
out = out.view((x.shape[0],-1)) out = self.fc(out) return out

接下来就还是熟悉的套路

数据加载

batch_size,num_workers=32,2
train_iter,test_iter = learntorch_utils.load_data(batch_size,num_workers,resize=48)
print('load data done,batch_size:%d' % batch_size)

模型定义

net = ResNet(1,10).cuda()

损失函数定义

l = nn.CrossEntropyLoss()

优化器定义

opt = torch.optim.Adam(net.parameters(),lr=0.01)

评估函数定义

num_epochs=5
def test():
acc_sum = 0
batch = 0
for X,y in test_iter:
X,y = X.cuda(),y.cuda()
y_hat = net(X)
acc_sum += (y_hat.argmax(dim=1) == y).float().sum().item()
batch += 1 test_acc = acc_sum/(batch*batch_size) # print('test acc:%f' % test_acc)
return test_acc

训练

def train():
for epoch in range(num_epochs):
train_l_sum,batch,train_acc_sum=0,1,0
start = time.time()
for X,y in train_iter:
X,y = X.cuda(),y.cuda() #把tensor放到显存
y_hat = net(X) #前向传播
loss = l(y_hat,y) #计算loss,nn.CrossEntropyLoss中会有softmax的操作
opt.zero_grad()#梯度清空
loss.backward()#反向传播,求出梯度
opt.step()#根据梯度,更新参数 # 数据统计
train_l_sum += loss.item()
train_acc_sum += (y_hat.argmax(dim=1) == y).float().sum().item()
train_loss = train_l_sum/(batch*batch_size)
train_acc = train_acc_sum/(batch*batch_size) if batch % 100 == 0: #每100个batch输出一次训练数据
print('epoch %d,batch %d,train_loss %.3f,train_acc:%.3f' % (epoch,batch,train_loss,train_acc)) if batch % 300 == 0: #每300个batch测试一次
test_acc = test()
print('epoch %d,batch %d,test_acc:%.3f' % (epoch,batch,test_acc)) batch += 1 end = time.time()
time_per_epoch = end - start
print('epoch %d,batch_size %d,train_loss %f,time %f' %
(epoch + 1,batch_size ,train_l_sum/(batch*batch_size),time_per_epoch))
test() train()

输出如下:

load data done,batch_size:32
epoch 0,batch 100,train_loss 0.082,train_acc:0.185
epoch 0,batch 200,train_loss 0.065,train_acc:0.297
epoch 0,batch 300,train_loss 0.053,train_acc:0.411
epoch 0,batch 300,test_acc:0.684
epoch 0,batch 400,train_loss 0.046,train_acc:0.487
epoch 0,batch 500,train_loss 0.041,train_acc:0.539
epoch 0,batch 600,train_loss 0.038,train_acc:0.578
epoch 0,batch 600,test_acc:0.763
epoch 0,batch 700,train_loss 0.035,train_acc:0.604
epoch 0,batch 800,train_loss 0.033,train_acc:0.628
epoch 0,batch 900,train_loss 0.031,train_acc:0.647
epoch 0,batch 900,test_acc:0.729
epoch 0,batch 1000,train_loss 0.030,train_acc:0.661
epoch 0,batch 1100,train_loss 0.029,train_acc:0.674
epoch 0,batch 1200,train_loss 0.028,train_acc:0.686
epoch 0,batch 1200,test_acc:0.802
epoch 0,batch 1300,train_loss 0.027,train_acc:0.696

从头学pytorch(二十):残差网络resnet的更多相关文章

  1. 从头学pytorch(二十二):全连接网络dense net

    DenseNet 论文传送门,这篇论文是CVPR 2017的最佳论文. resnet一文里说了,resnet是具有里程碑意义的.densenet就是受resnet的启发提出的模型. resnet中是把 ...

  2. 从头学pytorch(二) 自动求梯度

    PyTorch提供的autograd包能够根据输⼊和前向传播过程⾃动构建计算图,并执⾏反向传播. Tensor Tensor的几个重要属性或方法 .requires_grad 设为true的话,ten ...

  3. 深度残差网络(ResNet)

    引言 对于传统的深度学习网络应用来说,网络越深,所能学到的东西越多.当然收敛速度也就越慢,训练时间越长,然而深度到了一定程度之后就会发现越往深学习率越低的情况,甚至在一些场景下,网络层数越深反而降低了 ...

  4. 深度学习——手动实现残差网络ResNet 辛普森一家人物识别

    深度学习--手动实现残差网络 辛普森一家人物识别 目标 通过深度学习,训练模型识别辛普森一家人动画中的14个角色 最终实现92%-94%的识别准确率. 数据 ResNet介绍 论文地址 https:/ ...

  5. 深度残差网络——ResNet学习笔记

    深度残差网络—ResNet总结 写于:2019.03.15—大连理工大学 论文名称:Deep Residual Learning for Image Recognition 作者:微软亚洲研究院的何凯 ...

  6. 残差网络resnet理解与pytorch代码实现

    写在前面 ​ 深度残差网络(Deep residual network, ResNet)自提出起,一次次刷新CNN模型在ImageNet中的成绩,解决了CNN模型难训练的问题.何凯明大神的工作令人佩服 ...

  7. 使用dlib中的深度残差网络(ResNet)实现实时人脸识别

    opencv中提供的基于haar特征级联进行人脸检测的方法效果非常不好,本文使用dlib中提供的人脸检测方法(使用HOG特征或卷积神经网方法),并使用提供的深度残差网络(ResNet)实现实时人脸识别 ...

  8. 残差网络ResNet笔记

    发现博客园也可以支持Markdown,就把我之前写的博客搬过来了- 欢迎转载,请注明出处:http://www.cnblogs.com/alanma/p/6877166.html 下面是正文: Dee ...

  9. 从头学pytorch(一):数据操作

    跟着Dive-into-DL-PyTorch.pdf从头开始学pytorch,夯实基础. Tensor创建 创建未初始化的tensor import torch x = torch.empty(5,3 ...

随机推荐

  1. svn 冲突解决办法(黄色感叹号)

    右键:"TortoiseSVN"->"Resolved..."已解决的..., 选中全部的文件,然后OK,,然后就可以commit提交了.

  2. C# “不支持给定路径的格式”异常处理

    问题背景 无聊研究了一下怎么发送邮件(包含附件),但发现附带的文件路径除了报错就是报错,不知道为什么. 用了不下好几种方式,比如 var x = "E:\\Git\\cmd\\git.exe ...

  3. Eclipse文档注释导出doc

    选择要导出的项目,右键选择Export 直接next,在最后finish之前加上编码格式.要不然会出现乱码

  4. ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(10)之素材管理

    源码下载地址:http://www.yealuo.com/Sccnn/Detail?KeyValue=c891ffae-7441-4afb-9a75-c5fe000e3d1c 素材管理模块也是我们这个 ...

  5. Makefile记录

    需要把sum.c编译汇编成可执行程序zzj zzj:sum.o gcc -o zzj sum.osum.o:sum.c gcc -c -o sum.o sum.cclean: rm -rf *.o z ...

  6. 大数据vs计算机

    大数据有两个方向,一个是偏计算机的,另一个是偏经济的.你学过Java,所以你可以偏将计算机 基础1. 读书<Introduction to Data Mining>,这本书很浅显易懂,没有 ...

  7. codeforces 11D(状压dp)

    传送门:https://codeforces.com/problemset/problem/11/D 题意: 求n个点m条边的图里面环的个数 题解: 点的范围只有19,很容易想到是状压. dp[sta ...

  8. JLINK、ULINK和STlink仿真器详解

    JLink仿真器 德国SEGGER公司推出基于JTAG的仿真器.简单地说,是给一个JTAG协议转换盒,即一个小型USB到JTAG的转换盒,其连接到计算机用的是USB接口,而到目标板内部用的还是jtag ...

  9. sql查询分类和所有子类

    select * from [JianDu].[dbo].[ZuZhiJiGou] where id = 64 --查询节点 union allSELECT TOP 1000 a.* FROM [Ji ...

  10. Visio流程图表

    基本流程图: 流程图类别 基本流程图的四种类型 打开基本流程图 注意页面内引用跟跨页引用 就是两个按钮的作用 就是一个按钮的作用 点击跳转 按钮设置好之后可以输入数字 方便区分跳转 下面是跨职能流程图 ...