RSVP для вашего местного мероприятия TensorFlow Everywhere сегодня!
Эта страница переведена с помощью Cloud Translation API.
Switch to English

Использование формата SavedModel

Посмотреть на TensorFlow.org Запускаем в Google Colab Посмотреть исходный код на GitHub Скачать блокнот

SavedModel содержит полную программу TensorFlow, включая обученные параметры (например, tf.Variable s) и вычисления. Он не требует наличия оригинальной модели здания код для запуска, что делает его полезным для обмена или развертывания с TFLite , TensorFlow.js , TensorFlow сервировки или TensorFlow Hub .

Вы можете сохранить и загрузить модель в формате SavedModel, используя следующие API:

Создание SavedModel из Keras

Для быстрого введения в этом разделе экспортируется предварительно обученная модель Keras и обслуживается с ее помощью запросы классификации изображений. Остальная часть руководства заполнит детали и обсудит другие способы создания SavedModels.

import os
import tempfile

from matplotlib import pyplot as plt
import numpy as np
import tensorflow as tf

tmpdir = tempfile.mkdtemp()
physical_devices = tf.config.list_physical_devices('GPU')
for device in physical_devices:
  tf.config.experimental.set_memory_growth(device, True)
file = tf.keras.utils.get_file(
    "grace_hopper.jpg",
    "https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg")
img = tf.keras.preprocessing.image.load_img(file, target_size=[224, 224])
plt.imshow(img)
plt.axis('off')
x = tf.keras.preprocessing.image.img_to_array(img)
x = tf.keras.applications.mobilenet.preprocess_input(
    x[tf.newaxis,...])
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg
65536/61306 [================================] - 0s 0us/step

PNG

Вы будете использовать изображение Грейс Хоппер в качестве рабочего примера и предварительно обученную модель классификации изображений Keras, поскольку она проста в использовании. Также работают нестандартные модели, о которых мы подробнее поговорим позже.

labels_path = tf.keras.utils.get_file(
    'ImageNetLabels.txt',
    'https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')
imagenet_labels = np.array(open(labels_path).read().splitlines())
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt
16384/10484 [==============================================] - 0s 0us/step

pretrained_model = tf.keras.applications.MobileNet()
result_before_save = pretrained_model(x)

decoded = imagenet_labels[np.argsort(result_before_save)[0,::-1][:5]+1]

print("Result before saving:\n", decoded)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 0s 0us/step
Result before saving:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

Главный прогноз для этого изображения - «военная форма».

mobilenet_save_path = os.path.join(tmpdir, "mobilenet/1/")
tf.saved_model.save(pretrained_model, mobilenet_save_path)
INFO:tensorflow:Assets written to: /tmp/tmpfcgkddlh/mobilenet/1/assets

Путь сохранения следует соглашению, используемому службой TensorFlow, где последний компонент пути (здесь 1/ ) является номером версии для вашей модели - это позволяет таким инструментам, как Tensorflow Serving, определять относительную свежесть.

Вы можете загрузить SavedModel обратно в Python с помощью tf.saved_model.load и посмотреть, как классифицируется изображение адмирала Хоппера.

loaded = tf.saved_model.load(mobilenet_save_path)
print(list(loaded.signatures.keys()))  # ["serving_default"]
['serving_default']

Импортированные подписи всегда возвращают словари. Чтобы настроить имена подписей и ключи выходного словаря, см. Указание подписей во время экспорта .

infer = loaded.signatures["serving_default"]
print(infer.structured_outputs)
{'predictions': TensorSpec(shape=(None, 1000), dtype=tf.float32, name='predictions')}

Выполнение вывода из SavedModel дает тот же результат, что и исходная модель.

labeling = infer(tf.constant(x))[pretrained_model.output_names[0]]

decoded = imagenet_labels[np.argsort(labeling)[0,::-1][:5]+1]

print("Result after saving and loading:\n", decoded)
Result after saving and loading:
 ['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']

Запуск SavedModel в обслуживании TensorFlow

SavedModels можно использовать из Python (подробнее об этом ниже), но в производственных средах обычно используется выделенная служба для вывода без запуска кода Python. Это легко настроить из SavedModel с помощью TensorFlow Serving.

См. Руководство TensorFlow Serving REST для получения примера сквозного обслуживания тензорного потока.

Формат SavedModel на диске

SavedModel - это каталог, содержащий сериализованные подписи и состояние, необходимое для их запуска, включая значения переменных и словари.

