Помогают защитить Большой Барьерный Риф с TensorFlow на Kaggle Присоединяйтесь вызов

Использование формата 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.utils.load_img(file, target_size=[224, 224])
plt.imshow(img)
plt.axis('off')
x = tf.keras.utils.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
73728/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
24576/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
17235968/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)
2021-10-27 01:24:27.831628: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
INFO:tensorflow:Assets written to: /tmp/tmpbf9fpzwt/mobilenet/1/assets

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

Вы можете загрузить 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 Serving

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

Смотрите TensorFlow сервировки REST учебник для tensorflow-сервировки , например от конца до конца.

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

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

ls {mobilenet_save_path}
assets  saved_model.pb  variables

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

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

saved_model_cli show --dir {mobilenet_save_path} --tag_set serve
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.function -decorated методы и tf.Module s найдено с помощью рекурсивного обхода сохраняются. (См обучающей Checkpoint Более подробно об этом рекурсивном обходе.) Тем не менее, атрибуты любого 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/tmpbf9fpzwt/module_no_signatures/assets

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

При загрузке SavedModel в Python, все tf.Variable атрибутов, tf.function -decorated методы и tf.Module s восстанавливается в той же структуру объекта , как оригинал сохранен 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 атрибутом. Он содержит список функций без аргументов, значения которых предназначены для прибавления к общей потере.

Возвращаясь к первоначальному примеру 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 Обслуживание и saved_model_cli могут взаимодействовать с SavedModels. Чтобы помочь этим инструментам определить, какие конкретные функции использовать, необходимо указать обслуживающие подписи. tf.keras.Model s автоматически указать служит подпись, но вы должны явно объявить обслуживающие подписи для наших пользовательских модулей.

По умолчанию никаких подписей не объявлены в пользовательском 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/tmpbf9fpzwt/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/tmpbf9fpzwt/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/tmpbf9fpzwt/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 ++

Версия C ++ из SavedModel погрузчика обеспечивает 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. Например, вы можете использовать CLI для проверки этой модели SignatureDef s. Интерфейс командной строки позволяет быстро подтвердить, что тип и форма входного тензора соответствуют модели. Более того, если вы хотите протестировать свою модель, вы можете использовать CLI для проверки работоспособности, передав образцы входных данных в различных форматах (например, выражения Python) и затем извлекая выходные данные.

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

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

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

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

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

$ bazel build tensorflow/python/tools:saved_model_cli

Обзор команд

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

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

show команды

SavedModel содержит один или несколько вариантов модели (технически, v1.MetaGraphDef ы), которые были определены их меток наборов. Для того, чтобы служить моделью, вы могли бы задаться вопросом, какие 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>

где ВХОДЫ либо из следующих форматов:

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

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

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

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

.npy файл всегда содержит Numpy ndarray. Таким образом, при загрузке из .npy файла, содержимое будет назначена непосредственно на указанный тензором ввода. Если вы укажете variable_name с этим .npy файлом, variable_name будет игнорироваться и будет выдано предупреждение.

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

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

--input_exprs

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

`<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 перезаписывать существующие файлы вывода.