1、开山之作:LeNet

对经典LeNet-5做深入分析:

1)输入图像是单通道的28*28大小的图像,矩阵表示[28,28,1]

2)conv1所用卷积核尺寸5*5,滑动步长1,卷积核数目20,该层后图像尺寸变为28-5+1=24,输出矩阵[24,24,20]

3)pool1核尺寸为2*2,步长2,输出矩阵[12,12,20]

4)conv2所用卷积核尺寸5*5,滑动步长1,卷积核数目50,该层后图像尺寸变为12-5+1=8,输出矩阵[8,8,50]

5)pool2核尺寸为2*2,步长2,输出矩阵[4,4,50]

6)pool2后面接全连接层fc1,神经元数目为500,再接relu激活函数

7)再接fc2,神经元个数为10,得到10维的特征向量,送进softmax分类


def cov2(inputs,kernel_size,strides,padding,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size,name='w'))b=tf.Variable(tf.random_normal(shape=[kernel_size[-1]],name='b'))conv1=tf.nn.conv2d(inputs,w,strides=[1,1,1,1],padding=padding)conv1=tf.nn.bias_add(conv1,b)conv1=tf.nn.relu(conv1)return conv1def fully_connect(inputs,kernel_size,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size),name='w')b=tf.Variable(tf.random_uniform(shape=[kernel_size[-1]],name='b'))fc=tf.nn.bias_add(tf.matmul(inputs,w),b)return fcdef LeNet():X=tf.placeholder(tf.float32,shape=[None,28,28,1])conv1=cov2(X,[5,5,1,20],[1,1,1,1],'VALID','conv1')conv1=tf.nn.max_pool(conv1,ksize=[1,2,2,1],strides=[1,2,2,1],padding='VALID')conv2=cov2(conv1,[5,5,20,50],[1,1,1,1],'VALID','conv2')conv2=tf.nn.max_pool(conv2,ksize=[1,2,2,1],strides=[1,2,2,1],padding='VALID')fc1=tf.reshape(conv2,[-1,4*4*50])fc1=fully_connect(fc1,[fc1.get_shape().as_list()[-1],500],'fc1')fc2=fully_connect(fc1,[500,10],'fc2')with tf.Session() as sess:a=np.ones([28,28,1],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(fc2,feed_dict={X:[a]})print(c)

2、王者归来:AlexNet

网络前面5层是卷积层,后面3层是全连接层,最后softmax输出是1000类

1)AlexNet包含5层卷积和3层全连接层,层数比LeNet多了不少,但卷积神经网络总的流程并没有变化只是深度上加了不少

2)AlexNet针对是1000类分类问题,输入图片256*256的三通道彩色图片,为了增强模型的泛化能力,使用随机裁剪思路对原来256*256的图像进行随机裁剪,得到尺寸3*244*244的图像

3)使用多GPU训练,可以看到第一层卷积层后有两个完全一样的分支,加速训练

4)针对一个分支分析:第一层卷积层conv1的卷积核尺寸为11×11,滑动步长为4,卷积核数目为48。卷积后得到的输出矩阵为[48,55,55]。这里的55是个难以理解的数字,作者也没有对此说明,如果按照正常计算的话(224-11)/4+1 != 55的,所以这里是做了padding再做卷积的,即先padding图像至227×227,再做卷积(227-11)/4+1 = 55。这些像素层经过relu1单元的处理,生成激活像素层,尺寸仍为2组48×55×55的像素层数据。然后经过归一化处理,归一化运算的尺度为5*5。第一卷积层运算结束后形成的像素层的规模为48×27×27。

5)输入矩阵是[48,55,55].接着是池化层,做max pooling操作,池化运算的尺度为3*3,运算的步长为2,则池化后图像的尺寸为(55-3)/2+1=27。所以得到的输出矩阵是[48,27,27]。

def cov2(inputs,kernel_size,strides,padding,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size,name='w'))b=tf.Variable(tf.random_normal(shape=[kernel_size[-1]],name='b'))conv1=tf.nn.conv2d(inputs,w,strides=strides,padding=padding)conv1=tf.nn.bias_add(conv1,b)conv1=tf.nn.relu(conv1)return conv1
def fully_connect(inputs,kernel_size,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size),name='w')b=tf.Variable(tf.random_uniform(shape=[kernel_size[-1]],name='b'))fc=tf.nn.bias_add(tf.matmul(inputs,w),b)return fcdef AlexNet():X=tf.placeholder(tf.float32,shape=[None,227,227,3])conv1=cov2(X,[11,11,3,96],[1,4,4,1],padding='VALID',scope='conv1')        #[none,55,55,96]conv2=cov2(conv1,[5,5,96,256],[1,1,1,1],padding='SAME',scope='conv2')  #[none,55,55,256]conv2=tf.nn.max_pool(conv2,[1,3,3,1],[1,2,2,1],padding='VALID')               #[None,27,27,256]conv3=cov2(conv2,[3,3,256,384],[1,1,1,1],padding='SAME',scope='conv3') #[None,27,27,384]conv4=cov2(conv3,[3,3,384,384],[1,1,1,1],padding='SAME',scope='conv4') #[None,27,27,384]conv5=cov2(conv4,[3,3,384,256],[1,1,1,1],padding='SAME',scope='conv5') #[None,27,27,256]conv6=tf.nn.max_pool(conv5,[1,3,3,1],[1,2,2,1],padding='VALID')                #[None,13,13,256]fc1=tf.reshape(conv6,[-1,13*13*256])fc1=fully_connect(fc1,[fc1.get_shape().as_list()[1],4096],scope='fc1')fc2=fully_connect(fc1,[4096,1000],scope='fc2')with tf.Session() as sess:a=np.ones([227,227,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(fc2,feed_dict={X:[a]})print(c)

