5种经典卷积网络

5种经典卷积网络

将要介绍的5种卷积网络如下:

在这里插入图片描述

一:LetNet

​ LetNet由Yann LeCun于1998年提出,是卷积网络的开篇之作。通过共享卷积核减少了网络的参数。在统计卷积神经网络层数时,一般只统计卷积计算层和全连接计算层,其余操作可以认为是卷积计算层的附属。

​ LeNet一共有5层网络,使用如下模型配置。

  1. 模型概述

    输入为32x32x3的特征图,使用2层卷积网络,3层全连接。

    第一层卷积网络卷积核尺寸为5x5,个数为6个,步长为1。第二层卷积网络卷积核尺寸为5x5,卷积核个数为16个,步长为1。两层卷积层均不进行全0填充,均使用2x2池化核最大池化,且步长为2;使用三层全连接进行训练,第一层神经元个数为120个,第二层神经元个数为84个,第三层神经元个数为10个,均采用sigmoid函数作为激活函数。

  2. 模型搭建

    class LeNet5(Model):
    	def __init__(self):
    		super(LeNet5, self).__init()
    		self.c1 = Conv2D(filters=6, Kernel_size(5,5), activation="sigmoid")
    		self.p1 = MaxPool2D(pool_size=(2,2), strides=2)
    		self.c2 = Conv2D(filters=16, kernel_size=(5,5), 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="sigmoid")
    

二:AlexNet

  1. 模型概述

    AlexNet使用relu激活函数提升了训练速度,使用Dropout减少了过拟合。

    ​ AlexNet共有8层,第一层使用了96个3x3卷积核,步长为1,不使用全零填充,原论文种使用局部响应标准化LRM,它的功能是批标准化相似,所以我们使用BN代替,使用relu激活函数,使用3x3的池化核,步长为2,做最大池化,不使用Droout

    ​ 第二层使用了256个3x3卷积核,步长为1,不使用全零填充,实现BN操作实现特征标准化,使用relu激活函数,用3x3池化核,步长是2,做最大池化,不使用Dropout。

    ​ 第三层使用了384个3x3卷积核,步长为1,使用全零填充,不使用BN操作,使用relu激活函数,不使用池化和Dropout。

    ​ 第四层与第三层做了一样的操作。

    ​ 第五层使用了256个3x3卷积核,步长为1,使用全零填充,不使用BN操作,使用relu激活函数,用3x3池化核,步长是2,做最大池化,不使用Dropout

    ​ 第六,七,八层是全连接层,六七层使用2048个神经元,relu激活函数,50%Dropout,第八层使用10个神经元,用softmax使输出符合概率分布。

  2. Tensorflow搭建AlexNet网络结构

class AlexNet8(Model):
	def __init__(self):
		super(AlexNet8, self).__init__()
		# 第一层
		self.c1 = Conv2D(filters=96, kernel_size=(3,3))
		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))
		self.b2 = BatchNormalization()
		self.c2 = Activation("relu")
		self.p2 = MaxPool2D(pool_size(3,3), strides=2)
		
		# 第三层
		self.c3 = Conv2D(filters=384, kernel_size=(3,3), padding="same", activation="relu")
		
		# 第四层
		self.c4 = Conv2D(filters=384, kernel_size=(3,3), padding="same", activation="relu")
		
		# 第五层
		self.c5 = Conv2D(filters=256, kernel=(3,3), padding="same", 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="relu")
		self.d2 = Dropout(0.5)
		self.f3 = Dense(10, activation="softmax")

三:VGGNet

​ VGGNet诞生于2014年,当年ImageNet竞赛的亚军,Top5错误率减小到7.3%。

