1. 程式人生 > >簡單完整地講解tensorflow模型的儲存和恢復

簡單完整地講解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()

如有疑問,請在評論區裡面留言! 謝謝