整合两个张量流模型

时间:2018-08-20 21:39:40

标签: tensorflow deep-learning tensorflow-serving tensorflow-estimator ensemble-learning

我正在尝试从两个几乎完全相同的模型中创建一个模型,并在不同条件下对其进行训练,并在张量流内平均其输出。我们希望最终模型具有相同的推理接口。

我们保存了两个模型的检查点,这是我们试图解决问题的方法:

merged_graph = tf.Graph()
with merged_graph.as_default():
    saver1 = tf.train.import_meta_graph('path_to_checkpoint1_model1.meta', import_scope='g1')
    saver2 = tf.train.import_meta_graph('path_to_checkpoint1_model2.meta', import_scope='g2')

with tf.Session(graph=merged_graph) as sess:
  saver1.restore(sess, 'path_to_checkpoint1_model1')
  saver1.restore(sess, 'path_to_checkpoint1_model2')    

  sess.run(tf.global_variables_initializer())

  # export as a saved_model
  builder = tf.saved_model.builder.SavedModelBuilder(kPathToExportDir)
  builder.add_meta_graph_and_variables(sess,
                                       [tf.saved_model.tag_constants.SERVING],
                                       strip_default_attrs=True)    
  builder.save()

上述方法至少存在3个缺陷,我们尝试了许多路线,但无法使之起作用:

  1. model1和model2的图形具有各自的主要操作。结果,模型在加载期间失败,并显示以下错误: 失败的前提条件:

_

Expected exactly one main op in : model
Expected exactly one SavedModel main op. Found: [u'g1/group_deps', u'g2/group_deps']
  1. 两个模型都有自己的Placeholder节点用于输入(即合并后的g1 / Placeholder和g2 / Placeholder)。我们无法找到一种方法来删除占位符节点,以创建一个新的占位符节点来为两个模型提供输入(我们不希望有一个新的接口,我们需要将数据输入两个不同的占位符)。

  2. 两个图都有自己的init_all,restore_all节点。我们无法弄清楚如何将这些NoOp操作组合到单个节点中。这与问题#1相同。

我们也无法在tensorflow内部找到这种模式集合的示例实现。示例代码可能会回答上述所有问题。

注意:我的两个模型是使用tf.estimator.Estimator训练的,并已导出为save_models。结果,它们包含main_op。

2 个答案:

答案 0 :(得分:0)

对于问题1,不是必须具有save_model

对于问题2,可以使用input_map中的tf.train.import_meta_graph arg

对于问题3,您确实不需要全部还原或初始化所有操作

此代码快照可以向您展示如何组合两个图并在tensorflow中对它们的输出求平均:

import tensorflow as tf
merged_graph = tf.Graph()
with merged_graph.as_default():
    input = tf.placeholder(dtype=tf.float32, shape=WhatEverYourShape)
    saver1 = tf.train.import_meta_graph('path_to_checkpoint1_model1.meta', import_scope='g1',
                                        input_map={"YOUR/INPUT/NAME": input})
    saver2 = tf.train.import_meta_graph('path_to_checkpoint1_model2.meta', import_scope='g2',
                                        input_map={"YOUR/INPUT/NAME": input})

    output1 = merged_graph.get_tensor_by_name("g1/YOUR/OUTPUT/TENSOR/NAME")
    output2 = merged_graph.get_tensor_by_name("g2/YOUR/OUTPUT/TENSOR/NAME")
    final_output = (output1 + output2) / 2

with tf.Session(graph=merged_graph) as sess:
    saver1.restore(sess, 'path_to_checkpoint1_model1')
    saver1.restore(sess, 'path_to_checkpoint1_model2')
    # this line should NOT run because it will initialize all variables, your restore op will have no effect
    # sess.run(tf.global_variables_initializer())
    fianl_output_numpy = sess.run(final_output, feed_dict={input: YOUR_NUMPY_INPUT})

答案 1 :(得分:0)

我没有解决,但是找到了解决上述问题的方法。

主要问题是,每当使用saved_model API导出模型时,都会添加main_op node。由于我的两个模型都是使用此API导出的,因此两个模型都具有 main_op 节点,该节点将被导入到新图形中。然后,新图形将包含两个 main_ops ,稍后会加载,因为正好是一个主操作

我选择使用的解决方法不是使用save_model API导出最终模型,而是使用方便的freeze_graph导出到单个.pb文件中。

这是我的工作代码段:

# set some constants:
#   INPUT_SHAPE, OUTPUT_NODE_NAME, OUTPUT_FILE_NAME, 
#   TEMP_DIR, TEMP_NAME, SCOPE_PREPEND_NAME, EXPORT_DIR

# Set path for trained models which are exported with the saved_model API
input_model_paths = [PATH_TO_MODEL1, 
                     PATH_TO_MODEL2, 
                     PATH_TO_MODEL3, ...]
num_model = len(input_model_paths)

def load_model(sess, path, scope, input_node):
    tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING], 
                               path,
                               import_scope=scope, 
                               input_map={"Placeholder": input_node})  
    output_tensor = tf.get_default_graph().get_tensor_by_name(
        scope + "/" + OUTPUT_NODE_NAME + ":0")
    return output_tensor  

with tf.Session(graph=tf.Graph()) as sess:
  new_input = tf.placeholder(dtype=tf.float32, 
                             shape=INPUT_SHAPE, name="Placeholder")      

  output_tensors = []
  for k, path in enumerate(input_model_paths):
    output_tensors.append(load_model(sess, 
                                     path, 
                                     SCOPE_PREPEND_NAME+str(k), 
                                     new_input))
  # Mix together the outputs (e.g. sum, weighted sum, etc.)
  sum_outputs = output_tensors[0] + output_tensors[1]
  for i in range(2, num_model):
    sum_outputs = sum_outputs + output_tensors[i]
  final_output = tf.divide(sum_outputs, float(num_model), name=OUTPUT_NODE_NAME)

  # Save checkpoint to be loaded later by the freeze_graph!
  saver_checkpoint = tf.train.Saver()
  saver_checkpoint.save(sess, os.path.join(TEMP_DIR, TEMP_NAME))

  tf.train.write_graph(sess.graph_def, TEMP_DIR, TEMP_NAME + ".pbtxt")
  freeze_graph.freeze_graph(
      os.path.join(TEMP_DIR, TEMP_NAME + ".pbtxt"), 
      "", 
      False, 
      os.path.join(TEMP_DIR, TEMP_NAME),  
      OUTPUT_NODE_NAME, 
      "", # deprecated
      "", # deprecated
      os.path.join(EXPORT_DIR, OUTPUT_FILE_NAME),
      False,
      "")