简单完整地讲解tensorflow模型的保存和恢复

http://blog.csdn.net/liangyihuai/article/details/78515913
在本教程主要讲到:
1. 什么是Tensorflow模型?
2. 如何保存Tensorflow模式?
3. 如何还原预测/迁移学习Tensorflow模型
4. 如何导入其他已经训练好的模型,并进行自己的修改和完善

在阅读本教程之前,需要你具备一定的训练神经网络的基础知识。

1.什么是Tensorflow模型?:

训练神经网络后,需要将它保存以备将来使用和部署到生产环境。那么,什么是Tensorflow模式?Tensorflow模式主要包含我们神经网络的网络图和已经训练好的变量参数。因此,Tensorflow模型主要有两个文件:

A)元数据图(meta graph):

它保存了tensorflow完整的网络图结构。这个文件以.META为扩展名。

B)检查点文件(checkpoint file)

这是一个二进制文件,它包含的权重变量,biases变量和其他变量。这个文件有的扩展名为CKPT。从0.11版本之后就不是单单一个.ckpt文件,而是有两个文件:

mymodel.data-00000-of-00001
mymodel.index

.data文件是一个包含我们的训练的变量的文件。与此同时,Tensorflow也有一个名为checkpoint的文件。它只是不断的保存最新的检查点文件的记录。因此,总结一下,Tensorflow版本高于0.10的话,就是下面这些文件:
这里写图片描述

而0.11之前的版本只包含三个文件:

inception_v1.meta
inception_v1.ckpt
checkpoint

现在,我们知道了一个Tensorflow模型的样子,下面让我们学会如何保存模型。

2.保存Tensorflow模型:

比方说,你对图像分类训练卷积神经网络。作为一种标准的做法,在训练模型的时候,需要一直关注着模型损失值和模型准确度。一旦你发现,网络已经收敛,就可以手动停止训练。训练完成后,我们希望将所有的变量和网络模型保存下来,供以后使用。在Tensorflow中要保存所有这些,使用tf.train.Saver()来保存神经网络的网络结构图和相关变量。

saver = tf.train.Saver()

需要注意,Tensorflow变量的作用范围是在一个session里面。在保存模型的时候,应该在session里面通过save方法保存。

saver.save(sess, 'my-test-model')

在这里,sess是session对象,而“my-test-model”是模型的名称。让我们来看看一个完整的例子:

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')

# This will save following files in Tensorflow v >= 0.11
# my_test_model.data-00000-of-00001
# my_test_model.index
# my_test_model.meta
# checkpoint

如果我们希望在迭代1000次之后保存模型,可以把当前的迭代步数传进去。

saver.save(sess, 'my_test_model',global_step=1000)

这样“-1000”将追加在文件名中:

my_test_model-1000.index
my_test_model-1000.meta
my_test_model-1000.data-00000-of-00001
checkpoint

在训练的时候,假设每1000次就保存一次模型,但是,这些保存的文件中变化的只是神经网络的变量,而网络的结构是没有变化的,所以就没有必要重复保存.meta文件,可以使用下面的方式,只让网络结构保存一次。

saver.save(sess, 'my-model', global_step=step,write_meta_graph=False)

如果你想只保留最新的4个模型,并希望每2小时保存一次,可以使用max_to_keep和keep_checkpoint_every_n_hours。

#saves a model every 2 hours and maximum 4 latest models are saved.
saver = tf.train.Saver(max_to_keep=4, keep_checkpoint_every_n_hours=2)

请注意,如果我们没有在tf.train.Saver()指定任何参数,这样就表示要保存所有的变量。如果,我们不希望保存所有的变量,只是其中的一部分。我们可以指定要保存的变量或者集合。怎么做?在创建tf.train.Saver的时候我们把一个列表或者要保存变量的字典作为参数传进去。让我们来看一个例子:

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([w1,w2])
sess = tf.Session()
sess.run(tf.global_variables_initializer())
saver.save(sess, 'my_test_model',global_step=1000)

这用于保存Tensorflow图的特定部分。

3.导入一个预先训练模式:

如果你想用别人的预先训练模型好的模型作为你的模型的一部分,那么你需要做两件事情:

a)创建网络结构图:

您可以通过编写Python代码手动创建每一层和原始模型一样的网络结构图。但是,不要做重复的工作,因为在.meta文件中,原始的网络结构已经保存在其中,我们只需要导入就可以了,而不需要重写一遍。使用下面的代码导入原始网络结构图。

saver = tf.train.import_meta_graph('my_test_model-1000.meta')

加载了网络结构图之后,还需要加载变量数据(也就是相关参数)。

b)加载的(变量)参数:

使用restore()方法恢复模型的变量参数。

with tf.Session() as sess:
new_saver = tf.train.import_meta_graph('my_test_model-1000.meta')
new_saver.restore(sess, tf.train.latest_checkpoint('./'))

在此之后,W1和W2张量的值已经恢复:

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.

通过上面的讲解,相信你已经初步明白怎么样保存和恢复tensorflow的网络模型。在下一节中,将使用一个实际的例子讲解如何加载任意一个已经训练好的模型。

4.恢复训练模式

