[tensorflow] 通过Class的形式实现网络的创建、加深理解graph和session

时间:2021-08-31 11:27:03

一.理解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)))  
  1. 上述代码中,定义了一个ConvNet类。
  2. 训练的样本集合是mnist。
  3. 在使用时,创建了两个实例:net1和net2。
  4. 对net1输入了样本训练。
  5. 同时输入出net1和net2的预测准确率,如下所示:
net2 accuracy: 9.81%
net1 accuracy: 97.08%

  证明两个实例是相互独立的,对一个实例的训练并没有影响到另一个。


 


后续:

  后续的实验应该是两个实例同时进行训练,保存准确率较高的实例的参数,作为下一次训练共同的初始化数据。