通过 Vertex AI 上的预训练 TensorFlow 图片模型获取预测结果

1. 概览

在本实验中,您将使用 Vertex AI 从预训练的图片分类模型获取预测结果。

学习内容

您将了解如何:

  • 将 TensorFlow 模型导入 Vertex AI Model Registry
  • 获取在线预测结果
  • 更新 TensorFlow 服务函数

在 Google Cloud 上运行本实验的总费用约为 $1

2. Vertex AI 简介

本实验使用的是 Google Cloud 上提供的最新 AI 产品。Vertex AI 将整个 Google Cloud 的机器学习产品集成到无缝的开发体验中。以前,使用 AutoML 训练的模型和自定义模型是通过不同的服务访问的。现在,该新产品与其他新产品一起将这两种模型合并到一个 API 中。您还可以将现有项目迁移到 Vertex AI。

Vertex AI 包含许多不同的产品,可支持端到端机器学习工作流。本实验将重点介绍下面突出显示的产品:PredictionsWorkbench

Vertex 产品概览

3. 用例概览

在本实验中,您将学习如何从 TensorFlow Hub 获取预训练模型,并将其部署到 Vertex AI 上。TensorFlow Hub 是一个包含经过训练的模型的代码库,适用于各种问题领域,例如嵌入、文本生成、语音转文字、图像分割等。

本实验中使用的示例是一个基于 ImageNet 数据集预训练的 MobileNet V1 图片分类模型。通过利用 TensorFlow Hub 或其他类似深度学习代码库中的现成模型,您可以为许多预测任务部署高质量机器学习模型,而无需担心模型训练。

4. 设置您的环境

您需要一个启用了结算功能的 Google Cloud Platform 项目才能运行此 Codelab。如需创建项目,请按照此处的说明操作。

第 1 步:启用 Compute Engine API

前往 Compute Engine,然后选择启用(如果尚未启用)。

第 2 步:启用 Vertex AI API

前往 Cloud Console 的 Vertex AI 部分,然后点击启用 Vertex AI API

Vertex AI 信息中心

第 3 步:创建 Vertex AI Workbench 实例

在 Cloud Console 的 Vertex AI 部分中,点击“Workbench”:

Vertex AI 菜单

启用 Notebooks API(如果尚未启用)。

Notebook_api

启用后,点击代管式笔记本

Notebooks_UI

然后选择新建笔记本

new_notebook

为您的笔记本命名,然后在权限下选择服务账号

create_notebook

选择高级设置

安全性下,选择“启用终端”(如果尚未启用)。

enable_terminal

您可以保留所有其他高级设置。

接下来,点击创建。预配实例需要几分钟时间。

创建实例后,选择打开 JUPYTERLAB

open_jupyterlab

5. 注册模型

第 1 步:将模型上传到 Cloud Storage

点击此链接可前往 TensorFlow Hub 页面,查看在 ImageNet 数据集上训练的 MobileNet V1 模型。

选择下载以下载已保存的模型制品。

download_model

在 Google Cloud 控制台的 Cloud Storage 部分,选择 CREATE(创建)

create_bucket

为您的存储分区命名,然后选择 us-central1 作为区域。然后点击创建

specify_bucket

将您下载的 TensorFlow Hub 模型上传到存储分区。请务必先解压缩文件。

gcs_model

您的存储分区应如下所示:

imagenet_mobilenet_v1_050_128_classification_5/
  saved_model.pb
  variables/
    variables.data-00000-of-00001
    variables.index

第 2 步:将模型导入注册表

前往 Cloud 控制台的 Vertex AI Model Registry 部分。

model_registry

选择导入

选择作为新模型导入,然后为模型命名。

name_and_region

模型设置下,指定最新的预构建 TensorFlow 容器。然后,选择 Cloud Storage 中用于存储模型制品的路径。

select_container

您可以跳过可解释性部分。

然后选择导入

导入后,您会在模型注册表中看到模型

imported_model

6. 部署模型

在模型注册表中,选择模型右侧的三点状图标,然后点击部署到端点

deploy_model

定义端点下,选择创建新端点,然后为您的端点命名。

模型设置下,将计算节点数量上限设置为 1,将机器类型设置为 n1-standard-2,并将所有其他设置保持不变。然后,点击部署

