Tensorflow的持久化与重载

xiaoxiao2021-02-28  12

Tensorflow的参数公用,持久化与重载

1. 参数共用问题:

tf.variable_scope(name)tf.get_variable(name, shape, dtype, initializer)

在搭建网络的时候,一般都会通过variable_scope(name)来定义我们的命名域,方便图的结构展示,此时的op的名字就会变成:name/opname,方便统一的管理。

除了命名域之外,一般还会搭配一种参数的定义方法: tf.get_variable(name)和tf.Variable(name),这两种方法的区别是get_variable()方法对于那些名字相同的参数,当命名域scope调用reuse_variable()的时候,不会重新生成一个变量,而是用同一个名字的参数,而tf.Variable()则不能够重用参数

用以下的代码作为例子,假设有两层的CNN,分别有两个卷积核参数W1和W2,如果多次调用这个前向传播的网络,那么就会生成两次的卷积核(共4个)

def my_image_filter(input_images): conv1_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]), name="conv1_weights") conv1_biases = tf.Variable(tf.zeros([32]), name="conv1_biases") conv1 = tf.nn.conv2d(input_images, conv1_weights, strides=[1, 1, 1, 1], padding='SAME') relu1 = tf.nn.relu(conv1 + conv1_biases) conv2_weights = tf.Variable(tf.random_normal([5, 5, 32, 32]), name="conv2_weights") conv2_biases = tf.Variable(tf.zeros([32]), name="conv2_biases") conv2 = tf.nn.conv2d(relu1, conv2_weights, strides=[1, 1, 1, 1], padding='SAME') return tf.nn.relu(conv2 + conv2_biases)

如果对上面的前向传播方法进行命名域方法改造:

def my_image_filter(input_images): with tf.variable_scope("conv1"): # Variables created here will be named "conv1/weights", "conv1/biases". relu1 = conv_relu(input_images, [5, 5, 32, 32], [32]) with tf.variable_scope("conv2"): # Variables created here will be named "conv2/weights", "conv2/biases". return conv_relu(relu1, [5, 5, 32, 32], [32]) if __name__ =="__main__": with tf.variable_scope("image_filters") as scope: result1 = my_image_filter(image1) scope.reuse_variables() ## 重用了参数 result2 = my_image_filter(image2)

2.持久化与重载(saver方法)

Tensorflow的模型保存主要分为两个部分:图(Meta Graph)和各节点、参数的值(Checkpoint file)。

Tensorflow的持久化与重载主要用到了几个方法: - tf.train.saver() - saver.restore(sess, savepath) //saver是tf.train.saver()的实例

因tf的变量值存在与会话sess中,所以在保存的时候必须连着对话一起保存,在保存的时候,saver也会把当前的默认图也保存在你指定的路径中并形成.meta文件来保存图结构信息: import tensorflow as tf w1 = tf.Variable(tf.random_normal(shape=[2]), name='w1') w2 = tf.Variable(tf.random_normal(shape=[5]), name='w2') saver = tf.train.Saver() sess = tf.Session() sess.run(tf.global_variables_initializer()) saver.save(sess, 'my_test_model',globle_step=1000) # 以上的代码能够保存当前的图结构和状态信息并保存为以下文件: # globle_step是指每1000次保存一次 # my_test_model-1000.data-00000-of-00001 # my_test_model-1000.index # my_test_model-1000.meta # checkpoint

用以下的代码可以恢复之前的代码并继续训练等:

with tf.Session() as sess: saver = tf.train.import_meta_graph('my-model-1000.meta') saver.restore(sess,tf.train.latest_checkpoint('./')) print(sess.run('w1:0')) ##Model has been restored. Above statement will print the saved value of w1.

以下是实际上的应用:

在一般的情况之下,如果没有原来的文件,就需要重新的加载我们的图结构,也就是meta文件,然后在进行我们的参数重载,需要注意的是我们的网络中的placeholder的值是不会被保存在在网络中的,只会保存他的大小,可以在重载了图和数据后继续训练或应用。

import tensorflow as tf #准备我们的输入数据的placeholder,和feed_dict。 w1 = tf.placeholder("float", name="w1") w2 = tf.placeholder("float", name="w2") b1= tf.Variable(2.0,name="bias") feed_dict ={w1:4,w2:8} #定义一个我们即将要用于存储的节点运算 w3 = tf.add(w1,w2) w4 = tf.multiply(w3,b1,name="op_to_restore") sess = tf.Session() sess.run(tf.global_variables_initializer()) #创建一个Saver()对象来对参数和图结构进行保存 saver = tf.train.Saver() #Run the operation by feeding input print sess.run(w4,feed_dict) #Prints 24 which is sum of (w1+w2)*b1 #现在保存图结构和参数 saver.save(sess, 'my_test_model',global_step=1000)

在保存了图结构和参数之后,可以用以下方法来对图进行恢复或者修改: - tf.train.import_meta_graph(name) - saver.restore(sess, name) - graph.get_tensor_by_name(“name:0”)这里的参数中,name指的是节点名称,后面跟的“:0”是指节点参数。