3、稳步前行:ZF-Net

ZF-Net只是将AlexNet第一层卷积核由11变成7,步长由4变为2,第3、4、5卷积层转变为384,384,256.

def cov2(inputs,kernel_size,strides,padding,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size,name='w'))b=tf.Variable(tf.random_normal(shape=[kernel_size[-1]],name='b'))conv1=tf.nn.conv2d(inputs,w,strides=strides,padding=padding)conv1=tf.nn.bias_add(conv1,b)conv1=tf.nn.relu(conv1)return conv1def fully_connect(inputs,kernel_size,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size),name='w')b=tf.Variable(tf.random_uniform(shape=[kernel_size[-1]],name='b'))fc=tf.nn.bias_add(tf.matmul(inputs,w),b)return fcdef ZFNet():X=tf.placeholder(tf.float32,shape=[None,224,224,3])conv1=cov2(X,[7,7,3,96],[1,2,2,1],padding='SAME',scope='conv1')       #[none,110,110,96]conv1=tf.nn.max_pool(conv1,[1,3,3,1],[1,2,2,1],padding='SAME')        #[None,55,55,96]conv2=cov2(conv1,[5,5,96,256],[1,2,2,1],padding='SAME',scope='conv2')  #[none,26,26,256]conv2=tf.nn.max_pool(conv2,[1,3,3,1],[1,2,2,1],padding='SAME')        #[None,13,13,256]conv3=cov2(conv2,[3,3,256,384],[1,1,1,1],padding='SAME',scope='conv3') #[None,13,13,384]conv4=cov2(conv3,[3,3,384,384],[1,1,1,1],padding='SAME',scope='conv4') #[None,13,13,384]conv5=cov2(conv4,[3,3,384,256],[1,1,1,1],padding='SAME',scope='conv5') #[None,13,13,256]conv6=tf.nn.max_pool(conv5,[1,3,3,1],[1,2,2,1],padding='VALID')        #[None,6,6,256]fc1=tf.reshape(conv6,[-1,6*6*256])fc6=fully_connect(fc1,[fc1.get_shape().as_list()[1],4096],scope='fc6')fc7=fully_connect(fc6,[4096,1000],scope='fc7')with tf.Session() as sess:a=np.ones([224,224,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(fc7,feed_dict={X:[a]})print(c) 

4、越走越深:VGG-Nets

VGG16网络是最优的,16指conv+fc的总层数16,不包括max pool的层数。主要贡献是使用小尺寸的filter(1*1和3*3的小卷积核),及有规则的卷积-池化操作。3*3卷积核的优点:多个3*3的卷积层比一个大尺寸filter卷积层有更多的非线性,使得判决函数更加具有判决性。多个3*3的卷积层比一个大尺寸的filter有更少的参数,假设卷积层的输入和输出的特征图大小相同为c,那么三个3*3的卷积层参数个数3*(3*3*c*c)=27cc;一个7*7的卷积层参数49cc,所以可以把3个3*3的filter看成一个7*7filter的分解。1*1卷积核的优点:在不影响输入输出维数的情况下,

def VggNet():X=tf.placeholder(tf.float32,shape=[None,224,224,3])conv1=cov2(X,[3,3,3,64],[1,1,1,1],padding='SAME',scope='conv1_1')conv1=cov2(conv1,[3,3,64,64],[1,1,1,1],padding='SAME',scope='conv1_2')conv1=tf.nn.max_pool(conv1,[1,2,2,1],[1,2,2,1],padding='VALID')  #[none,112,112,64]conv2=cov2(conv1,[3,3,64,128],[1,1,1,1],padding='SAME',scope='conv2_1')conv2=cov2(conv2,[3,3,128,128],[1,1,1,1],padding='SAME',scope='conv2_2')conv2=tf.nn.max_pool(conv2,[1,2,2,1],[1,2,2,1],padding='VALID')  #[none,56,56,128]conv3=cov2(conv2,[3,3,128,256],[1,1,1,1],padding='SAME',scope='conv3_1') conv3=cov2(conv3,[3,3,256,256],[1,1,1,1],padding='SAME',scope='conv3_2')conv3=tf.nn.max_pool(conv3,[1,2,2,1],[1,2,2,1],padding='VALID')   #[none,28,28,256]conv4=cov2(conv3,[3,3,256,512],[1,1,1,1],padding='SAME',scope='conv4_1') conv4=cov2(conv4,[3,3,512,512],[1,1,1,1],padding='SAME',scope='conv4_2')conv4=tf.nn.max_pool(conv4,[1,2,2,1],[1,2,2,1],padding='VALID')   #[none,14,14,256]    conv5=cov2(conv4,[3,3,512,512],[1,1,1,1],padding='SAME',scope='conv5_1') conv5=cov2(conv5,[3,3,512,512],[1,1,1,1],padding='SAME',scope='conv5_2')conv5=tf.nn.max_pool(conv5,[1,2,2,1],[1,2,2,1],padding='VALID')   #[none,7,7,256]  fc1=tf.reshape(conv5,[-1,7*7*256])fc1=fully_connect(fc1,[fc1.get_shape().as_list()[1],4096],scope='fc1')fc2=fully_connect(fc1,[4096,4096],scope='fc2')fc3=fully_connect(fc2,[4096,1000],scope='fc3')with tf.Session() as sess:a=np.ones([224,224,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(fc3,feed_dict={X:[a]})print(c)

5、大浪推手:GoogleNet

(1)Inception-v1