endpoint_settings

部署完成后,部署状态将更改为已在 Vertex AI 上部署

deploy_status

7. 获取预测结果

打开您在设置步骤中创建的 Workbench 笔记本。在启动器中,创建一个新的 TensorFlow 2 笔记本。

tf_nb

执行以下单元格以导入必要的库

from google.cloud import aiplatform

import tensorflow as tf
import numpy as np
from PIL import Image

您从 TensorFlow Hub 下载的 MobileNet 模型已基于 ImageNet 数据集进行训练。MobileNet 模型的输出是与 ImageNet 数据集中类别标签对应的数字。如需将该数字转换为字符串标签,您需要下载图片标签。

# Download image labels

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())

为了连接端点,您需要定义端点资源。请务必替换 {PROJECT_NUMBER}{ENDPOINT_ID}

PROJECT_NUMBER = "{PROJECT_NUMBER}"
ENDPOINT_ID = "{ENDPOINT_ID}"

endpoint = aiplatform.Endpoint(
    endpoint_name=f"projects/{PROJECT_NUMBER}/locations/us-central1/endpoints/{ENDPOINT_ID}")

您可以在控制台的首页上找到项目编号。

project_number

以及 Vertex AI 端点部分中的端点 ID。

endpoint_id

接下来,您将测试端点。

首先,下载以下图片并将其上传到您的实例。

test_image

使用 PIL 打开图片。然后调整大小并按 255 缩放。请注意,您可以在模型的 TensorFlow Hub 页面上找到模型预期的图片大小。

IMAGE_PATH = "test-image.jpg"
IMAGE_SIZE = (128, 128)

