DL with python(15)——tensorflow实现3种经典CNN的搭建(LeNet、AlexNet、VGGNet)

本文涉及到的是中国大学慕课《人工智能实践:Tensorflow笔记》第五讲第11-13节的内容,对tensorflow环境下几种经典卷积神经网络的搭建进行介绍,其基础是DL with python(14)——tensorflow实现CNN的“八股”中的代码,将其中第三步的代码替换为本文中的任意代码均可直接运行,其他部分无需改变。

经典的卷积神经网络有以下几种,这里介绍前三种结构简单的,实现的方法也相对简单。
在这里插入图片描述

LeNet

LeNet由Yann LeCun于1998年提出,是卷积网络开篇之作。主要优点是通过共享卷积核,减少网络参数。

Yann Lecun, Leon Bottou, Y. Bengio, Patrick Haffner. Gradient-Based Learning Applied to Document Recognition. Proceedings of the IEEE, 1998.

在统计CNN层数时,一般只统计卷积计算层和全连接层,其余操作可以认为是卷积计算层的附属。完整的卷积操作包括CBAPD5个步骤,分别是Conv(卷积)、BatchNormalization(批归一化)、Activation(激活)、Pool(池化)、Dropout(舍弃)

如下图所示,LeNet有5层结构,包括2个卷积层和3个全连接层,但是其中卷积计算层中的B和D操作当时还未出现。

在tensorflow中的实现代码如下:

class LeNet(Model):  # 类的名字是LeNet,继承了tensorflow的Model类
    def __init__(self): # 定义网络结构
        super(LeNet, self).__init__() # 固定格式,注意类名
        self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='valid')  # 卷积层,6个卷积核,大小为5*5,无填充
        self.a1 = Activation('sigmoid')  # 激活层
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2)  # 池化层,2*2卷积核,步长2,无填充

        self.c2 = Conv2D(filters=16, kernel_size=(5, 5), padding='valid')  # 卷积层,16个卷积核,大小为5*5,无填充
        self.a2 = Activation('sigmoid')  # 激活层
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2)

        self.flatten = Flatten()   #拉直
        self.f1 = Dense(120, activation='sigmoid')
        self.f2 = Dense(84, activation='sigmoid')
        self.f3 = Dense(10, activation='softmax')
    # class类的调用
    def call(self, x): # 调用前面定义的各层,实现x到y的前向传播
        x = self.c1(x)
        x = self.a1(x)
        x = self.p1(x)

        x = self.c2(x)
        x = self.a2(x)
        x = self.p2(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.f2(x)
        y = self.f3(x)
        return y

model = LeNet()

总体上看,诞生于 1998 年的 LeNet5 与如今一些主流的 CNN 网络相比,其结构可以说是相当简单,不过它成功地利用“卷积提取特征→全连接分类”的经典思路解决了手写数字识别的问题,对神经网络研究的发展有着很重要的意义。

AlexNet

AlexNet 网络诞生于 2012 年,其 ImageNet Top5 错误率为 16.4 %,可以说 AlexNet 的出现使得已经沉寂多年的深度学习领域开启了黄金时代。主要优点是激活函数使用 Relu,提升训练速度;Dropout 防止过拟合。

Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton. ImageNet Classification with Deep Convolutional Neural Networks. In NIPS, 2012.

AlexNet 的总体结构和 LeNet5 有相似之处,但是有一些很重要的改进:
A)由五层卷积、三层全连接组成,输入图像尺寸为 224 * 224 * 3,网络规模远大于 LeNet5;
B)使用了 Relu 激活函数;
C)进行了舍弃(Dropout)操作,以防止模型过拟合,提升鲁棒性;
D)增加了一些训练上的技巧,包括数据增强、学习率衰减、权重衰减(L2 正则化)等。
AlexNet 的网络结构如图所示,一共有8层结构,包括5个卷积计算层和3个全连接层。
在这里插入图片描述
在tensorflow中的实现代码如下:

class AlexNet(Model):
    def __init__(self): # 定义网络结构
        super(AlexNet, self).__init__() # 固定格式,注意类名
        self.c1 = Conv2D(filters=96, kernel_size=(3, 3), padding='valid')
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')
        self.p1 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.c2 = Conv2D(filters=256, kernel_size=(3, 3), padding='valid')
        self.b2 = BatchNormalization()
        self.a2 = Activation('relu')
        self.p2 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.c3 = Conv2D(filters=384, kernel_size=(3, 3), padding='same')
        self.a3 = Activation('relu')

        self.c4 = Conv2D(filters=384, kernel_size=(3, 3), padding='same')
        self.a4 = Activation('relu')

        self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.a5 = Activation('relu')
        self.p5 = MaxPool2D(pool_size=(3, 3), strides=2)

        self.flatten = Flatten()
        self.f1 = Dense(2048, activation='relu')
        self.d1 = Dropout(0.5)
        self.f2 = Dense(2048, activation='sigmoid')
        self.d2 = Dropout(0.5)
        self.f3 = Dense(10, activation='softmax')
    # class类的调用
    def call(self, x): # 调用前面定义的各层,实现x到y的前向传播
        x = self.c1(x)
        x = self.b1(x)
        x = self.a1(x)
        x = self.p1(x)

        x = self.c2(x)
        x = self.b2(x)
        x = self.a2(x)
        x = self.p2(x)

        x = self.c3(x)
        x = self.a3(x)

        x = self.c4(x)
        x = self.a4(x)

        x = self.c5(x)
        x = self.a5(x)
        x = self.p5(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.d1(x)
        x = self.f2(x)
        x = self.d2(x)
        y = self.f3(x)
        return y

model = AlexNet()

VGG Net

在 AlexNet 之后,另一个性能提升较大的网络是诞生于 2014 年的 VGGNet,其 ImageNetTop5 错误率减小到了 7.3 %。

K. Simonyan, A. Zisserman. Very Deep Convolutional Networks for Large-Scale Image Recognition.In ICLR,2015.

VGGNet 网络的最大改进是在网络的深度上,由 AlexNet 的 8 层增加到了 16 层和 19 层,更深的网络意味着更强的表达能力,这得益于强大的运算能力支持。VGGNet 的另一个显著特点是仅使用了单一尺寸的 3×3 卷积核,事实上,3×3 的小卷积核在很多卷积网络中都被大量使用,这是由于在感受野相同的情况下,小卷积核堆积的效果要优于大卷积核,同时参数量也更少。VGGNet 就使用了 3×3 的卷积核替代了 AlexNet 中的大卷积核(11×11、7×7、5×5),取得了较好的效果(事实上课程中利用 Keras 实现 AlexNet 时已经采取了这种方式),VGGNet16 的网络结构如图所示。
在这里插入图片描述
VGGNet16 和 VGGNet19 并没有本质上的区别,只是网络深度不同,前者 16 层(13 层卷积、3 层全连接),后者 19 层(16 层卷积、3 层全连接)。根据特征图尺寸的变化,可以将 VGG16 模型分为六个部分(在 VGG16 中,每进行一次池化操作,特征图的边长缩小为 1/2,其余操作均未影响特征图尺寸):

class VGG16(Model):
    def __init__(self):
        super(VGG16, self).__init__()
        # 第一部分:两次卷积(64 个 3 * 3 卷积核、BN、Relu 激活)→最大池化→Dropout
        self.c1 = Conv2D(filters=64, kernel_size=(3, 3), padding='same')  # 卷积层1
        self.b1 = BatchNormalization()  # BN层1
        self.a1 = Activation('relu')  # 激活层1
        self.c2 = Conv2D(filters=64, kernel_size=(3, 3), padding='same', )
        self.b2 = BatchNormalization()  # BN层1
        self.a2 = Activation('relu')  # 激活层1
        self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d1 = Dropout(0.2)  # dropout层

        # 第二部分:两次卷积(128 个 3 * 3 卷积核、BN、Relu 激活)→最大池化→Dropout
        self.c3 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b3 = BatchNormalization()  # BN层1
        self.a3 = Activation('relu')  # 激活层1
        self.c4 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
        self.b4 = BatchNormalization()  # BN层1
        self.a4 = Activation('relu')  # 激活层1
        self.p2 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d2 = Dropout(0.2)  # dropout层

        # 第三部分:三次卷积(256 个 3 * 3 卷积核、BN、Relu 激活)→最大池化→Dropout
        self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b5 = BatchNormalization()  # BN层1
        self.a5 = Activation('relu')  # 激活层1
        self.c6 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b6 = BatchNormalization()  # BN层1
        self.a6 = Activation('relu')  # 激活层1
        self.c7 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
        self.b7 = BatchNormalization()
        self.a7 = Activation('relu')
        self.p3 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d3 = Dropout(0.2)

        # 第四部分:三次卷积(512 个 3 * 3 卷积核、BN、Relu 激活)→最大池化→Dropout
        self.c8 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b8 = BatchNormalization()  # BN层1
        self.a8 = Activation('relu')  # 激活层1
        self.c9 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b9 = BatchNormalization()  # BN层1
        self.a9 = Activation('relu')  # 激活层1
        self.c10 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b10 = BatchNormalization()
        self.a10 = Activation('relu')
        self.p4 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d4 = Dropout(0.2)

        # 第五部分:三次卷积(512 个 3 * 3 卷积核、BN、Relu 激活)→最大池化→Dropout
        self.c11 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b11 = BatchNormalization()  # BN层1
        self.a11 = Activation('relu')  # 激活层1
        self.c12 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b12 = BatchNormalization()  # BN层1
        self.a12 = Activation('relu')  # 激活层1
        self.c13 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
        self.b13 = BatchNormalization()
        self.a13 = Activation('relu')
        self.p5 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
        self.d5 = Dropout(0.2)

        # 第六部分:全连接(512 个神经元)→Dropout→全连接(512 个神经元)→Dropout→全连接(10 个神经元)
        self.flatten = Flatten()
        self.f1 = Dense(512, activation='relu')
        self.d6 = Dropout(0.2)
        self.f2 = Dense(512, activation='relu')
        self.d7 = Dropout(0.2)
        self.f3 = Dense(10, activation='softmax')

    def call(self, x):
        x = self.c1(x)
        x = self.b1(x)
        x = self.a1(x)
        x = self.c2(x)
        x = self.b2(x)
        x = self.a2(x)
        x = self.p1(x)
        x = self.d1(x)

        x = self.c3(x)
        x = self.b3(x)
        x = self.a3(x)
        x = self.c4(x)
        x = self.b4(x)
        x = self.a4(x)
        x = self.p2(x)
        x = self.d2(x)

        x = self.c5(x)
        x = self.b5(x)
        x = self.a5(x)
        x = self.c6(x)
        x = self.b6(x)
        x = self.a6(x)
        x = self.c7(x)
        x = self.b7(x)
        x = self.a7(x)
        x = self.p3(x)
        x = self.d3(x)

        x = self.c8(x)
        x = self.b8(x)
        x = self.a8(x)
        x = self.c9(x)
        x = self.b9(x)
        x = self.a9(x)
        x = self.c10(x)
        x = self.b10(x)
        x = self.a10(x)
        x = self.p4(x)
        x = self.d4(x)

        x = self.c11(x)
        x = self.b11(x)
        x = self.a11(x)
        x = self.c12(x)
        x = self.b12(x)
        x = self.a12(x)
        x = self.c13(x)
        x = self.b13(x)
        x = self.a13(x)
        x = self.p5(x)
        x = self.d5(x)

        x = self.flatten(x)
        x = self.f1(x)
        x = self.d6(x)
        x = self.f2(x)
        x = self.d7(x)
        y = self.f3(x)
        return y
        
model = VGG16()

总体来看,VGGNet的结构是相当规整的,它继承了AlexNet中的Relu激活函数、Dropout操作等有效的方法,同时采用了单一尺寸的 3 * 3 小卷积核,形成了规整的 C(Convolution,卷积)、B(Batch normalization)、A(Activation,激活)、(Pooling,池化)、D(Dropout)结构,这一典型结构在卷积神经网络中的应用是非常广的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值