AlexNet论文学习总结

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%)都会导致更差的性能。

AlexNet论文学习总结_第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
    • 一般的池化层因为没有重叠,所以pool_size 和 stride一般是相等的,例如8×8的一个图像,如果池化层的尺寸是2×2 ,那么经过池化后的操作得到的图像是 4×4 大小,这种设置叫做不覆盖的池化操作,如果 stride < pool_size, 那么就会产生覆盖的池化操作,这种有点类似于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。少量的权重衰减对于模型的学习是重要的。换句话说,权重衰减不仅仅是一个正则项:而且它减少了模型的训练误差。权重的更新规则:
      AlexNet论文学习总结_第2张图片
    • 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))

2012 ImageNet Classification with Deep Convolutional

代码实现

你可能感兴趣的:(MachineLearning)