​ VGGNet使用小尺寸卷积核,在减少参数的同时,提高了识别准确率。VGGNet网络结构规整,非常适合硬件加速。

  1. 模型概述

    以16层VGGNet为例,包括13层卷积层,3层全连接层。

    网络结构是两次(CBA,CBAPD),三次(CBA,CBA,CBAPD),即CBA,CBAPD,CBA,CBAPD,CBA,CBA,CBAPD,CBA,CBA,CBAPD,CBA,CBA,CBAPD,最后是三层全连接。

    卷积核的个数是从64(2层)到128(2层)到256(3层)到512(8层),逐渐增加,因为越靠后,特征图尺寸越小,通过增加卷积核的个数,增加了特征图的深度,保持了信息的承载能力。

    所有卷积层数均采用3x3卷积核,步长为1,采用BN操作,激活函数为relu,采用2x2最大化池化核,步长为2,dropout为0.2。全连接层前两层的神经元个数为512个,激活函数为relu,dropout为0.2,第三层全连接层神经元个数为10个,激活函数为softmax。

  2. 模型搭建

    模型搭建方法与上述模型的搭建方法一致,只是层数和参数不同而已。

四:InceptionNet

  1. 模型概述

​ InceptionNet诞生于2014年,当年ImageNet竞赛冠军,Top5错误率为6.67%

​ InceptionNet引入了Inception结构块,在同一层网络种使用了多个尺寸的卷积核提升了模型感知力,使用了批标准化缓解了梯度消失

​ Inception结构块在同一层网络中使用了多个尺寸的卷积核,可以提取不同尺寸的特征,通过1*1卷积核,作用到输入特征的每个像素点,通过设定少于输入特征图深度的1*1卷积核个数,减少了输出特征图深度,起到了降维的作用,减少了参数量和计算量。

​ Inception结构块包含四个分支,分别经过1*1卷积核输出到卷积连接器,经过1*1卷积核配合3*3卷积核输出到卷积连接器,经过1*1卷积核配合5*5卷积核输出到卷积连接器,经过3*3最大池化核配合1*1卷积核输出到卷积连接器,送到卷积连接器的特征数据尺寸相同,卷积连接器会把收到的四路特征数据按深度反向拼接,形成Inception结构块的输出。Inception结构块结构如下:
5种经典卷积网络_第1张图片

​ 其中第一分支卷积采用了16个1*1卷积核,步长为1,全零填充,采用BN操作,relu激活函数;第二分支先用16个1*1卷积核降维,步长为1,采用BN操作,relu激活函数,再用16个3*3卷积核,步长为1,全零填充,采用BN操作,relu激活函数;第三分支先用16个1*1卷积核降维,步长为1,全零填充,采用BN操作,relu激活函数再用16个5*5卷积核,步长为1,全零填充,采用BN操作,relu激活函数;第四分支先采用最大池化,池化核尺寸是3*3,步长是1,全零填充,再用16个1*1卷积核降维,步长为1,全零填充,采用BN操作,relu激活函数。

​ 卷机器把这四个分支按照深度反向堆叠在一起,构成Inception结构块的输出。由于Inception结构块中的卷积操作均采用了CBA结构,所以将其定义为以下的一个类:

class ConvBNRelu(Model):
	def __init__(self, ch, kernelsz=3, strides=1, padding="same")
		super(ConvBNRelu, self).__init__()
		self.model = tf.keras.models.Sequential([
			Conv2D(ch, kernelsz, strides=strides,padding=padding),
			BatchNormalization(),
			Activation("relu")
		])
		
		def call(self,x):
			x = self.model(x)
			return x
  1. Inception结构块的实现

