经典的卷积神经网络(VGG、ResNet、InceptionNet、MobileNet)

2024-03-29 10:32

本文主要是介绍经典的卷积神经网络(VGG、ResNet、InceptionNet、MobileNet),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、卷积网络发展

卷积神经网络的起源是神经认知机模型(neocongnitron),之后在1989年出现了卷积神经与网络的模型。直到2012年随着一些技术的成熟带来的机遇,卷积神经网络迎来了历史性的突破,AlexNet获得ImageNet大赛冠军引起了人们的注意,之后的卷积网络朝着四个方向发展

AlexNet:通过数据增强、Dropout来防止过拟合,所谓数据增强就是在原有的图片样本的基础上,通过对图片进行裁剪、反转、调整得到新的样本数据。Dropout是指在全连接层随机去掉某些神经元,从而产生不同的网络结构,相应的缺点是需要更多的训练时间。此外它还采用了非线性激活函数ReLU。以及在120万张大数据图像上进行训练。AlexNet在横向上通普通卷积网络一样经过多个卷积池化全连接操作,在纵向将一张图片数据分别在两个GPU上进行独立运算,并在第三个卷积层将数据交叉,这样可以极大增加数据运算的规模。

2、VGGNet

在AlexNet的基础上对卷积网络的层数进行加深就形成了多种VGG模型,最常用的是16层或19层的模型,此时已经达到了准确率提升的瓶颈,再增加层数已经无法提升正确率。如下左图所示,在VGG中使用两个3×3的叠代网络代替单个5×5网络的视野域,不仅可以学习到更多的内容,而且降低了参数。

 

下面在Cifar数据集上对VggNet进行实现

2.1、数据处理

首先定义一个类用于实现Cifar训练与测试数据集的读入,在初始化一个数据集对象时传入数据文件路径列表与是否需要重洗数据,对于训练集可以打乱数据顺序重用,而测试集则不需要。训练集数据有五个批次,依次遍历每个路径列表中的五个文件,通过load_data()函数读入五个批次中的所有数据。再将标签与数据值分别合并到_data与_labels变量中。对象可以通过next_batch()方法每次返回batch_size个数据。

import tensorflow as tf
import os
import pickle
import numpy as npCIFAR_DIR='D:\Temp\MachineLearning\data\cifar-10-batches-py'class   CifarData:def __init__(self,filelist,shuffle):all_data=[]all_labels=[]#循环读取多个批次的数据集文件for filename in filelist:data,labels=self.load_data(filename)      all_data.append(data)all_labels.append(labels)# 将所有data以纵向的方式合并到_dataself._data=np.vstack(all_data)# 数据归一化处理self._data=self._data/127.5 - 1# 将所有label以横向方式合并到_labelself._labels=np.hstack(all_labels)# 数据中样本个数self._example_num=self._data.shape[0]print(self._example_num)# 定位数据集遍历的位置指针self._indicator=0# 根据传入的shuffle决定是否对数据进行打乱self._shuffle=shuffleif self._shuffle:self.shuffle_data()def load_data(self,filename):"""读取一个批文件中的数据"""with open(filename,'rb') as f:data=pickle.load(f,encoding='bytes')return data[b'data'],data[b'labels']def shuffle_data(self):# 返回一个打乱的序号列表,例如将0~5 随机打乱为 [5,3,2,4,0,1]shuffle_list=np.random.permutation(self._example_num)# 根据随机列表重排数据和对应的标签值self._data=self._data[shuffle_list]self._labels=self._labels[shuffle_list]def next_batch(self,batch_size ):'''返回当前指针self._indicator之后batch_size个数据'''end_indicator=self._indicator+batch_size# 如果相加结果超过了样本个数if end_indicator>self._example_num:# 如果可以洗牌,打乱数据并从0开始重新取数据if self._shuffle:self.shuffle_data()self._indicator=0end_indicator=batch_sizeelse:raise Exception("没有更多数据了!")    # 返回从_indicator到end_indicator的data和labels,并将指针后移到end_indicatorbatch_data=self._data[self._indicator:end_indicator]batch_labels=self._labels[self._indicator:end_indicator]self._indicator=end_indicatorreturn batch_data,batch_labels# 拼接训练数据的路径名        
train_file=[os.path.join(CIFAR_DIR,'data_batch_%d'%i) for i in range (1,6)]
test_file=[os.path.join(CIFAR_DIR,'test_batch')]
# 创建训练数据对象
train_data=CifarData(train_file,True)