GoogLeNet论文指出获得高质量模型最保险的做法就是增加模型的深度(层数)或者宽度(层核或者神经元数),但是一般情况下更深或更宽的网络会出现以下问题:参数太多,容易过拟合,若训练数据集有限,问题更加突出;网络越大计算复杂度越大,难以应用;网络越深,梯度越往后穿越容易消失,难以优化模型

对Inception V1的结构做说明:

1)采用不同大小的卷积核意味着不同大小的感受野,最后拼接意味着不同尺度特征的融合。

2)卷积核大小采用1、3和5,主要是为了方便对齐。设定卷积步长stride=1后,只要分别设定pad=0,1,2,那么卷积后可以得到相同维度的特征,然后这些特征就可以直接拼接在一起。

3)网络越到后面,特征越抽象,而且每个特征所涉及的感受野变大,因此随着层数的增加,3*3和5*5卷积的比例也要增加。

4)使用1*1卷积主要用来降维,用了Inception之后整个网络结构的宽度和深度都可扩大,能够带来2~3倍的性能提升。

GoogleNet网络整体结构:https://blog.csdn.net/dcrmg/article/details/79246654

1)采用模块化的结构,方便增添和修改

2)网络最后采用average pooling来代替全连接层,但是,实际在最后还是加了一个全连接层,主要是方便finetune

辅助分类器:

Inception Net共有22层,除了最后一层的输出结果,中间节点的分类效果也有可能是很好的,GoogleNet将中间某一层的输出作为分类,并以一个较小的权重(0.3)加到最终的分类结果中。一共有2个这样的辅助分类节点。辅助分类器相当于对模型做了融合,同时给网络增加反向传播的梯度信号,在一定程度上提供正则化的作用。

辅助分类器的具体细节:

1)均值pooling层滤波器大小为5*5,步长为3,(4a)的输出为4*4*512,(4d)的输出为4*4*528

2)1*1卷积有用于降维128个滤波器和修正线性激活

3)全连接层有1024个单元和修正线性激活

4)dropout层的dropped的输出比率为70%

5)线性层将softmax损失作为分类器(和主分类器一样预测1000个类,在inference时移除)

(2)Inception-v2

大尺度的卷积可获更大的感受野。比如通道数相同的5*5卷积核参数量是3*3卷积核的25/9=2.78倍,因此作者提出使用两个3*3卷积代替5*5卷积,在保证感受野相同的前提下减少参数量,增加了一层非线性映射,是特征信息更加具有判别性。这种替代方法不会造成表达缺失,降低网络性能。任意n*n卷积均可由1*n和n*1卷积替代。作者发现这种替代方案在网路前几层的表现并不好,而在特征图尺寸在12~20之间由很好的效果。

对上图进行说明:

Figure 4表示Inception v1的结构

Figure 5 表示将5*5卷积替代为两个3*3卷积的结构,Inception-A

Figure 6 表示将n*n卷积替代为1*n卷积和n*1卷积,Inception-B

Figure 7主要应用在高维特征上,文中为8*8的feature map,Inception-C

最终的网络结构如下:

(3)Inception-v3

Inception v3整体上采用Inception v2的网络结构,并在优化算法、正则化等方面做了改进,总结如下:

1)优化算法使用RMSProp替代SGD

2)使用Label Smoothing Regularization方法。LSR是一种通过在输出y中加噪声,实现对模型进行约束,降低模型过拟合的方法。进行模型训练时,通常真实标签q(k|x)采用ont-hot的形式,而模型的输出一般为softmax归一后的概率分布p(k|x)

损失函数为: 

训练的目的使p(k|x)的分布尽可能接近q(k|x),但是这种方法很容易产生过拟合。举个例子,假设分类器需要区分“我们都喜欢玩耍”和“我们都喜欢学习”两句话。假设“玩耍”出现80次,“学习”出现20次,因为标签采用one-hot的形式,随着次数的增加,模型逐渐倾向于“玩耍”这句话的搭配,使这个搭配的预测概率逐渐趋向于100%,而“学习”这个搭配会逐渐被忽略。

为解决这个问题,作者以权重加入某一概率分布到原始标签中构成新的标签,形式如下:

论文中作者使用均匀分布: ,防止把模型预测值过分集中在给率较大的类别上,会对小概率类别增加更多的关注。此时,损失函数变为:

从损失函数可以看出,LSR相当于使用两个loss。当u服从均匀分布时,H(u,p)为常数,能够衡量预测分布p和均匀分布的不相似度程度,起到正则化的作用。

3)将第一个7*7卷积层分解为两个3*3卷积层。

4)辅助分类器的全连接层进行batch-normaliztion操作。

(4)Inception-v4

Inception-v4中的Inception模块分成3组,基本上与Inception-v3网络是一致的,但有细微的变化:

(5)Inception-ResNet

是在Inception模块中引入ResNet的残差结构,共有两个版本,其中Inception-ResNet-v1对标Inception-v3,两者计算复杂度类似,而Inception-ResNet-v2对标Inception-v4,两者计算复杂度类似。右图两个分别是Inception-ResNet-v1和Inception-ResNet-v2网络的stem模块结构,也即是Inception-v3和Inception-v4网络的stem模块。

对于Inception-v4中:5*Inception-resnet-A换成4*Inception-A

10*Inception-resnet-B换成7*Inception-B

5*Inception-resnet-C换成3*Inception-C

Inception-ResNet网络结构与stem模块

Inception-ResNet-v1的Inception模块如图16所示,与原始Inception模块对比,增加shortcut结构,而且在add之前使用了线性的1x1卷积对齐维度。对于Inception-ResNet-v2模型,与v1比较类似,只是参数设置不同。

Inception-ResNet-v1的Inception模块

