2012年AlexNet在ImageNet竞赛中以超过第二名10.9个百分点的绝对优势一举夺冠,自此,深度学习和卷积神经网络一举成名,一直火到了今天。
从上图还可以看到网络有两个分支,这是因为当时硬件条件有限,一块GPU显存不够,所以使用了两块GPU分别训练,在最后的全连接层进行特征融合得到最后的结果。
因为网络上比较流行上面这个图,但其实我个人更喜欢下面这个图,在逻辑和过程上更加清楚。
AlexNet 各层的详细描述
输入层: 输入大小为224 x 224的3通道图像,实际上会经过预处理变为227X227X3
第1层:卷积层(卷积、池化)
使用96个大小为11 x 11 x 3的卷积核,分两组(每组48个),按步长4个像素对输入层进行卷积运算,得到两组55 x 55 x48 的卷积结果。
对卷积结果使用ReLu激活函数,得到激活结果。
对激活结果使用窗口为3 x 3、步长为2个像素的重叠最大池化,得到27 x 27 x 48的池化结果。
对池化结果使用局部响应归一化操作,得到27 x 27 x 48的归一化结果。
第2层:卷积层(卷积、池化)
使用256个大小为27 x 27 x 48的卷积核,分两组(每组128个),按步长1个像素对第2层的归一化结果进行卷积运算,得到两组27 x 27 x 128 的卷积结果。
对卷积结果使用ReLu激活函数,得到激活结果。
对两组 27 x 27 x 128的激活结果使用窗口为3 x 3、步长为2个像素的重叠最大池化,得到两组13 x 13 x 128的池化结果。
对池化结果使用局部响应归一化操作,得到两组13 x 13 x128 的归一化结果。
第3层:卷积层
使用384个大小为13 x 13 x 256的卷集核,分两组,按步长为1个像素对上一层归一化结果卷积运算,得到两组13 x 13 x 192的卷积结果。
对卷积结果使用ReLu激活函数,得到激活结果。
第4层:卷积层
使用384个大小为13 x 13 x 192的卷集核,分两组,按步长为1个像素对上一层的激活结果进行卷积运算,得到两组两组 13 x 13 x 192 的卷积结果。
对卷积结果使用ReLu激活函数,得到激活结果
第5层:卷积层(卷积、池化)
使用256个大小为13 x 13 x 192的卷积核分两组,按步长为1个像素,对上一层的激活结果卷积运算,得到两组 13 x 13 x128卷积结果。
对卷积结果使用ReLu激活函数,得到激活结果。
对激活结果,进行窗口为3 x 3,步长为2个像素的重叠最大池化,得到两组13 x 3 x 128的池化结果。
第6层: 全链接层
使用4096个神经元分两组,对上一层的池化结果全链接处理。
对全链接结果使用ReLu激活函数。
对激活结果使用概率为0.5的dropout操作,得到dropout结果。
第7层: 全链接层
使用4096个神经元分两组,对上一层的池化结果全链接处理。
对全链接结果使用ReLu激活函数。
对激活结果使用概率为0.5的dropout操作,得到dropout结果。
第8层: 输出层
1000路的软最大输出层,用来覆盖1000类的标签分布。
AlexNet 的贡献
ReLU激活函数
Dropout
LRN
数据增强
代码实现
from tensorflow.keras import layers, models, Model, Sequential
def AlexNet_v1(im_height=224, im_width=224, class_num=1000):
# tensorflow中的tensor通道排序是NHWC
# 使用函数形式构建模型,必须加上输入层
input_image = layers.Input(shape=(im_height, im_width, 3), dtype="float32") # output(None, 224, 224, 3)
x = layers.ZeroPadding2D(((1, 2), (1, 2)))(input_image) # output(None, 227, 227, 3)
x = layers.Conv2D(48, kernel_size=11, strides=4, activation="relu")(x) # output(None, 55, 55, 48)
x = layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 27, 27, 48)
x = layers.Conv2D(128, kernel_size=5, padding="same", activation="relu")(x) # output(None, 27, 27, 128)
x = layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 13, 13, 128)
x = layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 192)
x = layers.Conv2D(192, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 192)
x = layers.Conv2D(128, kernel_size=3, padding="same", activation="relu")(x) # output(None, 13, 13, 128)
x = layers.MaxPool2D(pool_size=3, strides=2)(x) # output(None, 6, 6, 128)
x = layers.Flatten()(x) # output(None, 6*6*128)
x = layers.Dropout(0.2)(x) # 为下一层的Dense层加入dropout
x = layers.Dense(2048, activation="relu")(x) # output(None, 2048)
x = layers.Dropout(0.2)(x)
x = layers.Dense(2048, activation="relu")(x) # output(None, 2048)
x = layers.Dense(class_num)(x) # output(None, 5), 在这里是可以用layers.Dense(10, activation="softmax"),从而省略后面的softmax层
predict = layers.Softmax()(x)
model = models.Model(inputs=input_image, outputs=predict)
return model
class AlexNet_v2(Model):
def __init__(self, class_num=1000):
super(AlexNet_v2, self).__init__()
self.features = Sequential([
layers.ZeroPadding2D(((1, 2), (1, 2))), # output(None, 227, 227, 3)
layers.Conv2D(48, kernel_size=11, strides=4, activation="relu"), # output(None, 55, 55, 48)
layers.MaxPool2D(pool_size=3, strides=2), # output(None, 27, 27, 48)
layers.Conv2D(128, kernel_size=5, padding="same", activation="relu"), # output(None, 27, 27, 128)
layers.MaxPool2D(pool_size=3, strides=2), # output(None, 13, 13, 128)
layers.Conv2D(192, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 192)
layers.Conv2D(192, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 192)
layers.Conv2D(128, kernel_size=3, padding="same", activation="relu"), # output(None, 13, 13, 128)
layers.MaxPool2D(pool_size=3, strides=2)]) # output(None, 6, 6, 128)
self.flatten = layers.Flatten()
self.classifier = Sequential([
layers.Dropout(0.2),
layers.Dense(1024, activation="relu"), # output(None, 2048)
layers.Dropout(0.2),
layers.Dense(128, activation="relu"), # output(None, 2048)
layers.Dense(class_num), # output(None, 5)
layers.Softmax()
])
def call(self, inputs, **kwargs):
x = self.features(inputs)
x = self.flatten(x)
x = self.classifier(x)
return x