學習筆記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++文件(*http://_ops.cc文件)中註冊新操作。定義操作功能介面規範,操作名稱、輸入、輸出、屬性等。在C++文件(*http://_kenels.cc文件)實現操作,可以實現在CPU、GPU多個內核上。測試操作,編譯操作庫文件(*_ops.so文件),Python使用操作。
最佳實踐。詞嵌入。源代碼 https://github.com/tensorflow/models/blob/master/tutorials/embedding/word2vec_optimized.py 。
第一步,創建http://word2vec_ops.cc註冊兩個操作,SkipgramWord2vec、NegTrainWord2vec。
第二步,兩個操作在CPU設備實現,生成http://word2vec_kernels.cc文件。
第三步,編譯操作類文件,測試。在特定頭文件目錄下編譯,Python提供get_include獲取頭文件目錄,C++編譯器把操作編譯成動態庫。TensorFlow Python API提供tf.load_op_library函數載入動態庫,向TensorFlow 框架註冊操作。load_op_library返回包含操作和內核Python模塊。
參考資料:
《TensorFlow技術解析與實戰》
歡迎付費諮詢(150元每小時),我的微信:qingxingfengzi
推薦閱讀:
TAG:TensorFlow |