GoogLeNet网络结构中有3个Loss单元,在中间层加入辅助计算的loss单元,目的是计算损失时让低层特征也有很好的区分能力,从而让网络更好地被训练。两个辅助loss单元的计算被乘以0.3,和最后loss相加为最终的损失函数来训练网络。将后面的全连接层全部替换为简单的全局平均pooling,在最后参数会变的更少。(根据图进行分析)

def cov2(inputs,kernel_size,strides,padding,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size,name='w'))b=tf.Variable(tf.random_normal(shape=[kernel_size[-1]],name='b'))conv=tf.nn.conv2d(inputs,w,strides=strides,padding=padding)conv=tf.nn.bias_add(conv,b)conv=tf.nn.relu(conv)return convdef fully_connect(inputs,kernel_size,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size),name='w')b=tf.Variable(tf.random_uniform(shape=[kernel_size[-1]],name='b'))fc=tf.nn.bias_add(tf.matmul(inputs,w),b)return fcdef batch_normal(inputs,beta,gamma,phase_train,scope='bn',decay=0.99,eps=1e-3):with tf.variable_scope(scope):batch_mean,batch_var=tf.nn.moments(inputs,[0,1,2])ema=tf.train.ExponentialMovingAverage(decay=0.99)def mean_var_with_update():ema_apply_op=ema.apply([batch_mean,batch_var])with tf.control_dependencies([ema_apply_op]):return tf.identity(batch_mean),tf.identity(batch_var)mean,var=tf.cond(phase_train,mean_var_with_update,lambda:(ema.average(batch_mean),ema.average(batch_var)))normed=tf.nn.batch_normalization(inputs,mean,var,beta,gamma,eps)return normeddef Conv2d_BN(x,kernel_size,strides,padding,phase_train,scope):x=cov2(x,kernel_size,strides,padding,scope)x=batch_normal(x,tf.random_normal([kernel_size[-1]]),tf.random_normal([kernel_size[-1]]),phase_train,scope=scope+'_bn')return xdef Inception(inputs,inchan,outchan,phase_train,scope):branch1x1=Conv2d_BN(inputs,[1,1,inchan,outchan],[1,1,1,1],'SAME',phase_train,'branch1X1') branch3x3=Conv2d_BN(inputs,[1,1,inchan,outchan],[1,1,1,1],'SAME',phase_train,'branch3x3_1')branch3x3=Conv2d_BN(branch3x3,[3,3,outchan,outchan],[1,1,1,1],'SAME',phase_train,'branch3x3_2')branch5x5=Conv2d_BN(inputs,[1,1,inchan,outchan],[1,1,1,1],'SAME',phase_train,'branch5x5_1')branch5x5=Conv2d_BN(branch5x5,[5,5,outchan,outchan],[1,1,1,1],'SAME',phase_train,'branch5x5_2')branchpool=tf.nn.max_pool(inputs,[1,3,3,1],[1,1,1,1],'SAME')branchpool=Conv2d_BN(branchpool,[1,1,inchan,outchan],[1,1,1,1],'SAME',phase_train,'branchpool')x=tf.concat([branch1x1,branch3x3,branch5x5,branchpool],axis=3)return xdef GoogleNet():inputs=tf.placeholder(tf.float32,shape=[None,224,224,3])phase_train=tf.placeholder(tf.bool)x=Conv2d_BN(inputs,[7,7,3,64],[1,2,2,1],'SAME',phase_train,'x')x=tf.nn.max_pool(x,[1,3,3,1],[1,2,2,1],'SAME')x=Conv2d_BN(x,[3,3,64,192],[1,1,1,1],'SAME',phase_train,'x1')x=tf.nn.max_pool(x,[1,3,3,1],[1,2,2,1],'SAME')x=Inception(x,192,64,phase_train,'Inception3a')  #256x=Inception(x,256,120,phase_train,'Inception3b')  #480x=tf.nn.max_pool(x,[1,3,3,1],[1,2,2,1],'SAME') x=Inception(x,480,128,phase_train,'Inception4a') #512x=Inception(x,512,128,phase_train,'Inception4b') #512x=Inception(x,512,128,phase_train,'Inception4c') #512x=Inception(x,512,132,phase_train,'Inception4d') #528x=Inception(x,528,208,phase_train,'Inception4e') #832x=tf.nn.max_pool(x,[1,3,3,1],[1,2,2,1],'SAME')x=Inception(x,832,208,phase_train,'Inception5a') #832x=Inception(x,832,256,phase_train,'Inception5b') #1024x=cov2(x,[7,7,1024,1024],[1,7,7,1],'SAME','gap') #全局池化层[none,1,1,1024] x=fully_connect(tf.reshape(x,[-1,1024]),[1024,1000],scope='fc')  #[none,1000]with tf.Session() as sess:a=np.ones([224,224,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(x,feed_dict={inputs:[a],phase_train:True})print(np.shape(c)) 

6、 里程碑式创新: ResNet

闪光点:层数非常深,已经超百层、引入残差单元解决退化问题

zero_padding:对恒等层进行0填充方式将维度填充完整

projection:在恒等层采用1*1的卷积核来增加维度,会增加额外的参数

实线:执行3*3的卷积,channel个数一致:y=F(x)+x

虚线:channel个数不同,采用计算公式:y=F(x)+wx

下图展示两种形态的残差模块,左图是常规残差模块,有两个3*3卷积核组成,随着网络进一步加深,这种残差结构在实践中并不是十分有效。右图的“瓶颈残差模块”(bottleneck residual block)有更好的效果,依次1*1、3*3、1*1三个卷积层堆积而成,1*1的卷积能够降维或升维的作用,3*3的卷积可以在相对较低维度的输入上进行,达到提高计算效率的目的。

