tensorflow 1.0 學習:模型的儲存與恢復(Saver)
儲存模型:
- 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.
- 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)
必須強調的是:這裡4,5,6,11行中的name=’w1′, name=’w2′, name=’bias’, name=’op_to_restore’ 千萬不能省略,這是恢復還原模型的關鍵。
還原模型:
- 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('./'))
- # Access saved Variables directly
- print(sess.run('bias:0'))
- # This will print 2, which is the value of bias that we saved
- # 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
還原當然是用restore方法,這裡的18,19,23行就是剛才的name關鍵字指定的Tensor變數,必須找對才能進行還原恢復。
這段程式碼中,通過saver.save
函式將TensorFlow模型儲存到了save/model.ckpt檔案中,這裡程式碼中指定路徑為"save/model.ckpt"
,也就是儲存到了當前程式所在資料夾裡面的save
資料夾中。
TensorFlow模型會儲存在後綴為.ckpt
的檔案中。儲存後在save這個資料夾中會出現3個檔案,因為TensorFlow會將計算圖的結構和圖上引數取值分開儲存。
-
checkpoint
檔案儲存了一個目錄下所有的模型檔案列表,這個檔案是tf.train.Saver
類自動生成且自動維護的。在checkpoint
檔案中維護了由一個tf.train.Saver
類持久化的所有TensorFlow模型檔案的檔名。當某個儲存的TensorFlow模型檔案被刪除時,這個模型所對應的檔名也會從checkpoint
檔案中刪除。checkpoint
中內容的格式為CheckpointState Protocol Buffer. -
model.ckpt.meta
檔案儲存了TensorFlow計算圖的結構,可以理解為神經網路的網路結構
TensorFlow通過元圖(MetaGraph)來記錄計算圖中節點的資訊以及執行計算圖中節點所需要的元資料。TensorFlow中元圖是由MetaGraphDef Protocol Buffer定義的。MetaGraphDef 中的內容構成了TensorFlow持久化時的第一個檔案。儲存MetaGraphDef 資訊的檔案預設以.meta為字尾名,檔案model.ckpt.meta
中儲存的就是元圖資料。 -
model.ckpt
檔案儲存了TensorFlow程式中每一個變數的取值,這個檔案是通過SSTable格式儲存的,可以大致理解為就是一個(key,value)列表。model.ckpt
檔案中列表的第一行描述了檔案的元資訊,比如在這個檔案中儲存的變數列表。列表剩下的每一行儲存了一個變數的片段,變數片段的資訊是通過SavedSlice Protocol Buffer定義的。SavedSlice型別中儲存了變數的名稱、當前片段的資訊以及變數取值。TensorFlow提供了tf.train.NewCheckpointReader
類來檢視model.ckpt
檔案中儲存的變數資訊。如何使用tf.train.NewCheckpointReader
類這裡不做說明,自查。
將訓練好的模型引數儲存起來,以便以後進行驗證或測試,這是我們經常要做的事情。tf裡面提供模型儲存的是tf.train.Saver()模組。
模型儲存,先要建立一個Saver物件:如
saver=tf.train.Saver()
在建立這個Saver物件的時候,有一個引數我們經常會用到,就是 max_to_keep 引數,這個是用來設定儲存模型的個數,預設為5,即 max_to_keep=5,儲存最近的5個模型。如果你想每訓練一代(epoch)就想儲存一次模型,則可以將 max_to_keep設定為None或者0,如:
saver=tf.train.Saver(max_to_keep=0)
但是這樣做除了多佔用硬碟,並沒有實際多大的用處,因此不推薦。
當然,如果你只想儲存最後一代的模型,則只需要將max_to_keep設定為1即可,即
saver=tf.train.Saver(max_to_keep=1)
建立完saver物件後,就可以儲存訓練好的模型了,如:
saver.save(sess,'ckpt/mnist.ckpt',global_step=step)
第一個引數sess,這個就不用說了。第二個引數設定儲存的路徑和名字,第三個引數將訓練的次數作為字尾加入到模型名字中。
saver.save(sess, 'my-model', global_step=0) ==> filename: 'my-model-0'
...
saver.save(sess, 'my-model', global_step=1000) ==> filename: 'my-model-1000'
看一個mnist例項:
# -*- coding: utf-8 -*- """ Created on Sun Jun 4 10:29:48 2017 @author: Administrator """ import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=False) x = tf.placeholder(tf.float32, [None, 784]) y_=tf.placeholder(tf.int32,[None,]) dense1 = tf.layers.dense(inputs=x, units=1024, activation=tf.nn.relu, kernel_initializer=tf.truncated_normal_initializer(stddev=0.01), kernel_regularizer=tf.nn.l2_loss) dense2= tf.layers.dense(inputs=dense1, units=512, activation=tf.nn.relu, kernel_initializer=tf.truncated_normal_initializer(stddev=0.01), kernel_regularizer=tf.nn.l2_loss) logits= tf.layers.dense(inputs=dense2, units=10, activation=None, kernel_initializer=tf.truncated_normal_initializer(stddev=0.01), kernel_regularizer=tf.nn.l2_loss) loss=tf.losses.sparse_softmax_cross_entropy(labels=y_,logits=logits) train_op=tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss) correct_prediction = tf.equal(tf.cast(tf.argmax(logits,1),tf.int32), y_) acc= tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) sess=tf.InteractiveSession() sess.run(tf.global_variables_initializer()) saver=tf.train.Saver(max_to_keep=1) for i in range(100): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys}) val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) print('epoch:%d, val_loss:%f, val_acc:%f'%(i,val_loss,val_acc)) saver.save(sess,'ckpt/mnist.ckpt',global_step=i+1) sess.close()
程式碼中紅色部分就是儲存模型的程式碼,雖然我在每訓練完一代的時候,都進行了儲存,但後一次儲存的模型會覆蓋前一次的,最終只會儲存最後一次。因此我們可以節省時間,將儲存程式碼放到迴圈之外(僅適用max_to_keep=1,否則還是需要放在迴圈內).
在實驗中,最後一代可能並不是驗證精度最高的一代,因此我們並不想預設儲存最後一代,而是想儲存驗證精度最高的一代,則加個中間變數和判斷語句就可以了。
saver=tf.train.Saver(max_to_keep=1) max_acc=0 for i in range(100): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys}) val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) print('epoch:%d, val_loss:%f, val_acc:%f'%(i,val_loss,val_acc)) if val_acc>max_acc: max_acc=val_acc saver.save(sess,'ckpt/mnist.ckpt',global_step=i+1) sess.close()
如果我們想儲存驗證精度最高的三代,且把每次的驗證精度也隨之儲存下來,則我們可以生成一個txt檔案用於儲存。
saver=tf.train.Saver(max_to_keep=3) max_acc=0 f=open('ckpt/acc.txt','w') for i in range(100): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys}) val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) print('epoch:%d, val_loss:%f, val_acc:%f'%(i,val_loss,val_acc)) f.write(str(i+1)+', val_acc: '+str(val_acc)+'\n') if val_acc>max_acc: max_acc=val_acc saver.save(sess,'ckpt/mnist.ckpt',global_step=i+1) f.close() sess.close()
模型的恢復用的是restore()函式,它需要兩個引數restore(sess, save_path),save_path指的是儲存的模型路徑。我們可以使用tf.train.latest_checkpoint()來自動獲取最後一次儲存的模型。如:
model_file=tf.train.latest_checkpoint('ckpt/') saver.restore(sess,model_file)
則程式後半段程式碼我們可以改為:
sess=tf.InteractiveSession() sess.run(tf.global_variables_initializer()) is_train=False saver=tf.train.Saver(max_to_keep=3) #訓練階段 if is_train: max_acc=0 f=open('ckpt/acc.txt','w') for i in range(100): batch_xs, batch_ys = mnist.train.next_batch(100) sess.run(train_op, feed_dict={x: batch_xs, y_: batch_ys}) val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) print('epoch:%d, val_loss:%f, val_acc:%f'%(i,val_loss,val_acc)) f.write(str(i+1)+', val_acc: '+str(val_acc)+'\n') if val_acc>max_acc: max_acc=val_acc saver.save(sess,'ckpt/mnist.ckpt',global_step=i+1) f.close() #驗證階段 else: model_file=tf.train.latest_checkpoint('ckpt/') saver.restore(sess,model_file) val_loss,val_acc=sess.run([loss,acc], feed_dict={x: mnist.test.images, y_: mnist.test.labels}) print('val_loss:%f, val_acc:%f'%(val_loss,val_acc)) sess.close()
標紅的地方,就是與儲存、恢復模型相關的程式碼。用一個bool型變數is_train來控制訓練和驗證兩個階段。
整個源程式:
# -*- coding: utf-8 -*- """ Created on Sun Jun 4 10:29:48 2017 @author: Administrator """ import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = input_data.read_data_sets("MNIST_data/", one_hot=False) x = tf.placeholder(tf.float32, [None, 784]) y_=tf.placeholder(tf.int32,[None,]) dense1 = tf.layers.dense(inputs=x, units=1024, activation=tf.nn.relu, kernel_initializer=tf.truncated_normal_initializer(stddev=0.01), kernel_regularizer=tf.nn.l2_loss) dense2= tf.layers.dense(inputs=dense1, units=512,