前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >什么是AlexNet?

什么是AlexNet?

作者头像
老肥码码码
发布2020-01-17 11:15:07
1.6K0
发布2020-01-17 11:15:07
举报

对于CNN(卷积神经网络)最早可以追溯到1986年BP算法的提出,然后1989年LeCun将其用到多层神经网络中,直到1998年LeCun提出LeNet-5模型,神经网络的雏形完成。第一个典型的CNN就是LeNet5网络结构,但是今天我们要讲的主角是AlexNet也就是文章《ImageNet Classification with Deep Convolutional Neural Networks》所介绍的网络结构。Alex等人在2012年提出的AlexNet网络结构模型在ILSVRC-2012上以巨大的优势获得第一名,引爆了神经网络的应用热潮,使得卷积神经网络CNN成为在图像分类上的核心算法模型。这篇论文阐述了一个多层卷积网络,目标是将120万高分辨率的图像分成1000类。

Net Structure

  • AlexNet首先用一张227×227×3的图片作为输入,实际上原文中使用的图像是224×224×3,但是如果你尝试去推导一下,你会发现227×227这个尺寸更好一些。
  • 第一层我们使用96个11×11的过滤器,步幅为4,由于步幅是4,因此尺寸缩小到55×55,缩小了4倍左右。然后用一个3×3的过滤器构建最大池化层,f=3,步幅s为2,卷积层尺寸缩小为27×27×96。接着再执行一个5×5的卷积,padding之后,输出是27×27×256。然后再次进行最大池化,尺寸缩小到13×13。再执行一次same卷积,相同的padding,得到的结果是13×13×384,用384个过滤器再做一次same卷积,再做一次同样的操作,最后再进行一次最大池化,尺寸缩小到6×6×256。6×6×256等于9216,将其展开为9216个单元,
  • 然后是三层全连接层,最后使用softmax函数输出识别的结果,看它究竟是1000个可能的对象中的哪一个。
  • 第2,4,5卷积层的神经元只与位于同一GPU上的前一层的核映射相连接。第3卷积层的核与第2层的所有核映射相连。全连接层的神经元与前一层的所有神经元相连。第1,2卷积层之后是响应归一化层。最大池化层加在了响应归一化层和第5卷积层之后。ReLU非线性应用在每个卷积层和全连接层的输出上。
  • 最终整个网络包含5个卷积层和3个全连接层,深度似乎是非常重要的:移除任何卷积层(每个卷积层包含的参数不超过模型参数的1%)都会导致更差的性能。

New and Unusual Features

  • ReLU
    • 激活函数采用ReLU,而不是tanh和sigmoid函数,与饱和神经元相比,有ReLU的网络学习速度要快好几倍
  • Training on Multiple GPUs
  • 在写这篇论文的时候,GPU的处理速度还比较慢,所以AlexNet采用了非常复杂的方法在两个GPU上进行训练。大致原理是,这些层分别拆分到两个不同的GPU上,同时还专门有一个方法用于两个GPU进行交流。
  • Local Response Normalization
    • “局部响应归一化层”(Local Response Normalization),即LRN层。局部响应归一层的基本思路是,假如这是网络的一块,比如是13×13×256,LRN要做的就是选取一个位置,从这个位置穿过整个通道,能得到256个数字,并进行归一化。进行局部响应归一化的动机是,对于这张13×13的图像中的每个位置来说,我们可能并不需要太多的高激活神经元。但是后来,很多研究者发现LRN起不到太大作用,因此我们现在并不用LRN来训练网络。
  • Overlapping Pooling
    • 一般的池化层因为没有重叠,所以poolsize 和 stride一般是相等的,例如8×8的一个图像,如果池化层的尺寸是2×2 ,那么经过池化后的操作得到的图像是 4×4 大小,这种设置叫做不覆盖的池化操作,如果 stride < poolsize, 那么就会产生覆盖的池化操作,这种有点类似于convolutional化的操作,这样可以得到更准确的结果。论文指出,在训练模型过程中,覆盖的池化层更不容易过拟合。