def cov2(inputs,kernel_size,strides,padding,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size,name='w'))b=tf.Variable(tf.random_normal(shape=[kernel_size[-1]],name='b'))conv=tf.nn.conv2d(inputs,w,strides=strides,padding=padding)conv=tf.nn.bias_add(conv,b)conv=tf.nn.relu(conv)return convdef fully_connect(inputs,kernel_size,scope):with tf.name_scope(scope):w=tf.Variable(tf.random_uniform(shape=kernel_size),name='w')b=tf.Variable(tf.random_uniform(shape=[kernel_size[-1]],name='b'))fc=tf.nn.bias_add(tf.matmul(inputs,w),b)return fcdef batch_normal(inputs,beta,gamma,phase_train,scope='bn',decay=0.99,eps=1e-3):with tf.variable_scope(scope):batch_mean,batch_var=tf.nn.moments(inputs,[0,1,2])ema=tf.train.ExponentialMovingAverage(decay=0.99)def mean_var_with_update():ema_apply_op=ema.apply([batch_mean,batch_var])with tf.control_dependencies([ema_apply_op]):return tf.identity(batch_mean),tf.identity(batch_var)mean,var=tf.cond(phase_train,mean_var_with_update,lambda:(ema.average(batch_mean),ema.average(batch_var)))normed=tf.nn.batch_normalization(inputs,mean,var,beta,gamma,eps)return normeddef Conv2d_BN(x,kernel_size,strides,padding,phase_train,scope):x=cov2(x,kernel_size,strides,padding,scope)x=batch_normal(x,tf.random_normal([kernel_size[-1]]),tf.random_normal([kernel_size[-1]]),phase_train,scope=scope+'_bn')return xdef Conv_Block(inpt,channels,phase_train,strides=[1,1,1,1],with_conv_shortcut=False):x=Conv2d_BN(inpt,[1,1,channels[0],channels[1]],strides,'SAME',phase_train,'bottleneck1x1')x=Conv2d_BN(x,[3,3,channels[1],channels[2]],strides,'SAME',phase_train,'bottleneck2x2')x=Conv2d_BN(x,[1,1,channels[2],channels[3]],strides,'SAME',phase_train,'bottleneck3x3')if with_conv_shortcut:shortcut=Conv2d_BN(inpt,[3,3,channels[0],channels[3]],strides,'SAME',phase_train,'shortcut')x=tf.add(x,shortcut)  else:x=tf.add(x,inpt)return xdef ResNet50():inputs=tf.placeholder(tf.float32,shape=[None,224,224,3])phase_train=tf.placeholder(tf.bool)x=tf.pad(inputs,[[0,0],[3,3],[3,3],[0,0]])  #0进行填充x=Conv2d_BN(x,[7,7,3,64],[1,2,2,1],'VALID',phase_train,'Conv2d_BN_1')x=tf.nn.max_pool(x,[1,3,3,1],[1,2,2,1],'SAME')x=Conv_Block(x,[64,64,64,256],phase_train,[1,1,1,1],True)x=Conv_Block(x,[256,64,64,256],phase_train,[1,1,1,1],False)x=Conv_Block(x,[256,64,64,256],phase_train,[1,1,1,1],False)x=Conv_Block(x,[256,128,128,512],phase_train,[1,1,1,1],True)x=Conv_Block(x,[512,128,128,512],phase_train,[1,1,1,1],False)x=Conv_Block(x,[512,128,128,512],phase_train,[1,1,1,1],False)x=Conv_Block(x,[512,128,128,512],phase_train,[1,1,1,1],False) x=Conv_Block(x,[512,256,256,1024],phase_train,[1,1,1,1],True)x=Conv_Block(x,[1024,256,256,1024],phase_train,[1,1,1,1],False)x=Conv_Block(x,[1024,256,256,1024],phase_train,[1,1,1,1],False)x=Conv_Block(x,[1024,256,256,1024],phase_train,[1,1,1,1],False)    x=Conv_Block(x,[1024,256,256,1024],phase_train,[1,1,1,1],False)x=Conv_Block(x,[1024,256,256,1024],phase_train,[1,1,1,1],False) x=Conv_Block(x,[1024,512,512,2048],phase_train,[1,1,1,1],True)x=Conv_Block(x,[2048,512,512,2048],phase_train,[1,1,1,1],False)x=Conv_Block(x,[2048,512,512,2048],phase_train,[1,1,1,1],False) x=tf.nn.max_pool(x,[1,7,7,1],[1,1,1,1],'SAME')  #[none,56,56,2048]x=fully_connect(tf.reshape(x,[-1,56*56*2048]),[56*56*2048,1000],scope='fc')  #[none,1000]with tf.Session() as sess:a=np.ones([224,224,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(x,feed_dict={inputs:[a],phase_train:True})print(np.shape(c))import tensorflow as tf
import numpy as np
TRAINING=tf.Variable(initial_value=True,dtype=tf.bool,trainable=False)
def bottleneck_block(X_input,kernel_size,filters,stage,block,with_conv_shortcut=True):conv_name_base='res'+str(stage)+block+'_branch'bn_name_base='bn'+str(stage)+block+'_branch'if with_conv_shortcut: name_scope='conv_block_stage'+str(stage)else: name_scope='id_block_stage'+str(stage)with tf.name_scope(name_scope):filter1,filter2,filter3=filtersX_shortcut=X_inputx=tf.layers.conv2d(X_input,filter1,(1,1),padding='same',name=conv_name_base+'2a')x=tf.layers.batch_normalization(x,axis=3,name=bn_name_base+'2a',training=TRAINING)x=tf.nn.relu(x)x=tf.layers.conv2d(x,filter2,(kernel_size,kernel_size),padding='same',name=conv_name_base+'2b')x=tf.layers.batch_normalization(x,axis=3,name=bn_name_base+'2b',training=TRAINING)x=tf.nn.relu(x)x=tf.layers.conv2d(x,filter3,(1,1),padding='same',name=conv_name_base+'2c')x=tf.layers.batch_normalization(x,axis=3,name=bn_name_base+'2c',training=TRAINING) if with_conv_shortcut:X_shortcut=tf.layers.conv2d(X_shortcut,filter3,(3,3),padding='same',name=conv_name_base+'1')X_shortcut = tf.layers.batch_normalization(X_shortcut, axis=3, name=bn_name_base + '1', training=TRAINING)x_add_shortcut=tf.add(x,X_shortcut)add_result=tf.nn.relu(x_add_shortcut)return add_resultdef ResNet50():inputs=tf.placeholder(tf.float32,shape=[None,224,224,3])x=tf.pad(inputs,[[0,0],[3,3],[3,3],[0,0]])  #0进行填充#stage1x=tf.layers.conv2d(x,filters=64,kernel_size=(7,7),strides=(2,2),name='conv1')x=tf.layers.batch_normalization(x,axis=3,name='bn_conv1',training=TRAINING)x=tf.nn.relu(x)x=tf.layers.max_pooling2d(x,pool_size=(3,3),strides=(2,2),padding='same') #[none,55,55,64]#stage2x=bottleneck_block(x,kernel_size=3,filters=[64,64,256],stage=2,block='a')x=bottleneck_block(x,kernel_size=3,filters=[64,64,256],stage=2,block='b',with_conv_shortcut=False)x=bottleneck_block(x,kernel_size=3,filters=[64,64,256],stage=2,block='c',with_conv_shortcut=False) #[none,56,56,256]  #stage3x=bottleneck_block(x,kernel_size=3,filters=[128,128,512],stage=3,block='a')x=bottleneck_block(x,kernel_size=3,filters=[128,128,512],stage=3,block='b',with_conv_shortcut=False)x=bottleneck_block(x,kernel_size=3,filters=[128,128,512],stage=3,block='c',with_conv_shortcut=False)    x=bottleneck_block(x,kernel_size=3,filters=[128,128,512],stage=3,block='d',with_conv_shortcut=False) #[none,56,56,512]#stage4x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='a')x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='b',with_conv_shortcut=False)x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='c',with_conv_shortcut=False)    x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='d',with_conv_shortcut=False) x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='e',with_conv_shortcut=False)        x=bottleneck_block(x,kernel_size=3,filters=[256,256,1024],stage=4,block='f',with_conv_shortcut=False) #[none,56,56,1024]#stage5x=bottleneck_block(x,kernel_size=3,filters=[512,512,2048],stage=5,block='a')x=bottleneck_block(x,kernel_size=3,filters=[512,512,2048],stage=5,block='b',with_conv_shortcut=False)x=bottleneck_block(x,kernel_size=3,filters=[512,512,2048],stage=5,block='c',with_conv_shortcut=False) #[none,56,56,2048] x=tf.layers.average_pooling2d(x,(7,7),(7,7))  flatten=tf.layers.flatten(x,name='flatten')logits = tf.layers.dense(flatten, units=1000, activation=tf.nn.relu)with tf.Session() as sess:a=np.ones([224,224,3],dtype=np.float32)tf.global_variables_initializer().run()c=sess.run(logits,feed_dict={inputs:[a],TRAINING:True})print(np.shape(c))

