【2022】较为全面的AlexNet总结

目录

网络特色

网络结构和描述

使用LRN-Local Response Normalization局部抑制

全连接层使用DropOut

使用Relu函数代替Sigmoid和tanh

用Relu的原因

relu组成非线性的原理

使用Overlapping Pooling(重叠池化)

Pytorch实现


网络特色

1、使用ReLU作为激活函数代替了传统的Sigmoid和Tanh

ReLU为非饱和函数,论文中验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。

2、在多个GPU上进行模型的训练,不但可以提高模型的训练速度,还能提升数据的使用规模

3、使用LRN对局部的特征进行归一化

结果作为ReLU激活函数的输入能有效降低错误率

4、使用随机丢弃技术(dropout)选择性地忽略训练中的单个神经元

在AlexNet的最后几个全连接层中使用了Dropout来避免模型的过拟合

5、重叠最大池化(overlapping max pooling)

重叠池化,即池化范围z与步长s存在关系z > s(如最大池化下采样中核大小为3 × 3,步距为2),提高了一点点准确度,非常玄学。

最大池化,避免平均池化(average pooling)的平均效应。

后面我会单独分别介绍这5个特点。

网络结构和描述

经典的上下两层结构,目的是用多GPU并行处理。

参考源1

参考源2

为了简化网络结构,将作者原论文中的在两个GPU上的并行结构合并,接下来我们对AlexNet的每一层作详细的分析。

1、Conv1: kernels:48×2=96;kernel_size:11;padding:[1, 2] ;stride:4

卷积层1输入的尺寸为224×224,卷积核的数量为96,论文中两片GPU分别计算48个核; 卷积核的大小为 11 × 11 × 3 ;卷积核步距stride = 4;padding=[1, 2]表示在原输入图像上左侧补一列0,右侧2列0,上侧一行0,下侧2行0。
输出feature map的尺寸为:N = (W − F + 2P ) / S + 1 = [ 224 - 11 + (1 + 2)] / 4 + 1 = 55
输出结果Relu后,使用LRN进行局部抑制

2、Maxpool1: kernel_size:3;pading:0;stride:2

卷积层Conv1之后接着进行了局部响应规范化操作( Local Response Normalized),将规范化的结果送入大小为3 × 3 3\times33×3,步距为2的池化核进行最大池化下采样。
输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (55 - 3) / 2 + 1 = 27

3、Conv2: kernels:128×2=256; kernel_size:5; padding: [2, 2]; stride:1

卷积层2使用256个卷积核做常规的卷积操作
输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (27 - 5 + 4) / 1 + 1 = 27
输出结果Relu后,使用LRN进行局部抑制

4、Maxpool2: kernel_size:3; pading:0; stride:2

与下采样层Conv2类似,在上述卷积层之后接着进行了局部响应规范化操作,然后将结果送入大小为3 × 3 3\times33×3,步距为2的池化核进行最大池化下采样。
输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (27 - 3) / 2 + 1 = 13

5、Conv3: kernels:192×2=384; kernel_size:3; padding: [1, 1]; stride:1

与Conv1和Conv2不同,Conv3、Conv4、Conv5后均不接局部响应归一化LRN层**
输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (13 - 3 + 2) / 1 + 1 = 13

6、Conv4: kernels:192×2=384; kernel_size:3; padding: [1, 1]; stride:1

输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (13 - 3 + 2) / 1 + 1 = 13

7、Conv5: kernels:128×2=256; kernel_size:3; padding: [1, 1]; stride:1

输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (13 - 3 + 2) / 1 + 1 = 13

8、Maxpool3: kernel_size:3 padding: 0 stride:2

输出的feature map尺寸为:N = (W − F + 2P ) / S + 1 = (13 - 3) / 2 + 1 = 6

9、全连接层FC1、FC2、FC3

FC1和FC2分别有4096个神经元,FC3输出softmax为1000个(ImageNet数据集分类类别)。
每个全连接层之间都有DropOut。