现在你已经明白了如何保存和恢复Tensorflow模型,让我们制定了切实可行的指导,以恢复任何预先训练的模型,并用它来预测,微调或进一步训练。在tensorflow网络中,如果有占位符,那么就需要将数据传入占位符中。但是,需要注意的当保存tensorflow模型的时候,占位符的数据不会被保存(占位符本身的变量是被保存的)。

import tensorflow as tf

#Prepare to feed input, i.e. feed_dict and placeholders
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}

#Define a test operation that we will restore
w3 = tf.add(w1,w2)
w4 = tf.multiply(w3,b1,name="op_to_restore")
sess = tf.Session()
sess.run(tf.global_variables_initializer())

#Create a saver object which will save all the variables
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 

#Now, save the graph
saver.save(sess, 'my_test_model',global_step=1000)

所以,当我们要恢复它,我们不仅要恢复网络图和相关变量参数,而且还准备新的feed_dict(数据)传入占位符中。

通过graph.get_tensor_by_name()方法可以恢复所保存的占位符和算子。比如,下面的w1是一个占位符,op_to_restore是一个算子。

#How to access saved variable/Tensor/placeholders 
w1 = graph.get_tensor_by_name("w1:0")

## How to access saved operation
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

如果我们只是想在同一个神经网络中运行不同的数据,你可以简单地通过feed_dict输入数据到占位符中。

import tensorflow as tf

sess=tf.Session()    
#First let's load meta graph and restore weights
saver = tf.train.import_meta_graph('my_test_model-1000.meta')
saver.restore(sess,tf.train.latest_checkpoint('./'))


# Now, let's access and create placeholders variables and
# create feed-dict to feed new data

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}

#Now, access the op that you want to run. 
op_to_restore = graph.get_tensor_by_name("op_to_restore:0")

print sess.run(op_to_restore,feed_dict)
#This will print 60 which is calculated 
#using new values of w1 and w2 and saved value of b1.

下面是完整的程序,需要修改路径model_saving_path;

import tensorflow as tf;
import os;

model_saving_path = "C:\\Users\\USER\\Desktop\\temp\\temp_model4"
model_name = 'saving_restoring';


def save():
    w1 = tf.placeholder(dtype=tf.float32, name='w1');
    w2 = tf.placeholder(dtype=tf.float32, 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');
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        saver = tf.train.Saver();
        print(sess.run(w4, feed_dict));
        saver.save(sess, os.path.join(model_saving_path, model_name), global_step=1000);


def restore0():
    with tf.Session() as sess:
        saver = tf.train.import_meta_graph(
            os.path.join(model_saving_path, model_name+'-1000.meta'))
        saver.restore(sess, tf.train.latest_checkpoint(model_saving_path))

        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');
        print(sess.run(op_to_restore, feed_dict))


def restore():
"""不能以这样的方式恢复占位符,会报错:
InvalidArgumentError (see above for traceback): You must feed a value for placeholder tensor 'w1_1' with dtype float
因为对于一个占位符而已,它所包含的不仅仅是占位符变量的定义部分,还包含数据,而tensorflow不保存占位符的数据部分。应通过graph.get_tensor_by_name的方式获取,然后在feed数据进去"""
    w1 = tf.placeholder(dtype=tf.float32, name='w1');
    w2 = tf.placeholder(dtype=tf.float32, name='w2');
    with tf.Session() as sess:
        saver = tf.train.import_meta_graph(
            os.path.join(model_saving_path, model_name+'-1000.meta'))
        saver.restore(sess, tf.train.latest_checkpoint(model_saving_path))

        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');
        print(sess.run(op_to_restore, feed_dict))

save()
restore0();

如果你想在原来的神经网络图只是添加更多的层,然后训练它。在上面程序的基础之上修改得到下面程序:

def restore2():
    with tf.Session() as sess:
        saver = tf.train.import_meta_graph(
            os.path.join(model_saving_path, model_name+'-1000.meta'))
        saver.restore(sess, tf.train.latest_checkpoint(model_saving_path))

        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');
        # Add more to the current graph
        add_on_op = tf.multiply(op_to_restore, 2)
        print(sess.run(add_on_op, feed_dict))
        # This will print 120.

如果我只想恢复原来神经网络的一部分参数或者一部分算子,然后利用这一部分参数或者算子构建新的神经网络模型,怎么办?可以使用graph.get_tensor_by_name()方法。下面是一个真实的例子。在这里,我们使用.meta加载VGG(预先训练好的网络),并做一些修改。

......
......
saver = tf.train.import_meta_graph('vgg.meta')
# Access the graph
graph = tf.get_default_graph()
## Prepare the feed_dict for feeding data for fine-tuning 

#Access the appropriate output for fine-tuning
fc7= graph.get_tensor_by_name('fc7:0')

#use this if you only want to change gradients of the last layer
fc7 = tf.stop_gradient(fc7) # It's an identity function
fc7_shape= fc7.get_shape().as_list()

new_outputs=2
weights = tf.Variable(tf.truncated_normal([fc7_shape[3], num_outputs], stddev=0.05))
biases = tf.Variable(tf.constant(0.05, shape=[num_outputs]))
output = tf.matmul(fc7, weights) + biases
pred = tf.nn.softmax(output)

# Now, you run this with fine-tuning data in sess.run()

如有疑问,请在评论区里面留言! 谢谢

猜你喜欢

转载自blog.csdn.net/liangyihuai/article/details/78515913
今日推荐