tensorflow的ckpt及pb模型持久化方式及轉(zhuǎn)化詳解
使用tensorflow訓(xùn)練模型的時(shí)候,模型持久化對(duì)我們來(lái)說(shuō)非常重要。
如果我們的模型比較復(fù)雜,需要的數(shù)據(jù)比較多,那么在模型的訓(xùn)練時(shí)間會(huì)耗時(shí)很長(zhǎng)。如果在訓(xùn)練過(guò)程中出現(xiàn)了模型不可預(yù)期的錯(cuò)誤,導(dǎo)致訓(xùn)練意外終止,那么我們將會(huì)前功盡棄。為了解決這一問(wèn)題,我們可以使用模型持久化(保存為ckpt文件格式)來(lái)保存我們?cè)谟?xùn)練過(guò)程中的臨時(shí)數(shù)據(jù)。、
如果我們訓(xùn)練出的模型需要提供給用戶做離線預(yù)測(cè),那么我們只需要完成前向傳播過(guò)程。這個(gè)時(shí)候我們就可以使用模型持久化(保存為pb文件格式)來(lái)只保存前向傳播過(guò)程中的變量并將變量固定下來(lái),這時(shí)候用戶只需要提供一個(gè)輸入即可得到前向傳播的預(yù)測(cè)結(jié)果。
ckpt和pb持久化方式的區(qū)別在于ckpt文件將模型結(jié)構(gòu)與模型權(quán)重分離保存,便于訓(xùn)練過(guò)程;pb文件則是graph_def的序列化文件,便于發(fā)布和離線預(yù)測(cè)。官方提供freeze_grpah.py腳本來(lái)將ckpt文件轉(zhuǎn)為pb文件。
CKPT模型持久化
首先定義前向傳播過(guò)程;
聲明并得到一個(gè)Saver;
使用Saver.save()保存模型;
# coding=UTF-8 支持中文編碼格式 import tensorflow as tf import shutil import os.path MODEL_DIR = "/home/zheng/PycharmProjects/ckptLoad/Models/" MODEL_NAME = "model.ckpt" #下面的過(guò)程你可以替換成CNN、RNN等你想做的訓(xùn)練過(guò)程,這里只是簡(jiǎn)單的一個(gè)計(jì)算公式 input_holder = tf.placeholder(tf.float32, shape=[1], name="input_holder") #輸入占位符,并指定名字,后續(xù)模型讀取可能會(huì)用的 W1 = tf.Variable(tf.constant(5.0, shape=[1]), name="W1") B1 = tf.Variable(tf.constant(1.0, shape=[1]), name="B1") _y = (input_holder * W1) + B1 predictions = tf.add(_y, 50, name="predictions") #輸出節(jié)點(diǎn)名字,后續(xù)模型讀取會(huì)用到,比50大返回true,否則返回false init = tf.global_variables_initializer() saver = tf.train.Saver() #聲明saver用于保存模型 with tf.Session() as sess: sess.run(init) print "predictions : ", sess.run(predictions, feed_dict={input_holder: [10.0]}) #輸入一個(gè)數(shù)據(jù)測(cè)試一下 saver.save(sess, os.path.join(MODEL_DIR, MODEL_NAME)) #模型保存 print("%d ops in the final graph." % len(tf.get_default_graph().as_graph_def().node)) #得到當(dāng)前圖有幾個(gè)操作節(jié)點(diǎn)
predictions : [ 101.]
28 ops in the final graph.
注:代碼含義請(qǐng)參考注釋,需要注意的是可以自定義模型保存的路徑
ckpt模型持久化使用起來(lái)非常簡(jiǎn)單,只需要我們聲明一個(gè)tf.train.Saver,然后調(diào)用save()函數(shù),將會(huì)話模型保存到指定的目錄。執(zhí)行代碼結(jié)果,會(huì)在我們指定模型目錄下出現(xiàn)4個(gè)文件
checkpoint : 記錄目錄下所有模型文件列表
ckpt.data : 保存模型中每個(gè)變量的取值
ckpt.meta : 保存整個(gè)計(jì)算圖的結(jié)構(gòu)
ckpt模型加載
# -*- coding: utf-8 -*-) import tensorflow as tf from numpy.random import RandomState # 定義訓(xùn)練數(shù)據(jù)batch的大小 batch_size = 8 #下面的過(guò)程你可以替換成CNN、RNN等你想做的訓(xùn)練過(guò)程,這里只是簡(jiǎn)單的一個(gè)計(jì)算公式 input_holder = tf.placeholder(tf.float32, shape=[1], name="input_holder") #輸入占位符,并指定名字,后續(xù)模型讀取可能會(huì)用的 W1 = tf.Variable(tf.constant(5.0, shape=[1]), name="W1") B1 = tf.Variable(tf.constant(1.0, shape=[1]), name="B1") _y = (input_holder * W1) + B1 predictions = tf.add(_y, 50, name="predictions") #輸出節(jié)點(diǎn)名字,后續(xù)模型讀取會(huì)用到,比50大返回true,否則返回false #saver=tf.train.Saver() # creare a session,創(chuàng)建一個(gè)會(huì)話來(lái)運(yùn)行TensorFlow程序 with tf.Session() as sess: saver = tf.train.import_meta_graph('/home/zheng/Models/model/model.meta') saver.restore(sess, tf.train.latest_checkpoint('/home/zheng/Models/model')) #saver.restore(sess, tf.train.latest_checkpoint('/home/zheng/Models/model')) # 初始化變量 sess.run(tf.global_variables_initializer()) print "predictions : ", sess.run(predictions, feed_dict={input_holder: [10.0]})
代碼結(jié)果,可以看到運(yùn)行結(jié)果一樣
predictions : [ 101.]
PB模型持久化
定義運(yùn)算過(guò)程
通過(guò) get_default_graph().as_graph_def() 得到當(dāng)前圖的計(jì)算節(jié)點(diǎn)信息
通過(guò) graph_util.convert_variables_to_constants 將相關(guān)節(jié)點(diǎn)的values固定
通過(guò) tf.gfile.GFile 進(jìn)行模型持久化
# coding=UTF-8 import tensorflow as tf import shutil import os.path from tensorflow.python.framework import graph_util MODEL_DIR = "/home/zheng/PycharmProjects/pbLoad/Models/" MODEL_NAME = "model" #output_graph = "model/pb/add_model.pb" #下面的過(guò)程你可以替換成CNN、RNN等你想做的訓(xùn)練過(guò)程,這里只是簡(jiǎn)單的一個(gè)計(jì)算公式 input_holder = tf.placeholder(tf.float32, shape=[1], name="input_holder") W1 = tf.Variable(tf.constant(5.0, shape=[1]), name="W1") B1 = tf.Variable(tf.constant(1.0, shape=[1]), name="B1") _y = (input_holder * W1) + B1 predictions = tf.add(_y, 50, name="predictions") init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) print "predictions : ", sess.run(predictions, feed_dict={input_holder: [10.0]}) graph_def = tf.get_default_graph().as_graph_def() #得到當(dāng)前的圖的 GraphDef 部分, #通過(guò)這個(gè)部分就可以完成重輸入層到 #輸出層的計(jì)算過(guò)程 output_graph_def = graph_util.convert_variables_to_constants( # 模型持久化,將變量值固定 sess, graph_def, ["predictions"] #需要保存節(jié)點(diǎn)的名字 ) with tf.gfile.GFile(os.path.join(MODEL_DIR,MODEL_NAME), "wb") as f: # 保存模型 f.write(output_graph_def.SerializeToString()) # 序列化輸出 print("%d ops in the final graph." % len(output_graph_def.node)) print (predictions) # for op in tf.get_default_graph().get_operations(): 打印模型節(jié)點(diǎn)信息 # print (op.name)
結(jié)果輸出
predictions : [ 101.] Converted 2 variables to const ops. 9 ops in the final graph. Tensor("predictions:0", shape=(1,), dtype=float32)
并在指定目錄下生成pb文件模型,保存了從輸入層到輸出層這個(gè)計(jì)算過(guò)程的計(jì)算圖和相關(guān)變量的值,我們得到這個(gè)模型后傳入一個(gè)輸入,既可以得到一個(gè)預(yù)估的輸出值
pb模型文件加載
# -*- coding: utf-8 -*-) from tensorflow.python.platform import gfile import tensorflow as tf from numpy.random import RandomState sess = tf.Session() with gfile.FastGFile('./Models/model', 'rb') as f: graph_def = tf.GraphDef() graph_def.ParseFromString(f.read()) sess.graph.as_default() tf.import_graph_def(graph_def, name='') # 導(dǎo)入計(jì)算圖 # 需要有一個(gè)初始化的過(guò)程 sess.run(tf.global_variables_initializer()) # 需要先復(fù)原變量 sess.run('W1:0') sess.run('B1:0') # 輸入 input_x = sess.graph.get_tensor_by_name('input_holder:0') #input_y = sess.graph.get_tensor_by_name('y-input:0') op = sess.graph.get_tensor_by_name('predictions:0') ret = sess.run(op, feed_dict={input_x:[10]}) print(ret)
輸出結(jié)果
[ 101.]
我們可以看到結(jié)果一致。
ckpt格式轉(zhuǎn)pb格式
通過(guò)傳入 CKPT 模型的路徑得到模型的圖和變量數(shù)據(jù)
通過(guò) import_meta_graph 導(dǎo)入模型中的圖
通過(guò) saver.restore 從模型中恢復(fù)圖中各個(gè)變量的數(shù)據(jù)
通過(guò) graph_util.convert_variables_to_constants 將模型持久化
# coding=UTF-8 import tensorflow as tf import os.path import argparse from tensorflow.python.framework import graph_util MODEL_DIR = "/home/zheng/PycharmProjects/ckptToPb/model/" MODEL_NAME = "frozen_model" def freeze_graph(model_folder): checkpoint = tf.train.get_checkpoint_state(model_folder) #檢查目錄下ckpt文件狀態(tài)是否可用 input_checkpoint = checkpoint.model_checkpoint_path #得ckpt文件路徑 output_graph = os.path.join(MODEL_DIR, MODEL_NAME) #PB模型保存路徑 output_node_names = "predictions" #原模型輸出操作節(jié)點(diǎn)的名字 saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=True) #得到圖、clear_devices :Whether or not to clear the device field for an `Operation` or `Tensor` during import. graph = tf.get_default_graph() #獲得默認(rèn)的圖 input_graph_def = graph.as_graph_def() #返回一個(gè)序列化的圖代表當(dāng)前的圖 with tf.Session() as sess: saver.restore(sess, input_checkpoint) #恢復(fù)圖并得到數(shù)據(jù) print "predictions : ", sess.run("predictions:0", feed_dict={"input_holder:0": [10.0]}) # 測(cè)試讀出來(lái)的模型是否正確,注意這里傳入的是輸出 和輸入 節(jié)點(diǎn)的 tensor的名字,不是操作節(jié)點(diǎn)的名字 output_graph_def = graph_util.convert_variables_to_constants( #模型持久化,將變量值固定 sess, input_graph_def, output_node_names.split(",") #如果有多個(gè)輸出節(jié)點(diǎn),以逗號(hào)隔開(kāi) ) with tf.gfile.GFile(output_graph, "wb") as f: #保存模型 f.write(output_graph_def.SerializeToString()) #序列化輸出 print("%d ops in the final graph." % len(output_graph_def.node)) #得到當(dāng)前圖有幾個(gè)操作節(jié)點(diǎn) if __name__ == '__main__': #parser = argparse.ArgumentParser() #parser.add_argument("model_folder", type=str, help="input ckpt model dir") #命令行解析,help是提示符,type是輸入的類型, # 這里運(yùn)行程序時(shí)需要帶上模型ckpt的路徑,不然會(huì)報(bào) error: too few arguments #aggs = parser.parse_args() #freeze_graph(aggs.model_folder) freeze_graph("/home/zheng/PycharmProjects/ckptLoad/Models/") #模型目錄
注意改變ckpt模型目錄及pb文件保存目錄 。
運(yùn)行結(jié)果為
predictions : [ 101.] Converted 2 variables to const ops. 9 ops in the final graph.
總結(jié):cpkt文件格式將模型保存為4個(gè)文件,pb文件格式為一個(gè)。ckpt模型持久化方式將圖結(jié)構(gòu)與權(quán)重參數(shù)分開(kāi)保存,多了模型更多的細(xì)節(jié),適合模型訓(xùn)練階段;而pb持久化方式完成了從輸入到輸出的前向傳播,完成了端到端的形式,更是個(gè)離線使用。
以上這篇tensorflow的ckpt及pb模型持久化方式及轉(zhuǎn)化詳解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Python Selenium 之?dāng)?shù)據(jù)驅(qū)動(dòng)測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了Python Selenium 之?dāng)?shù)據(jù)驅(qū)動(dòng)測(cè)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08使用Python通過(guò)oBIX協(xié)議訪問(wèn)Niagara數(shù)據(jù)的示例
這篇文章主要介紹了使用Python通過(guò)oBIX協(xié)議訪問(wèn)Niagara數(shù)據(jù)的示例,幫助大家更好的理解和學(xué)習(xí)python,感興趣的朋友可以了解下2020-12-12使用mypy對(duì)python程序進(jìn)行靜態(tài)檢查
大家好,本篇文章主要講的是使用mypy對(duì)python程序進(jìn)行靜態(tài)檢查,感興趣的同學(xué)快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下哦2021-11-11Python實(shí)現(xiàn)排序方法常見(jiàn)的四種
本文給大家分享python四種常見(jiàn)排序方法,每種方法通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-07-07解決Pycharm出現(xiàn)的部分快捷鍵無(wú)效問(wèn)題
今天小編就為大家分享一篇解決Pycharm出現(xiàn)的部分快捷鍵無(wú)效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10Python實(shí)現(xiàn)多任務(wù)版的udp聊天器
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)多任務(wù)版的udp聊天器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Python將json文件寫(xiě)入ES數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Python將json文件寫(xiě)入ES數(shù)據(jù)庫(kù)的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-04-04