import tensorflow as tf sess=tf.Session() #这里开始加载我们之前保存的图文件和参数。 saver = tf.train.import_meta_graph('my_test_model-1000.meta') ## restore这里的最后一个参数是选择最后保存的那个checkpoint,通过查找index文件可以找到 saver.restore(sess,tf.train.latest_checkpoint('./')) # 下面是重新读取图中的placeholder信息来进行运算。 # 从读取到的节点来构成心的feed_dict来进行训练 graph = tf.get_default_graph() w1 = graph.get_tensor_by_name("w1:0") w2 = graph.get_tensor_by_name("w2:0") feed_dict ={w1:13.0,w2:17.0} #现在抽取想要计算的节点名称来进行计算 op_to_restore = graph.get_tensor_by_name("op_to_restore:0") #这里是在原来的图上新添加的运算节点,如果只想用原来的图的话可以不添加,直接run add_on_op = tf.multiply(op_to_restore,2) print sess.run(add_on_op,feed_dict) #This will print 120.

以上就是一般的运用,需要注意的是: 这样的加载暂时我只能运用在前向传播网络中,在有求导等运算的时候,只能用来再一次的训练,而不能直接的运用到实际的预测中,因为优化器的运算中并没有用tf.get_variable来进行参数的构造,也就是不能重用,在多次调用的时候会重新新建节点而该节点并不在之前保存的checkpoint文件中而导致报错。例如新建了link_1/w_1而不是重用原来的link/w导致报错。

3. 通过固化参数到图中来保存(便于广泛的传播)

除了上面的Saver的方法来保存模型之外,还有其他的方法来保存,这种方法也被称为“固化”,通过把参数固化到网络图结构中,不用每次都加载meta文件中图,然后再加载checkpoint文件中的参数,而只需要把固化网络保存到一个文件中即可,当然也必须要知道需要求的节点名称才能运用:

tf.get_default_graph()返回当前会话的默认图 tf.Graph.as_graph_def()返回一个图的序列化的GraphDef表示 序列化的GraphDef可以导入至另一个图中(使用 import_graph_def()) 或者使用C++ Session API graph_util模块的convert_variables_to_constants( sess, ----------------变量所在的会话 input_graph_def, -------------持有需要保存的网络结构的GraphDef对象 output_node_names, -------------需要保存的节点名称,注意命名域[scope_name/op_name] variable_names_whitelist=None, --要转换的变量名(在默认情况下,所有的变量都被转换)。 variable_names_blacklist=None ---变量名的集合,省略转换为常量 ) import_graph_def( graph_def, -----pb文件解析出来的GraphDef对象 input_map=None, ----需要映射到图中op的值,这里是用来填放feed_dict的 return_elements=None, --网络需要返回的值 name=None, -------这个操作的名称 op_dict=None, -------已经弃用的选项 producer_op_list=None )

以下是代码结构:

text_input = tf.placeholder( dtype=tf.int32, shape=[BATCH_SIZE, self.LENTH_MAX], name="text_input") text_input_change = tf.reshape(text_input, [BATCH_SIZE, -1], name="Input") keep_prob = tf.placeholder(dtype=tf.float32, name="keep_prob") label = tf.placeholder(dtype=tf.int64, shape=[BATCH_SIZE, ], name="label") embeding_var = tf.Variable(tf.random_uniform( shape=[self.VOCABULARY_SIZE, self.EMBEDING_SIZE]), dtype=tf.float32, name='embeding_var') ## batch_embeding: size [BATCH_SIZE, LENTH_MAX, EMBEDING_SIZE] batch_embeding = tf.nn.embedding_lookup(embeding_var, text_input_change) batch_embeding_normal = tf.reshape( batch_embeding, [-1, self.LENTH_MAX, self.EMBEDING_SIZE, 1]) # output = self.interface(batch_embeding_normal, keep_prob) output = self.interface_column(batch_embeding_normal, keep_prob) with tf.variable_scope("loss"): loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits( logits=output, labels=label, name="loss")) with tf.variable_scope("accuracy"): accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(tf.sigmoid(output),1), label), dtype = tf.float32)) result = tf.nn.softmax(output, name='result')

以上是对网络结构的一个定义,具体代码请看github。 然后我们对我们需要的图结构和输出节点进行保存,由于我们图结构是相对复杂的,所以在保存的时候一般指明我们需要的节点,然后根据依赖关系来保存图。

graph_def = tf.get_default_graph().as_graph_def() output_graph = graph_util.convert_variables_to_constants(sess, graph_def, ['result']) ## 通过gfile模块来保存固化的图为pb文件 with tf.gfile.GFile("./log/combined_model.pb","wb") as f: f.write(output_graph.SerializeToString()) ##对图进行序列化保存

重新加载图:

model_filename = "./log/combined_model.pb" graph = tf.Graph() with graph.as_default(): ## 从存储的pb文件中读取GraphDef对象 with tf.gfile.FastGFile(model_filename, 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) ## 读取文本 train_datareader = read_utils.ReadData( self.LENTH_MAX, mode="train", load_size=self.LOAD_SIZE, vocabulary_size=self.VOCABULARY_SIZE) textnum = train_datareader.Test(text) ## 构造新的feed_dict feed_dict = {'text_input':textnum, 'keep_prob':1.0} ## 把新的feed_dict填入固化的图中运算 result=tf.import_graph_def(graph_def, input_map=feed_dict, return_elements=['result:0']) ##不指明返回节点时,无返回类型的操作,把文件中的graph导入默认graph with tf.Session(graph = graph) as sess: last = sess.run(result)

通过以上的方法就能够把训练好的图用于实际应用之中,而且只需要一个文件即可再次使用。

转载请注明原文地址: https://www.6miu.com/read-2649989.html

最新回复(0)