Оптимизация графа для обслуживаемого тензорного потока, созданного с помощью tf.Estimator

Контекст:

У меня есть простой классификатор на основе tf.estimator.DNNClassifier, который принимает текст и вероятность вывода по тегам намерения. Я могу обучить экспортировать модель в обслуживаемый объект, а также обслуживать его, используя обслуживание тензорного потока . Проблема в том, что этот обслуживаемый файл слишком велик (около 1 ГБ), поэтому я хотел попробовать преобразовывает график тензорного потока, чтобы попытаться уменьшить размер обслуживаемых файлов.

Проблема:

Я понимаю, как взять saved_model.pb и использовать freeze_model.py для создания нового .pb файла, который можно использовать для вызова преобразований. Результат этих преобразований (также файл .pb) не обслуживается и не может использоваться с обслуживанием тензорного потока.

Как разработчику перейти от:

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

Есть документация, которая предполагает, что это, безусловно, возможно, но не все интуитивно понятно из документации относительно того, как это сделать.

Что я пробовал:

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)

Моя идея заключалась в том, чтобы загрузить обслуживаемый объект, извлечь graph_def из meta_graph_def, преобразовать graph_def и затем попытаться воссоздать обслуживаемый объект. Это кажется неправильным подходом.

Есть ли способ успешно выполнить преобразования (чтобы уменьшить размер файла при выводе) на графе из экспортированного обслуживаемого объекта, а затем воссоздать обслуживаемый объект с преобразованным графом?

Спасибо.

Обновление (28 августа 2018 г.):

Найден contrib.meta_graph_transform (), который выглядит многообещающе.

Обновление (03.12.2018):

Связанная с этим проблема с github, которую я открыл, кажется, что она решена в подробном блоге пост, который указан в конце билета.


person gobrewers14    schedule 22.08.2018    source источник
comment
Я действительно делал нечто подобное раньше. Я всегда конвертирую в saved_model вместо замороженной модели, тогда я хотел попробовать некоторую оптимизацию. После нескольких часов поиска я создал сценарий, который преобразует сохраненную_модель2frozen_model и Frozen_model2saved_model.   -  person Adam Fjeldsted    schedule 30.08.2018


Ответы (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

person RakTheGeek    schedule 16.05.2019