完整代码:AlexNet实现完整代码
AlexNet
AlexNet由Hinton和他的学生Alex Krizhevsky设计,在2012年ImageNet竞赛中以超过第二名10.9个百分点的绝对优势一举夺冠。从此深度学习和卷积神经网络名声鹊起,深度学习的研究如雨后春笋般出现。
论文:ImageNet Classification with Deep Convolutional Neural Networks
AlexNet网络结构
操作 | 输入数据维度 | 输出数据维度 | 卷积核 | 卷积步长 | 池化核大小 | 池化步长 | 填充 | 激活函数 |
---|---|---|---|---|---|---|---|---|
输入层 | — | (227,227,3) | — | — | — | — | — | — |
卷积层 | (227,227,3) | (55,55,96) | (11,11,96) | 4 | — | — | valid | relu |
最大池化层 | (55,55,96) | (27,27,96) | — | — | (3,3) | 2 | — | — |
卷积层 | (27,27,96) | (27,27,256) | (5,5,256) | 1 | — | — | same | relu |
最大池化层 | (27,27,256) | (13,13,256) | — | — | (3,3) | 2 | — | — |
卷积层 | (13,13,256) | (13,13,384) | (3,3,384) | 1 | — | — | same | relu |
卷积层 | (13,13,384) | (13,13,384) | (3,3,384) | 1 | — | — | same | relu |
卷积层 | (13,13,384) | (13,13,256) | (3,3,256) | 1 | — | — | same | relu |
最大池化层 | (13,13,256) | (6,6,256) | — | — | (3,3) | 2 | — | — |
全连接层 | (9216,) | (4096,) | — | — | — | — | — | relu |
全连接层 | (4096,) | (4096,) | — | — | — | — | — | relu |
全连接层(输出层) | (4096,) | (1000,) | – | – | – | — | — | softmax |
AlexNet新技术点:
(1)使用ReLu作为激活函数,验证其效果在较深的网络超过了Sigmoid函数,成功解决了Sigmoid在网络较深时的梯度弥散问题。ReLu激活函数在很久之前就被提出,但直到AlexNet的出现才将其发扬光大。
(2)使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用Dropout。
(3)使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。
(4)提出了LRN(局部响应归一化)层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。现在大多数使用BN(Batch Normalization),批归一化。
(5)使用GPU加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算。AlexNet使用了两块GTX 580 GPU进行训练,单个GTX 580只有3GB显存,这限制了可训练的网络的最大规模。因此作者将AlexNet分布在两个GPU上,在每个GPU的显存中储存一半的神经元的参数。因为GPU之间通信方便,可以互相访问显存,而不需要通过主机内存,所以同时使用多块GPU也是非常高效的。同时,AlexNet的设计让GPU之间的通信只在网络的某些层进行,控制了通信的性能损耗。
(6)数据增强,随机地从256256的原始图像中截取224224大小的区域(以及水平翻转的镜像),相当于增加了2*(256-224)^2=2048倍的数据量。如果没有数据增强,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。同时,AlexNet论文中提到了会对图像的RGB数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%。
Keras实现
import keras
from keras import backend as K
def LRN(alpha=1e-4, k=2, beta=0.75, n=5):
def f(X):
b, r, c, ch = X.shape
half = n // 2
square = K.square(X)
extra_channels = K.spatial_2d_padding(square, ((0, 0), (half, half)), data_format='channels_first')
scale = k
for i in range(n):
scale += alpha * extra_channels[:, :, :, i:i + int(ch)]
scale = scale ** beta
return X / scale
return keras.layers.Lambda(f, output_shape=lambda input_shape: input_shape)
def AlexNet(input_shape=None, classes=None):
inp = keras.layers.Input(input_shape)
x = keras.layers.Conv2D(96, kernel_size=(11, 11), strides=4, activation='relu')(inp)
x = LRN()(x)
x = keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(x)
x = keras.layers.Conv2D(256, kernel_size=(5, 5), strides=1, activation='relu', padding='same')(x)
x = LRN()(x)
x = keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(x)
x = keras.layers.Conv2D(384, kernel_size=(3, 3), strides=1, activation='relu', padding='same')(x)
x = keras.layers.Conv2D(384, kernel_size=(3, 3), strides=1, activation='relu', padding='same')(x)
x = keras.layers.Conv2D(256, kernel_size=(3, 3), strides=1, activation='relu', padding='same')(x)
x = keras.layers.MaxPool2D(pool_size=(3, 3), strides=2)(x)
x = keras.layers.Flatten()(x)
x = keras.layers.Dense(4096, activation='relu')(x)
x = keras.layers.Dropout(0.5)(x)
x = keras.layers.Dense(4096, activation='relu')(x)
x = keras.layers.Dropout(0.5)(x)
output = keras.layers.Dense(classes, activation='softmax')(x)
model = keras.models.Model(inputs=inp, outputs=output)
return model