簡體   English   中英

在內存中序列化和反序列化Tensorflow模型並繼續訓練

[英]Serializing and deserializing Tensorflow model in memory and continue training

我已經看到了這個問題的變化,但我還沒有找到滿意的答案。 基本上,我想從keras model.to_json()model.get_weights()model.from_json()model.set_weights()到tensorflow等效。 我想我已經接近那里了,但我正處於被困的地步。 如果我能在相同的字符串中獲得權重和圖表,我更願意,但我明白這是不可能的。

目前,我所擁有的是:

g = optimizer.minimize(loss_op,
                       global_step=tf.train.get_global_step())
de = g.graph.as_graph_def()
json_string = json_format.MessageToJson(de)

gd = tf.GraphDef()
gd = json_format.Parse(json_string, gd)

這似乎創建了圖表,但顯然元圖不包含在變量,權重等中。還有元圖,但我唯一看到的是export_meta_graph,它似乎沒有以相同的方式序列化。 我看到MetaGraph有一個proto函數,但我不知道如何序列化這些變量。

簡而言之,您如何采用張量流模型(模型如權重,圖形等),將其序列化為字符串(最好是json),然后對其進行反序列化並繼續訓練或提供預測。

以下是讓我接近並且我已經嘗試過的東西,但是大多數都需要寫入磁盤的限制,在這種情況下我無法做到:

GitHub上的要點

這是我找到的最接近的一個,但序列化元圖的鏈接不存在。

請注意,來自@Maxim的解決方案將在每次運行時在圖表中創建新操作。

如果您經常運行該函數,這將導致您的代碼變得越來越慢。

解決此問題的兩種解決方案:

  1. 與圖表的其余部分同時創建分配操作並重用它們:

    assign_ops = [] for var_name in tf.trainable_variables(): assign_placeholder = tf.placeholder(var.dtype, shape=value.shape) assign_op = var.assign(assign_placeholder) assign_ops.append(assign_op)

  2. 對變量使用load函數,我更喜歡這個,因為它不需要上面的代碼:

    self.params = tf.trainable_variables()

    def get_weights(self): values = tf.get_default_session().run(self.params) return values

    def set_weights(self, weights): for i, value in enumerate(weights): value = np.asarray(value) self.params[i].load(value, self.sess)

(我不能評論所以我把它作為答案而不是)

你可以使用freeze_graph

此腳本包含在Tensorflow中,允許您獲取GraphDef原型,SaverDef原型以及存儲在檢查點文件中的一組變量值。

通過這種方式,您可以輸出一個GraphDef,其中所有變量ops都轉換為包含變量值的const ops。

要恢復凍結模型,您必須重新初始化圖形並重新映射凍結模型中的輸入,請參閱此示例

如果你想要keras Model.get_weights()Model.set_weights()的等價物,這些方法與keras內部結構並沒有很強的聯系,可以很容易地提取出來。

原始代碼

以下是keras源代碼中的外觀:

def get_weights(self):
  weights = []
  for layer in self.layers:
    weights += layer.weights
  return K.batch_get_value(weights)   # this is just `get_session().run(weights)`

def set_weights(self, weights):
  tuples = []
  for layer in self.layers:
    num_param = len(layer.weights)
    layer_weights = weights[:num_param]
    for sw, w in zip(layer.weights, layer_weights):
      tuples.append((sw, w))
    weights = weights[num_param:]
  K.batch_set_value(tuples)  # another wrapper over `get_session().run(...)`

Keras的weights是numpy數組(不是json)的列表。 如您所見,它使用模型體系結構已知的事實( self.layers ),它允許它重建從變量到值的正確映射。 一些看似非平凡的工作是在K.batch_set_value完成的,但實際上它只是准備分配操作並在會話中運行它們。

在純張量流中獲取和設置權重

def tensorflow_get_weights():
  vars = tf.trainable_variables()
  values = tf.get_default_session().run(vars)
  return zip([var.name for var in vars], values)

def tensorflow_set_weights(weights):
  assign_ops = []
  feed_dict = {}
  for var_name, value in weights:
    var = tf.get_default_session().graph.get_tensor_by_name(var_name)
    value = np.asarray(value)
    assign_placeholder = tf.placeholder(var.dtype, shape=value.shape)
    assign_op = tf.assign(var, assign_placeholder)
    assign_ops.append(assign_op)
    feed_dict[assign_placeholder] = value
  tf.get_default_session().run(assign_ops, feed_dict=feed_dict)

