標籤:

學習筆記TF049:TensorFlow 模型存儲載入、隊列線程、載入數據、自定義操作

生成檢查點文件(chekpoint file),擴展名.ckpt,tf.train.Saver對象調用Saver.save()生成。包含權重和其他程序定義變數,不包含圖結構。另一程序使用,需要重新創建圖形結構,告訴TensorFlow如何處理權重。

生成圖協議文件(graph proto file),二進位文件,擴展名.pb,tf.tran.write_graph()保存,只包含圖形結構,不包含權重,tf.import_graph_def載入圖形。

模型存儲,建立一個tf.train.Saver()保存變數,指定保存位置,擴展名.ckpt。

神經網路,兩個全連接層和一個輸出層,訓練MNIST數據集,存儲訓練好的模型。

載入數據、定義模型:

#載入數據

mnist = input_data.read_data_sets("MNIST_data/",one_hot=True)

trX,trY,teX,teY = mnist.train.images,mnist.train.labels,mnist.test.images,mnist.test.labels

X = tf.placeholder("float",[None,784])

Y = tf.placeholder("float",[None,10])

#初始化權重參數

w_h = init_weights([784,625])

w_h2 = init_weights([625,625])

w_o = int_weights([625,10])

#定義權重函數

def init_weights(shape):

return tf.Variable(tf.random_normal(shape,stddev=0.01))

#定義模型

def model(X,w_h,w_h2,w_o,p_keep_input,p_keep_hidden):

#第一個全連接層

X = tf.nn.dropout(X,p_keep_input)

h = tf.nn.relu(tf.matmul(X,w_h))

h = tf.nn.dropout(h,p_keep,hidden)

#第二個全連接層

h2 = tf.nn.relu(tf.matmul(h,w_h2))

h2 = tf.nn.dropout(h2,p_keep_hidden)

return tf.matmul(h2,w_o)#輸出預測值

#定義損失函數

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(py_x,Y))

train_op = tf.train.RMSPropOptimizer(0.001,0.9).minimize(cost)

predict_op = tf.argmax(py_x,1)

訓練模型及存儲模型:

#定義存儲路徑

ckpt_dir = "./ckpt_dir"

if not os.path.exists(ckpt_dir):

os.makedirs(ckpt_dir)

定義計數器,為訓練輪數計數:

#計數器變數,設置它的trainable=False,不需要被訓練

global_step = tf.Variable(0,name=global_step,trainable=False)

定義完所有變數,tf.train.Saver()保存、提取變數,後面定義變數不被存儲:

#聲明完所有變數,調tf.train.Saver

saver = tf.train.Saver()

#之後變數不被存儲

non_storable_variable = tf.Variable(777)

訓練模型並存儲:

with tf.Session() as sess:

tf.initialize_all_variables().run()

start = global_step.eval() #得到global_stepp初始值

print("Start from:",start)

for i in range(start,100):

#128 batch_size

for start,end in zip(range(0,len(trX),128),range(128,len(trX)+1,128)):

sess.run(train_op,feed_dict={X:trX[start:end],Y:trY[start:end],p_keep_input:0.8, p_keep_hidden:0.5})

global_step.assign(i).eval() #更新計數器

saver.save(sess,ckpt_dir+"/model.ckpt",global_step=global_step) #存儲模型

訓練過程,ckpt_dir出現16個文件,5個model.ckpt-{n}.data00000-of-00001文件,保存的模型。5個model.ckpt-{n}.meta文件,保存的元數據。TensorFlow默認只保存最近5個模型和元數據,刪除前面沒用模型和元數據。5個model.ckpt-{n}.index文件。{n}代表迭代次數。1個檢查點文本文件,保存當前模型和最近5個模型。

將之前訓練參數保存下來,在出現意外狀競接著上一次地方開始訓練。每個固定輪數在檢查點保存一個模型(.ckpt文件),隨時將模型拿出來預測。

載入模型。

saver.restore載入模型:

with tf.Session() as sess:

tf.initialize_all_variables().run()

ckpt = tf.train.get_checkpoint_state(ckpt_dir)

if ckpt and ckpt.model_checkpoint_path:

print(ckpt.model_checkpoint_path)

saver.restore(sess,ckpt.model_checkpoint_path) #載入所有參數

圖存儲載入。

僅保存圖模型,圖寫入二進位協議文件:

v = tf.Variable(0,name=my_variable)

sess = tf.Session()

tf.train.write_graph(sess.graph_def,/tmp/tfmodel,train.pbtxt)

讀取:

with tf.Session() as _sess:

with grile.FastGFile("/tmp/tfmodel/train.pbtxt",rb) as f:

graph_def = tf.GraphDef()

graph_def.ParseFromString(f.read())

_sess.graph.as_default()

tf.import_graph_def(graph_def,name=tfgraph)

隊列、線程。

隊列(queue),圖節點,有狀態節點。其他節點(入隊節點enqueue,出隊節點depueue),修改它內容。入隊節點把新元素插到隊列末尾,出隊節點把隊列前面元素刪除。

FIFOQueue、RandomShuffleQueue,源代碼在tensorflow-1.0.0/tensorflow/python/ops/data_flow_ops.py 。

FIFOQueue,創建先入先出隊列。循環神經網路結構,讀入訓練樣本需要有序。

創建含隊列圖:

import tensorflow as tf

#創建先入先出隊列,初始化隊列插入0.1、0.2、0.3數字

q = tf.FIFOQueue(3,"float")

init = q.enqueue_many(([0.1,0.2,0.3]))

#定義出隊、+1、入隊操作

x = q.dequeue()

y = x +1

q_inc = q.enqueue([y])

開戶會話,執行2次q_inc操作,查看隊列內容:

with tf.Session() as sess:

sess.run(init)

quelen = sess.run(q.size())

for i in range(2):

sess.run(q_inc) #執行2次操作,隊列值變0.3,1.1,1.2

quelen = sess.run(q.size())

for i in range(quelen):

print(sess.run(q.dequeue())) #輸出隊列值

RandomShuffleQueue,創建隨機隊列。出隊列,以隨機順序產生元素。訓練圖像樣本,CNN網路結構,無序讀入訓練樣本,每次隨機產生一個訓練樣本。

非同步計算時非常重要。TensorFlow會話支持多線程,在主線程訓練操作,RandomShuffleQueue作訓練輸入,開多個線程準備訓練樣本。樣本壓入隊列,主線程從隊列每次取出mini-batch樣本訓練。

創建隨機隊列,最大長度10,出隊後最小長度2:

q = tf.RandomShuffleQueue(capacity=10,min_after_dequeue=2,dtype="float")

開戶會話,執行10次入隊操作,8次出隊操作:

sess = tf.Session()

for i in range(0,10): #10次入隊

sess.run(q.enqueue(i))

for i in range(0,8): #8次出隊

print(sess.run(q.dequeue()))

阻斷,隊列長度等於最小值,執行出隊操作;隊列長度等於最大值,執行入隊操作。

設置繪畫運行時等待時間解除阻斷:

run_iptions = tf.RunOptions(timeout_in_ms = 10000) #等待10秒

try:

sess.run(q.dequeue(),options=run_options)

except tf.errors.DeadlineExceededError:

print(out of range)

會話主線程入隊操作,數據量很大時,入隊操作從硬碟讀取數據,放入內存,主線程需要等待入隊操作完成,才能進行訓練操作。會話運行多個線程,線程管理器QueueRunner創建一系列新線程進行入隊操作,主線程繼續使用數據,訓練網張和讀取數據是非同步的,主線程訓練網路,另一線程將數據從硬碟讀入內存。

隊列管理器。

創建含隊列圖:

q = tf.FIFOQueue(1000,"float")

counter = tf.Variable(0.0) #計數器

increment_op = tf.assign_add(counter,tf.constant(1.0)) #操作:給計算器加1

