通过 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 页面,查看在 ImagNet 数据集上训练的 MobileNet V1 模型。

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

download_model

在 Google Cloud 控制台的“Cloud Storage”部分中,选择创建

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

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

然后选择IMPORT

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

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 传递给服务函数。

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

由于已部署的模型需要以原始(未压缩)字节的形式输入数据,因此您需要确保在将 base64 编码的数据作为输入传递给已部署的模型之前,先将其转换回原始字节(例如 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 Predictions 的大小限制。

再次下载图片标签

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 中的导航菜单,浏览到“存储空间”,选择您的存储桶,然后点击“删除”:

删除存储空间