2.2、模型图的构建,

首先定义占位符x,y,并对x进行数据处理

x=tf.placeholder(tf.float32,[None,3072])
y=tf.placeholder(tf.int64,[None])
# 将一维向量x转化为具有三通道的多维图片,并交换向量通道
x_img=tf.reshape(x,[-1,3,32,32])
x_img=tf.transpose(x_img,[0,2,3,1])

接下来定义卷积网络,vgg的关键特点就是在普通卷积网络的基础上,增加了卷积层的数量,例如可以定义两个卷积层,在定义一个池化层,依次类推定义三个这样的结构

# 卷积层1_1
conv1_1=tf.layers.conv2d(x_img,   #输入32,      #输出通道数(3,3),   #卷积核大小padding='same',          #填充使图小大小不变activation=tf.nn.relu,   #激活函数name='conv1_1'             )
# VggNet两个卷积层,一个池化层
conv1_2=tf.layers.conv2d(conv1_1,32,(3,3),padding='same',activation=tf.nn.relu,name='conv1_2')
# 池化层,步长至少为2才能缩小图像,32×32输出为16×16
pool1=tf.layers.max_pooling2d(conv1_2,    #输入(2,2),    #池化核(2,2),    #步长name='pool1')
# 卷积层2_1、2_2与第二个池化层
conv2_1=tf.layers.conv2d(pool1,32,(3,3),padding='same',activation=tf.nn.relu,name='conv2_1')
conv2_2=tf.layers.conv2d(conv2_1,32,(3,3),padding='same',activation=tf.nn.relu,name='conv2_2')
pool2=tf.layers.max_pooling2d(conv2_2,(2,2),(2,2),name='pool2')   
# 卷积层3_1、3_2与第三个池化层
conv3_1=tf.layers.conv2d(pool2,32,(3,3),padding='same',activation=tf.nn.relu,name='conv3_1')
conv3_2=tf.layers.conv2d(conv3_1,32,(3,3),padding='same',activation=tf.nn.relu,name='conv3_2')
pool3=tf.layers.max_pooling2d(conv3_2,(2,2),(2,2),name='pool3')   

2.3、进行训练

定义损失函数、准确率、优化方法:

# 将输出的张量扁平化
flatten=tf.layers.flatten(pool3)
y_predict=tf.layers.dense(flatten,10)
# 使用交叉熵作为损失函数
loss=tf.losses.sparse_softmax_cross_entropy(y,y_predict)# 将标签预测向量中最大的下标作为预测值,例如[0.1,0.8...0.01],则预测为第二类
predict=tf.math.argmax(y_predict,1)
# 通过equal函数逐一比较predict,y_reshape的每一个元素
correction=tf.equal(predict,y)
accuracy=tf.reduce_mean(tf.cast(correction,tf.float64))
#定义优化方法
with tf.name_scope('train_op'):train_op=tf.train.AdamOptimizer(1e-3).minimize(loss)

接下来便可以开始训练:

with tf.Session() as sess:sess.run(init)for i in range(train_steps):batch_data,batch_labels=train_data.next_batch(batch_size)loss_val,acc_val,_=sess.run([loss,accuracy,train_op],feed_dict={x:batch_data,y:batch_labels})if (i+1) % 500==0:print("第%d步:损失:%.5f,精确度:%.5f"%(i,loss_val,acc_val))#每训练1000次,在test数据集上进行一次测试if (i+1)%1000==0:# 定义测试集数据对象test_data=CifarData(test_file,False)   all_acc=[]for j in range(test_steps):test_batch_data,test_batch_labels=test_data.next_batch(batch_size)test_acc=sess.run([accuracy],feed_dict={x:test_batch_data,y:test_batch_labels})all_acc.append(test_acc)# 将测得的多个准确率求均值test_acc_mean=np.mean(all_acc)print("第%d步测试集准确率%.4f"%(i,test_acc_mean))

3、ResNet

ResNet结合了Vgg与GoogleNet的特点。随着其卷积层深度增加,出现了训练误差反而增加的问题,这是由于梯度消失导致的后面的网络层无法对前面的层进行调整。为了解决这个问题,ResNet提出了shortcut捷径连接,将输入跳过中间多层与卷积核结果相加,这一层的神经网络可以不用学习整个上一层的输出,而是学习上一个网络输出的残差,因此ResNet又叫做残差网络。

如下左图所示,本层输出H(x)=F(x)+x,F(x)为残差函数,通过F去学习模型与目标的差距,如果模型以及很好地拟合目标,则F(x)=0,即H(x)=0+x,本层实现恒等输出。如果模型和目标存在差距,则通过F(x)学习再加上输入x,与直接用H(x)拟合更为方便。

在上面VggNet的基础上修改网络模型,实现ResNet对Cifar数据集的训练,残差网络最重要的是残差连接块的定义,通过函数res_block()定义残差块。输出结果由两部分相加组成,一部分进行卷积操作,另一部分恒等变换,卷积操作可能降采样,会使输出的通道数会翻倍,导致两部分的矩阵维度不一样,无法进行相加。所以这个时候需要对恒等变化也要做一次降采样。

如果输出通道数是输入的两倍,说明卷积的步长为2,在卷积的过程中进行了降采样,并将increase_dim设为True,在之后对x也进行降采样。否则卷积步长为1,卷积过程不会降采样,x不需要处理,直接将x和卷积结果相加得到输出。

def res_block(x,out_chanel):'''实现残差连接块'''in_chanel=x.shape[-1]# 如果输出通道是输入的两倍,说明需要进行降采样操作if in_chanel*2==out_chanel:increase_dim=Truestrides=(2,2)           # 将步长设为2,卷积操作会进行降采样elif in_chanel==out_chanel:increase_dim=Falsestrides=(1,1)           # 步长为1,不进行降采样else:raise Exception('输入输出通道数目无法匹配')# 对输入进行卷积操作conv1=tf.layers.conv2d(x,out_chanel,(3,3),strides,padding='same',activation=tf.nn.relu,name='conv1')conv2=tf.layers.conv2d(conv1,out_chanel,(3,3),(1,1),padding='same',activation=tf.nn.relu,name='conv2')# 如果需要降采样,则对恒等变换部分进行降采样,并补充通道数if increase_dim:pool_x=tf.layers.average_pooling2d(x,(2,2),(2,2))# pool_x为[None,img_width,img_height,chanel],通过tf.pad()对第四维的左右各补充半个in_chanel,使其翻倍pad_x=tf.pad(pool_x,[[0,0],[0,0],[0,0],[in_chanel//2,in_chanel//2]])else:pad_x=x         # 不需要降采样,残差直接为xout_x=conv2+pad_x   # 将卷积结果和残差相加得到输出return out_x

有了残差连接块后便可以实现残差网络,例如上面右边图ResNet中的34-layer所示,首先将输入进行一次卷积操作,然后经过四个残差连接层,每个残差连接层中的块数不同,通过两个循环嵌套调用res_block()实现残差连接块,最后经过一个全连接层将结果输出。

def res_net(x,block_num_list,filter_base,class_num):'''构建残差网络-x,输入-block_num_list,残差连接块数列表,例如[3,4,6,3]-filter_base,初始通道数-class_num,Cifar数据集的类别数,即最后网络输出的通道数'''layer_num=len(block_num_list)layers=[] # 截取x的后三个维度信息,[None,width,height,chanel]->[width,height,chanel]input_size=x.shape[1:]with tf.variable_scope('conv0'):# 首先进行一次卷积操作,并将结果放到layers列表conv0=tf.layers.conv2d(x,filter_base,(3,3),(1,1),padding='same',activation=tf.nn.relu,name='conv0')layers.append(conv0)# 根据残差连接块数列表,循环实现多个残差连接层for layer_id in range(layer_num):for i in range(block_num_list[layer_id]):with tf.variable_scope('conv%d_%d'%(layer_id,i)):# 输出通道数是在初始通道的基础上,每经过一层翻一倍,乘以2的layer_id次方out_chanel=filter_base*(2**layer_id)conv=res_block(layers[-1],out_chanel)layers.append(conv)# 通过断言检查输出维度是否符合预期,如不符合,报错停止multiplier=2 ** (layer_num - 1)    assert layers[-1].get_shape().as_list()[1:] \== [input_size[0] // multiplier,input_size[1] // multiplier,filter_base * multiplier]# 最后经过一个全连接层将结果输出with tf.variable_scope('fc'):# layers[-1]:[None,width,height,chanel],在width,height上求平均global_pool=tf.reduce_mean(layers[-1],[1,2])logits=tf.layers.dense(global_pool,class_num)layers.append(logits)return layers[-1]

4、InceptionNet

GoogleNet在每个卷积层中设置了不同大小的卷积核来增加卷积层功能,例如有5×5、3×3等多种卷积核。卷积核种类多了之后造成特征值的厚度非常大,为了解决这个问题,提出了Inception版本,在卷积核之前通过一个1×1的卷积核进行降维。如下图所示,InceptionNet最大的特点就是在同一卷积层使用不同的卷积核处理数据。

例如下面实现inception块,每个inception块由1×1、3×3、5×5的卷积核与max_pooling构成,将输入x经过不同的卷积操作后将结果拼接起来得到输出

def inception_block(x,out_chanel_list,name):'''实现 inception网络块:param x: 输入:param out_chanel_list: 不同卷积核输出通道列表:param name: 命名域名称:return: 多个卷积层的拼接结果'''with tf.variable_scope(name):# 定义1×1、3×3、5×5卷积操作与max_poolingconv1_1=tf.layers.conv2d(x,out_chanel_list[0],(1,1),(1,1),padding='same',activation=tf.nn.relu,name='conv1_1')conv3_3=tf.layers.conv2d(x,out_chanel_list[1],(3,3),(1,1),padding='same',activation=tf.nn.relu,name='conv3_3')conv5_5=tf.layers.conv2d(x,out_chanel_list[2],(5,5),(1,1),padding='same',activation=tf.nn.relu,name='conv5_5')max_pooling=tf.layers.max_pooling2d(x,(2,2),(2,2),name='max_pooling')# 对max_pooling的结果进行填充,将mx与input相差的部分填充到mx的两侧mx_shape=max_pooling.shape[1:]input_shape=x.shape[1:]width_padding=(input_shape[0]-mx_shape[0])//2height_padding=(input_shape[1]-mx_shape[1])//2# max_pooling->[None,width,height,chanel],对中间的宽和高进行填充mx_padded=tf.pad(max_pooling,[[0,0],[width_padding,width_padding],[height_padding,height_padding],[0,0]])# 将四个输出结果在第三个维度上进行拼接concat_layer=tf.concat([conv1_1,conv3_3,conv5_5,mx_padded],axis=3)   return concat_layer

接下来利用inception块构建inception网络,将输入x_img首先经过一个卷积、池化层,接着将池化结果传入联系的两个inception块,之后再经过一个池化层。类似地,再经过第三层两个inception块和池化。将结果展开,经过全连接层,得到y的预测值。

# 先经过一个卷积层和池化层
conv1=tf.layers.conv2d(x_img,32,(3,3),padding='same',activation=tf.nn.relu,name='conv1')
pooling1=tf.layers.max_pooling2d(conv1,(2,2),(2,2),name='pooling1')   
# 第二层经过两个inception块和池化层
inception_2a=inception_block(pooling1,[16,16,16],'inception_2a')
inception_2b=inception_block(inception_2a,[16,16,16],'inception_2b')
pooling2=tf.layers.max_pooling2d(inception_2b,(2,2),(2,2),name='pooling2')
# 第三层
inception_3a=inception_block(pooling2,[16,16,16],'inception_3a')
inception_3b=inception_block(inception_3a,[16,16,16],'inception_3b')
pooling3=tf.layers.max_pooling2d(inception_3b,(2,2),(2,2),name='pooling3')
# 将结果展开,进行全连接层
flatten=tf.layers.flatten(pooling3)
y_predict=tf.layers.dense(flatten,10)

5、MobileNet

MobileNet引入了深度可分离卷积操作,如下图所示,其思想是每个卷积核不关注所有通道,而是只处理几个通道的输出。通过这样的优化减少计算量

如下实现可分离卷积块的操作,将输入的x张量在第四维度上拆分成单个向量并分别进行卷积操作,将卷积后的结果再在第四维上拼接称为一个张量,卷积后返回结果

def separate_block(x,out_chanel,name):'''可分离卷积块:param x: 输入:param out_chanel: 输出通道数:param name: 命名域名称:return: 经过可分离卷积的结果'''with tf.variable_scope(name):input_chanel=x.shape[-1]# 输入的x->[None,width,height,chanel],在第四维(axis=3)上将x拆分为单个向量x_split=tf.split(x,input_chanel,axis=3)output_list=[]for i in range(input_chanel):# 分别对拆分后的每个向量进行卷积操作,并将结果添加到output_listoutput=tf.layers.conv2d(x_split[i],1,(3,3),(1,1),padding='same',activation=tf.nn.relu,name='con_%d'%i)output_list.append(output)# 将单个向量在第四维上拼接起来concat_layer=tf.concat(output_list,axis=3)# 将拼接后的结果再进行一次卷积后输出conv2=tf.layers.conv2d(concat_layer,out_chanel,(1,1),(1,1),padding='same',activation=tf.nn.relu,name='conv2')       return conv2

利用可分离卷积块实现MobileNet网络,其过程与InceptionNet类似,首先经过一次卷积和池化,之后每经过两个可分离卷积块separate_block()做一次池化操作,最后经全连接层输出。

# 先经过一个卷积层和池化层
conv1=tf.layers.conv2d(x_img,32,(3,3),padding='same',activation=tf.nn.relu,name='conv1')
pooling1=tf.layers.max_pooling2d(conv1,(2,2),(2,2),name='pooling1')   
# 第二层经过两个separate_block和池化层
separate_2a=separate_block(pooling1,32,'separate_2a')
separate_2b=separate_block(separate_2a,32,'separate_2b')
pooling2=tf.layers.max_pooling2d(separate_2b,(2,2),(2,2),name='pooling2')
# 第三层
separate_3a=separate_block(pooling2,32,'separate_3a')
separate_3b=separate_block(separate_3a,32,'separate_3b')
pooling3=tf.layers.max_pooling2d(separate_3b,(2,2),(2,2),name='pooling3')# 将结果展开,进行全连接层
flatten=tf.layers.flatten(pooling3)
y_predict=tf.layers.dense(flatten,10)

 

 

这篇关于经典的卷积神经网络(VGG、ResNet、InceptionNet、MobileNet)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858362

相关文章

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

HotSpot虚拟机的经典垃圾收集器

读《深入理解Java虚拟机》第三版笔记。 关系 Serial、ParNew、Parallel Scavenge、Parallel Old、Serial Old(MSC)、Concurrent Mark Sweep (CMS)、Garbage First(G1)收集器。 如图: 1、Serial 和 Serial Old 收集器 2、ParNew 收集器 3、Parallel Sc

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

机器学习之监督学习(三)神经网络

机器学习之监督学习(三)神经网络基础 0. 文章传送1. 深度学习 Deep Learning深度学习的关键特点深度学习VS传统机器学习 2. 生物神经网络 Biological Neural Network3. 神经网络模型基本结构模块一:TensorFlow搭建神经网络 4. 反向传播梯度下降 Back Propagation Gradient Descent模块二:激活函数 activ

图神经网络框架DGL实现Graph Attention Network (GAT)笔记

参考列表: [1]深入理解图注意力机制 [2]DGL官方学习教程一 ——基础操作&消息传递 [3]Cora数据集介绍+python读取 一、DGL实现GAT分类机器学习论文 程序摘自[1],该程序实现了利用图神经网络框架——DGL,实现图注意网络(GAT)。应用demo为对机器学习论文数据集——Cora,对论文所属类别进行分类。(下图摘自[3]) 1. 程序 Ubuntu:18.04

基于深度学习 卷积神经网络resnext50的中医舌苔分类系统

项目概述 本项目旨在通过深度学习技术,特别是利用卷积神经网络(Convolutional Neural Networks, CNNs)中的ResNeXt50架构,实现对中医舌象图像的自动分类。该系统不仅能够识别不同的舌苔类型,还能够在PyQt5框架下提供一个直观的图形用户界面(GUI),使得医生或患者能够方便地上传舌象照片并获取分析结果。 技术栈 深度学习框架:采用PyTorch或其他

图神经网络(2)预备知识

1. 图的基本概念         对于接触过数据结构和算法的读者来说,图并不是一个陌生的概念。一个图由一些顶点也称为节点和连接这些顶点的边组成。给定一个图G=(V,E),  其 中V={V1,V2,…,Vn}  是一个具有 n 个顶点的集合。 1.1邻接矩阵         我们用邻接矩阵A∈Rn×n表示顶点之间的连接关系。 如果顶点 vi和vj之间有连接,就表示(vi,vj)  组成了

嵌入式面试经典30问:二

1. 嵌入式系统中,如何选择合适的微控制器或微处理器? 在嵌入式系统中选择合适的微控制器(MCU)或微处理器(MPU)时,需要考虑多个因素以确保所选组件能够满足项目的具体需求。以下是一些关键步骤和考虑因素: 1.1 确定项目需求 性能要求:根据项目的复杂度、处理速度和数据吞吐量等要求,确定所需的处理器性能。功耗:评估系统的功耗需求,选择低功耗的MCU或MPU以延长电池寿命或减少能源消耗。成本

Leetcode面试经典150题-128.最长连续序列-递归版本另解

之前写过一篇这个题的,但是可能代码比较复杂,这回来个简洁版的,这个是递归版本 可以看看之前的版本,两个版本面试用哪个都保过 解法都在代码里,不懂就留言或者私信 class Solution {/**对于之前的解法,我现在提供一共更优的解,但是这种可能会比较难懂一些(思想方面)代码其实是很简洁的,总体思想如下:不需要排序直接把所有数放入map,map的key是当前数字,value是当前数开始的

力扣 739. 每日温度【经典单调栈题目】

1. 题目 理解题意: 1.1. 给一个温度集合, 要返回一个对应长度的结果集合, 这个结果集合里面的元素 i 是 当前 i 位置的元素的下一个更高温度的元素的位置和当前 i 位置的距离之差, 若是当前元素不存在下一个更高温度的元素, 则这个位置用0代替; 2. 思路 本题用单调栈来求解;单调栈就适用于来求当前元素左边或者右边第一个比当前元素大或者小的元素;【单调栈:让栈中的元素保持单调