class InceptionBlk(Model):
	def __init__(self, ch, strides=1):
		super(InceptionBlk, self).__init__()
		self.ch = ch
		self.strides = strides
		# 第一分支
		self.c1 = ConvBNRelu(ch, kernelsz=1, strides=strides) 
		# 第二分支
		self.c2_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
		self.c2_2 = ConvBNRelu(ch, kernelsz=3, strides=1)
		# 第三分支
		self.c3_1 = ConvBNRelu(ch, kernelsz=1. strides=strides)
		self.c3_2 = ConvBNRelu(ch, kernelsz=5, strides=1)
		# 第四分支
		self.p4_1 = Maxpool2D(3, strides=1, padding="same")
		self.c4_2 = ConvBNRelu(ch, kernelsz=1,strides=strides)
	
	def call(self,x):
		x1 = self.c1(x)
		x2_1 = self.c2_1(x)
		x2_2 = self.c2_2(x2_1)
		x3_1 = self.c3_1(x)
		x3_2 = self.c3_2(x3_1)
		x4_1 = self.p4_1(x)
		x4_2 = self.c4_2(x4_1)
		x = tf.concat([x1,x2_2,x3_2,x4_2], axis=3)  # axis=3表示堆叠方向是沿着深度方向
		return x 
  1. Inception网络的实现

    第一层采用16个3*3卷积核,步长为1,全零填充,采用BN操作,relu激活。随后是4个Inception结构块顺序相连,每两个Inception结构块组成一个block,每个block中第一个Inception结构块的卷积步长是2,第二个Inception结构块卷积步长是1,这使得第一个Inception结构块输出特征图尺寸减半,因此,我们把输出特征图深度加深,尽可能保持特征抽取中信息的承载量一致。第一个block的起始深度是16,经过Inception结构后是64。第二个block起始是32,经过Inception结构后是128。之后将128个通道的数据送入平均池化,再送入10个分类的全连接。

class Inception10(Model):
	def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):
		super(Inception10, self).__init__()
		self.in_channels = init_ch
		self.out_channels = init_ch
		self.num_blocks = num_blocks
		self.init_ch = init_ch
		# 第一层
		self.c1 = ConvBNRelu(init_ch)
		self.blocks = tf.keras.models.Sequential()
		# 循环每个block
		for block_id in range(num_blocks):
			# 循环每个block中的Inception
			for layer_id in range(2):
				# 对于block中的第一个Inception,设置步长为2,使特征图尺寸减半
				if layer_id == 0:
					block = InceptionBlk(self.out_channels, strides=2)
				# 用第二个Inception继续训练特征数据
				else:
					block = InceptionBLK(self.out_channels, strides=1)
				self.blocks.add(block)
			# 执行完第一个block后,由于特征尺寸已减半,所以加深通道保证信息量
			self.out_channels *= 2
		# 送入平均池
		self.p1 = GlobalAveragePooling2D()
		# 全连接训练
		self.f1 = Dense(num_classes, activation="softmax")
	
	def call(self,x):
		x = self.c1(x)
		x = self.blocks(x)
		x = self.p1(x)
		y = self.f1(x)
		return y

五:ResNet

  1. 模型概述

    ResNet诞生于2015年,当年的ImageNet冠军,Top5的错误率为3.75%。

    ResNet提出了层间残差跳连,引入了前方信息,缓解梯度消失,使神经网络层数增加成为可能。我们总结以上模型的层数,如下表所示:

模型名称 网络层数
LeNet 5
AlexNet 8
VGG 16/19
InceptionNet v1 22

​ 由上表可见,随着网络层数的增加,效果是越来越好的。但ResNet作者经过实验发现,56层卷积网络的错误率要高于20层。他认为单纯堆叠神经网络层数,会使神经网络模型退化,以至于后边的特征丢失了前边特征的原本模样,于是他用了一根跳连线,将前边的特征直接接到了后边 ,使输出结果F(x)包含了堆叠卷积的非线性输出F(x)和跳过两层卷积直接连过来的恒等映射x,让它们对应元素相加,这一操作有效缓解了神经网络模型堆叠导致的退化,使得神经网路可以向着更深层次发展,如下图所示:

5种经典卷积网络_第2张图片

​ 在ResNet块中的"+“和Inception块中的”+“是不同的,Inception块中的”+“是沿深度方向叠加,ResNet块中的”+"是特征图对应元素相加 (矩阵值相加)

