当前位置: 首页 > 科技观察

没有复杂的数学描述,通过简单的代码理解卷积模块

时间:2023-03-17 11:46:08 科技观察

相比晦涩复杂的数学或文字描述,或许代码可以帮助我们更好地理解各种卷积模块。计算机科学家Paul-LouisPr?ve使用Keras对瓶颈模块、Inception模块、残差模块等进行了介绍和讲解,并在***留下了AmoebaNetNormalCell代码实现的练习题。我尽力定期阅读与机器学习和人工智能相关的论文。这是跟上疾病进展的唯一方法。作为一名计算机科学家,我经常在阅读科学文本或表述数学概念时碰壁。我发现直接用纯代码更容易理解。因此,在这篇文章中,我希望向您介绍在Keras中实现的选定的最先进架构中的一些重要卷积模块。如果您在GitHub上寻找常见架构的实现,您会发现数量惊人的代码。在实践中,包含足够多的注释并通过额外的参数提高模型的能力是很好的做法,但这也会干扰我们对架构本质的理解。为了简化和缩短代码片段,我将使用一些别名函数:defconv(x,f,k=3,s=1,p='same',d=1,a='relu'):returnConv2D(ffilters=f,kkernel_size=k,sstrides=s,padding=p,ddilation_rate=d,aactivation=a)(x)defdense(x,f,a='relu'):returnDense(f,aactivation=a)(x)defmaxpool(x,k=2,s=2,p='相同'):returnMaxPooling2D(pool_size=k,sstrides=s,padding=p)(x)defavgpool(x,k=2,s=2,p='相同'):returnAveragePooling2D(pool_size=k,sstrides=s,padding=p)(x)defgavgpool(x):returnGlobalAveragePooling2D()(x)defsepconv(x,f,k=3,s=1,p='相同',d=1,a='relu'):returnSeparableConv2D(ffilters=f,kkernel_size=k,sstrides=s,padding=p,ddilation_rate=d,aactivation=a)(x)我发现去掉这些模板代码可以具有更好的可读性。当然,只有当你理解我的一个字母的首字母缩略词时才有效。那么让我们开始吧。瓶颈模块中一个卷积层的参数个数取决于卷积核(kernel)的大小、输入滤波器的个数、输出滤波器的个数。您的网络越宽,3×3卷积的成本就越高。defbottleneck(x,f=32,r=4):x=conv(x,f//r,k=1)x=conv(x,f//r,k=3)返回conv(x,f,k=1)bottleneckmodule背后的想法是使用成本较低的1×1卷积以一定的速率r减少通道数,从而使后续的3×3卷积具有更少的参数。***,然后我们使用另一个1×1卷积来加宽网络。Inception模块Inception模块引入的思想是并行使用不同的操作,然后融合结果。通过这种方式,网络可以学习不同类型的过滤器。defnaive_inception_module(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)returnconcatenate([a,b,c,d])这里我们使用一个***pooling层来融合卷积核大小分别为1、3、5的卷积层。此代码是Inception模块最简单的基本实现。在实践中,这也是结合上面的瓶颈思想,代码稍微复杂一些。Inception模块definition_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)returnconcatenate([a,b,c,d])ResidualmoduleResNet(残差网络)是微软研究人员提出的一种架构,可以让神经网络有任意层,也可以提高模型的准确性。您现在可能已经熟悉这种方法,但在ResNet出现之前情况就大不相同了。defresidual_block(x,f=32,r=4):m=conv(x,f//r,k=1)m=conv(m,f//r,k=3)m=conv(m,f,k=1)returnadd([x,m])残差模块的思想是在卷积模块的输出上加上初始激活。这样,网络可以通过学习过程决定使用多少新的卷积来输出。请注意,Inception模块用于连接输出,而Residual模块用于添加它们。ResNeXt模块从名字也可以看出,ResNeXt与ResNet密切相关。研究人员引入了卷积模块的基数术语作为类似于宽度(通道数)和深度(层数)的附加维度。基数是指模块中存在的并行路径的数量。这听起来类似于Inception模块(具有4个并行操作)。但是,不是并行使用不同类型的操作,而是当基数为4时,并行使用的4个操作是相同的。如果他们做同样的事情,为什么要并行化?这是个好问题。这个概念也被称为分组卷积,可以追溯到最早的AlexNet论文。不过,当时这种方法主要用于在多个GPU上划分训练过程,而ResNeXt则使用它们来提高参数效率。defresnext_block(x,f=32,r=2,c=4):l=[]foriinrange(c):m=conv(x,f//(c*r),k=1)m=conv(m,f//(c*r),k=3)m=conv(m,f,k=1)l.append(m)m=add(l)returnadd([x,m])的思路是所有输入通道被分成不同的组。卷积仅在其指定的通道组内运行,而不是跨组运行。研究发现,每组都会学习不同类型的特征,同时也会提高权重的效率。假设一个瓶颈模块首先使用4的压缩比将256个输入通道减少到64个,然后将它们返回到256个通道用于输出。如果我们想引入32的基数和2的压缩比,那么我们将有32个并行的1×1卷积层,其中每个卷积层有4个输出通道(256/(32*2))。之后,我们使用32个3×3卷积层和4个输出通道,然后是32个1×1层和256个输出通道。最后一步涉及堆叠这32条并行路径,这些路径在添加初始输入以构建剩余连接之前提供输出。左:ResNet模块;右图:具有大致相同参数复杂度的RexNeXt模块对此有很多了解。上图是它的工作过程图。也许你可以复制这段代码并尝试自己用Keras建立一个小型网络。这么复杂的描述竟然可以概括成这么简单的9行代码,是不是很神奇?顺带一提,如果base等于通道数,那么你就得到了所谓的depthwiseseparableconvolution。自从Xception架构出现以来,这种方法已经被很多人使用。密集模块密集模块是残差模块的极端版本??,其中每个卷积层都获得模块中所有先前卷积层的输出。首先,我们将输入激活添加到一个列表中,然后进入一个遍历模块深度的循环。每个卷积输出也连接到这个列表,以便后续迭代得到越来越多的输入特征图。该程序一直持续到达到所需的深度。defdense_block(x,f=32,d=5):l=xforiinrange(d):x=conv(l,f)l=concatenate([l,x])returnl虽然有必要得到一个架构一样好DenseNet花了几个月的研究,但实际的基本构建块就是这么简单。太奇妙了。Squeeze-and-Excitation模块SENet在ImageNet上短暂地实现了最先进的性能。它基于ResNeXt,侧重于对有关网络通道的信息进行建模。在常规卷积层中,每个通道的点积计算中的堆叠操作具有相同的权重。Squeeze-and-Excitation模块SENet引入了一个非常简单的模块,可以添加到任何现有架构中。它创建了一个小型神经网络,学习如何根据输入对每个过滤器进行加权。如您所见,它本身并不是一个卷积模块,但可以添加到任何卷积模块中,并有望提高其性能。我想将它添加到mixin模块。defse_block(x,f,rate=16):m=gavgpool(x)m=dense(m,f//rate)m=dense(m,f,a='sigmoid')returnmultiply([x,m])每个通道被压缩成一个单一的值,并被馈送到一个双层神经网络。根据渠道的分布,网络学习根据渠道的重要性对这些渠道进行加权。***,这些权重与卷积激活相乘。SENet有少量额外的计算开销,但有可能改进任何卷积模型。在我看来,这样的模块还没有得到足够的研究关注。NASNetNormalCell的难度来了。介绍了一些简单但有效的设计后,现在我们进入设计神经网络架构的算法世界。NASNet的设计方式令人惊叹,但实际架构相对复杂。但我们知道它在ImageNet上确实非常非常好。NASNet的提议者手动定义了一个具有不同类型的卷积层和池化层的搜索空间,具有不同的可能设置。它们还定义了这些层如何并行或顺序排列以及添加或连接。定义完成后,他们构建了一个基于递归神经网络的强化学习(RL)算法,并因提出在CIFAR-10数据集上表现良好的特定设计而获得奖励。由此产生的架构不仅在CIFAR-10上表现良好,而且在ImageNet上也达到了最先进的水平。NASNet由正常细胞和还原细胞组成,它们相互重复。defnormal_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])returnconcatenate([a,b,c,d,e])您可以像这样用Keras实现NormalCell。它没有什么新东西,但是这个特定层的组合和设置方式工作得很好。InvertedResidualModules现在您了解了瓶颈模块和可分离卷积。让我们把它们放在一起。如果你做了一些测试,你会发现由于可分离卷积已经减少了参数的数量,压缩它们可能会损害性能,而不是提高性能。研究人员想出了一种与瓶颈残差??模块相反的方法。他们使用低成本的1×1卷积增加通道数量,因为后续的可分离卷积层可以大大减少参数数量。关闭这些通道后,它会添加到初始激活中。definv_residual_block(x,f=32,r=4):m=conv(x,f*r,k=1)m=sepconv(m,f,a='linear')返回加法([m,x])***还有一点:这个separableconvolution之后没有激活函数。相反,它直接添加到输入中。研究表明,将此模块合并到框架中时非常有效。AmoebaNetNormalCellAmoebaNet的NormalCellAmoebaNet是目前在ImageNet上表现最好的,甚至可能是广义图像识别任务上最好的。与NASNet类似,它是通过一种算法设计的,该算法使用与之前描述的相同的搜索空间。唯一的区别是他们没有使用强化学习算法,而是一种通常称为“进化”的通用算法。该算法如何工作的细节超出了本文的范围。最终,与NASNet相比,研究人员通过进化算法找到了计算成本更低的更好解决方案。它在ImageNet上达到了97.87%的Top-5准确率——这是单一架构的新高。查看它的代码,这个模块没有添加任何您还没有看到的新内容。大家可以按照上图尝试实现这个新的NormalCell,来测试一下自己是否掌握了。总结希望本文能帮助您理解重要的卷积模块,并帮助您意识到实现它们并不像您想象的那么困难。有关这些架构的详细信息,请参阅他们各自的论文。你会发现,一旦你理解了一篇论文的核心思想,理解其余部分就容易多了。请注意,批归一化通常在实际实现中添加,并且应用激活函数的位置各不相同。原文链接:https://towardsdatascience.com/history-of-convolutional-blocks-in-simple-code-96a7ddceac0c:almosthuman2014)》]点此阅读本作者更多好文