enqueue_op = q.enqueue(counter) #操作:計數器值加入隊列

創建隊列管理器QueueRunner,兩個操作向隊列q添加元素。只用一個線程:

qr = tf.train.QueueRunner(q,enqueue_ops=[increment_op,enqueue_op] * 1)

啟動會話,從隊列管理器qr創建線程:

#主線程

with tf.Session() as sess:

sess.run(tf.global_variables_initializer())

enqueue_threads = qr.create_threads(sess,start=True) #啟動入隊線程

#主線程

for i in range(10):

print(sess.run(q.dequeue()))

不是自然數列,線程被陰斷。加1操作和入隊操作不同步。加1操作執行多次後,才進行一次入隊操作。主線程訓練(出隊操作)和讀取數據線程的訓練(入隊操作)非同步,主線程一直等待數據送入。

入隊線程自顧執行,出隊操作完成,程序無法結束。tf.train.Coordinator實現線程同步,終止其他線程。

線程、協調器。

協調器(coordinator)管理線程。

#主線程

sess = tf.Session()

sess.run(tf.global_variables_initializer())

#Coordinator:協調器,協調線程間關係可以視為一種信號量,用來做同步

coord = tf.train.Coordinator()

#啟動入隊線程,協調器是線程的參數

enqueue_threads = qr.create_threads(sess,coord = coord,start=True)

#主線程

for i in range(0,10):

print(sess.run(q.dequeue()))

coord.request_stop() #通知其他線程關閉

coord.join(enqueue_threads) #join操作等待其他線程結束,其他所有線程關閉之後,函數才能返回

關閉隊列線程,執行出隊操作,拋出tf.errors.OutOfRange錯誤。coord.request_stop()和主線程出隊操作q.dequeue()調換位置。

coord.request_stop()

#主線程

for i in range(0,10):

print(sess.run(q.dequeue()))

coord.join(enqueue_threads)

tf.errors.OutOfRangeError捕捉錯誤:

coord.request_stop()

#主線程

for i in range(0,10):

try:

print(sess.run(q.dequeue()))

except tf.errors.OutOfRangeError:

break

coord.join(enqueue_threads)

所有隊列管理器默認加在圖tf.GraphKeys.QUEUE_RENNERS集合。

載入數據。

預載入數據(preloaded data),在TensorFlow圖中定義常量或變數保存所有數據。填充數據(feeding),Python產生數據,數據填充後端。從文件讀取數據(reading from file),隊列管理器從文件中讀取數據。

預載入數據。數據直接嵌在數據流圖中,訓練數據大時,很消耗內存。

x1 = tf.constant([2,3,4])

x2 = tf.constant([4,0,1])

y = tf.add(x1,x2)

填充數據。sess.run()中的feed_dict參數,Python產生數據填充後端。數據量大、消耗內存,數據類型轉換等中間環節增加開銷。

import tensorflow as tf

#設計圖

a1 = tf.placeholder(tf.int16)

a2 = tf.placeholder(tf.int16)

b = tf.add(x1,x2)

#用Python產生數據

li1 = [2,3,4]

li2 = [4,0,1]

#打開會話,數據填充後端

with tf.Session() as sess:

print sess.run(b,feed_dict={a1:li1,a2:li2})

從文件讀取數據。圖中定義好文件讀取方法,TensorFlow從文件讀取數據,解碼成可使用樣本集。

把樣本數據寫入TFRecords二進位文件。再從隊列讀取。

TFRecords二進位文件,更好利用內存,方便複製和移動,不需要單獨標記文件。tensorflow-1.1.0/tensorflow/examples/how_tos/reading_data/convert_to_records.py。

生成TFRecords文件。定義主函數,給訓練、驗證、測試數據集做轉換。獲取數據。編碼uint8。數據轉換為tf.train.Example類型,寫入TFRecords文件。轉換函數convert_to,數據填入tf.train.Example協議緩衝區(protocol buffer),協議組沖區序列轉為字元串,tf.python_io.TFRecordWriter寫入TFRecords文件。55000個訓練數據,5000個驗證數據,10000個測試數據。黑白圖像,單通道。寫入協議緩衝區,height、width、depth、label編碼int64類型,image_raw編碼成二進位。序列化為字元串。運行結束,在/tmp/data生成文件train.tfrecords、validation.tfrecords、test.tfrecords。