im = Image.open(IMAGE_PATH)
im = im.resize(IMAGE_SIZE
im = np.array(im)/255.0

接下来,将 NumPy 数据转换为列表,以便在 HTTP 请求的正文中发送。

x_test = im.astype(np.float32).tolist()

最后,向端点发出预测调用,然后查找相应的字符串标签。

# make prediction request
result = endpoint.predict(instances=[x_test]).predictions

# post process result
predicted_class = tf.math.argmax(result[0], axis=-1)
string_label = imagenet_labels[predicted_class]

print(f"label ID: {predicted_class}")
print(f"string label: {string_label}")

8. [可选] 使用 TF Serving 优化预测

对于更贴近实际的示例,您可能需要直接将图片本身发送到端点,而不是先将其加载到 NumPy 中。这种方式更高效,但您必须修改 TensorFlow 模型的服务函数。此修改需要将输入数据转换为模型预期的格式。

第 1 步:修改广告投放函数

打开一个新的 TensorFlow 笔记本,然后导入必要的库。

from google.cloud import aiplatform

import tensorflow as tf

这次,您将使用 hub.KerasLayer 将模型加载到 TensorFlow 中,而不是下载已保存的模型工件。hub.KerasLayer 会将 TensorFlow SavedModel 封装为 Keras 层。如需创建模型,您可以将 Keras Sequential API 与下载的 TF Hub 模型作为层一起使用,并指定模型的输入形状。

tfhub_model = tf.keras.Sequential(
    [hub.KerasLayer("https://tfhub.dev/google/imagenet/mobilenet_v1_050_128/classification/5")]
)
tfhub_model.build([None, 128, 128, 3])

定义指向您之前创建的存储分区的 URI。

BUCKET_URI = "gs://{YOUR_BUCKET}"
MODEL_DIR = BUCKET_URI + "/bytes_model"

当您向在线预测服务器发送请求时,HTTP 服务器会收到该请求。HTTP 服务器会从 HTTP 请求内容正文中提取预测请求。提取的预测请求会转发到服务函数。对于 Vertex AI 预构建的预测容器,请求内容会作为 tf.string 传递给服务函数。

如需将图片传递给预测服务,您需要将压缩的图片字节编码为 base 64,这样一来,在通过网络传输二进制数据时,内容就不会被修改。

由于部署的模型需要原始(未压缩)字节的输入数据,因此您需要确保先将 base 64 编码数据转换回原始字节(例如 JPEG),然后进行预处理以匹配模型输入要求,然后再将其作为输入传递到已部署模型。

为解决此问题,您可以定义一个服务函数 (serving_fn),并将其作为预处理步骤附加到模型。您需要添加 @tf.function 修饰器,以便将传送函数融合到底层模型(而不是 CPU 上的上游)。

CONCRETE_INPUT = "numpy_inputs"


def _preprocess(bytes_input):
    decoded = tf.io.decode_jpeg(bytes_input, channels=3)
    decoded = tf.image.convert_image_dtype(decoded, tf.float32)
    resized = tf.image.resize(decoded, size=(128, 128))
    return resized


@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def preprocess_fn(bytes_inputs):
    decoded_images = tf.map_fn(
        _preprocess, bytes_inputs, dtype=tf.float32, back_prop=False
    )
    return {
        CONCRETE_INPUT: decoded_images
    }  # User needs to make sure the key matches model's input


@tf.function(input_signature=[tf.TensorSpec([None], tf.string)])
def serving_fn(bytes_inputs):
    images = preprocess_fn(bytes_inputs)
    prob = m_call(**images)
    return prob


m_call = tf.function(tfhub_model.call).get_concrete_function(
    [tf.TensorSpec(shape=[None, 128, 128, 3], dtype=tf.float32, name=CONCRETE_INPUT)]
)

tf.saved_model.save(tfhub_model, MODEL_DIR, signatures={"serving_default": serving_fn})

当您以 HTTP 请求数据包的形式发送预测数据时,图片数据采用 base64 编码,但 TensorFlow 模型会获取 Numpy 输入。您的分发函数将从 base64 转换为 numpy 数组。

发出预测请求时,您需要将请求路由到部署函数,而不是模型,因此您需要知道部署函数的输入层名称。我们可以从传送函数签名中获取此名称。

loaded = tf.saved_model.load(MODEL_DIR)

serving_input = list(
    loaded.signatures["serving_default"].structured_input_signature[1].keys()
)[0]
print("Serving function input name:", serving_input)

第 2 步:导入到注册表并部署

在前面的部分中,您了解了如何通过界面将模型导入 Vertex AI Model Registry。在本部分中,您将了解改用 SDK 的替代方法。请注意,您仍然可以在此处改用界面。

model = aiplatform.Model.upload(
    display_name="optimized-model",
    artifact_uri=MODEL_DIR,
    serving_container_image_uri="us-docker.pkg.dev/vertex-ai/prediction/tf2-cpu.2-8:latest",
)

print(model)

您也可以使用 SDK(而非界面)部署模型。

endpoint = model.deploy(
     deployed_model_display_name='my-bytes-endpoint',
     traffic_split={"0": 100},
     machine_type="n1-standard-4",
     accelerator_count=0,
     min_replica_count=1,
     max_replica_count=1,
   )

第 3 步:测试模型

现在,您可以测试端点了。由于我们修改了传送函数,因此这次您可以在请求中直接发送图片(采用 base64 编码),而无需先将图片加载到 NumPy 中。这样,您还可以发送更大的图片,而不会超出 Vertex AI 预测大小限制。

重新下载图片标签

import numpy as np
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())

对图片进行 Base64 编码。

import base64

with open("test-image.jpg", "rb") as f:
    data = f.read()
b64str = base64.b64encode(data).decode("utf-8")

进行预测调用,指定我们之前在 serving_input 变量中定义的传送函数的输入层名称。

instances = [{serving_input: {"b64": b64str}}]

# Make request
result = endpoint.predict(instances=instances).predictions

# Convert image class to string label
predicted_class = tf.math.argmax(result[0], axis=-1)
string_label = imagenet_labels[predicted_class]

print(f"label ID: {predicted_class}")
print(f"string label: {string_label}")

🎉 恭喜!🎉

您学习了如何使用 Vertex AI 执行以下操作:

  • 托管和部署预训练模型

如需详细了解 Vertex 的不同部分,请参阅相关文档

9. 清理

由于 Vertex AI Workbench 代管式笔记本具有空闲关停功能,因此我们无需操心关停实例的事。如果您要手动关停实例,请点击控制台的 Vertex AI Workbench 部分中的“停止”按钮。如果您想完全删除该笔记本,请点击“删除”按钮。

可停止实例

如需删除存储桶,请使用 Cloud Console 中的导航菜单,浏览到“存储空间”,选择您的存储桶,然后点击“删除”:

删除存储空间