​ ResNet块中有两种情况,如下图所示:

5种经典卷积网络_第3张图片

​ 一种情况用图中的实线表示,这种情况,两层堆叠卷积没有改变特征图的维度,也就是它们特征图的个数,高,宽和深度都相同,可以直接将F(x)和x相加;另一种情况用图中的虚线表示,这种情况中这两层堆叠卷积改变了特征图的维度,需要借助1*1的卷积来调整x的维度,使W(x)与F(x)的维度一致。

  1. ResNet结构块的实现

​ 将ResNet的两种结构封装到一起,写出ResNet类,

import tensorflow as tf

from tensorflow.keras import Model
from tensorflow.keras.layers import Conv2D, BatchNormalization, Activation

class ResNetBlock(Model):
    def __init__(self, filters, strides=1, residual_path=False):
        super(ResNetBlock, self).__init__()
        self.filters = filters
        self.strides = strides
        self.residual_path = residual_path

        self.c1 = Conv2D(filters, (3,3), strides=strides, padding="same", use_bias=False)
        self.b1 = BatchNormalization()
        self.a1 = Activation("relu")

        self.c2 = Conv2D(filters, (3,3), strides=1, padding="same", use_bias=False)
        self.b2 = BatchNormalization()

        # residual_path为True时,对输入进行下采样,即用1*1的卷积核做卷积操作,保证x能和F(x)维度相同,顺利相加
        if residual_path:
            self.down_c1 = Conv2D(filters, (1, 1), strides=strides, padding="same",use_bias=False)
            self.down_b1 = BatchNormalization()
        self.a2 = Activation("relu")

    def call(self, inputs):
        residual = inputs
        # 将输入通过卷积,BN,激活层,计算F(x)
        x = self.c1(residual)
        x = self.b1(x)
        x = self.a1(x)

        x = self.c2(x)
        y = self.b2(x)
        # 将输入特征转换为与F(x)相同即与y相同维度的特征数据
        if self.residual_path:
            residual = self.down_c1(inputs)
            residual = self.down_b1(residual)

        # 拼接数据
        out = self.a2(y + residual)
        return out
  1. 搭建ResNet18网络

    第一层是一个卷积层,之后是8个ResNet块,然后放入平均池化,最后送入10个分类的全连接

# 搭建ResNet18网络
class ResNet18(Model):
    def __init__(self, block_list, initial_filters=64):  # block_list表示每个block有几个卷积层
        super(ResNet18, self).__init__()
        self.num_block = len(block_list)  # block的个数
        self.block_list = block_list
        self.out_filters = initial_filters
        # 第一层为卷积层
        self.c1 = Conv2D(self.out_filters, (3,3), strides=1, padding="same", use_bias=False, kernel_initializer="he_normal")
        self.b1 = BatchNormalization()
        self.a1 = Activation("relu")
        self.blocks = tf.keras.Sequential()

        # 构建ResNet网络结构
        for block_id in range(self.num_block):  # 第几个block
            for layer_id in range(block_list[block_id]):  # 第几个卷积层
                if block_id != 0 and layer_id == 0:  # 除第一个block,其余每个block的第一个ResNet进行下采样
                    block = ResNetBlock(self.out_filters, strides=2, residual_path=True)
                else:
                    block = ResNetBlock(self.out_filters, strides=1, residual_path=False)
                self.blocks.add(block)
            self.out_filters *= 2


        # 将数据加入到平均池化
        self.p1 = tf.keras.layers.GlobalAveragePooling2D()

        # 全连接
        self.f1 = tf.keras.layers.Dense(10)

    def call(self, inputs):
        x = self.c1(inputs)
        x = self.b1()
        x = self.a1()
        x = self.blocks(x)
        x = self.p1(x)
        y = self.f1(x)
        return y

ResNet18([2,2,2,2])

你可能感兴趣的:(Computer,View,深度学习,卷积神经网络,tensorflow,经典模型,python)