ls {mobilenet_save_path}
assets  saved_model.pb  variables

В файле saved_model.pb хранится фактическая программа или модель saved_model.pb и набор именованных сигнатур, каждая из которых идентифицирует функцию, которая принимает тензорные входные данные и производит тензорные выходные данные.

SavedModels может содержать несколько вариантов модели (несколько v1.MetaGraphDefs , идентифицированных с --tag_set флага saved_model_cli для saved_model_cli ), но это редко. API, которые создают несколько вариантов модели, включают tf.Estimator.experimental_export_all_saved_models и tf.Estimator.experimental_export_all_saved_models в tf.saved_model.Builder 1.x.

saved_model_cli show --dir {mobilenet_save_path} --tag_set serve
2021-02-11 02:25:22.757135: I tensorflow/stream_executor/platform/default/dso_loader.cc:49] Successfully opened dynamic library libcudart.so.11.0
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "serving_default"

Каталог variables содержит стандартную контрольную точку обучения (см. Руководство по контрольным точкам обучения ).

ls {mobilenet_save_path}/variables
variables.data-00000-of-00001  variables.index

Каталог assets содержит файлы, используемые графиком TensorFlow, например текстовые файлы, используемые для инициализации словарных таблиц. В этом примере он не используется.

SavedModels может иметь каталог assets.extra для любых файлов, не используемых графиком TensorFlow, например, информацию для потребителей о том, что делать с SavedModel. Сам TensorFlow не использует этот каталог.

Сохранение нестандартной модели

tf.saved_model.save поддерживает сохранение объектов tf.Module и его подклассов, таких как tf.keras.Layer и tf.keras.Model .

Давайте посмотрим на пример сохранения и восстановления tf.Module .

