一.理解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%
證明兩個實例是相互獨立的,對一個實例的訓練並沒有影響到另一個。
后續:
后續的實驗應該是兩個實例同時進行訓練,保存准確率較高的實例的參數,作為下一次訓練共同的初始化數據。