Reducing Overfitting

  • Data Augmentation 数据扩充是防止过拟合的一种简单方法,只需要对原始的数据进行合适的变换,就会得到更多有差异的数据集,防止过拟合。本文中主要采用了两种方法来进行数据扩充,这两种方式都是从原始图像通过非常少的计算量产生变换的图像,因此变换图像不需要存储在硬盘上。
    • 第一种数据增强方式包括产生图像平移水平翻转。我们从256× 256图像上通过随机提取224 × 224的图像块(以及这些图像块的水平翻转)实现了这种方式,然后在这些提取的图像块上进行训练。在测试时,网络会提取5个224 × 224的图像块(四个角上的图像块和中心的图像块)和它们的水平翻转(因此总共10个图像块)进行预测,然后对网络在10个图像块上的softmax层的预测结果进行平均。
    • 第二种数据增强方式是改变训练图像的RGB通道的强度。在整个ImageNet训练集上对RGB像素值集合执行主成分分析(PCA)。对于每幅训练图像,我们加上多倍找到的主成分,大小成正比的对应特征值乘以一个随机变量,这个随机变量通过均值为0,标准差为0.1的高斯分布得到。这个方案近似抓住了自然图像的一个重要特性,即光照的强度和颜色发生变化时,物体本身没有发生变化。
  • Dropout
    • 以0.5的概率对每个隐层神经元的输出设为0。那些用这种方式“丢弃”的神经元不再进行前向传播并且不参与反向传播。因此每次输入时,神经网络会采样一个不同的架构,但所有架构共享权重。这个技术减少了复杂的神经元互适应,因为一个神经元不能依赖特定的其它神经元的存在。
    • 在前两层全连接层使用Dropout。
    • 该技术大致上使要求收敛的迭代次数翻了一倍。
  • Details of Learning
    • 使用随机梯度下降来训练模型,样本的batch size为128,动量为0.9,权重衰减率为0.0005。少量的权重衰减对于模型的学习是重要的。换句话说,权重衰减不仅仅是一个正则项:而且它减少了模型的训练误差。权重的更新规则:
    • i是迭代索引,v是动量变量,ϵ是学习率, 是目标函数对w,在wi上的第i批微分Di的平均。
    • 使用均值为0,标准差为0.01的高斯分布对每一层的权重进行初始化。在第2,4,5卷积层和全连接隐层将神经元偏置初始化为常量1。这个初始化通过为ReLU提供正输入加速了早期阶段的学习,对剩下的层的神经元偏置初始化为0。
    • 当验证误差在当前的学习率下停止改善时,遵循启发式的方法将学习率除以10。

Code with Tensorflow

class AlexNet(object):
    def __init__(self, x, keep_prob, num_classes, skip_layer,
                 weights_path='DEFAULT'):
        """
        Create the graph of the AlexNet model.
        Args:
            x: Placeholder for the input tensor.
            keep_prob: Dropout probability.
            num_classes: Number of classes in the dataset.
            skip_layer: List of names of the layer, that get trained from
                scratch
            weights_path: Complete path to the pretrained weight file, if it
                isn't in the same folder as this code
        """
        # Parse input arguments into class variables
        self.X = x
        self.NUM_CLASSES = num_classes
        self.KEEP_PROB = keep_prob
        self.SKIP_LAYER = skip_layer
        if weights_path == 'DEFAULT':
            self.WEIGHTS_PATH = 'bvlc_alexnet.npy'
        else:
            self.WEIGHTS_PATH = weights_path
        # Call the create function to build the computational graph of AlexNet
        self.create()
    def create(self):
        # 1st Layer: Conv (w ReLu) -> Lrn -> Pool
        conv1 = conv(self.X, 11, 11, 96, 4, 4, padding='VALID', name='conv1')
        norm1 = lrn(conv1, 2, 1e-04, 0.75, name='norm1')
        pool1 = max_pool(norm1, 3, 3, 2, 2, padding='VALID', name='pool1')
        # 2nd Layer: Conv (w ReLu)  -> Lrn -> Pool with 2 groups
        conv2 = conv(pool1, 5, 5, 256, 1, 1, groups=2, name='conv2')
        norm2 = lrn(conv2, 2, 1e-04, 0.75, name='norm2')
        pool2 = max_pool(norm2, 3, 3, 2, 2, padding='VALID', name='pool2')
        # 3rd Layer: Conv (w ReLu)
        conv3 = conv(pool2, 3, 3, 384, 1, 1, name='conv3')
        # 4th Layer: Conv (w ReLu) splitted into two groups
        conv4 = conv(conv3, 3, 3, 384, 1, 1, groups=2, name='conv4')
        # 5th Layer: Conv (w ReLu) -> Pool splitted into two groups
        conv5 = conv(conv4, 3, 3, 256, 1, 1, groups=2, name='conv5')
        pool5 = max_pool(conv5, 3, 3, 2, 2, padding='VALID', name='pool5')
        # 6th Layer: Flatten -> FC (w ReLu) -> Dropout
        flattened = tf.reshape(pool5, [-1, 6*6*256])
        fc6 = fc(flattened, 6*6*256, 4096, name='fc6')
        dropout6 = dropout(fc6, self.KEEP_PROB)
        # 7th Layer: FC (w ReLu) -> Dropout
        fc7 = fc(dropout6, 4096, 4096, name='fc7')
        dropout7 = dropout(fc7, self.KEEP_PROB)
        # 8th Layer: FC and return unscaled activations
        self.fc8 = fc(dropout7, 4096, self.NUM_CLASSES, relu=False, name='fc8')
    def load_initial_weights(self, session):
        # Load the weights into memory
        weights_dict = np.load(self.WEIGHTS_PATH, encoding='bytes').item()
        # Loop over all layer names stored in the weights dict
        for op_name in weights_dict:
            # Check if layer should be trained from scratch
            if op_name not in self.SKIP_LAYER:
                with tf.variable_scope(op_name, reuse=True):
                    # Assign weights/biases to their corresponding tf variable
                    for data in weights_dict[op_name]:
                        # Biases
                        if len(data.shape) == 1:
                            var = tf.get_variable('biases', trainable=False)
                            session.run(var.assign(data))
                        # Weights
                        else:
                            var = tf.get_variable('weights', trainable=False)
                            session.run(var.assign(data))
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 算法与数据之美 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Net Structure
  • New and Unusual Features
  • Reducing Overfitting
  • Code with Tensorflow
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档