在這里,我假設您要序列化/反序列化整個模型(即所有可訓練的變量)和默認會話。 如果不是這種情況,上述功能很容易定制。

測試

x = tf.placeholder(shape=[None, 5], dtype=tf.float32, name='x')
W = tf.Variable(np.zeros([5, 5]), dtype=tf.float32, name='W')
b = tf.Variable(np.zeros([5]), dtype=tf.float32, name='b')
y = tf.add(tf.matmul(x, W), b)

with tf.Session() as session:
  session.run(tf.global_variables_initializer())

  # Save the weights
  w = tensorflow_get_weights()
  print(W.eval(), b.eval())

  # Update the model
  session.run([tf.assign(W, np.ones([5, 5])), tf.assign(b, np.ones([5]) * 2)])
  print(W.eval(), b.eval())

  # Restore the weights
  tensorflow_set_weights(w)
  print(W.eval(), b.eval())

如果運行此測試,您應該看到模型在零處凍結,然后更新,然后恢復為零。

感謝Maxim幫我解決問題。 我想發布一個答案,圖表和權重轉換為json,適合遇到此問題的人。 為了序列化圖形而不是權重,我創建了一個要點,封裝了Maxim在這里寫的內容: 帶有非json序列化權重的Tensorflow圖

現在為了序列化/反序列化圖形和權重,我在這里創建了一個單獨的要點: 帶有json序列化權重和圖形的Tensorflow圖

為了完成解釋,我首先通過不返回獲取權重中的變量來略微調整權重函數,並在設置權重中抓取當前變量。 這是一個重要的警告,特別是如果圖表與當前的可訓練變量略有不同:

import tensorflow as tf
import numpy as np
from google.protobuf import json_format
import json

def tensorflow_get_weights():
    vs = tf.trainable_variables()
    values = tf.get_default_session().run(vs)
    return values

def tensorflow_set_weights(weights):
    assign_ops = []
    feed_dict = {}
    vs = tf.trainable_variables()
    zipped_values = zip(vs, weights)
    for var, value in zipped_values:
        value = np.asarray(value)
        assign_placeholder = tf.placeholder(var.dtype, shape=value.shape)
        assign_op = var.assign(assign_placeholder)
        assign_ops.append(assign_op)
        feed_dict[assign_placeholder] = value
    tf.get_default_session().run(assign_ops, feed_dict=feed_dict)

接下來,我創建了兩個實用程序函數,用於將權重轉換為json:

def convert_weights_to_json(weights):
    weights = [w.tolist() for w in weights]
    weights_list = json.dumps(weights)
    return weights_list

def convert_json_to_weights(json_weights):
    loaded_weights = json.loads(json_weights)
    loaded_weights = [np.asarray(x) for x in loaded_weights]
    return loaded_weights

比我有一個最初開始訓練的方法。 此方法將初始化變量,運行優化,獲取權重和圖形,並將它們轉換為json。 看起來像:

def run_initial_with_json_weights(opti, feed_dict):
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for i in range(0, 250):
            sess.run(opti, feed_dict=feed_dict)
        first_weights = tensorflow_get_weights()
        g = tf.get_default_graph().as_graph_def()
    json_string = json_format.MessageToJson(g)
    return json_string, convert_weights_to_json(first_weights)

現在我們已經有序列化的權重和圖表,如果我們想繼續訓練和/或做出預測,我們可以做到以下幾點。 此方法反序列化graphdef和權重,運行優化,然后進行預測。

def run_serialized(json_graph, json_weights, feed_dict):
    gd = tf.GraphDef()
    gd = json_format.Parse(json_graph, gd)
    weights = convert_json_to_weights(json_weights)

    with tf.Session() as sess:
        tf.import_graph_def(gd)
        sess.run(tf.global_variables_initializer())
        nu_out = tf.get_default_graph().get_tensor_by_name('outer/Sigmoid:0')
        mini = tf.get_default_graph().get_tensor_by_name('mini:0')
        tensorflow_set_weights(weights)

        for i in range(0, 50):
             sess.run(mini, feed_dict=feed_dict)

        predicted = sess.run(nu_out, feed_dict=feed_dict)

    return predicted

完整的xor示例在上面的要點中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM