ResNet是15年提出的经典网络了。在ResNet提出之前,人们发现当模型层数提升到一定程度后,再增加层数就不再能提升模型效果了——这就导致深度学习网络看似出现了瓶颈,通过增加层数来提升效果的方式似乎已经到头了。ResNet解决了这一问题。
ResNet的核心思想就是引入了残差边。即一条直接从输入添加到输出的边。

这样做有什么用处呢?可以这样理解:假如新加的这些层的学习效果非常差,那我们就可以通过一条残差边将这一部分直接“跳过”。实现这一目的很简单,将这些层的权重参数设置为0就行了。这样一来,不管网络中有多少层,效果好的层我们保留,效果不好的我们可以跳过。总之,添加的新网络层至少不会使效果比原来差,就可以较为稳定地通过加深层数来提高模型的效果了。
此外,使用残差边的另一个好处在于可以避免梯度消失的问题。因为它的这个特点,它可以训练几百甚至上千层的网络。
残差网络为什么可以避免梯度消失其实也很好理解:
在没有残差边的时候,假如网络层数很深的话,要想更新底层的(靠近输入数据部分)的网络权重,首先对其求梯度,根据链式法则需要一直向前累乘,只要其中的任何一个因数过小就会导致求出来的梯度很小很小,这个小梯度就算乘以再大的学习率也是无济于事;
而当我们有了残差边时,求梯度可以直接经过“高速公路”直达我们想要求梯度的对象,此时不管通过链式法则走正常路线得到的梯度多么小,两条路线相加的结果都不会小,就可以很有效地进行梯度更新了。
因为残差网络的种种优势,现在很多的主流网络,残差边已经成为标配了,可见其深远的影响力。
ResNet根据层数的不同分为很多种,比如下图中的每一列,也就分别对应着ResNet18,ResNet34,ResNet50等网络结构。其中ResNet152层数最多,也被普遍认为是效果最不错的(代价也是需要更多的计算量)。一般来说ResNet可以作为首选的网络去做分类任务,如果追求最高的精度就用ResNet152。

以ResNet34(上图中框起来的)来举例子解释一下它的结构,34其实就代表它有34层,它最上面首先有7*7的卷积层(算1层),再经过(3+4+6+3)=16个残差块,每个残差快有两层卷积(这里就有16*2=32层),在最后连上一个全连接层(最后1层),所以共34层。
残差块是长什么样子的呢?用残差边“包”起来的一部分就可以看作是个残差快。我在图中写了stage1到stage4(补充一下:一般图像的尺寸减半、通道数翻倍时我们就认为算一个stage,不过这些维度在上图中看不出来,上图中显示的都是卷积核的尺寸和通道数,不要搞混了),每个stage其实都用了不同的残差快,ResNet的重点也就在于残差块中的结构。
我们把ResNet34中间结构部分截取出来,从stage1开始往下,其具体结构如下图:

通过各个stage的标记位置可以对比两张图,我们对ResNet残差块具体是怎么连接的就清楚多了。可以发现除了正常的卷积之外,最显著的特点就在于每个残差块旁边都带了要么实线要么虚线的残差边。
就通过这些重复的残差块结构,我们通过控制层数以及残差块中卷积核尺寸和通道数,就得到了各个不同的ResNet了。到此,不管是ResNet几,其网络结构都可以按同样逻辑理解了。
等等,图中实线和虚线的残差边有啥区别?先说结论:
实线的残差边,就是直接相连到结果,也就是最简单最简单的一条线,对应identity_block;
虚线的残差边,其实中间要经过一个1*1的卷积来改变通道数,对应convolution_block。
从stage2开始,每个stage都是先来一个convolution_block,再接若干个identity_block。(如下图是ResNet网络样式图。其中蓝色的是convolution_block,红色的是identity_block)

具体来看,为什么在stage的一开始需要一个convolution_block呢?上面说了一个convolution_block的残差边是一个1*1的卷积(如下图)。

上篇文章整理过1*1卷积的作用,其中一个作用也是在这里的作用就是扩大通道数。为什么要扩大通道数呢?假如说这个convolution_block对应着下图这里的虚线位置:

可以发现在a处本来是64个特征图的,经过了2次3*3、通道数为128的卷积核,到b通道数就变成了128;那么如果从a直接到b的这条残差边什么也不做的话,让64通道直接与128通道相加是不成立的。而如果64通道的特征图经过了一次1*1卷积,只要卷积核个数为128,就可以实现通道数翻倍从而合理相加了。这就是1*1卷积的作用,在这种通道数翻倍的地方也可以进行残差边的应用,而这整体就成为了convolution_block。
而identity_block就很简单啦(如下图)就是一条边,对应着所有实线部分。当输入输出的尺寸、通道数前后都没有变化时,输入就可以直接加到输出上。

总之,在整个ResNet网络结构中,从输入到输出,就不断进行着特征图尺寸缩小以及特征图数量增加的过程。当特征图数量增加时,残差边就不得不进行1*1的卷积来同样扩大通道数,从而可以与正常卷积而得到的操作结果的尺寸保持一致。
和NiN网络类似,ResNet最后也使用了全局平均池化,但此时我们不再限制通道数直接等于结果数从而直接经过一个全局平均池化就可以得到结果维数——而是再加上一个全连接层来得到结果。(最后卷积层结果有几个通道数,比如512,输入神经元个数就是512;结果有几维,比如10,输出层神经元个数就是10)
这篇文章整理了一下ResNet网络的基本思想和大致结构,提到了一些优势作用,也区分了一下convolution_block和identity_block的不同之处等。
最后,在ResNet中很多地方都使用了Batch Normal来进行标准化,这个在上面各个过程中一直有意的忽略掉了,Batch Normal主要是用来解决梯度消失和梯度爆炸问题的,可以暂时就当成是一个不会改变尺寸和通道数的处理方式。关于BN展开有点多,以后再作补充。
如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
我目前正在尝试学习RubyonRails和测试框架RSpec。assigns在此RSpec测试中做什么?describe"GETindex"doit"assignsallmymodelas@mymodel"domymodel=Factory(:mymodel)get:indexassigns(:mymodels).shouldeq([mymodel])endend 最佳答案 assigns只是检查您在Controller中设置的实例变量的值。这里检查@mymodels。 关于ruby-o
这段代码似乎创建了一个范围从a到z的数组,但我不明白*的作用。有人可以解释一下吗?[*"a".."z"] 最佳答案 它叫做splatoperator.SplattinganLvalueAmaximumofonelvaluemaybesplattedinwhichcaseitisassignedanArrayconsistingoftheremainingrvaluesthatlackcorrespondinglvalues.Iftherightmostlvalueissplattedthenitconsumesallrvaluesw
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案
你能解释一下吗?我想评估来自两个不同来源的值和计算。一个消息来源为我提供了以下信息(以编程方式):'a=2'第二个来源给了我这个表达式来评估:'a+3'这个有效:a=2eval'a+3'这也有效:eval'a=2;a+3'但我真正需要的是这个,但它不起作用:eval'a=2'eval'a+3'我想了解其中的区别,以及如何使最后一个选项起作用。感谢您的帮助。 最佳答案 您可以创建一个Binding,并将相同的绑定(bind)与每个eval相关联调用:1.9.3p194:008>b=binding=>#1.9.3p194:009>eva
我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT
我无法运行Spring。这是错误日志。myid-no-MacBook-Pro:myid$spring/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/lib/spring/sid.rb:17:in`fiddle_func':uninitializedconstantSpring::SID::DL(NameError)from/Users/myid/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/spring-0.0.10/li
我在RoR应用程序中有一个提交表单,是使用simple_form构建的。当字段为空白时,应用程序仍会继续下一步,而不会提示错误或警告。默认情况下,这些字段应该是required:true;但即使手动编写也行不通。该应用有3个步骤:NewPost(新View)->Preview(创建View)->Post。我的Controller和View的摘录会更清楚:defnew@post=Post.newenddefcreate@post=Post.new(params.require(:post).permit(:title,:category_id))ifparams[:previewButt