在本文中,我想引导您浏览在Keras中实现的最新架构的重要卷积模块的选定列表。
当你在GitHub上寻找流行架构的实现时,你会惊讶于它们包含了多少代码。使用额外的参数来包含足够的注释和增强模型是一个很好的做法,但同时它也会分散架构的本质。为了简化和缩短代码片段,我将使用一些别名函数:
def conv(x,f,k=3,s=1,p='相同',d=1,a='relu'):
返回Conv2D(filters=f,kernel_size=k,steps=s,
填充=p,膨胀_速率=d,激活=a)(x)
def密集(x,f,a='relu'):
返回密集(f,激活=a)(x)
def maxpool(x,k=2,s=2,p='same'):
返回maxpool2d(pool _ size=k,跨步=s,填充=p)(x)
def avgpool(x,k=2,s=2,p='相同'):
返回average pool 2d(pool _ size=k,跨距=s,填充=p)(x)
def gavgpool(x):
返回全局平均池2D()(x)
def sepconv(x,f,k=3,s=1,p='相同',d=1,a='relu'):
返回SeparableConv2D(filters=f,kernel_size=k,steps=s,
填充=p,膨胀_速率=d,激活=a)(x)
00-1010卷积层的参数数量取决于内核大小、输入滤波器数量和输出滤波器数量。你的网络越宽,3x3卷积就越贵。
def瓶颈(x,f=32,r=4):
x=conv(x,f//r,k=1)
x=conv(x,f//r,k=3)
返回conv(x,f,k=1)
瓶颈块背后的思想是使用1x1卷积,以一定的速率将通道数减少R,这样下一个3x3卷积的参数就会更少。最后,我们使用另一个1x1卷积来扩展网络。
00-1010事件模块引入了并行使用不同操作和组合结果的思想。这样,网络可以学习不同类型的过滤器。
def幼稚_初始_模块(x,f=32):
a=conv(x,f,k=1)
b=conv(x,f,k=3)
c=conv(x,f,k=5)
d=maxpool(x,k=3,s=1)
返回连接([a,b,c,d])
这里,我们使用1、3和5的内核大小来合并卷积层。这个片段展示了事件模块的简单实现。的实际实现将它与瓶颈概念结合在一起,这使得它有点复杂。
初始模块
def initiation _ module(x,f=32,r=4):
a=conv(x,f,k=1)
b=conv(x,f//3,k=1)
b=conv(b,f,k=3)
c=conv(x,f//r,k=1)
c=conv(c,f,k=5)
d=maxpool(x,k=3,s=1)
d=conv(d,f,k=1)
返回连接([a,b,c,d])
Bottleneck Block
ResNet是微软研究人员引入的一种架构,它允许神经网络具有任意层数,同时仍能提高模型的准确性。
def残差_block(x,f=32,r=4):
p>m = conv(x, f//r, k=1)
m = conv(m, f//r, k=3)
m = conv(m, f, k=1)
return add([x, m])
想法是将初始激活添加到卷积块的输出。这样,网络可以通过学习过程决定用于输出的新卷积的数量。注意, inception module连接输出,而residual block添加它们。
ResNeXt Block
根据其名称,您可以猜测ResNeXt与ResNet密切相关。作者将卷积块中的基数(cardinality )一词引入另一个维度,如宽度(通道数)和深度(层数)。
基数是指块中出现的并行路径的数量。这听起来类似于inception block,它具有并行发生的4个操作。但是,不是并行使用不同类型的操作,基数为4将简单地使用相同的操作4次。
如果他们做同样的事情,你为什么要把它们并列?好问题。这个概念也被称为分组卷积,并且可以追溯到原始的AlexNet论文。虽然,当时它主要用于将训练过程分成多个GPU,而ResNeXt使用它们来提高参数效率。
def resnext_block(x, f=32, r=2, c=4):
l = []
for i in range(c):
m = conv(x, f//(c*r), k=1)
m = conv(m, f//(c*r), k=3)
m = conv(m, f//(c*r), k=1)
l.append(m)
m = add(l)
return add([x, m])
我们的想法是把所有的输入通道都分成组。卷积只会在它们专用的通道中起作用,而不是在所有的通道中。结果发现,在提高权重效率的同时,每个组将学习不同类型的特征。
想象一个bottleneck block,首先使用4的压缩率将256个输入通道减少到64,然后将它们作为输出返回到256个通道。如果我们想要引入32的基数和2的压缩,我们将使用32个1x1卷积层并行,每个4(256 /(32 * 2))个输出通道。之后我们将使用32个3x3卷积层和4个输出通道,接着是32个1x1层,每个层有256个输出通道。最后一步涉及添加这32个并行路径,在添加初始输入以创建残差连接之前为我们提供单个输出。
使用上面的图像来获取正在发生的事情的可视化表示,或者复制这些片段,在Keras中自己构建一个小的网络。
顺便说一下,如果基数等于通道数我们会得到一个叫做深度可分离卷积的东西。自从引入Xception异常体系结构以来,这些问题得到了广泛的关注。
Dense Block
Dense Block是残差块的极端版本,其中每个卷积层获得块中所有先前卷积层的输出。首先,我们将输入激活添加到列表中,之后我们进入循环,遍历块的深度。每个卷积输出也连接到列表,以便随后的迭代获得越来越多的输入要素映射。该方案持续到达到期望的深度。
def dense_block(x, f=32, d=5):
l = x
for i in range(d):
x = conv(l, f)
l = concatenate([l, x])
return l
虽然需要数月的研究才能获得与DenseNet一样的架构,但实际的构建块可以像这样简单
Squeeze-and-Excitation Block
SENet是ImageNet在短期内的最新技术。它建立在ResNext之上,专注于对网络的渠道信息进行建模。在常规卷积层中,每个通道对于点积计算中的加法操作将具有相同的权重。
SENet引入了一个非常简单的模块,可以添加到任何现有的体系结构中。它创建了一个微小的神经网络,学习了如何根据输入对每个滤波器进行加权。正如你所看到的,它本身不是卷积块,但因为它可以添加到任何卷积块并可能增强其性能,我想将它添加到mix中。
def se_block(x, f, rate=16):
m = gavgpool(x)
m = dense(m, f // rate)
m = dense(m, f, a='sigmoid')
return multiply([x, m])
每个通道被压缩成一个单独的值,并被输入到一个双层神经网络中。取决于该通道分布,该网络将基于它们的重要性来学会对通道进行加权。最后,这个权重与卷积激活相乘。
SENets引入了一个微小的计算开销,同时可能改进任何卷积模型。
NASNet Normal Cell
NASNet在设计方面令人难以置信,但实际架构相对复杂。我们所知道的是,它在ImageNet上运行得非常好。
通过手工,作者定义了不同类型的卷积和池化层的搜索空间,每个层都有不同的可能设置。他们还定义了这些层如何平行排列,顺序排列以及如何添加或连接这些层。一旦定义了这一点,他们就建立了一个基于递归神经网络的强化学习(RL)算法,如果特定的设计方案在CIFAR-10数据集上表现良好,该算法会得到奖励。
由此产生的架构不仅在CIFAR-10上表现良好,而且还在ImageNet上实现了最先进的结果。NASNet由Normal Cell和Reduction Cell组成,它们彼此重复。
def normal_cell(x1, x2, f=32):
a1 = sepconv(x1, f, k=3)
a2 = sepconv(x1, f, k=5)
a = add([a1, a2])
b1 = avgpool(x1, k=3, s=1)
b2 = avgpool(x1, k=3, s=1)
b = add([b1, b2])
c2 = avgpool(x2, k=3, s=1)
c = add([x1, c2])
d1 = sepconv(x2, f, k=5)
d2 = sepconv(x1, f, k=3)
d = add([d1, d2])
e2 = sepconv(x2, f, k=3)
e = add([x2, e2])
return concatenate([a, b, c, d, e])
这是如何在Keras中实现正常单元的。除了层和设置的这种组合非常好用之外没有什么新的事情发生。
Inverted Residual Block
到现在为止,你已经听说过bottleneck block和可分离的卷积。我们把它们放在一起吧。如果你运行一些测试,你会注意到因为可分离的卷积已经减少了参数的数量,压缩它们可能会损害性能而不是增加它。
作者提出了实际做与bottleneck residual block相反的想法。它们使用廉价的1x1卷积增加了通道数,因为以下可分离卷积层已经大大减少了参数的数量。它会在将频道添加到初始激活之前关闭频道。
def inv_residual_block(x,f = 32,r = 4):
m = conv(x,f * r,k = 1)
m = sepconv(m,f,a ='linear')
return add([m,x] )
这个难题的最后一部分是可分离卷积后面没有激活函数。相反,它直接添加到输入中。这个块在放入架构时非常有效。
AmoebaNet Normal Cell
通过AmoebaNet,我们可以在ImageNet上获得当前最先进的技术,并且可能是图像识别。与NASNet一样,它是使用与以前相同的搜索空间的算法设计的。唯一的转折是,他们为经常被称为进化的遗传算法切换了强化学习算法。
结论
我希望这篇文章能让你对重要的卷积块有一个深刻的理解,并且实现它们可能比你想象的要容易。要更详细地了解这些架构,请查看各自的论文。你会注意到,一旦你掌握了论文的核心理念,就会更容易理解其余部分。还请注意,实际的实现通常会将BatchNormalization添加到mix 中,并且在应用激活函数的位置方面会有所不同。