7、继往开来:DenseNet

闪光点:密集连接,缓解梯度消失问题,加强特征传播,鼓励特征复用,极大的减少参数量

DenseNet是一种具有密集连接的卷积神经网络。在该网络中,任何两层之间都有直接的连接,也就是说,网络每一层的输入都是前面所有层输出的并集,而该层所学习的特征图也会被直接传给后面所有层作为输入。

8、SqueezeNet

SqueezeNet的核心在于Fire module,由squeeze层和expand层,squeeze层是1*1卷积核的卷积层,expand层1*1和3*3卷积核的卷积层,expand层中,把1*1和3*3得到feature map进行concat。

9、MobileNet

1)参数数量:关系到模型大小,单位为M,通常用float32,模型大小是参数数量的4倍

2)理论计算量(FLOPs):可以用来衡量算法/模型的复杂度,大模型的单位通常为G,小模型的单位通常为M.通常只考虑乘加操作(Multi-Adds)的数量,而且只考虑conv和fc等参数层的计算量,忽略bn和prelu等等。

3) 计算公式:假设卷积核大小为Kh*Kw,输入通道数为Cin,输出通道数Cout,输出特征图的宽和高分别为W和H,这里忽略偏置项:

CONV标准卷积层:
params:Kh*Kw*Cin*Cout
FLOPs:Kh*Kw*Cin*Cout*H*W=params*H*W
FC全连接层:
params:Cin*Cout
FLOPs:Cin*Cout

基于深度可分离卷积的。深度可分离卷积干的活是:把标准卷积分解成深度卷积和逐点卷积,可以大幅度降低参数量和提升运算速度。分解过程如下:

输入的特征映射F尺寸为(DF,DF,M),采用标准卷积K为(DK,DK,M,N)(如图a所示),输出的特征映射为G尺寸为(DG,DG,N)。标准卷积的卷积计算公式为:

输入的通道数为M,输出的通道数为N。对应计算量为:

可将标准卷积(DK,DK,M,N)拆分为深度卷积和逐点卷积:

a、深度卷积负责滤波作用,尺寸为(DK,DK,1,M)如图b所示。输出特征为(DG,DG,M)

b、逐点卷积负责转换通道,尺寸为(1,1,M,N)如图c所示。输出特征为(DG,DG,N)

