一、前言
首先附上上一篇博客代码的码云链接,即右面的小图标:,相关代码已经在工作站站上跑了,读者可根据自己设备的情况,对图片数据做相应的更改以匹配自己设备的需求。有问题可以在下方留言,共同学习交流。
本篇将以2014年的ILSVRC竞赛的冠军网络GoogLeNet为切入点。使用上篇博客所使用到的数据集并且使用相应的数据提取方式及框架来训练整个网络,也就是说相对上一篇博客所做的工作,这里只添加一个新文件google_net.py,替换掉vgg_net.py文件。这样的话相对于整个项目,这个只需要在train.py的文件夹内修改导入的文件和导入文件中的函数名就可以了。同样的,为了使这一个简便的方法成立,便必须要使网络中的命名规则不改变。
二、介绍
1.GoogLeNet的层数从VGG的16层(最高19层)加深到了22层,其主要的创新点在于它的Inception,这个是一个网中网的结构,即原来的节点也是一个网络。用了Inception之后整个网络结构的宽度和深度都有所扩大,并且能够带来2-3倍性能的提升。GoogLeNet中去除了最后的全连接层,在与VGG类似的以往的网络中,全连接层几乎占据了90%的参数量,占用了非常大的内存容量和使用率,并且还会容易造成过拟合,为了解决这个问题,GoogLeNet使用全剧平均池化层替代了全连接层。这相当程度上的减少了数据量并且提高了运行效率。
2.MLP卷积层,该卷积层主要通过将高维度特征转化为低维度特征,将神经网络的思想融合在具体的卷积操作之中。也就是我们刚刚提到的网中网结构。也就是说把原来的卷积层替换为一个网络的过程。
3.全局均值池化,其具体做法就是:对每个特征图的整张图片进行全局均值池化,生成一个值,即每张特征图相当于一个输出特征,也就是输出一个分类结果。也就是说,最后一层的特征图数量要跟所要分类的类别相同,那么我们可以想象的到我们整个GoogLeNet的网络结构跟之前VGG网络结构有所不同,如果分类数量比较少的话,整个网络的特征数量是先增加后降低的。
4.Inception,其采用MLP卷积层的思想,将中间的全连接层替换成了多通道卷积层。该多通道卷积层即为一个Inception,堆积起来就形成了原始的GoogLeNet网络。
三、任务目标
1.简单构建GoogLeNet网络
2.与之前的内容结合到一个项目中去,作为一个可选择使用的文件或者函数
3.相关参数要求同07-vgg16-TensorFlow 20 classification of flower identification
四、任务主要内容
1.修改train.py文件
导入文件的时候导入本次编写的文件GoogLeNet.py
import GoogLeNet
在主函数中修改vgg_net.train()为如下
GoogLeNet.train()
2.创建GoogLeNet文件
首先将vgg_net.py文件中的内容copy进来并删除里面的vgg16_net函数
然后创建函数inception_v2()
#定义网络结构
def inception_v2(net):
shape=net.get_shape()
#1*1卷积 0.4的比例
net1=tf.nn.conv2d(net,filter=get_variable('w01',[1,1,shape[3],shape[3]*0.2]),strides=[1,1,1,1],padding='SAME')
net1=tf.nn.bias_add(net1,bias=get_variable('b01',[shape[3]*0.2]))
net1=tf.nn.relu(net1)
#1*1卷积,3*3卷积
net2=tf.nn.conv2d(net,filter=get_variable('w11',[1,1,shape[3],shape[3]*0.6]),strides=[1,1,1,1],padding='SAME')
net2=tf.nn.bias_add(net2,bias=get_variable('b11',[shape[3]*0.6]))
net2=tf.nn.relu(net2)
net2=tf.nn.conv2d(net2,filter=get_variable('w12',[3,3,shape[3]*0.6,shape[3]*0.3]),strides=[1,1,1,1],padding='SAME')
net2=tf.nn.bias_add(net2,bias=get_variable('b12',[shape[3]*0.3]))
net2=tf.nn.relu(net2)
#3*3最大池化,1*1最大池化
net3=tf.nn.max_pool(net,ksize=[1,3,3,1],strides=[1,1,1,1],padding='SAME')
net3=tf.nn.relu(net3)
net3=tf.nn.max_pool(net3,ksize=[1,1,1,1],strides=[1,1,1,1],padding='SAME')
net3=tf.nn.relu(net3)
#1*1卷积,3*3卷积,3*3卷积
net4=tf.nn.conv2d(net,filter=get_variable('w31',[1,1,shape[3],shape[3]]),strides=[1,1,1,1],padding='SAME')
net4=tf.nn.bias_add(net4,bias=get_variable('b31',[shape[3]]))
net4=tf.nn.relu(net4)
net4=tf.nn.conv2d(net4,filter=get_variable('w32',[3,3,shape[3],shape[3]*0.6]),strides=[1,1,1,1],padding='SAME')
net4=tf.nn.bias_add(net4,bias=get_variable('b32',[shape[3]*0.6]))
net4=tf.nn.relu(net4)
net4=tf.nn.conv2d(net4,filter=get_variable('w33',[3,3,shape[3]*0.6,shape[3]*0.3]),strides=[1,1,1,1],padding='SAME')
net4=tf.nn.bias_add(net4,bias=get_variable('b33',[shape[3]*0.3]))
net4=tf.nn.relu(net4)
#concat 聚合
net_out=tf.concat(concat_dim=3,values=[net1,net2,net3,net4])
return net_out
再之后就可以方便的按照GoogLeNet的网络结构构建一个不是特别标准的GoogLeNet的网络结构了
def googlenet(x,y):
conv1_out=64
conv2_out=192
with tf.variable_scope('input'):
network=tf.reshape(x,shape=[-1,224,224,3],name='input')
with tf.variable_scope('conv1-64'):
network = tf.nn.conv2d(network, filter=get_variable('w', [3, 3, 3, conv1_out]),strides=[1,2,2,1],padding='SAME')
network = tf.nn.bias_add(network, bias=get_variable('b', [conv1_out]))
network = tf.nn.relu(network)
with tf.variable_scope('max_pool_1'):
network = tf.nn.max_pool(network, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.variable_scope('conv2-192'):
network = tf.nn.conv2d(network, filter=get_variable('w', [3, 3, conv1_out, conv2_out]), strides=[1, 1, 1, 1],
padding='SAME')
network = tf.nn.bias_add(network, bias=get_variable('b', [conv2_out]))
network = tf.nn.relu(network)
with tf.variable_scope('max_pool_2'):
network = tf.nn.max_pool(network, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.variable_scope('inception_v2_1'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_2'):
network=inception_v2(network)
with tf.variable_scope('max_pool_3'):
network = tf.nn.max_pool(network, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.variable_scope('inception_v2_3'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_4'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_5'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_6'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_7'):
network=inception_v2(network)
with tf.variable_scope('max_pool_4'):
network = tf.nn.max_pool(network, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
with tf.variable_scope('inception_v2_8'):
network=inception_v2(network)
with tf.variable_scope('inception_v2_9'):
network=inception_v2(network)
with tf.variable_scope('avg_pool_5'):
network = tf.nn.avg_pool(network, ksize=[1, 7, 7, 1], strides=[1, 1, 1, 1], padding='SAME')
with tf.variable_scope('dropout_1'):
network=tf.nn.dropout(network,keep_prob=0.4)
with tf.variable_scope('fc_1'):
image_shape = network.get_shape()
print(image_shape)
network = tf.reshape(network, shape=(-1, image_shape[3]))
network = tf.add(tf.matmul(network, get_variable('w', image_shape[3]), 20),get_variable('b', [20]))
return tf.nn.softmax(network)
最后就是修改train函数的调用了,这个时候只需将pre = vgg16_net(X, Y)替换为pre = googlenet(X, Y)
即可。
附刚开始训练时的截图:
五、分析与总结
1.我们两次构建的网络模型都比较臃肿,就是说如果不做相应的处理在自己的PC机上是很可能跑不起来的。博主为了时间效率问题不得不对代码有所退让,不然看着工作站跑一个程序运行个一年半载的时间可是经受不住的,所以读者可以根据自身实际情况修改网络结构或者修改图片大小和图片数量还有迭代数量方式等参数,然后得到令人满意的结果。
2.本次展示的图片是在博主PC机中截取的,工作站目前还在运行vgg的代码。
3.下一篇会针对ResNet(残差)网络做介绍并展示出代码。
六、参考书目
李金洪 2018 《深度学习之TensorFlow入门、原理与进阶实战》 机械工业出版社 ISBN 978-7-111-59005-7