本笔记参照TensorFlow官方教程,主要是对‘Save a model-Training checkpoints’教程内容翻译和内容结构编排,原文链接:Using the SavedModel format
目录
一、用Keras创建‘已保存模型’(Creating a SavedModel form Keras)
二、在TensorFlow Serving里运行一个‘已保存模型’(SavedModel)
三、模型保存到硬盘上的格式(The SavedModel format on disk)
四、输出自定义模型(Exporting custom models)
4.1验证输出签名(Identifying a signature to export)
五、用Python重新使用‘已保存模型’(Reusing SavedModels in Python)
5.1 基本的微调(Basic fine-tuning)
5.2 一般的微调(General fine-tuning)
六、‘已保存模型’中的控制流(Control flow in SavedModels)
七、用Estimator保存模型(SavedModels from Extimators)
八、用C++加载‘已保存模型’(Load a SavedModel in C++)
九、‘已保存模型’的命令行交互细节(Detais of the SavedModel command line interface)
9.1 安装‘已保存模型’CLI(Install the SavedModel CLI)
9.2 命令概览
9.3 显示命令
9.4 运行命令
9.5 保存输出
‘已保存模型’含有整个TensorFlow程序,包含权重和计算。它并不需要原始的模型代码,这样很适合分享和部署(部署到TFLite,TensorFlow.js,TensorFlow Serving,或TensorFlow Hub)。
如果你有Python代码写的模型,并且只想加载权重,那可以设置检查点,详情参考:guide to training checkpoints
为了快速介绍,本节导出一个预先训练好的Keras模型,并使用它来处理图像分类请求。指南的其余部分将详细介绍并讨论创建savedmodel的其他方法。
from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
from matplotlib import pyplot as plt
import numpy as np
physical_devices = tf.config.experimental.list_physical_devices('GPU')
if physical_devices:
tf.config.experimental.set_memory_growth(physical_devices[0], 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
我们将使用‘Grace Hopper’的图片当作运行示例,并使用预先训练好的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)
print()
decoded = imagenet_labels[np.argsort(result_before_save)[0,::-1][:5]+1]
print("Result before saving:\n", decoded)
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.6/mobilenet_1_0_224_tf.h5
17227776/17225924 [==============================] - 1s 0us/step
Result before saving:
['military uniform' 'bow tie' 'suit' 'bearskin' 'pickelhaube']
这张图片的最高预测是‘军服’
tf.saved_model.save(pretrained_model, "/tmp/mobilenet/1/")
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/ops/resource_variable_ops.py:1781: calling BaseResourceVariable.__init__ (from tensorflow.python.ops.resource_variable_ops) with constraint is deprecated and will be removed in a future version.
Instructions for updating:
If using Keras pass *_constraint arguments to layers.
INFO:tensorflow:Assets written to: /tmp/mobilenet/1/assets
保存路径遵循TensorFlow Serving使用的约定,其中最后一个路径组件(这里是1/)是模型的版本号——它允许TensorFlow Serving这样的工具来推断相对的新鲜度。
SavedModel有一个命名函数叫‘Signatures’。Keras模型在serving_default签名密钥下导出它们的前向传递。SavedModel命令行接口对于检查磁盘上的SavedModels很有用:
!saved_model_cli show --dir /tmp/mobilenet/1 --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
inputs['input_1'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 224, 224, 3)
name: serving_default_input_1:0
The given SavedModel SignatureDef contains the following output(s):
outputs['act_softmax'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 1000)
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
我们可以用tf.saved_model.load将‘SavedModel’加载回Python,观察‘Admiral Hopper’的图像是如何被分类的。
loaded = tf.saved_model.load("/tmp/mobilenet/1/")
print(list(loaded.signatures.keys())) # ["serving_default"]
['serving_default']
导入的签名总是返回字典。
infer = loaded.signatures["serving_default"]
print(infer.structured_outputs)
{'act_softmax': TensorSpec(shape=(None, 1000), dtype=tf.float32, name='act_softmax')}
从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']
SavedModels可以从Python中使用(更多信息见下面),但是生产环境通常使用专用的服务进行推理,而不运行Python代码。这很容易通过使用‘TensorFlow Serving’从‘SavedModel’中设置。
有关服务的更多细节,请参阅:TensorFlow Serving REST tutorial包括在笔记本或本地计算机上安装tensorflow_model_server的说明。
作为一个快速的草图,为了服务上面导出的mobilenet模型,只需将模型服务器指向SavedModel目录
nohup tensorflow_model_server \
--rest_api_port=8501 \
--model_name=mobilenet \
--model_base_path="/tmp/mobilenet" >server.log 2>&1
然后发送一个请求:
!pip install -q requests
import json
import numpy
import requests
data = json.dumps({"signature_name": "serving_default",
"instances": x.tolist()})
headers = {"content-type": "application/json"}
json_response = requests.post('http://localhost:8501/v1/models/mobilenet:predict',
data=data, headers=headers)
predictions = numpy.array(json.loads(json_response.text)["predictions"])
预测的结果与Python的结果相同。
SavedModel是一个包含序列化签名和运行这些签名所需的状态的目录,包括变量值和词汇表。
!ls /tmp/mobilenet/1
assets saved_model.pb variables
saved_model.pb文件存储了实际的TensorFlow程序或模型,以及一组命名的签名,每个签名标识一个接受张量输入并产生张量输出的函数。
‘SavedModel’可能包含模型的多个变体(多个v1.MetaGraphDefs,用–tag_set标志来标识saved_model_cil),但是这种情况很少见。创建模型的多个变体的api包括tf.Estimator.experimental_export_all_saved_models和TensorFlow 1.x 中的tf.saved_model.Builder。
!saved_model_cli show --dir /tmp/mobilenet/1 --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "serving_default"
变量目录包含一个标准的训练检查点(参考学习笔记16)
!ls /tmp/mobilenet/1/variables
variables.data-00000-of-00002 variables.data-00001-of-00002 variables.index
assets目录包含TensorFlow图使用的文件,例如用于初始化词汇表的文本文件。在本例中未使用它。
SavedModels可能有一个‘assets.extra’目录,用来记录没有被TensorFlow图使用的文件,例如为使用者提供关于如何处理SavedModel的信息。TensorFlow本身并不使用这个目录。
在第一节,‘tf.saved_model.save’自动决定了用于‘tf.keras.Model’对象的签名。这是因为Keras模型有一个明确的方法来导出和形状(shapes)已知的输入。用低级别的模型创建API也能让‘tf.saved_model.save’工作,但如果打算为模型提供服务,则需要指出使用哪个函数作为签名。
class CustomModule(tf.Module):
def __init__(self):
super(CustomModule, self).__init__()
self.v = tf.Variable(1.)
@tf.function
def __call__(self, 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.function装饰的方法。而这些函数将包含在SavedModel中,如果通过tf.saved_model.load将‘SavedModel’重新加载到Python程序中,不显示地声明服务签名工具(如TensorFlow Serving和saved_model_cli)就不能访问他们。
‘mutate’方法有一个声明过的‘input_signature’,因此在SavedModel中已经有足够的信息来保存它的计算图。'call’方法没有声明过的签名,因此它的签名是在保存之前从它的使用方式中推断出来的:调用该方法一次或多次将为参数可见的张量形状(shape)和dtypes的每个特定组合创建计算图。
module(tf.constant(0.))
tf.saved_model.save(module, "/tmp/module_no_signatures")
INFO:tensorflow:Assets written to: /tmp/module_no_signatures/assets
对于没有input_signature的函数,在保存之前使用的任何输入形状在加载之后都是可用的。因为我们只使用了标量调用了‘call’方法,所以它只接受标量值。
imported = tf.saved_model.load("/tmp/module_no_signatures")
assert 3. == imported(tf.constant(3.)).numpy()
imported.mutate(tf.constant(2.))
assert 6. == imported(tf.constant(3.)).numpy()
这个函数将不接受向量:
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'),), {})].
‘get_concrete_function’让我们不需要调用函数,就可以往函数里添加输入形状。它需要‘tf.TensorSpec’对象来取代Tensor参数,表示输入的形状(shapes)和dtypes。‘形状’可以为‘None’,表示任何形状都可以,或者表示一个轴(axis)大小的列表。如果轴(axis)大小为‘None’,那这个轴(axis)可以接受任何大小。(这个常被用于批大小(batch size))我们也可以给‘tf.TensorSpec’取名,默认情况下为函数参数的关键字(以下为‘x’)
module.__call__.get_concrete_function(x=tf.TensorSpec([None], tf.float32))
tf.saved_model.save(module, "/tmp/module_no_signatures")
imported = tf.saved_model.load("/tmp/module_no_signatures")
assert [3.] == imported(tf.constant([3.])).numpy()
INFO:tensorflow:Assets written to: /tmp/module_no_signatures/assets
我们没有将导出的函数标识为签名,所以它没有。
!saved_model_cli show --dir /tmp/module_no_signatures --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
4.1标识输出签名(Identifying a signature to export)
要指出一个函数应该是签名,请在保存时指定签名参数。
call = module.__call__.get_concrete_function(tf.TensorSpec(None, tf.float32))
tf.saved_model.save(module, "/tmp/module_with_signature", signatures=call)
INFO:tensorflow:Assets written to: /tmp/module_with_signature/assets
注意:我们第一次用‘get_concrete_function’将‘tf.function’转换为‘ConcreteFunction’。这是必要的,因为函数是在没有固定的input_signature的情况下创建的,因此没有与之相关的一组确定的张量输入。
!saved_model_cli show --dir /tmp/module_with_signature --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
inputs['x'] tensor_info:
dtype: DT_FLOAT
shape: unknown_rank
name: serving_default_x:0
The given SavedModel SignatureDef contains the following output(s):
outputs['output_0'] tensor_info:
dtype: DT_FLOAT
shape: unknown_rank
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
imported = tf.saved_model.load("/tmp/module_with_signature")
signature = imported.signatures["serving_default"]
assert [3.] == signature(x=tf.constant([3.]))["output_0"].numpy()
imported.mutate(tf.constant(2.))
assert [6.] == signature(x=tf.constant([3.]))["output_0"].numpy()
assert 2. == imported.v.numpy()
我们导出了一个签名,其密钥默认为“serving_default”。要导出多个签名,请传递一个字典。
@tf.function(input_signature=[tf.TensorSpec([], tf.string)])
def parse_string(string_input):
return imported(tf.strings.to_number(string_input))
signatures = {"serving_default": parse_string,
"from_float": imported.signatures["serving_default"]}
tf.saved_model.save(imported, "/tmp/module_with_multiple_signatures", signatures)
INFO:tensorflow:Assets written to: /tmp/module_with_multiple_signatures/assets
!saved_model_cli show --dir /tmp/module_with_multiple_signatures --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the following keys:
SignatureDef key: "__saved_model_init_op"
SignatureDef key: "from_float"
SignatureDef key: "serving_default"
用‘saved_model_cli’可以从命令行直接运行‘SavedModel’
!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve --signature_def serving_default --input_exprs="string_input='3.'"
!saved_model_cli run --dir /tmp/module_with_multiple_signatures --tag_set serve --signature_def from_float --input_exprs="x=3."
2019-10-08 01:03:58.756878: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1
2019-10-08 01:03:58.763375: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.764046: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1618] Found device 0 with properties:
name: Tesla V100-SXM2-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.53
pciBusID: 0000:00:05.0
2019-10-08 01:03:58.764312: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:03:58.765636: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10.0
2019-10-08 01:03:58.766994: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10.0
2019-10-08 01:03:58.767300: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10.0
2019-10-08 01:03:58.768874: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10.0
2019-10-08 01:03:58.770021: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10.0
2019-10-08 01:03:58.773585: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2019-10-08 01:03:58.773693: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.774186: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.774586: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1746] Adding visible gpu devices: 0
2019-10-08 01:03:58.774854: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 AVX512F FMA
2019-10-08 01:03:58.781107: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2000189999 Hz
2019-10-08 01:03:58.781693: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x530c040 executing computations on platform Host. Devices:
2019-10-08 01:03:58.781724: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
2019-10-08 01:03:58.867097: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.867624: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x530df00 executing computations on platform CUDA. Devices:
2019-10-08 01:03:58.867654: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Tesla V100-SXM2-16GB, Compute Capability 7.0
2019-10-08 01:03:58.867828: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.868302: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1618] Found device 0 with properties:
name: Tesla V100-SXM2-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.53
pciBusID: 0000:00:05.0
2019-10-08 01:03:58.868362: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:03:58.868377: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10.0
2019-10-08 01:03:58.868386: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10.0
2019-10-08 01:03:58.868397: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10.0
2019-10-08 01:03:58.868427: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10.0
2019-10-08 01:03:58.868439: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10.0
2019-10-08 01:03:58.868453: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2019-10-08 01:03:58.868540: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.868976: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.869357: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1746] Adding visible gpu devices: 0
2019-10-08 01:03:58.869401: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:03:58.870402: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1159] Device interconnect StreamExecutor with strength 1 edge matrix:
2019-10-08 01:03:58.870426: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1165] 0
2019-10-08 01:03:58.870437: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1178] 0: N
2019-10-08 01:03:58.870554: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.870982: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:03:58.871384: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1304] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 6849 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/tools/saved_model_cli.py:339: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.
Result for output key output_0:
6.0
2019-10-08 01:04:01.666328: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcuda.so.1
2019-10-08 01:04:01.672683: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.673132: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1618] Found device 0 with properties:
name: Tesla V100-SXM2-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.53
pciBusID: 0000:00:05.0
2019-10-08 01:04:01.673331: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:04:01.674645: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10.0
2019-10-08 01:04:01.675852: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10.0
2019-10-08 01:04:01.676166: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10.0
2019-10-08 01:04:01.677709: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10.0
2019-10-08 01:04:01.678883: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10.0
2019-10-08 01:04:01.682458: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2019-10-08 01:04:01.682565: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.683041: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.683469: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1746] Adding visible gpu devices: 0
2019-10-08 01:04:01.683760: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 AVX512F FMA
2019-10-08 01:04:01.690090: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 2000189999 Hz
2019-10-08 01:04:01.690722: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x564b9b0 executing computations on platform Host. Devices:
2019-10-08 01:04:01.690750: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Host, Default Version
2019-10-08 01:04:01.777802: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.778326: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x564d870 executing computations on platform CUDA. Devices:
2019-10-08 01:04:01.778358: I tensorflow/compiler/xla/service/service.cc:175] StreamExecutor device (0): Tesla V100-SXM2-16GB, Compute Capability 7.0
2019-10-08 01:04:01.778567: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.779037: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1618] Found device 0 with properties:
name: Tesla V100-SXM2-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.53
pciBusID: 0000:00:05.0
2019-10-08 01:04:01.779102: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:04:01.779122: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcublas.so.10.0
2019-10-08 01:04:01.779138: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcufft.so.10.0
2019-10-08 01:04:01.779156: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcurand.so.10.0
2019-10-08 01:04:01.779174: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusolver.so.10.0
2019-10-08 01:04:01.779192: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcusparse.so.10.0
2019-10-08 01:04:01.779220: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudnn.so.7
2019-10-08 01:04:01.779297: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.779765: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.780205: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1746] Adding visible gpu devices: 0
2019-10-08 01:04:01.780258: I tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libcudart.so.10.0
2019-10-08 01:04:01.781294: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1159] Device interconnect StreamExecutor with strength 1 edge matrix:
2019-10-08 01:04:01.781320: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1165] 0
2019-10-08 01:04:01.781329: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1178] 0: N
2019-10-08 01:04:01.781448: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.781878: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:1006] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2019-10-08 01:04:01.782327: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1304] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 6849 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/tools/saved_model_cli.py:339: load (from tensorflow.python.saved_model.loader_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.loader.load or tf.compat.v1.saved_model.load. There will be a new function for importing SavedModels in Tensorflow 2.0.
Result for output key output_0:
6.0
让我们再看看上面的‘CustomModule’类,以及这种类型的模块对象是如何作为SavedModels保存并加载回来的。是否注意到在没有使用签名的情况下调用了module(…)?
在派生自‘tf.keras.Model’或‘tf.Model’对象上调用‘tf.saved_model.save(obj,’/path’)’,将‘tf.Variable’的属性保存为‘obj’,递归地遍历其属性引用的对象,以及保存在这些对象上发现的变量属性的当前值。同样,这些对象上的‘tf.function-decorated’方法保存了它们的计算图。然而,原始的Python类型,Python方法代码和‘Python-valued’的数据成员丢失了。
调用‘obj=tf.saved_model.load(’/path’)'方法来恢复已保存的对象(现在简化为占位符类型),它们的变量属性以及各自保存的值,以及它们的‘tf.function’装饰方法。只要保存了一个包含张量形状和非张量值组合的计算图,就可以像以前一样调用这些方法。重新在Python代码里跟踪‘tf.function’将不可能,并且会产生异常。
恢复的‘tf.function’可以提供比在‘.signatures’字典里的具体函数更丰富的Python化的API给恢复的模型。然而,对于依靠签名的非Python环境,这些API是不可用的。
5.1 基本的微调(Basic fine-tuning)
变量对象是可用的,我们可以导入函数来进行后向传播。这样对于简单场景下的‘SavedModel’微调(fine-tuning)已经足够了。
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
5.2 一般的微调(General fine-tuning)
Keras的‘SavedModel’提供了比普通的‘call’更多的细节来处理更高级的微调场景。TensorFlow Hub建议在共享的SavedModels中提供以下功能(如果适用),以便进行微调:
- 如果模型使用丢弃(dropout)或前向传递在训练和推理之间有所不同的其它技术(如批处理正则化batch normalization),则使用‘call’方法,其中‘Python-valued’'training=‘参数默认为False,可以被设置为‘True’。
- 除了‘call’属性,还有‘.variable’和‘.trainable_variable’属性与变量列表相关联。’.trainable_variables’中省略了一个原本是可训练的变量,但在微调期间将其冻结。
- 对于像Keras这样将权重调整器表示为层或子模型属性的框架,还可以有一个.regularization_losses属性。它包含一个零参数函数的列表,这些函数的值是用来增加总损失的。
回到MobileNet初始化,我们可以看到这些动作:
loaded = tf.saved_model.load("/tmp/mobilenet/1/")
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, ...
任何可以进入‘tf.function’的东西也可以进入‘SavedModel’。对于包括依赖于张量的条件逻辑的‘AutoGraph’,由常规Python控制流指定。
@tf.function(input_signature=[tf.TensorSpec([], tf.int32)])
def control_flow(x):
if x < 0:
tf.print("Invalid!")
else:
tf.print(x % 3)
to_export = tf.Module()
to_export.control_flow = control_flow
tf.saved_model.save(to_export, "/tmp/control_flow")
INFO:tensorflow:Assets written to: /tmp/control_flow/assets
imported = tf.saved_model.load("/tmp/control_flow")
imported.control_flow(tf.constant(-1)) # Invalid!
imported.control_flow(tf.constant(2)) # 2
imported.control_flow(tf.constant(3)) # 0
Invalid!
2
0
七、用Estimator保存模型(SavedModels from Extimators)
Estimator通过‘tf.Estimator.export_saved_model’导出‘SavedModels’。更多详情参看学习笔记9。
input_column = tf.feature_column.numeric_column("x")
estimator = tf.estimator.LinearClassifier(feature_columns=[input_column])
def input_fn():
return tf.data.Dataset.from_tensor_slices(
({"x": [1., 2., 3., 4.]}, [1, 1, 0, 0])).repeat(200).shuffle(64).batch(16)
estimator.train(input_fn)
serving_input_fn = tf.estimator.export.build_parsing_serving_input_receiver_fn(
tf.feature_column.make_parse_example_spec([input_column]))
export_path = estimator.export_saved_model(
"/tmp/from_estimator/", serving_input_fn)
INFO:tensorflow:Using default config.
WARNING:tensorflow:Using temporary folder as model directory: /tmp/tmp9qiqjm1c
INFO:tensorflow:Using config: {'_model_dir': '/tmp/tmp9qiqjm1c', '_tf_random_seed': None, '_save_summary_steps': 100, '_save_checkpoints_steps': None, '_save_checkpoints_secs': 600, '_session_config': allow_soft_placement: true
graph_options {
rewrite_options {
meta_optimizer_iterations: ONE
}
}
, '_keep_checkpoint_max': 5, '_keep_checkpoint_every_n_hours': 10000, '_log_step_count_steps': 100, '_train_distribute': None, '_device_fn': None, '_protocol': None, '_eval_distribute': None, '_experimental_distribute': None, '_experimental_max_worker_delay_secs': None, '_session_creation_timeout_secs': 7200, '_service': None, '_cluster_spec': <tensorflow.python.training.server_lib.ClusterSpec object at 0x7f10599bab00>, '_task_type': 'worker', '_task_id': 0, '_global_id_in_cluster': 0, '_master': '', '_evaluation_master': '', '_is_chief': True, '_num_ps_replicas': 0, '_num_worker_replicas': 1}
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/training/training_util.py:236: Variable.initialized_value (from tensorflow.python.ops.variables) is deprecated and will be removed in a future version.
Instructions for updating:
Use Variable.read_value. Variables in 2.X are initialized automatically both in eager and graph (inside tf.defun) contexts.
INFO:tensorflow:Calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/feature_column/feature_column_v2.py:518: Layer.add_variable (from tensorflow.python.keras.engine.base_layer) is deprecated and will be removed in a future version.
Instructions for updating:
Please use `layer.add_weight` method instead.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_estimator/python/estimator/canned/linear.py:308: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/keras/optimizer_v2/ftrl.py:143: calling Constant.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.
Instructions for updating:
Call initializer instance with the dtype argument instead of passing it to the constructor
INFO:tensorflow:Done calling model_fn.
INFO:tensorflow:Create CheckpointSaverHook.
INFO:tensorflow:Graph was finalized.
INFO:tensorflow:Running local_init_op.
INFO:tensorflow:Done running local_init_op.
INFO:tensorflow:Saving checkpoints for 0 into /tmp/tmp9qiqjm1c/model.ckpt.
INFO:tensorflow:loss = 0.6931472, step = 0
INFO:tensorflow:Saving checkpoints for 50 into /tmp/tmp9qiqjm1c/model.ckpt.
INFO:tensorflow:Loss for final step: 0.3829868.
INFO:tensorflow:Calling model_fn.
INFO:tensorflow:Done calling model_fn.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/tensorflow_core/python/saved_model/signature_def_utils_impl.py:145: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
INFO:tensorflow:Signatures INCLUDED in export for Classify: ['serving_default', 'classification']
INFO:tensorflow:Signatures INCLUDED in export for Regress: ['regression']
INFO:tensorflow:Signatures INCLUDED in export for Predict: ['predict']
INFO:tensorflow:Signatures INCLUDED in export for Train: None
INFO:tensorflow:Signatures INCLUDED in export for Eval: None
INFO:tensorflow:Restoring parameters from /tmp/tmp9qiqjm1c/model.ckpt-50
INFO:tensorflow:Assets added to graph.
INFO:tensorflow:No assets to write.
INFO:tensorflow:SavedModel written to: /tmp/from_estimator/temp-b'1570496648'/saved_model.pb
这种SavedModel接受序列化‘tf.Example’协议缓存,这对服务是有用的。但我们可以用’tf.saved_model.load’加载它并用Python运行它。
imported = tf.saved_model.load(export_path)
def predict(x):
example = tf.train.Example()
example.features.feature["x"].float_list.value.extend([x])
return imported.signatures["predict"](
examples=tf.constant([example.SerializeToString()]))
print(predict(1.5))
print(predict(3.5))
{'class_ids': <tf.Tensor: id=55292, shape=(1, 1), dtype=int64, numpy=array([[1]])>, 'classes': <tf.Tensor: id=55293, shape=(1, 1), dtype=string, numpy=array([[b'1']], dtype=object)>, 'all_class_ids': <tf.Tensor: id=55290, shape=(1, 2), dtype=int32, numpy=array([[0, 1]], dtype=int32)>, 'all_classes': <tf.Tensor: id=55291, shape=(1, 2), dtype=string, numpy=array([[b'0', b'1']], dtype=object)>, 'logistic': <tf.Tensor: id=55294, shape=(1, 1), dtype=float32, numpy=array([[0.56012416]], dtype=float32)>, 'probabilities': <tf.Tensor: id=55296, shape=(1, 2), dtype=float32, numpy=array([[0.43987584, 0.56012416]], dtype=float32)>, 'logits': <tf.Tensor: id=55295, shape=(1, 1), dtype=float32, numpy=array([[0.24166596]], dtype=float32)>}
{'class_ids': <tf.Tensor: id=55300, shape=(1, 1), dtype=int64, numpy=array([[0]])>, 'classes': <tf.Tensor: id=55301, shape=(1, 1), dtype=string, numpy=array([[b'0']], dtype=object)>, 'all_class_ids': <tf.Tensor: id=55298, shape=(1, 2), dtype=int32, numpy=array([[0, 1]], dtype=int32)>, 'all_classes': <tf.Tensor: id=55299, shape=(1, 2), dtype=string, numpy=array([[b'0', b'1']], dtype=object)>, 'logistic': <tf.Tensor: id=55302, shape=(1, 1), dtype=float32, numpy=array([[0.2347819]], dtype=float32)>, 'probabilities': <tf.Tensor: id=55304, shape=(1, 2), dtype=float32, numpy=array([[0.76521814, 0.2347819 ]], dtype=float32)>, 'logits': <tf.Tensor: id=55303, shape=(1, 1), dtype=float32, numpy=array([[-1.1815039]], dtype=float32)>}
‘tf.estimator.export.build_raw_serving_input_receiver_fn’允许我们用原始张量(raw tensors)而不是‘tf.train.Example’创建输入函数。
八、用C++加载‘已保存模型’(Load a SavedModel in C++)
C++版本的SavedModel加载器提供了一个从路径加载SavedModel的API,同时运行‘SessionOptions’和‘RunOptions’。我们需要指定关联被加载图的标签(tags)。SavedModel的加载版本被称为SavedModelBundle,它包含MetaGraphDef和加载它的会话。
const string export_dir = ...
SavedModelBundle bundle;
...
LoadSavedModel(session_options, run_options, export_dir, {kSavedModelTagTrain},
&bundle);
九、‘已保存模型’的命令行交互细节(Detais of the SavedModel command line interface)
我们可以用SavedModel命令交互界面去检查和执行一个SavedModel。例如,我们可以用CLI(Command Line Interface)去检查模型的‘SignatureDef’。CLI让我们可以快速的确认输入张量的形状和dtype是否和模型匹配。并且,如果我们想测试我们的模型,我们可以使用CLI来进行全面检查(sanity check),方法是传入各种格式的示例输入(例如Python表达式),然后获得输出。
9.1 安装‘已保存模型’CLI(Install the SavedModel CLI)
一般来说,我们可以通过以下两种方式安装TensorFlow:
- 通过安装预构建的TensorFlow二进制文件
- 通过从源代码构建TensorFlow。
如果我们通过预构建的TensorFlow二进制文件安装TensorFlow,那SavedModel CLI已经安装到我们系统里了,路径名为bin/saved_model_cli。
如果我们从源代码构建TensorFlow,那我们必须通过下面额外的命令来创建‘saved_model_cli’:
$ bazel build tensorflow/python/tools:saved_model_cli
9.2 命令概览
SavedModel CLI支持下面两种命令:
- ‘show’,显示‘SavedModel’里可用的计算
- ‘run’,运行‘SavedModel’里的计算
9.3 ‘show’命令
一个SavedModel含有一个或多个模型变体(v1.MetaGraphDefs),由它们的标记集来标识。为了服务模型,我们可能会好奇每个模型变体里有哪种‘SignatureDef’,它们的输入和输出是什么。‘show’命令允许我们按层次顺序检查SavedModel的内容。这里的语法:
usage: saved_model_cli show [-h] --dir DIR [--all]
[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]
例如,下面的命令显示了所有SavedModel里可用的标记集(tag-sets):
$ 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
要显示特定SignatureDef的所有输入和输出TensorInfo,请将SignatureDef键传递给signature_def选项。当你想要知道张量的键值,输入张量的类型和形状,以便以后执行计算图形时,这是非常有用的。例如:
$ 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
9.4 运行(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 ndarry’
- ‘–input_exprs’选项让我们可以传递Python表达
- ‘–input_example’选项让我们可以传递‘tf.train.Example’。
–inputs:为了传递文件里的数据,需要指定‘–inputs’选项,它需要遵守下面的通用格式:
--inputs <INPUTS>
也可以是下面的格式:
<input_key>=<filename>
<input_key>=<filename>[<variable_name>]
我们可以传递多个输入。如果传递多个输入,则使用分号分隔每个输入。
‘saved_model_cli’用‘numpy.load’来加载文件名。文件名可以是下面任何一种格式:‘.npy’、‘.npz’、‘pickle format’
‘.npy’文件通常包含一个‘numpy ndarray’。因此,当从‘.npy’文件加载时,内容将被直接分配给指定的输入张量。如果我们使用.npy文件指定了variable_name,那么variable_name将被忽略,并发出警告。
在从.npz(zip)文件加载时,可以选择指定variable_name来标识zip文件中的变量,以便为输入张量键加载该变量。如果我们没有指定variable_name,那么SavedModel CLI将检查zip文件中是否只包含一个文件,并为指定的输入张量键加载它。
从pickle文件加载时,如果在方括号中没有指定variable_name,那么pickle文件中的任何内容都将传递给指定的输入张量键。否则,SavedModel CLI将假定字典存储在pickle文件中,并使用variable_name对应的值。
–input_exprs:要想通过Python表达式传递输入,则需指定‘–input_exprs’选项。当我们没有数据文件,但是仍然想要用一些简单的输入来检查模型,这些输入与模型的SignatureDefs的dtype和shape匹配时,这是非常有用的。例如:
`<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"]}]`
9.5 保存输出
默认情况下,SavedModel CLI将输出写入stdout。如果将目录传递给——outdir选项,则输出将保存为给定目录下以输出张量键命名的.npy文件。
使用‘–overwrite’来覆盖现有的输出的文件