使用LRN-Local Response Normalization局部抑制

LRN基于生物学“侧抑制”的理论,即被激活的神经元会抑制周围其他神经元。

我们使用局部归一化,可以理解为把局部的神经元放在一个评价体系中,看这些神经元中谁是被激活的、活跃的神经元(Relu输出值高的),这些神经元在局部的评价体系中给的评分就越高。

如下公式:

a(i,x,y)表示输入图像中通道i、坐标为(x,y)的像素通过Relu后的输出值。假如某个像素是正数,a(i,x,y)=像素值。

N:通道总数量

n:选择的局部区域通道数量

累加a(j,x,y):正如前文所说,要选定一个局部区间,对这个局部区间进行归一化,因此肯定得是某一项/局部所有项之和这样的形式。

n,k,α,β分别表示函数中的depth_radius,bias,alpha,beta,其中n,k,α,β都是自定义的,特别注意一下∑叠加的方向是沿着通道方向的。

计算公式

形象化的绘图如下:

注意图中n/2的地方,那是一个沿着channel方向的区间,代表的就是你选择的局部区域,这个区域包含m个通道,m<=N。

如果n=N,这个区域就是0-N,即所有通道。

下面具体的计算方式很形象:

 import tensorflow as tf
 import numpy as np
 x = np.array([i for i in range(1,33)]).reshape([2,2,2,4])
 y = tf.nn.lrn(input=x,depth_radius=2,bias=0,alpha=1,beta=1)
  
 with tf.Session() as sess:
     print(x)
     print('#############')
     print(y.eval())

上图中有两个Batch,每组中一列可以看成一个通道,如[1,5,9,13]是第一批、第一个通道:

计算第二批、通道2中的26那个位置的局部归一化输出:

因为代码中指定depth_radius=2,所以表示局部区间是从26这个点所在的通道开始,往前数两个通道,再往后数两个的通道。这个通道区间中对应26位置[(1,0)]的像素值分别是25 26 27 28,再用上面公式计算即可:

26/(0+1*(25^2 + 26^2 + 27^2 + 28^2)) ^1 = 0.00923952。

下面这张图来自于:参考2-CSDN,就当总结一下吧。

全连接层使用DropOut

以前学神经网络的时候我们学过添加正则项避免过拟合,本质是降低网络的学习能力。

对于全连接层来说,可以使用dropout来降低网络的学习能力,进而避免过拟合。

dropout是就是随机使得一些神经元失活:

为了保证期望不变,我们应该在使得一些神经元失活的基础上,提高那些没失活神经元的学习能力:

以概率 p 将原始数据元素变为 0,即丢弃数据;以概率 1-p 将原始数据元素变大

