集成两个张量流模型

Ensemble two tensorflow models

我正在尝试从 两个几乎相同的模型 中创建一个模型,在不同条件下训练并平均它们的输出 在 tensorflow 中 .我们希望最终模型具有相同的推理接口。

我们已经保存了两个模型的检查点,下面是我们尝试解决问题的方法:

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. 模型 1 和模型 2 的图表有自己的主要操作。结果,模型在加载期间失败并出现以下错误: 失败的先决条件:

_

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

  2. 两个图有各自的init_all、restore_all节点。我们不知道如何将这些 NoOp 操作组合到单个节点中。这与问题 #1 相同。

我们也找不到在 tensorflow 中集成这种模式的示例实现。示例代码可能会回答上述所有问题。

注意:我的两个模型使用 tf.estimator.Estimator 训练并导出为 saved_models。因此,它们包含 main_op.

对于问题 1,saved_model 不是必须的

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

对于问题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})

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

主要问题是每当使用 saved_model API 导出模型时都会添加 main_op node。因为我的两个模型都是用这个 API 导出的,所以它们都有 main_op 节点,这些节点将被导入到新图形中。然后,新图将包含两个 main_ops,稍后将无法加载,因为 预计只有一个主操作

我选择使用的解决方法是不使用 saved_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,
      "")