{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "id": "q9KfUf1BI6Kl" }, "outputs": [], "source": [ "##### Copyright 2021 The TensorFlow Authors." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "cellView": "form", "id": "WvqLCVQ6I58i", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "ZoFRICPTNUca" }, "source": [ "# 迁移 SavedModel 工作流\n", "\n", "\n", " \n", " \n", " \n", " \n", "
在 TensorFlow.org 上查看 在 Google Colab 运行 在 Github 上查看源代码 下载笔记本
" ] }, { "cell_type": "markdown", "metadata": { "id": "nGyIb7MYJfaM" }, "source": [ "将模型从 TensorFlow 1 的计算图和会话迁移到 TensorFlow 2 API(例如 `tf.function`、`tf.Module` 和 `tf.keras.Model`)后,您可以迁移模型保存和加载代码。此笔记本提供了如何在 TensorFlow 1 和 TensorFlow 2 中以 SavedModel 格式保存和加载的示例。下面是从 TensorFlow 1 迁移到 TensorFlow 2 的相关 API 变更的快速概览:\n", "\n", " | TensorFlow 1 | 迁移到 TensorFlow 2\n", "--- | --- | ---\n", "**保存** | `tf.compat.v1.saved_model.Builder`
`tf.compat.v1.saved_model.simple_save` | `tf.saved_model.save`
Keras:`tf.keras.models.save_model`\n", "**加载** | `tf.compat.v1.saved_model.load` | `tf.saved_model.load`
Keras:`tf.keras.models.load_model`\n", "**签名**:一组输入
和输出张量,
可用于运行
| 使用 `*.signature_def` 效用函数生成
例如 `tf.compat.v1.saved_model.predict_signature_def`) | 编写一个 `tf.function` 并使用 `tf.saved_model.save` 中的 signatures 参数将其导出。
\n", "**分类
和回归**:
特殊类型的签名 | 使用
`tf.compat.v1.saved_model.classification_signature_def`、
`tf.compat.v1.saved_model.regression_signature_def`
和某些 Estimator 导出生成。 | 这两种签名类型已从 TensorFlow 2 中移除。
如果应用库需要这些方法名称,
可以使用 `tf.compat.v1.saved_model.signature_def_utils.MethodNameUpdater`。\n", "\n", "有关映射的更深入解释,请参阅下面的从 TensorFlow 1 到 TensorFlow 2 的变化部分。" ] }, { "cell_type": "markdown", "metadata": { "id": "r5mR2xsNAGsB" }, "source": [ "## 安装\n", "\n", "下面的示例展示了如何使用 TensorFlow 1 和 TensorFlow 2 API 将相同的虚拟 TensorFlow 模型(定义为下面的 `add_two`)导出并加载到 SavedModel 格式。首先,设置导入和效用函数:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "B94QZyy-kOGQ", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "import tensorflow as tf\n", "import tensorflow.compat.v1 as tf1\n", "import shutil\n", "\n", "def remove_dir(path):\n", " try:\n", " shutil.rmtree(path)\n", " except:\n", " pass\n", "\n", "def add_two(input):\n", " return input + 2" ] }, { "cell_type": "markdown", "metadata": { "id": "ZNVpH5tOCgd9" }, "source": [ "## TensorFlow 1:保存和导出 SavedModel\n", "\n", "在 TensorFlow 1 中,使用 `tf.compat.v1.saved_model.Builder`、`tf.compat.v1.saved_model.simple_save` 和 `tf.estimator.Estimator.export_saved_model` API 来构建、保存及导出 TensorFlow 计算图和会话:" ] }, { "cell_type": "markdown", "metadata": { "id": "THRLul5ijmTE" }, "source": [ "### 1. 使用 SavedModelBuilder 将计算图保存为 SavedModel" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "dcZDQaI8jl3h", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "remove_dir(\"saved-model-builder\")\n", "\n", "with tf.Graph().as_default() as g:\n", " with tf1.Session() as sess:\n", " input = tf1.placeholder(tf.float32, shape=[])\n", " output = add_two(input)\n", " print(\"add two output: \", sess.run(output, {input: 3.}))\n", "\n", " # Save with SavedModelBuilder\n", " builder = tf1.saved_model.Builder('saved-model-builder')\n", " sig_def = tf1.saved_model.predict_signature_def(\n", " inputs={'input': input},\n", " outputs={'output': output})\n", " builder.add_meta_graph_and_variables(\n", " sess, tags=[\"serve\"], signature_def_map={\n", " tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY: sig_def\n", " })\n", " builder.save()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "PwtC27VFlwCa", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "!saved_model_cli run --dir saved-model-builder --tag_set serve \\\n", " --signature_def serving_default --input_exprs input=10" ] }, { "cell_type": "markdown", "metadata": { "id": "gnBDNTxKG_vR" }, "source": [ "### 2. 为应用构建 SavedModel" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "jtMxe2rjHSq9", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "remove_dir(\"simple-save\")\n", "\n", "with tf.Graph().as_default() as g:\n", " with tf1.Session() as sess:\n", " input = tf1.placeholder(tf.float32, shape=[])\n", " output = add_two(input)\n", " print(\"add_two output: \", sess.run(output, {input: 3.}))\n", "\n", " tf1.saved_model.simple_save(\n", " sess, 'simple-save',\n", " inputs={'input': input},\n", " outputs={'output': output})" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "AdnqemvIHb2P", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "!saved_model_cli run --dir simple-save --tag_set serve \\\n", " --signature_def serving_default --input_exprs input=10" ] }, { "cell_type": "markdown", "metadata": { "id": "r0BNzzAHjnkp" }, "source": [ "### 3. 将 Estimator 推断计算图导出为 SavedModel\n", "\n", "在 Estimator `model_fn`(定义如下)的定义中,您可以通过在 `tf.estimator.EstimatorSpec` 中返回 `export_outputs` 来定义模型中的签名。下面是不同类型的输出:\n", "\n", "- `tf.estimator.export.ClassificationOutput`\n", "- `tf.estimator.export.RegressionOutput`\n", "- `tf.estimator.export.PredictOutput`\n", "\n", "这些输出将分别产生分类、回归和预测特征类型。\n", "\n", "使用 `tf.estimator.Estimator.export_saved_model` 导出 Estimator 时,这些签名将随模型一起保存。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3nQ5Stnxjhfs", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "def model_fn(features, labels, mode):\n", " output = add_two(features['input'])\n", " step = tf1.train.get_global_step()\n", " return tf.estimator.EstimatorSpec(\n", " mode,\n", " predictions=output,\n", " train_op=step.assign_add(1),\n", " loss=tf.constant(0.),\n", " export_outputs={\n", " tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY: \\\n", " tf.estimator.export.PredictOutput({'output': output})})\n", "est = tf.estimator.Estimator(model_fn, 'estimator-checkpoints')\n", "\n", "# Train for one step to create a checkpoint.\n", "def train_fn():\n", " return tf.data.Dataset.from_tensors({'input': 3.})\n", "est.train(train_fn, steps=1)\n", "\n", "# This utility function `build_raw_serving_input_receiver_fn` takes in raw\n", "# tensor features and builds an \"input serving receiver function\", which\n", "# creates placeholder inputs to the model.\n", "serving_input_fn = tf.estimator.export.build_raw_serving_input_receiver_fn(\n", " {'input': tf.constant(3.)}) # Pass in a dummy input batch.\n", "estimator_path = est.export_saved_model('exported-estimator', serving_input_fn)\n", "\n", "# Estimator's export_saved_model creates a time stamped directory. Move this\n", "# to a set path so it can be inspected with `saved_model_cli` in the cell below.\n", "!rm -rf estimator-model\n", "import shutil\n", "shutil.move(estimator_path, 'estimator-model')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "8_gD2gkE7CMu", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "!saved_model_cli run --dir estimator-model --tag_set serve \\\n", " --signature_def serving_default --input_exprs input=[10]" ] }, { "cell_type": "markdown", "metadata": { "id": "DyBvrNQgIhIo" }, "source": [ "## TensorFlow 2:保存和导出 SavedModel" ] }, { "cell_type": "markdown", "metadata": { "id": "BZmFH-eIjqjB" }, "source": [ "### 保存并导出使用 tf.Module 定义的 SavedModel\n", "\n", "要在 TensorFlow 2 中导出模型,必须定义 `tf.Module` 或 `tf.keras.Model` 来保存模型的所有变量和函数。随后,可以调用 `tf.saved_model.save` 来创建 SavedModel。请参阅[使用 SavedModel 格式](../saved_model.ipynb)指南中的*保存自定义模型*部分来了解详情。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "_j-PwgP_jrgw", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "class MyModel(tf.Module):\n", " @tf.function\n", " def __call__(self, input):\n", " return add_two(input)\n", "\n", "model = MyModel()\n", "\n", "@tf.function\n", "def serving_default(input):\n", " return {'output': model(input)}\n", "\n", "signature_function = serving_default.get_concrete_function(\n", " tf.TensorSpec(shape=[], dtype=tf.float32))\n", "tf.saved_model.save(\n", " model, 'tf2-save', signatures={\n", " tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature_function})" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "slvU4vZN756F", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "!saved_model_cli run --dir tf2-save --tag_set serve \\\n", " --signature_def serving_default --input_exprs input=10" ] }, { "cell_type": "markdown", "metadata": { "id": "UYpSfbBJjr33" }, "source": [ "### 保存并导出使用 Keras 定义的 SavedModel\n", "\n", "已弃用:对于 Keras 对象,建议使用新的高级 `.keras` 格式和 `tf.keras.Model.export`,如[此处](https://tensorflow.google.cn/guide/keras/save_and_serialize)的指南所示。对于现有代码,继续支持低级 SavedModel 格式。\n", "\n", "用于保存和导出的 Keras API(`Mode.save` 或 `tf.keras.models.save_model`)可以从 `tf.keras.Model` 导出 SavedModel。请查看[保存和加载 Keras 模型](../..guide/keras/save_and_serialize),了解更多详细信息。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "mMcjhzyRjvp6", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "inp = tf.keras.Input(3)\n", "out = add_two(inp)\n", "model = tf.keras.Model(inputs=inp, outputs=out)\n", "\n", "@tf.function(input_signature=[tf.TensorSpec(shape=[], dtype=tf.float32)])\n", "def serving_default(input):\n", " return {'output': model(input)}\n", "\n", "model.save('keras-model', save_format='tf', signatures={\n", " tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY: serving_default})" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "4P93WP5R7-VT", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "!saved_model_cli run --dir keras-model --tag_set serve \\\n", " --signature_def serving_default --input_exprs input=10" ] }, { "cell_type": "markdown", "metadata": { "id": "SEKe9rGgoGCw" }, "source": [ "## 加载 SavedModel\n", "\n", "可以使用 TensorFlow 1 或 TensorFlow 2 API 加载使用上述任何 API 保存的 SavedModel。\n", "\n", "TensorFlow 1 SavedModel 在加载到 TensorFlow 2 时通常可用于推断,但只有在 SavedModel 包含*资源变量*时才能进行训练(生成梯度)。您可以检查变量的数据类型,如果变量数据类型包含“_ref\"”,则它是引用变量。\n", "\n", "只要 SavedModel 随签名一起保存,就可以在 TensorFlow 1 中加载和执行 TensorFlow 2 SavedModel。\n", "\n", "下面的部分包含代码示例,展示了如何加载前面部分中保存的 SavedModel 以及调用导出的签名。" ] }, { "cell_type": "markdown", "metadata": { "id": "hLztK_0YoTEP" }, "source": [ "### TensorFlow 1:使用 tf.saved_model.load 加载 SavedModel\n", "\n", "在 TensorFlow 1 中,可以使用 `tf.saved_model.load` 将 SavedModel 直接导入当前计算图和会话。可以在张量输入和输出名称上调用 `Session.run`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "IMO0laj-m0p9", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "def load_tf1(path, input):\n", " print('Loading from', path)\n", " with tf.Graph().as_default() as g:\n", " with tf1.Session() as sess:\n", " meta_graph = tf1.saved_model.load(sess, [\"serve\"], path)\n", " sig_def = meta_graph.signature_def[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY]\n", " input_name = sig_def.inputs['input'].name\n", " output_name = sig_def.outputs['output'].name\n", " print(' Output with input', input, ': ', \n", " sess.run(output_name, feed_dict={input_name: input}))\n", "\n", "load_tf1('saved-model-builder', 5.)\n", "load_tf1('simple-save', 5.)\n", "load_tf1('estimator-model', [5.]) # Estimator's input must be batched.\n", "load_tf1('tf2-save', 5.)\n", "load_tf1('keras-model', 5.)" ] }, { "cell_type": "markdown", "metadata": { "id": "FbR3sfvooVBN" }, "source": [ "### TensorFlow 2:加载使用 tf.saved_model 保存的模型\n", "\n", "在 TensorFlow 2 中,对象会加载到存储变量和函数的 Python 对象中。这与从 TensorFlow 1 保存的模型兼容。\n", "\n", "查看 `tf.saved_model.load` API 文档和[使用 SavedModel 格式](../..guide/saved_model)指南中的[加载和使用自定义模型](../../guide/saved_model#loading_and_using_a_custom_model)部分来了解详情。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "OA52ezWV_KgL", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "def load_tf2(path, input):\n", " print('Loading from', path)\n", " loaded = tf.saved_model.load(path)\n", " out = loaded.signatures[tf.saved_model.DEFAULT_SERVING_SIGNATURE_DEF_KEY](\n", " tf.constant(input))['output']\n", " print(' Output with input', input, ': ', out)\n", "\n", "load_tf2('saved-model-builder', 5.)\n", "load_tf2('simple-save', 5.)\n", "load_tf2('estimator-model', [5.]) # Estimator's input must be batched.\n", "load_tf2('tf2-save', 5.)\n", "load_tf2('keras-model', 5.)" ] }, { "cell_type": "markdown", "metadata": { "id": "Gz3VFn5aAfmK" }, "source": [ "使用 TensorFlow 2 API 保存的模型还可以访问附加到模型的 `tf.function` 和变量(而不是作为签名导出的那些条目)。例如:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "IfMTp-TGAfOs", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "loaded = tf.saved_model.load('tf2-save')\n", "print('restored __call__:', loaded.__call__)\n", "print('output with input 5.', loaded(5))" ] }, { "cell_type": "markdown", "metadata": { "id": "VMoErNKHoXEg" }, "source": [ "### TensorFlow 2:加载使用 Keras 保存的模型\n", "\n", "已弃用:对于 Keras 对象,建议使用新的高级 `.keras` 格式和 `tf.keras.Model.export`,如[此处](https://tensorflow.google.cn/guide/keras/save_and_serialize)的指南所示。对于现有代码,继续支持低级 SavedModel 格式。\n", "\n", "Keras 加载 API (`tf.keras.models.load_model`) 允许您将保存的模型重新加载回 Keras 模型对象。请注意,这只允许您加载使用 Keras 保存的 SavedModel(`Model.save` 或 `tf.keras.models.save_model`)\n", "\n", "使用 `tf.saved_model.save` 保存的模型应使用 `tf.saved_model.load` 加载。可以使用 `tf.saved_model.load` 加载使用 `Model.save` 保存的 Keras 模型,但这样做只会获得 TensorFlow 计算图。有关详情,请参阅 `tf.keras.models.load_model` API 文档与[保存和加载 Keras 模型](https://tensorflow.google.cn/guide/keras/save_and_serialize#savedmodel_format)指南。" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "ZFUAxK0YeIAe", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "loaded_model = tf.keras.models.load_model('keras-model')\n", "loaded_model.predict_on_batch(tf.constant([1, 3, 4]))" ] }, { "cell_type": "markdown", "metadata": { "id": "Tz4eAAGY19MM" }, "source": [ "## GraphDef 和 MetaGraphDef\n", "\n", "<a name=\"graphdef_and_metagraphdef\">\n", "\n", "没有直接方式可以将原始 `GraphDef` 或 `MetaGraphDef` 加载到 TF2。但是,可以使用 [`v1.wrap_function`](https://tensorflow.google.cn/api_docs/python/tf/compat/v1/wrap_function) 将导入计算图的 TF1 代码转换为 TF2 [`concrete_function`](https://tensorflow.org/guide/concrete_function)。\n", "\n", "首先,保存 MetaGraphDef:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "grKam9zGnNRZ", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "# Save a simple multiplication computation:\n", "with tf.Graph().as_default() as g:\n", " x = tf1.placeholder(tf.float32, shape=[], name='x')\n", " v = tf.Variable(3.0, name='v')\n", " y = tf.multiply(x, v, name='y')\n", " with tf1.Session() as sess:\n", " sess.run(v.initializer)\n", " print(sess.run(y, feed_dict={x: 5}))\n", " s = tf1.train.Saver()\n", " s.export_meta_graph('multiply.pb', as_text=True)\n", " s.save(sess, 'multiply_values.ckpt')" ] }, { "cell_type": "markdown", "metadata": { "id": "bJnCe7eYrXev" }, "source": [ "利用 TF1 API,可以使用 `tf1.train.import_meta_graph` 导入计算图并恢复值:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "3bbcGZ4CoVDL", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "with tf.Graph().as_default() as g:\n", " meta = tf1.train.import_meta_graph('multiply.pb')\n", " x = g.get_tensor_by_name('x:0')\n", " y = g.get_tensor_by_name('y:0')\n", " with tf1.Session() as sess:\n", " meta.restore(sess, 'multiply_values.ckpt')\n", " print(sess.run(y, feed_dict={x: 5}))" ] }, { "cell_type": "markdown", "metadata": { "id": "s9OnHOLDrnco" }, "source": [ "没有用于加载计算图的 TF2 API,但您仍然可以将其导入到可以在 Eager 模式下执行的具体函数中:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "id": "yAyGmDLlpVBX", "vscode": { "languageId": "python" } }, "outputs": [], "source": [ "def import_multiply():\n", " # Any graph-building code is allowed here.\n", " tf1.train.import_meta_graph('multiply.pb')\n", "\n", "# Creates a tf.function with all the imported elements in the function graph.\n", "wrapped_import = tf1.wrap_function(import_multiply, [])\n", "import_graph = wrapped_import.graph\n", "x = import_graph.get_tensor_by_name('x:0')\n", "y = import_graph.get_tensor_by_name('y:0')\n", "\n", "# Restore the variable values.\n", "tf1.train.Saver(wrapped_import.variables).restore(\n", " sess=None, save_path='multiply_values.ckpt')\n", "\n", "# Create a concrete function by pruning the wrap_function (similar to sess.run).\n", "multiply_fn = wrapped_import.prune(feeds=x, fetches=y)\n", "\n", "# Run this function\n", "multiply_fn(tf.constant(5.)) # inputs to concrete functions must be Tensors." ] }, { "cell_type": "markdown", "metadata": { "id": "GZ5vGJ0IDorc" }, "source": [ "## 从 TensorFlow 1 到 TensorFlow 2 的变化\n", "\n", "<a id=\"changes_from_tf1_to_tf2\">\n", "\n", "本部分列出了 TensorFlow 1 中的关键保存和加载术语、它们的 TensorFlow 2 等效项以及发生了哪些变化。\n", "\n", "### SavedModel\n", "\n", "[SavedModel](../../guide/saved_model.ipynb) 是一种存储带参数和计算的完整 TensorFlow 程序的格式。它包含应用平台用来运行模型的签名。\n", "\n", "文件格式本身没有重大变化,因此可以使用 TensorFlow 1 或 TensorFlow 2 API 加载和应用 SavedModel。\n", "\n", "**TensorFlow 1 和 TensorFlow 2 的区别**\n", "\n", "除了 API 变更外,TensorFlow 2 中的*应用*和*推断*用例没有更新。在*重用*和*组合*从 SavedModel 加载的模型的能力中引入了改进。\n", "\n", "在 TensorFlow 2 中,程序由 `tf.Variable`、`tf.Module` 或更高级别的 Keras 模型 (`tf.keras.Model`) 和层 (`tf.keras.layers`) 等对象表示。不再具有在会话中存储值的全局变量,并且计算图现在存在于不同的 `tf.function` 中。因此,在模型导出期间,SavedModel 会分别保存每个组件和函数计算图。\n", "\n", "使用 TensorFlow Python API 编写 TensorFlow 程序时,您必须构建一个对象来管理变量、函数和其他资源。通常,可以通过使用 Keras API 来完成此目的,但也可以通过创建或子类化 `tf.Module` 来构建对象。\n", "\n", "Keras 模型 (`tf.keras.Model`) 和 `tf.Module` 会自动跟踪附加到它们的变量和函数。SavedModel 会保存各个模块、变量和函数之间的相应连接,以便在加载时可以恢复。\n", "\n", "### 签名\n", "\n", "签名是 SavedModel 的端点 – 它们告诉用户如何运行模型以及需要哪些输入。\n", "\n", "在 TensorFlow 1 中,签名是通过列出输入和输出张量来创建的。在 TensorFlow 2 中,签名是通过传入*具体函数*来生成的。(在[计算图和 tf.function 简介](../intro_to_graphs.ipynb)指南中阅读更多关于 TensorFlow 函数的信息,特别是*多态性:一个函数,多个计算图*部分。)简而言之,具体函数是从 `tf.function` 生成的:\n", "\n", "```python\n", "# Option 1: Specify an input signature.\n", "@tf.function(input_signature=[...])\n", "def fn(...):\n", " ...\n", " return outputs\n", "\n", "tf.saved_model.save(model, path, signatures={\n", " 'name': fn\n", "})\n", "```\n", "\n", "```python\n", "# Option 2: Call `get_concrete_function`\n", "@tf.function\n", "def fn(...):\n", " ...\n", " return outputs\n", "\n", "tf.saved_model.save(model, path, signatures={\n", " 'name': fn.get_concrete_function(...)\n", "})\n", "```\n", "\n", "### `Session.run`\n", "\n", "在 TensorFlow 1 中,只要您已经知道张量名称,就可以使用导入的计算图调用 `Session.run`。这样就可以检索恢复的变量值,或者运行未在签名中导出的模型部分。\n", "\n", "在 TensorFlow 2 中,可以直接访问变量,例如权重矩阵 (`kernel`):\n", "\n", "```python\n", "model = tf.Module()\n", "model.dense_layer = tf.keras.layers.Dense(...)\n", "tf.saved_model.save('my_saved_model')\n", "loaded = tf.saved_model.load('my_saved_model')\n", "loaded.dense_layer.kernel\n", "```\n", "\n", "或者调用附加到模型对象的 `tf.function`:例如 `loaded.__call__`。\n", "\n", "与 TF1 不同,没有办法提取函数的一部分并访问中间值。您*必须*在保存的对象中导出所有需要的功能。\n" ] }, { "cell_type": "markdown", "metadata": { "id": "b6NG9JvUwJxn" }, "source": [ "## TensorFlow Serving 迁移指南\n", "\n", "SavedModel 最初是为了与 [TensorFlow Serving](https://tensorflow.google.cn/tfx/guide/serving) 一起使用而创建的。此平台提供不同类型的预测请求:分类、回归和预测。\n", "\n", "**TensorFlow 1** API 允许您使用效用函数创建这些类型的签名:\n", "\n", "- `tf.compat.v1.saved_model.classification_signature_def`\n", "- `tf.compat.v1.saved_model.regression_signature_def`\n", "- `tf.compat.v1.saved_model.predict_signature_def`\n", "\n", "[分类](https://tensorflow.google.cn/tfx/serving/signature_defs#classification_signaturedef) (`classification_signature_def`) 和[回归](https://tensorflow.google.cn/tfx/serving/signature_defs#regression_signaturedef) (`regression_signature_def`) 会限制输入和输出,因此输入必须是 `tf.Example`,输出必须是 `classes`、`scores` 或 `prediction`。同时,[预测签名](https://tensorflow.google.cn/tfx/serving/signature_defs#predict_signaturedef) (`predict_signature_def`) 没有限制。\n", "\n", "使用 **TensorFlow 2** API 导出的 SavedModel 与 TensorFlow Serving 兼容,但仅包含预测签名。分类和回归签名已被移除。\n", "\n", "如果您需要使用分类和回归签名,则可以使用 `tf.compat.v1.saved_model.signature_def_utils.MethodNameUpdater` 修改导出的 SavedModel。" ] }, { "cell_type": "markdown", "metadata": { "id": "a3acd3b86215" }, "source": [ "## 后续步骤\n", "\n", "要详细了解 TensorFlow 2 中的 SavedModel,请查看以下指南:\n", "\n", "- [使用 SavedModel 格式](https://tensorflow.google.cn/guide/saved_model)\n", "- [保存和加载 Keras 模型](https://tensorflow.google.cn/guide/keras/save_and_serialize)\n", "\n", "如果您使用的是 TensorFlow Hub,则可能会发现下列指南十分有用:\n", "\n", "- [TensorFlow Hub:TensorFlow 1/TensorFlow 2 的模型兼容性](https://tensorflow.google.cn/hub/model_compatibility)\n", "- [使用 TensorFlow Hub 从 TensorFlow 1 迁移到 TensorFlow 2](https://tensorflow.google.cn/hub/migration_tf2)" ] } ], "metadata": { "colab": { "collapsed_sections": [], "name": "saved_model.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 0 }