[tensorflow] 通过Class的形式实现网络的创建、加深理解graph和session
一.理解graph和session
对tensorflow中的graph和session进行更深入的了解,能够帮助理解tensorflow的运行机制,使得可以在一个运行程序中调用不同的神经网络。总而言之,是深入学习必须掌握的东西。
- 在程序一开始,tensorflow会自动生成一个默认的graph。如果不显示的指定graph,所有的操作(添加张量、节点)都会自动加入默认图。
- 可以通过g=tf.Graph()显示获得一个图的变量。然后通过 with g.as_default():上下文管理器,在后文中进行网络的搭建工作。当然也有将g直接显示申明为默认图的函数,我忘记了,以后看见了来添加。但是不推荐显示申明为默认图,因为考虑到以后有多张图存在的情况下,显示申明会让逻辑处理起来很混乱,还是按照前一种方式规范操作来得好。
- 一个图可以有多个session,但是一个session只能执行一个指定图里的操作。
import tensorflow as tf tf.reset_default_graph()#复位初始图 g=tf.Graph()#获得图的对象 with g.as_default(): a=tf.Variable('a') sess=tf.Session() sess.run(tf.global_variables_initializer()) print(sess.run(a))#这里正常输出'a' b=tf.Variable('b') with sess.as_default(): sess.run(tf.global_variables_initializer()) print(sess.run(b))#这里报错“ is not an element of this graph ”
上述代码中,第一个print正常执行,正常输出张量a。但是定义张量b时,是定义在了全局默认的图中,并非在g中。而session对应的图是g,不是默认图,所以出错。
import tensorflow as tf tf.reset_default_graph()#复位初始图 g=tf.Graph()#获得图的对象 with g.as_default(): a=tf.Variable('a') sess=tf.Session() sess.run(tf.global_variables_initializer()) print(sess.run(a)) b=tf.Variable('b') with tf.Session() as sess2: sess2.run(tf.global_variables_initializer()) print(sess2.run(b))
代码修改过后,则能正常执行第二个print,输出张量b。因为以当前的默认graph重新申明了session来执行该操作。
二.通过class方式实现一个卷积网络
此种方式生卷积网络的类。目标是可以新建多个类的实例进行训练等操作,而不相互影响。此时就应该注重管理graph和session。
实现代码如下:
# -*- coding: utf-8 -*- """ Created on Fri Feb 16 12:31:04 2018 @author: FC """ import tensorflow as tf import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) class ConvNet(): def __init__(self,layers=['c','p','c','p'],datain=[784],ind=[28,28,1],outd=[10]): self.layers=layers #每一层的属性 self.ind=ind #图片的属性 self.outd=outd #输入的标记样本数组维度 self.datain=datain #输入的数据样本数组维度 self.convkernelsize=3#卷积核大小 self.midchannels=24 #中间层每层的通道数 self.net={} #层的集合 self.__NetInit() def __weight_variable(self,shape,name='w'): initial = tf.truncated_normal(shape, stddev=0.1) return tf.Variable(initial,name=name) def __bias_variable(self,shape,name='b'): initial = tf.constant(0.1, shape=shape) return tf.Variable(initial,name=name) def __AddInlayer(self,x,name='In-Layer'): #输入处理层 with tf.name_scope(name): x_reshape=tf.reshape(x, [-1]+self.ind) w=self.__weight_variable([self.convkernelsize,self.convkernelsize ,self.ind[2],self.midchannels]) b=self.__bias_variable([self.midchannels]) conv=tf.nn.conv2d(x_reshape, w, strides=[1, 1, 1, 1], padding='SAME') net=tf.nn.relu(conv+b,name=name) return net def __AddConvLayer(self,x,layertype='c',name='Mid_ConvLayer'): if ((layertype!='c') and (layertype!='p')): #只能是池化或者卷积层 return if (layertype=='p'): with tf.name_scope(name+layertype): net = tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') return net if (layertype=='c'): with tf.name_scope(name+layertype): w=self.__weight_variable([self.convkernelsize,self.convkernelsize ,self.midchannels,self.midchannels]) b=self.__bias_variable([self.midchannels]) conv=tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME') net=tf.nn.relu(conv+b,name=name+layertype) return net def __AddFcLayer(self,x,name='Out-FcLayer'): #全连接输出层 inshape=x.shape width=inshape[1]*inshape[2]*inshape[3] width=tf.cast(width,tf.int32) # print(type(width)) with tf.name_scope(name): w1=self.__weight_variable([width,1024],name='w1') b1=self.__bias_variable([1024],name='b1') x_flat=tf.reshape(x,[-1,width],name='x_flat') fc1=tf.nn.relu(tf.matmul(x_flat,w1)+b1,name='fc1') w2=self.__weight_variable([1024]+self.outd,name='w2') b2=self.__bias_variable(self.outd,name='b2') net=tf.nn.softmax(tf.matmul(fc1,w2)+b2,name=name) return net def __AddTrainstep(self,y,y_,name='Training'): with tf.name_scope(name): cross_entropy = -tf.reduce_sum(y_*tf.log(y)) train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float")) return train_step,accuracy def __NetInit(self): #由实际输入决定 tf.reset_default_graph()#复位初始图 self.Graph=tf.Graph() #如果在一个进程中创建了多个Graph,则需要创建不同的Session来加载每个Graph #而每个Graph则可以加载在多个Session中进行计算。 #上下文管理器 with self.Graph.as_default(): #1. 初始化网络结构 self.x=tf.placeholder("float",[None]+self.datain,name='x-input') self.y_=tf.placeholder("float", [None]+self.outd,name='y-input') #添加网络 self.net[0]=self.__AddInlayer(self.x) for i in range(len(self.layers)): self.net[i+1]=self.__AddConvLayer(self.net[i],layertype=self.layers[i]) self.outnet=self.__AddFcLayer(self.net[len(self.layers)]) self.trainstep,self.accuracy=self.__AddTrainstep(self.outnet,self.y_) self.sess=tf.Session() #2. 初始化图中变量 self.sess.run(tf.global_variables_initializer()) def Train(self,datax,datay): self.sess.run(self.trainstep,feed_dict={self.x:datax, self.y_:datay}) # for i in range(1000): # batch = mnist.train.next_batch(64) # if i%1000 == 0: # train_accuracy = self.sess.run(self.accuracy,feed_dict={self.x:batch[0], self.y_: batch[1]}) # print ("step %d, training accuracy %g"%(i, train_accuracy)) # self.sess.run(self.trainstep,feed_dict={self.x: batch[0], self.y_: batch[1]}) # # print ("test accuracy %g"%self.sess.run(self.accuracy,feed_dict={ # self.x: mnist.test.images, self.y_: mnist.test.labels})) def GetAccuracy(self,datax,datay): return self.sess.run(self.accuracy,feed_dict={self.x: datax, self.y_: datay}) def SaveGraph(self): #调用此函数,可通过tensorboard查看网络形状 with self.Graph.as_default(): merge = tf.summary.merge_all() writer = tf.summary.FileWriter('./log',self.sess.graph) def Close(self): self.sess.close() if __name__=='__main__': net1=ConvNet(layers=['c','c','p','c','c','p']) # net1.SaveGraph() for i in range(1000): batch = mnist.train.next_batch(64) net1.Train(batch[0],batch[1]) net2=ConvNet(layers=['c','c','p','c','c','p','c']) print("net2 accuracy: %g%%"%(100*net2.GetAccuracy(mnist.test.images,mnist.test.labels))) print("net1 accuracy: %g%%"%(100*net1.GetAccuracy(mnist.test.images,mnist.test.labels)))
- 上述代码中,定义了一个ConvNet类。
- 训练的样本集合是mnist。
- 在使用时,创建了两个实例:net1和net2。
- 对net1输入了样本训练。
- 同时输入出net1和net2的预测准确率,如下所示:
net2 accuracy: 9.81%
net1 accuracy: 97.08%
证明两个实例是相互独立的,对一个实例的训练并没有影响到另一个。
后续:
后续的实验应该是两个实例同时进行训练,保存准确率较高的实例的参数,作为下一次训练共同的初始化数据。