class CustomModule(tf.Module):

  def __init__(self):
    super(CustomModule, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function
  def __call__(self, x):
    print('Tracing with', x)
    return x * self.v

  @tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
  def mutate(self, new_v):
    self.v.assign(new_v)

module = CustomModule()

Когда вы сохраняете tf.Module , tf.Variable атрибуты tf.Variable , tf.function методы tf.Module и tf.Module найденные посредством рекурсивного обхода. (Подробнее об этом рекурсивном обходе см. В учебнике по контрольным точкам.) Однако все атрибуты, функции и данные Python теряются. Это означает, что при сохранении tf.function код Python не сохраняется.

Если код Python не сохранен, как SavedModel знает, как восстановить функцию?

Вкратце, tf.function работает путем отслеживания кода Python для создания ConcreteFunction (вызываемой оболочки вокруг tf.Graph ). При сохранении tf.function вы действительно сохраняете tf.function для ConcreteFunctions.

Чтобы узнать больше о взаимосвязи между tf.function и ConcreteFunctions, см. Руководство tf.function .

module_no_signatures_path = os.path.join(tmpdir, 'module_no_signatures')
module(tf.constant(0.))
print('Saving model...')
tf.saved_model.save(module, module_no_signatures_path)
Tracing with Tensor("x:0", shape=(), dtype=float32)
Saving model...
Tracing with Tensor("x:0", shape=(), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpfcgkddlh/module_no_signatures/assets

Загрузка и использование нестандартной модели

Когда вы загружаете SavedModel в Python, все атрибуты tf.Variable , tf.function методы и tf.Module восстанавливаются в той же структуре объектов, что и исходный сохраненный tf.Module .

imported = tf.saved_model.load(module_no_signatures_path)
assert imported(tf.constant(3.)).numpy() == 3
imported.mutate(tf.constant(2.))
assert imported(tf.constant(3.)).numpy() == 6

Поскольку код Python не сохраняется, вызов tf.function с новой входной сигнатурой завершится ошибкой:

imported(tf.constant([3.]))
ValueError: Could not find matching function to call for canonicalized inputs ((,), {}). Only existing signatures are [((TensorSpec(shape=(), dtype=tf.float32, name=u'x'),), {})].

Базовая тонкая настройка

Доступны переменные объекты, и вы можете выполнять обратную передачу с помощью импортированных функций. Этого достаточно для точной настройки (т.е. переобучения) SavedModel в простых случаях.

optimizer = tf.optimizers.SGD(0.05)

def train_step():
  with tf.GradientTape() as tape:
    loss = (10. - imported(tf.constant(2.))) ** 2
  variables = tape.watched_variables()
  grads = tape.gradient(loss, variables)
  optimizer.apply_gradients(zip(grads, variables))
  return loss
for _ in range(10):
  # "v" approaches 5, "loss" approaches 0
  print("loss={:.2f} v={:.2f}".format(train_step(), imported.v.numpy()))
loss=36.00 v=3.20
loss=12.96 v=3.92
loss=4.67 v=4.35
loss=1.68 v=4.61
loss=0.60 v=4.77
loss=0.22 v=4.86
loss=0.08 v=4.92
loss=0.03 v=4.95
loss=0.01 v=4.97
loss=0.00 v=4.98

Общая тонкая настройка

SavedModel от Keras предоставляет больше деталей, чем простой __call__ для решения более сложных случаев точной настройки. TensorFlow Hub рекомендует предоставить следующие из них, если применимо, в общих моделях SavedModels для точной настройки:

  • Если в модели используется выпадение или другой метод, в котором прямой проход различается между обучением и выводом (например, пакетная нормализация), метод __call__ принимает необязательный аргумент с Python-оценкой training= который по умолчанию имеет значение False но может иметь значение True .
  • Рядом с атрибутом __call__ атрибуты .variable и .trainable_variable с соответствующими списками переменных. Переменная, которая изначально была обучаемой, но должна быть заморожена во время точной настройки, опускается в .trainable_variables .
  • Для таких фреймворков, как Keras, которые представляют регуляризаторы веса как атрибуты слоев или .regularization_losses , также может быть атрибут .regularization_losses . Он содержит список функций без аргументов, значения которых предназначены для прибавления к общей потере.

Возвращаясь к первоначальному примеру MobileNet, вы можете увидеть некоторые из них в действии:

loaded = tf.saved_model.load(mobilenet_save_path)
print("MobileNet has {} trainable variables: {}, ...".format(
          len(loaded.trainable_variables),
          ", ".join([v.name for v in loaded.trainable_variables[:5]])))
MobileNet has 83 trainable variables: conv1/kernel:0, conv1_bn/gamma:0, conv1_bn/beta:0, conv_dw_1/depthwise_kernel:0, conv_dw_1_bn/gamma:0, ...

trainable_variable_ids = {id(v) for v in loaded.trainable_variables}
non_trainable_variables = [v for v in loaded.variables
                           if id(v) not in trainable_variable_ids]
print("MobileNet also has {} non-trainable variables: {}, ...".format(
          len(non_trainable_variables),
          ", ".join([v.name for v in non_trainable_variables[:3]])))
MobileNet also has 54 non-trainable variables: conv1_bn/moving_mean:0, conv1_bn/moving_variance:0, conv_dw_1_bn/moving_mean:0, ...

Указание подписей при экспорте

Такие инструменты, как TensorFlow Serving и saved_model_cli могут взаимодействовать с SavedModels. Чтобы помочь этим инструментам определить, какие конкретные функции использовать, необходимо указать обслуживающие подписи. tf.keras.Model автоматически определяет обслуживающие подписи, но вам нужно будет явно объявить обслуживающую подпись для наших настраиваемых модулей.

По умолчанию в настраиваемом tf.Module подписи не объявляются.

assert len(imported.signatures) == 0

Чтобы объявить обслуживающую подпись, укажите ConcreteFunction с помощью signatures kwarg. При указании одиночной подписи ключ подписи будет 'serving_default' , который сохраняется как константа tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY .

module_with_signature_path = os.path.join(tmpdir, 'module_with_signature')
call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
tf.saved_model.save(module, module_with_signature_path, signatures=call)
Tracing with Tensor("x:0", dtype=float32)
Tracing with Tensor("x:0", dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpfcgkddlh/module_with_signature/assets

imported_with_signatures = tf.saved_model.load(module_with_signature_path)
list(imported_with_signatures.signatures.keys())
['serving_default']

Чтобы экспортировать несколько подписей, передайте словарь ключей подписей в ConcreteFunctions. Каждый ключ подписи соответствует одной ConcreteFunction.

module_multiple_signatures_path = os.path.join(tmpdir, 'module_with_multiple_signatures')
signatures = {"serving_default": call,
              "array_input": module.__call__.get_concrete_function(tf.TensorSpec([None], tf.float32))}

tf.saved_model.save(module, module_multiple_signatures_path, signatures=signatures)
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
Tracing with Tensor("x:0", shape=(None,), dtype=float32)
INFO:tensorflow:Assets written to: /tmp/tmpfcgkddlh/module_with_multiple_signatures/assets

imported_with_multiple_signatures = tf.saved_model.load(module_multiple_signatures_path)
list(imported_with_multiple_signatures.signatures.keys())
['serving_default', 'array_input']

По умолчанию имена выходных тензоров довольно общие, например output_0 . Чтобы управлять именами выходов, измените вашу tf.function чтобы она возвращала словарь, который отображает имена выходов на выходы. Имена входов являются производными от имен аргументов функции Python.

class CustomModuleWithOutputName(tf.Module):
  def __init__(self):
    super(CustomModuleWithOutputName, self).__init__()
    self.v = tf.Variable(1.)

  @tf.function(input_signature=[tf.TensorSpec([], tf.float32)])
  def __call__(self, x):
    return {'custom_output_name': x * self.v}

module_output = CustomModuleWithOutputName()
call_output = module_output.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
module_output_path = os.path.join(tmpdir, 'module_with_output_name')
tf.saved_model.save(module_output, module_output_path,
                    signatures={'serving_default': call_output})
INFO:tensorflow:Assets written to: /tmp/tmpfcgkddlh/module_with_output_name/assets

imported_with_output_name = tf.saved_model.load(module_output_path)
imported_with_output_name.signatures['serving_default'].structured_outputs
{'custom_output_name': TensorSpec(shape=(), dtype=tf.float32, name='custom_output_name')}

Загрузить SavedModel в C ++

Версия загрузчика SavedModel для C ++ предоставляет API для загрузки SavedModel из пути, одновременно разрешая SessionOptions и RunOptions. Вы должны указать теги, связанные с загружаемым графиком. Загруженная версия SavedModel называется SavedModelBundle и содержит MetaGraphDef и сеанс, в котором он загружен.

const string export_dir = ...
SavedModelBundle bundle;
...
LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain},
               &bundle);

Подробная информация об интерфейсе командной строки SavedModel

Вы можете использовать интерфейс командной строки (CLI) SavedModel для проверки и выполнения SavedModel. Например, вы можете использовать интерфейс командной строки для проверки SignatureDef модели. Интерфейс командной строки позволяет быстро подтвердить, что тип и форма входного тензора соответствуют модели. Более того, если вы хотите протестировать свою модель, вы можете использовать CLI для проверки работоспособности, передав образцы входных данных в различных форматах (например, выражения Python), а затем извлекая выходные данные.

Установите интерфейс командной строки SavedModel

Вообще говоря, вы можете установить TensorFlow одним из следующих двух способов:

  • Установив предварительно созданный двоичный файл TensorFlow.
  • Создав TensorFlow из исходного кода.

Если вы установили TensorFlow с помощью предварительно созданного двоичного файла TensorFlow, тогда в вашей системе уже установлен интерфейс командной строки SavedModel по пути bin/saved_model_cli .

Если вы saved_model_cli из исходного кода, вы должны выполнить следующую дополнительную команду для сборки saved_model_cli :

$ bazel build tensorflow/python/tools:saved_model_cli

Обзор команд

Интерфейс командной строки SavedModel поддерживает следующие две команды в SavedModel:

  • show , в котором показаны вычисления, доступные из SavedModel.
  • run , который запускает вычисление из SavedModel.

show команду

SavedModel содержит один или несколько вариантов модели (технически v1.MetaGraphDef s), идентифицируемых их наборами тегов. Для обслуживания модели вы можете задаться вопросом, какие SignatureDef есть в каждом варианте модели и каковы их входы и выходы. Команда show позволяет вам исследовать содержимое SavedModel в иерархическом порядке. Вот синтаксис:

usage: saved_model_cli show [-h] --dir DIR [--all]
[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]

Например, следующая команда показывает все доступные наборы тегов в SavedModel:

$ saved_model_cli show --dir /tmp/saved_model_dir
The given SavedModel contains the following tag-sets:
serve
serve, gpu

Следующая команда показывает все доступные ключи SignatureDef для набора тегов:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve
The given SavedModel `MetaGraphDef` contains `SignatureDefs` with the
following keys:
SignatureDef key: "classify_x2_to_y3"
SignatureDef key: "classify_x_to_y"
SignatureDef key: "regress_x2_to_y3"
SignatureDef key: "regress_x_to_y"
SignatureDef key: "regress_x_to_y2"
SignatureDef key: "serving_default"

Если в наборе тегов несколько тегов, необходимо указать все теги, каждый тег должен быть разделен запятой. Например:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu

Чтобы показать все входы и выходы TensorInfo для определенного SignatureDef , передайте ключ SignatureDef в параметр signature_def . Это очень полезно, когда вы хотите узнать значение ключа тензора, dtype и форму входных тензоров для последующего выполнения графа вычислений. Например:

$ saved_model_cli show --dir \
/tmp/saved_model_dir --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['x'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: x:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['y'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: y:0
Method name is: tensorflow/serving/predict

Чтобы отобразить всю доступную информацию в SavedModel, используйте параметр --all . Например:

$ saved_model_cli show --dir /tmp/saved_model_dir --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classify_x2_to_y3']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x2:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y3:0
  Method name is: tensorflow/serving/classify

...

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['x'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['y'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y:0
  Method name is: tensorflow/serving/predict

run команду

Вызовите команду run чтобы запустить вычисление графика, передавая входные данные и затем отображая (и при необходимости сохраняя) выходные данные. Вот синтаксис:

usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET --signature_def
                           SIGNATURE_DEF_KEY [--inputs INPUTS]
                           [--input_exprs INPUT_EXPRS]
                           [--input_examples INPUT_EXAMPLES] [--outdir OUTDIR]
                           [--overwrite] [--tf_debug]

Команда run предоставляет следующие три способа передачи входных данных в модель:

  • --inputs позволяет передавать в файлах numpy ndarray.
  • --input_exprs позволяет передавать выражения Python.
  • --input_examples позволяет передавать tf.train.Example .

--inputs

Чтобы передать входные данные в файлы, укажите параметр --inputs , который принимает следующий общий формат:

--inputs <INPUTS>

где INPUTS - это один из следующих форматов:

  • <input_key>=<filename>
  • <input_key>=<filename>[<variable_name>]

Вы можете передать несколько INPUTS . Если вы передаете несколько входов, используйте точку с запятой для разделения каждого из INPUTS .

saved_model_cli использует numpy.load для загрузки имени файла . Имя файла может быть в любом из следующих форматов:

  • .npy
  • .npz
  • формат рассола

Файл .npy всегда содержит numpy ndarray. Следовательно, при загрузке из файла .npy содержимое будет напрямую назначено указанному входному тензору. Если вы укажете имя_переменной с этим файлом .npy , имя_переменной будет проигнорировано и будет выдано предупреждение.

При загрузке из .npz (zip) вы можете дополнительно указать имя_переменной, чтобы идентифицировать переменную в файле zip для загрузки для входного тензорного ключа. Если вы не укажете имя_переменной , интерфейс командной строки SavedModel проверит, что в zip-файл включен только один файл, и загрузит его для указанного входного тензорного ключа.

При загрузке из файла pickle, если в квадратных скобках не указано variable_name , все, что находится внутри файла pickle, будет передано указанному входному тензорному ключу. В противном случае интерфейс командной строки SavedModel будет предполагать, что словарь хранится в файле pickle, и будет использоваться значение, соответствующее переменной_name .

--input_exprs

Чтобы передавать входные данные через выражения Python, укажите параметр --input_exprs . Это может быть полезно, когда у вас нет файлов данных, но вы все еще хотите проверить работоспособность модели с помощью некоторых простых входных данных, которые соответствуют dtype и форме SignatureDef модели. Например:

`<input_key>=[[1],[2],[3]]`

В дополнение к выражениям Python вы также можете передавать numpy-функции. Например:

`<input_key>=np.ones((32,32,3))`

(Обратите внимание, что модуль numpy уже доступен вам как np .)

--input_examples

Чтобы передать tf.train.Example качестве входных данных, укажите параметр --input_examples . Для каждого ключа ввода требуется список словарей, где каждый словарь является экземпляром tf.train.Example . Ключи словаря - это функции, а значения - это списки значений для каждой функции. Например:

`<input_key>=[{"age":[22,24],"education":["BS","MS"]}]`

Сохранить вывод

По умолчанию интерфейс командной строки SavedModel записывает вывод в стандартный вывод. Если каталог передается параметру --outdir , выходные данные будут сохранены как файлы .npy названные в честь выходных тензорных ключей в данном каталоге.

Используйте --overwrite чтобы перезаписать существующие выходные файлы.