深度分类卷积实例

输入图片的大小为(6,6,3),原卷积操作是用(4,4,3,5)的卷积(4*4是卷积核大小,3是卷积核通道数,5个卷积核数量),stride=1,无padding,输出的特征映射为(3,3,5)。将标准卷积中选取序号为n的卷积核,大小为(4,4,3),标准卷积过程示意图如下:

输入有3个通道,对应着有3个大小为(4,4,1)的深度卷积核,卷积结果共有3个大小为(3,3,1),我们按通道排列得到输出卷积结果(3,3,3).相比之下计算量减少了:4*4*3*5转为4*4*1*3+1*1*3*5,即参数量减少了21/80。

宽度因子α

用于控制输入和输出的通道数,即输入通道从M变为αM,输出通道从N变为αN。

模型层次

模型层次

第一层

conv0

conv0/bn

conv0/scale

conv0/relu

第二层

conv1/dw

conv1/dw/bn

conv1/dw/scale

conv1/dw/relu

conv1

conv1/bn

conv1/scale

conv1/relu

第三层

conv2/dw

conv2/dw/bn

conv2/dw/scale

conv2/dw/relu

conv2

conv2/bn

conv2/scale

conv2/relu

第四层

conv3/dw

conv3/dw/bn

conv3/dw/scale

conv3/dw/relu

conv3

conv3/bn

conv3/scale

conv3/relu

第五层

conv4/dw

conv4/dw/bn

conv4/dw/scale

conv4/dw/relu

conv4

conv4/bn

conv4/scale

conv4/relu

第六层

conv5/dw

conv5/dw/bn

conv5/dw/scale

conv5/dw/relu

conv5

conv5/bn

conv5/scale

conv5/relu

第七层

conv6/dw

conv6/dw/bn

conv6/dw/scale

conv6/dw/relu

conv6

conv6/bn

conv6/scale

conv6/relu

第八层

conv7/dw

conv7/dw/bn

conv7/dw/scale

conv7/dw/relu

conv7

conv7/bn

conv7/scale

conv7/relu

第九层

conv8/dw

conv8/dw/bn

conv8/dw/scale

conv8/dw/relu

conv8

conv8/bn

conv8/scale

conv8/relu

第十层

conv9/dw

conv9/dw/bn

conv9/dw/scale

conv9/dw/relu

conv9

conv9/bn

conv9/scale

conv9/relu

第十一层

conv10/dw

conv10/dw/bn

conv10/dw/scale

conv10/dw/relu

conv10

conv10/bn

conv10/scale

conv10/relu

第十二层

conv11/dw

conv11/dw/bn

conv11/dw/scale

conv11/dw/relu

conv11

conv11/bn

conv11/scale

conv11/relu

第十三层

conv12/dw

conv12/dw/bn

conv12/dw/scale

conv12/dw/relu

conv12

conv12/bn

conv12/scale

conv12/relu

第十四层

conv13/dw

conv13/dw/bn

conv13/dw/scale

conv13/dw/relu

conv13

conv13/bn

conv13/scale

conv13/relu

第十五层

pool

第十六层

fc

10、MobileNetV2:Inverted Residuals and linear bottlenecks

主要改进点

a、引入残差结构,先升维再降维,增强梯度的传播,显著减少推理期间所需的内存占用(Inverted Residuals)

b、去掉Narrow layer后的ReLU,保留特征多样性,增强网络的表达能力

c、网络为全卷积的,使得模型可以适应不同尺寸的图像,使用RELU(最高输出为6)激活函数,使得模型在低精度计算下具有更强的鲁棒性

d、MobileNet v2 building block如下所示,若需要下采样,可在DWise时采用步长为2的卷积;小网络使用小的扩张系数,大网络使用大一点的扩张系数,推荐是5~10,论文中t=6

11、MobileNetV3

MobileNetV3有两大创新点:

a、互补搜索技术组合:由资源受限的NAS执行模块级搜索,NetAdapt执行局部搜索

b、网络结构改进:将最后一步的平均池化层前移并移除最后一个卷积层,引入h-swish激活函数

12、ShuffleNet

创新点:利用group convolution和channel shuffle来设计卷积神经网络模型,减少模型使用的参数数量;

如图a所示,为了提升模型效率,采用group convolution,但会有一个副作用,输出一部分来自输入部分通道。

如图b所示,采用channel shuffle来改善各组间信息流通不畅,具体方法:把各组的channel平均分为g(上图g=3)份,依次序的重新构成feature map。

ShuffleNet unit的演化过程:

图(a):一个带有depth-wise convolution的bottleneck unit

图(b):对1*1 conv换成1*1 Gconv,并在第一个1*1 Gconv之后增加一个channel shuffle操作

图(c):在旁路增加AVG pool,目的是为了减少feature map的分辨率,最后不采用Add,而是concat,从而弥补分辨率减少而带来的信息损失。

template <typename Dtype>
__global__ void ShuffleChannelKernel(const int nthreads, const int feature_map_size,
Dtype *output, const Dtype *input, int group_row, int group_column, int sp_sz)
{CUDA_KERNEL_LOOP(index, nthreads) {const int n = index / group_row / group_column / sp_sz;const int i = (index / group_column / sp_sz) % group_row;const int j = index / sp_sz % group_column;const int k = index - (n * feature_map_size + (i * group_column + j) * sp_sz);Dtype* p_o = output + n * feature_map_size + (j * group_row + i) * sp_sz;p_o[k] = input[index];}
}template <typename Dtype>
void ShuffleChannelLayer<Dtype>::Forward_gpu(const vector<Blob<Dtype>*>& bottom,const vector<Blob<Dtype>*>& top)
{
const Dtype* bottom_data = bottom[0]->gpu_data();
Dtype* top_data = top[0]->mutable_gpu_data();const int num = bottom[0]->num();
const int feature_map_size = bottom[0]->count(1);
const int sp_sz = bottom[0]->count(2);
const int chs = bottom[0]->channels();int group_row = group_;
int group_column = int(chs / group_row);
CHECK_EQ(chs, (group_column * group_row)) << "Wrong group size.";int count = num * group_column * group_row * sp_sz;ShuffleChannelKernel<Dtype> << <CAFFE_GET_BLOCKS(count), CAFFE_CUDA_NUM_THREADS >> >(count, feature_map_size, top_data, bottom_data, group_row, group_column, sp_sz);
}