可以看到现在的期望就变成了 E(x') = 0p + (1-p)x/ (1-p) = x,并没有变化。

具体实现:

 import torch
 from torch import nn
 from d2l import torch as d2l
  
 def dropout_layer(X, dropout):
     assert 0 <= dropout <= 1
     # 在本情况中,所有元素都被丢弃
     if dropout == 1:
         return torch.zeros_like(X)
     # 在本情况中,所有元素都被保留
     if dropout == 0:
         return X
     # torch.rand() 生成 0~1 之间的随机均匀分布, mask 最终生成与 输入 X 同维度 且元素只有 0 或 1 的张量
     mask = (torch.rand(X.shape) > dropout).float()
     return mask * X / (1.0 - dropout)

使用Relu函数代替Sigmoid和tanh

用Relu的原因

  1. Relu计算简单
    相较于sigmoid来说,没有指数运算,速度快
  2. sigmoid在深层网络出现梯度为零的情况,relu可以缓解此问题
    因为sigmoid的值恒大于0,在深层网络中,正值不断累加为一个很大的值,而sigmoid函数在x很大时的梯度为0,进而无法使用梯度下降法更新此处梯度。而relu的函数值可以取0,在一定程度上缓解了此问题。
  3. Relu把一些输入以0输出,在一定程度上简化的(稀疏的)特征图,留下了更重要的信息。

使用ReLU激活后,输出的矩阵产生很多0值,而通过Sigmoid激活函数后,得到的矩阵中的元素的值都是处于0到1之间,所以,ReLU的输出要比Sigmoid的稀疏程度高的多,而稀疏程度高,则意味着我们去找这些矩阵所表含的规律时就比较容易。(卷积神经网络基础题——CNN中为什么用ReLU,而不用Sigmoid?_G5Lorenzo的博客-CSDN博客_为什么用relu不用sigmoid

relu组成非线性的原理

  • 叠加操作(串联神经元),表达式为: Relu(13∗Relu(10∗Relu(−2∗x+1,0)+2,0)−2,0)

  • 加法操作(并联神经元),表达式为:Relu(−2∗x+1,0)+Relu(3∗t−3,0)+Relu(−2∗t−5,0)+Relu(4∗t−100,0)

可以看到对于stack操作,你无论stack多少次,最终的函数图像都只能是一边是全零,一边是斜率为正或负的线性函数。

但是对于加法操作,可以很大程度上改变图像的样子,我这里做了四个add操作,函数图像就有点像二次函数了。当你add操作足够多,结合stack操作和bias(为了有负值),理论上是能拟合绝大部分在闭区间上的连续函数的。(事实上,Relu函数无法真正意义上拟合所有函数,但对于神经网络来说,已经够用了)

使用Overlapping Pooling(重叠池化)

相对于传统的no-overlapping pooling,采用Overlapping Pooling不仅可以提升预测精度,同时一定程度上可以减缓过拟合。
相比于正常池化(步长s=2,窗口z=2) 重叠池化(步长s=2,窗口z=3) 可以减少top-1, top-5分别为0.4% 和0.3%;重叠池化可以避免过拟合。

Pytorch实现

 import torch
 import torch.nn as nn
 ​
 ​
 class AlexNet(nn.Module):
     def __init__(self, num_classes=1000):
         super().__init__()
         self.features = nn.Sequential(
             nn.Conv2d(3, 96, kernel_size=11, stride=4, padding=2),  # [None, 3, 224, 224] --> [None, 96, 55, 55]
             nn.ReLU(inplace=True),
             nn.MaxPool2d(kernel_size=3, stride=2),  # [None, 96, 55, 55] --> [None, 96, 27, 27]
             nn.Conv2d(96, 256, kernel_size=5, padding=2),  # [None, 96, 27, 27] --> [None, 256, 27, 27]
             nn.ReLU(inplace=True),
             nn.MaxPool2d(kernel_size=3, stride=2),  # [None, 256, 27, 27] --> [None, 256, 13, 13]
             nn.Conv2d(256, 384, kernel_size=3, padding=1),  # [None, 256, 27, 27] --> [None, 384, 13, 13]
             nn.ReLU(inplace=True),
             nn.Conv2d(384, 384, kernel_size=3, padding=1),  # [None, 384, 13, 13] --> [None, 384, 13, 13]
             nn.ReLU(inplace=True),
             nn.Conv2d(384, 256, kernel_size=3, padding=1),  # [None, 384, 13, 13] --> [None, 256, 13, 13]
             nn.ReLU(inplace=True),
             nn.MaxPool2d(kernel_size=3, stride=2)  # [None, 256, 13, 13] --> [None, 256, 6, 6]
         )
 ​
         self.classifier = nn.Sequential(
             nn.Dropout(p=0.2),
             nn.Linear(256 * 6 * 6, 2048),
             nn.ReLU(inplace=True),
             nn.Dropout(p=0.2),
             nn.Linear(2048, 2048),
             nn.ReLU(inplace=True),
             nn.Linear(2048, num_classes)
         )
 ​
     def forward(self, inputs):
         x = self.features(inputs)
         x = torch.flatten(x, start_dim=1)
         outputs = self.classifier(x)
         return outputs
 ​
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值