1. 程式人生 > >Tensorflow 模型持久化

Tensorflow 模型持久化

當我們使用 tensorflow 訓練神經網路的時候,模型持久化對於我們的訓練有很重要的作用。

  • 如果我們的神經網路比較複雜,訓練資料比較多,那麼我們的模型訓練就會耗時很長,如果在訓練過程中出現某些不可預計的錯誤,導致我們的訓練意外終止,那麼我們將會前功盡棄。為了避免這個問題,我們就可以通過模型持久化(儲存為CKPT格式)來暫存我們訓練過程中的臨時資料。

  • 如果我們訓練的模型需要提供給使用者做離線的預測,那麼我們只需要前向傳播的過程,只需得到預測值就可以了,這個時候我們就可以通過模型持久化(儲存為PB格式)只儲存前向傳播中需要的變數並將變數的值固定下來,這個時候只需使用者提供一個輸入,我們就可以通過模型得到一個輸出給使用者。

儲存為 CKPT 格式的模型

  1. 定義運算過程
  2. 宣告並得到一個 Saver
  3. 通過 Saver.save 儲存模型
# coding=UTF-8 支援中文編碼格式
import tensorflow as tf
import shutil
import os.path

MODEL_DIR = "model/ckpt"
MODEL_NAME = "model.ckpt"

# if os.path.exists(MODEL_DIR): 刪除目錄
#     shutil.rmtree(MODEL_DIR)
if not tf.gfile.Exists(MODEL_DIR): #建立目錄
    tf.gfile.MakeDirs(MODEL_DIR)

#下面的過程你可以替換成CNN、RNN等你想做的訓練過程,這裡只是簡單的一個計算公式
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.greater(_y, 50, name="predictions") #輸出節點名字,後續模型讀取會用到,比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]}) #輸入一個數據測試一下
    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)) #得到當前圖有幾個操作節點

for op in tf.get_default_graph().get_operations(): #列印模型節點資訊
    print (op.name, op.values())

執行後生成的檔案如下:

這裡寫圖片描述

  • checkpoint : 記錄目錄下所有模型檔案列表
  • ckpt.data : 儲存模型中每個變數的取值
  • ckpt.meta : 儲存整個計算圖的結構

儲存為 PB 格式模型

  1. 定義運算過程
  2. 通過 get_default_graph().as_graph_def() 得到當前圖的計算節點資訊
  3. 通過 graph_util.convert_variables_to_constants 將相關節點的values固定
  4. 通過 tf.gfile.GFile 進行模型持久化
# coding=UTF-8
import tensorflow as tf
import shutil
import os.path
from tensorflow.python.framework import graph_util


# MODEL_DIR = "model/pb"
# MODEL_NAME = "addmodel.pb"

# if os.path.exists(MODEL_DIR): 刪除目錄
#     shutil.rmtree(MODEL_DIR)
#
# if not tf.gfile.Exists(MODEL_DIR): #建立目錄
#     tf.gfile.MakeDirs(MODEL_DIR)

output_graph = "model/pb/add_model.pb"

#下面的過程你可以替換成CNN、RNN等你想做的訓練過程,這裡只是簡單的一個計算公式
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.greater(_y, 50, name="predictions") #比50大返回true,否則返回false
predictions = tf.add(_y, 10,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() #得到當前的圖的 GraphDef 部分,通過這個部分就可以完成重輸入層到輸出層的計算過程

    output_graph_def = graph_util.convert_variables_to_constants(  # 模型持久化,將變數值固定
        sess,
        graph_def,
        ["predictions"] #需要儲存節點的名字
    )
    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))
    print (predictions)

# for op in tf.get_default_graph().get_operations(): 列印模型節點資訊
#     print (op.name)

*GraphDef:這個屬性記錄了tensorflow計算圖上節點的資訊。

這裡寫圖片描述

  • add_model.pb : 裡面儲存了重輸入層到輸出層這個計算過程的計算圖和相關變數的值,我們得到這個模型後傳入一個輸入,既可以得到一個預估的輸出值

CKPT 轉換成 PB格式

  1. 通過傳入 CKPT 模型的路徑得到模型的圖和變數資料
  2. 通過 import_meta_graph 匯入模型中的圖
  3. 通過 saver.restore 從模型中恢復圖中各個變數的資料
  4. 通過 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 = "model/pb"
MODEL_NAME = "frozen_model.pb"

if not tf.gfile.Exists(MODEL_DIR): #建立目錄
    tf.gfile.MakeDirs(MODEL_DIR)

def freeze_graph(model_folder):
    checkpoint = tf.train.get_checkpoint_state(model_folder) #檢查目錄下ckpt檔案狀態是否可用
    input_checkpoint = checkpoint.model_checkpoint_path #得ckpt檔案路徑
    output_graph = os.path.join(MODEL_DIR, MODEL_NAME) #PB模型儲存路徑

    output_node_names = "predictions" #原模型輸出操作節點的名字
    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() #獲得預設的圖
    input_graph_def = graph.as_graph_def()  #返回一個序列化的圖代表當前的圖

    with tf.Session() as sess:
        saver.restore(sess, input_checkpoint) #恢復圖並得到資料

        print "predictions : ", sess.run("predictions:0", feed_dict={"input_holder:0": [10.0]}) # 測試讀出來的模型是否正確,注意這裡傳入的是輸出 和輸入 節點的 tensor的名字,不是操作節點的名字

        output_graph_def = graph_util.convert_variables_to_constants(  #模型持久化,將變數值固定
            sess,
            input_graph_def,
            output_node_names.split(",") #如果有多個輸出節點,以逗號隔開
        )
        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)) #得到當前圖有幾個操作節點

        for op in graph.get_operations():
            print(op.name, op.values())

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("model_folder", type=str, help="input ckpt model dir") #命令列解析,help是提示符,type是輸入的型別,
    # 這裡執行程式時需要帶上模型ckpt的路徑,不然會報 error: too few arguments
    aggs = parser.parse_args()
    freeze_graph(aggs.model_folder)
    # freeze_graph("model/ckpt") #模型目錄

部分參考: TensorFlow實戰Google深度學習框架、http://blog.csdn.net/lujiandong1/article/details/53385092