【问题标题】:Graph optimizations on a tensorflow serveable created using tf.Estimator使用 tf.Estimator 创建的张量流服务图优化
【发布时间】:2019-01-28 23:06:03
【问题描述】:

上下文

我有一个基于tf.estimator.DNNClassifier 的简单分类器,它通过意图标签获取文本和输出概率。我能够将模型导出到可服务对象,并使用tensorflow serving 为可服务对象提供服务。问题是这个 servable 太大(大约 1GB),所以我想尝试一些 tensorflow graph transforms 来尝试减小所服务文件的大小。

问题

我了解如何获取saved_model.pb 并使用freeze_model.py 创建一个新的.pb 文件,该文件可用于调用转换。这些转换的结果(也是一个.pb 文件)不是可服务的,不能与 tensorflow 服务一起使用。

开发者如何去:

saved model -> graph transforms -> back to a servable

documentation 表明这当然是可能的,但从文档中看如何做到这一点并不直观。

我的尝试

import tensorflow as tf

from tensorflow.saved_model import simple_save
from tensorflow.saved_model import signature_constants
from tensorflow.saved_model import tag_constants
from tensorflow.tools.graph_transforms import TransformGraph


with tf.Session(graph=tf.Graph()) as sess_meta:
    meta_graph_def = tf.saved_model.loader.load(
        sess_meta,
        [tag_constants.SERVING],
        "/model/path")

    graph_def = meta_graph_def.graph_def

    other_graph_def = TransformGraph(
        graph_def,
        ["Placeholder"],
        ["dnn/head/predictions/probabilities"],
        ["quantize_weights"])


    with tf.Graph().as_default():
        graph = tf.get_default_graph()
        tf.import_graph_def(other_graph_def)
        in_tensor = graph.get_tensor_by_name(
            "import/Placeholder:0")
        out_tensor = graph.get_tensor_by_name(
            "import/dnn/head/predictions/probabilities:0")

        inputs = {"inputs": in_tensor}
        outputs = {"outputs": out_tensor}

        simple_save(sess_meta, "./new", inputs, outputs)

我的想法是加载 servable,从 meta_graph_def 中提取 graph_def,转换 graph_def,然后尝试重新创建 servable。这似乎是不正确的方法。

有没有办法从导出的 servable 对图成功执行转换(以减少推理时的文件大小),然后使用转换后的图重新创建 servable?

谢谢。

更新(2018-08-28)

找到了contrib.meta_graph_transform(),看起来很有希望。

更新(2018-12-03)

我打开的一个相关github issue 似乎已在工单末尾列出的详细博客文章中解决。

【问题讨论】:

  • 其实我之前也做过类似的事情。我总是转换为 saved_model 而不是冻结模型,然后我想尝试一些优化。经过数小时的搜索,我制作了一个脚本来转换 save_model2frozen_model 和 freeze_model2saved_model。

标签: python tensorflow tensorflow-serving tensorflow-estimator


【解决方案1】:

我们可以使用下面提到的方法优化或减小 TensorFlow 模型的大小:

  1. 冻结:将存储在 SavedModel 的检查点文件中的变量转换为直接存储在模型图中的常量。这会减小模型的整体大小。

  2. 修剪:去除预测路径中未使用的节点和图的输出,合并重复节点,以及清理摘要、身份等其他节点操作。

  3. 常量折叠:查找模型中始终计算为常量表达式的任何子图,并将它们替换为这些常量。折叠批量规范:将批量归一化中引入的乘法折叠到上一层的权重乘法中。

  4. 量化:将权重从浮点转换为较低精度,例如 16 位或 8 位。

冻结图的代码如下:

from tensorflow.python.tools import freeze_graph

output_graph_filename = os.path.join(saved_model_dir, output_filename)
initializer_nodes = ''

freeze_graph.freeze_graph(input_saved_model_dir=saved_model_dir,
      output_graph=output_graph_filename,
      saved_model_tags = tag_constants.SERVING,
      output_node_names=output_node_names,initializer_nodes=initializer_nodes,
      input_graph=None, input_saver=False, input_binary=False, 
      input_checkpoint=None, restore_op_name=None, filename_tensor_name=None,
      clear_devices=False, input_meta_graph=False)

剪枝和常量折叠的代码如下:

from tensorflow.tools.graph_transforms import TransformGraph

def get_graph_def_from_file(graph_filepath):
  with ops.Graph().as_default():
    with tf.gfile.GFile(graph_filepath, 'rb') as f:
      graph_def = tf.GraphDef()
      graph_def.ParseFromString(f.read())
      return graph_def

def optimize_graph(model_dir, graph_filename, transforms, output_node):
  input_names = []
  output_names = [output_node]
  if graph_filename is None:
    graph_def = get_graph_def_from_saved_model(model_dir)
  else:
    graph_def = get_graph_def_from_file(os.path.join(model_dir, 
         graph_filename))
  optimized_graph_def = TransformGraph(graph_def, input_names,      
      output_names, transforms)
  tf.train.write_graph(optimized_graph_def, logdir=model_dir, as_text=False, 
     name='optimized_model.pb')
  print('Graph optimized!')

我们通过传递所需优化的列表来调用模型上的代码,如下所示:

transforms = ['remove_nodes(op=Identity)', 'merge_duplicate_nodes',
 'strip_unused_nodes','fold_constants(ignore_errors=true)',
 'fold_batch_norms']

optimize_graph(saved_model_dir, "frozen_model.pb" , transforms, 'head/predictions/class_ids')

量化代码如下:

transforms = ['quantize_nodes', 'quantize_weights',]
optimize_graph(saved_model_dir, None, transforms, 'head/predictions/class_ids')

应用优化后,我们需要将优化图转换回 GraphDef。代码如下所示:

def convert_graph_def_to_saved_model(export_dir, graph_filepath):
  if tf.gfile.Exists(export_dir):
    tf.gfile.DeleteRecursively(export_dir)
  graph_def = get_graph_def_from_file(graph_filepath)
  with tf.Session(graph=tf.Graph()) as session:
    tf.import_graph_def(graph_def, name='')
    tf.saved_model.simple_save(
        session,
        export_dir,
        inputs={
            node.name: session.graph.get_tensor_by_name(
                '{}:0'.format(node.name))
            for node in graph_def.node if node.op=='Placeholder'},
        outputs={'class_ids': session.graph.get_tensor_by_name(
            'head/predictions/class_ids:0')}
    )
    print('Optimized graph converted to SavedModel!')

示例代码如下:

optimized_export_dir = os.path.join(export_dir, 'optimized')
optimized_filepath = os.path.join(saved_model_dir, 'optimized_model.pb')
convert_graph_def_to_saved_model(optimized_export_dir, optimized_filepath)

有关更多信息,请参阅@gobrewers14 提到的以下链接:

https://medium.com/google-cloud/optimizing-tensorflow-models-for-serving-959080e9ddbf

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-29
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多