從隊列讀取。創建張量,從二進位文件讀取一個樣本。創建張量,從二進位文件隨機讀取一個mini-batch。把每一批張量傳入網路作為輸入節點。代碼 tensorflow-1.1.0/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py。首先定義從文件中讀取並解析一個樣本。輸入文件名隊列。解析example。寫明features里key名稱。圖片 string類型。標記 int64類型。BytesList 重新解碼,把string類型0維Tensor變成unit8類型一維Tensor。image張量形狀Tensor("input/sub:0",shape=(784,),dtype=float32)。把標記從uint8類型轉int32類型。label張量形狀Tensor("input/Cast_1:0",shape=(),dtype=int32)。

tf.train.shuffle_batch樣本隨機化,獲得最小批次張量。輸入參數,train 選擇輸入訓練數據/驗證數據,batch_size 訓練每批樣本數,num_epochs 過幾遍數據 設置0/None表示永遠訓練下去。返回結果,images,類型float, 形狀[batch_size,mnist.IMAGE_PIXELS],範圍[-0.5,0.5]。labels,類型int32,形狀[batch_size],範圍[0,mnist.NUM_CLASSES]。tf.train.QueueRunner用tf.train.start_queue_runners()啟動線程。獲取文件路徑,/tmp/data/train.tfrecords, /tmp/data/validation.records。tf.train.string_input_producer返回一個QueueRunner,裡面有一個FIFOQueue。如果樣本量很大,分成若干文件,文件名列表傳入。隨機化example,規整成batch_size大小。留下部分隊列,保證每次有足夠數據做隨機打亂。

生成batch張量作網路輸入,訓練。輸入images、labels。構建從揄模型預測數據的圖。定義損失函數。加入圖操作,訓練模型。初始化參數,string_input_producer內療創建一個epoch計數變數,歸入tf.GraphKeys.LOCAL_VARIABLES集合,單獨用initialize_local_variables()初始化。開啟輸入入階線程。進入永久循環。每100次訓練輸出一次結果。通知其他線程關閉。數據集大小55000,2輪訓練,110000個數據,batch_size大小100,訓練次數1100次,每100次訓練輸出一次結果,輸出11次結果。

TensorFlow使用TFRecords文件訓練樣本步驟:在生成文件名隊列中,設定epoch數量。訓練時,設定為無窮循環。在讀取數據時,如果捕捉到錯誤,終止。

實現自定義操作。

需要熟練掌握C++語言。對張量流動和前向傳播、反向傳播有深理解。

步驟。在C++文件(*_ops.cc文件)中註冊新操作。定義操作功能介面規範,操作名稱、輸入、輸出、屬性等。在C++文件(*_kenels.cc文件)實現操作,可以實現在CPU、GPU多個內核上。測試操作,編譯操作庫文件(*_ops.so文件),Python使用操作。

最佳實踐。詞嵌入。源代碼 github.com/tensorflow/m

第一步,創建word2vec_ops.cc註冊兩個操作,SkipgramWord2vec、NegTrainWord2vec。

第二步,兩個操作在CPU設備實現,生成word2vec_kernels.cc文件。

第三步,編譯操作類文件,測試。在特定頭文件目錄下編譯,Python提供get_include獲取頭文件目錄,C++編譯器把操作編譯成動態庫。TensorFlow Python API提供tf.load_op_library函數載入動態庫,向TensorFlow 框架註冊操作。load_op_library返回包含操作和內核Python模塊。

參考資料:

《TensorFlow技術解析與實戰》

歡迎付費諮詢(150元每小時),我的微信:qingxingfengzi


推薦閱讀:

TAG:TensorFlow |