13、Xception

借鉴(非采用)depth-wise convolution改进Inception V3,卷积的时候要将通道卷积与空间卷积进行分离。

第一个:原版depth-wise convolution,先逐通道卷积,再1*1卷积;而Xception是反过来,先1*1卷积,再逐通道卷积

第二个:原版Depth-wise convolution的两个卷积之间是不带激活函数的,而Xception在经过1*1卷积之后会带上一个relu的非线性激活函数.

(一)CNN网络结构相关推荐

  1. CNN网络结构发展最全整理

    来源:人工智能AI技术 本文约2500字,建议阅读9分钟 本文为你整理CNN网络结构发展史. 作者丨zzq 来源丨https://zhuanlan.zhihu.com/p/68411179 CNN基本 ...

  2. 收藏 | 一文遍览CNN网络结构的发展

    来源:人工智能AI技术本文约2600字,建议阅读8分钟 本文介绍了十五种经典的CNN网络结构. CNN基本部件介绍 1. 局部感受野 在图像中局部像素之间的联系较为紧密,而距离较远的像素联系相对较弱. ...

  3. CNN网络结构发展演变:从LeNet到HRNet(一)

    个人经验总结博客,写的不好的地方还请各路大神指正,不喜勿喷.网络结构图基本都是引用的,如有雷同,实在抱歉,可在下方评论中留言是否删除. 我们知道CNN网络结构一直在更新迭代,卷积可以理解为:" ...

  4. CNN网络结构的发展:从LeNet到EfficientNet

    关注上方"深度学习技术前沿",选择"星标公众号", 资源干货,第一时间送达! 作者:zzq https://zhuanlan.zhihu.com/p/68411 ...

  5. CNN网络结构进化概述

    网络工程问题是深度学习中比较基础的问题,网络工程的难点在于,缺乏对深度神经网络的理论理解(即常说的黑盒模型),无法根据理论来设计网络结构,实际中更多的是不断的尝试,根据实验反馈出来的结果确定某一结构是 ...

  6. CNN网络结构的发展

    经典网络结构 部分网络的pytorch实现github代码:CNN-nets 1. LeNet5 由两个卷积层,两个池化层,两个全连接层组成. 卷积核都是5×5,stride=1,池化层使用maxpo ...

  7. 有关CNN网络结构的总结

    从神经网络到卷积神经网络(CNN) 我们知道神经网络的结构是这样的: 那卷积神经网络跟它是什么关系呢? 其实卷积神经网络依旧是层级网络,只是层的功能和形式做了变化,可以说是传统神经网络的一个改进.比如 ...

  8. CS231n笔记-CNN网络结构

    所有图片来自PPT官网Index of /slides/2022 代码:Batch Normalization和Dropout_iwill323的博客-CSDN博客 目录 全连接层存在的问题 卷积层 ...

  9. 深度学习(07)-- 经典CNN网络结构(Inception (v1-v4))

    文章目录 目录 1.Inception介绍 1.1 Inception结构 1.2 Inception V1(GoogleNet) 1.3 Inception V2(Batch Norm) 1.4 I ...

最新文章

  1. BIP_BI Pubisher的SQL/XSL/FO扩展函数应用(概念)
  2. openresty完全开发指南_送给你,PBA商业分析指南(全书下载)
  3. 金融数据信噪比的影响力又一力证
  4. 获取当前周一日期_Excel工作表中最全的日期、时间函数,效率办公必备!
  5. 计算机跨专业考研方向java,【计算机考研】 初试出成绩前应该做的6件事
  6. YbtOJ#20237-[冲刺NOIP2020模拟赛Day10]区间均值【树状数组】
  7. python csv转为html,在Python中将CSV转换为HTML表格
  8. 转载——CVE-2019-0807
  9. 云数据库精华问答 | 现有数据库优化到云环境有什么好处​?
  10. spring Boot报错 之五种(不打包运行)
  11. WindowManager添加一个悬浮的Window
  12. 【从线性回归到BP神经网络】第四部分:BP神经网络
  13. Excel数据导入sql临时表操作步骤
  14. 局域网共享工具_局域网共享精灵 V1.0局域网便携共享文件+打印机——墨涩网
  15. oracle临时表的创建
  16. 【Maven】阿里镜像仓库地址
  17. EndNote毕业论文参考文献格式输出
  18. 82、组合分配式气体灭火系统所需的气体单向阀的数量
  19. Pelles C 五光十色中的一抹经典
  20. nodejs实现文件/头像上传

热门文章

  1. Modbus在Android上的应用之Modbus TCP Slave
  2. Verilog 概述
  3. linux部署webgoat
  4. Nginx原理及配置
  5. 计算机网络中的基带与宽带
  6. Maven的三种打包方式(jar、shade、assembly)
  7. NHCP H5: Vm and Template Management Topic | Cloud computing
  8. Discuz发帖时将远程图片自动下载并保存至服务器
  9. Deeper GNN
  10. rtmp测试的地址(2022版)