1. 概览
在本实验中,您将使用 Vertex AI 为 TensorFlow 模型运行超参数调优作业。虽然本实验使用 TensorFlow 构建模型代码,但这些概念也适用于其他机器学习框架。
学习内容
您将了解如何:
- 修改训练应用代码以自动进行超参数调节
 - 从 Vertex AI 界面配置并启动超参数调优作业
 - 使用 Vertex AI Python SDK 配置并启动超参数调节作业
 
在 Google Cloud 上运行此实验的总费用约为 3 美元。
2. Vertex AI 简介
本实验使用的是 Google Cloud 上提供的最新 AI 产品。Vertex AI 将整个 Google Cloud 的机器学习产品集成到无缝的开发体验中。以前,使用 AutoML 训练的模型和自定义模型是通过不同的服务访问的。现在,该新产品与其他新产品一起将这两种模型合并到一个 API 中。您还可以将现有项目迁移到 Vertex AI。如果您有任何反馈,请参阅支持页面。
Vertex AI 包含许多不同的产品,可支持端到端机器学习工作流。本实验将重点介绍下面突出显示的产品:训练和 Workbench。

3. 设置您的环境
您需要一个启用了结算功能的 Google Cloud Platform 项目才能运行此 Codelab。如需创建项目,请按照此处的说明操作。
第 1 步:启用 Compute Engine API
前往 Compute Engine,然后选择启用(如果尚未启用)。您需要用它来创建笔记本实例。
第 2 步:启用 Container Registry API
前往 Container Registry,然后选择启用(如果尚未启用)。您将使用此产品为您的自定义训练作业创建容器。
第 3 步:启用 Vertex AI API
前往 Cloud Console 的 Vertex AI 部分,然后点击启用 Vertex AI API。

第 4 步:创建 Vertex AI Workbench 实例
在 Cloud Console 的 Vertex AI 部分中,点击“Workbench”:

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

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

然后选择新建笔记本。

为您的笔记本命名,然后点击高级设置。

在“高级设置”下,启用空闲关闭,并将分钟数设置为 60。这意味着,您的笔记本处于未使用状态时会自动关闭,以免产生不必要的费用。

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

您可以保留所有其他高级设置。
接下来,点击创建。预配实例需要几分钟时间。
创建实例后,选择打开 JupyterLab。

首次使用新实例时,系统会要求您进行身份验证。为此,请按照界面中的步骤操作。

4. 容器化训练应用代码
您将在本实验中训练和调优的模型是根据 TensorFlow 数据集中的马或人数据集训练的图片分类模型。
您将训练应用代码放入 Docker 容器中,并将此容器推送到 Google Container Registry,以便将此超参数调节作业提交给 Vertex AI。使用此方法,您可以为使用任何框架构建的模型的调整超参数。
首先,通过“启动器”菜单在笔记本实例中打开终端窗口:

创建一个名为 horses_or_humans 的新目录并通过 cd 命令进入该目录:
mkdir horses_or_humans
cd horses_or_humans
第 1 步:创建 Dockerfile
容器化代码的第一步是创建 Dockerfile。在 Dockerfile 中,您将添加运行映像所需的所有命令。它将安装所有必要的库(包括 CloudML Hypertune 库),并为训练代码设置入口点。
在终端中,创建一个空的 Dockerfile:
touch Dockerfile
打开 Dockerfile 并将以下代码复制到其中:
FROM gcr.io/deeplearning-platform-release/tf2-gpu.2-7
WORKDIR /
# Installs hypertune library
RUN pip install cloudml-hypertune
# Copies the trainer code to the docker image.
COPY trainer /trainer
# Sets up the entry point to invoke the trainer.
ENTRYPOINT ["python", "-m", "trainer.task"]
此 Dockerfile 使用 Deep Learning Container TensorFlow Enterprise 2.7 GPU Docker 映像。Google Cloud 上的 Deep Learning Containers 预安装了许多常见的机器学习和数据科学框架。下载该映像后,此 Dockerfile 会为训练代码设置入口点。您尚未创建这些文件。在下一步中,您将添加用于训练和调优模型的代码。
第 2 步:添加模型训练代码
从终端运行以下命令,为训练代码创建一个目录,并创建一个用于添加代码的 Python 文件:
mkdir trainer
touch trainer/task.py
您的 horses_or_humans/ 目录中现在应包含以下内容:
+ Dockerfile
+ trainer/
    + task.py
接下来,打开您刚刚创建的 task.py 文件,并复制以下代码。
import tensorflow as tf
import tensorflow_datasets as tfds
import argparse
import hypertune
NUM_EPOCHS = 10
def get_args():
  '''Parses args. Must include all hyperparameters you want to tune.'''
  parser = argparse.ArgumentParser()
  parser.add_argument(
      '--learning_rate',
      required=True,
      type=float,
      help='learning rate')
  parser.add_argument(
      '--momentum',
      required=True,
      type=float,
      help='SGD momentum value')
  parser.add_argument(
      '--num_units',
      required=True,
      type=int,
      help='number of units in last hidden layer')
  args = parser.parse_args()
  return args
def preprocess_data(image, label):
  '''Resizes and scales images.'''
  image = tf.image.resize(image, (150,150))
  return tf.cast(image, tf.float32) / 255., label
def create_dataset():
  '''Loads Horses Or Humans dataset and preprocesses data.'''
  data, info = tfds.load(name='horses_or_humans', as_supervised=True, with_info=True)
  # Create train dataset
  train_data = data['train'].map(preprocess_data)
  train_data  = train_data.shuffle(1000)
  train_data  = train_data.batch(64)
  # Create validation dataset
  validation_data = data['test'].map(preprocess_data)
  validation_data  = validation_data.batch(64)
  return train_data, validation_data
def create_model(num_units, learning_rate, momentum):
  '''Defines and compiles model.'''
  inputs = tf.keras.Input(shape=(150, 150, 3))
  x = tf.keras.layers.Conv2D(16, (3, 3), activation='relu')(inputs)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(32, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Conv2D(64, (3, 3), activation='relu')(x)
  x = tf.keras.layers.MaxPooling2D((2, 2))(x)
  x = tf.keras.layers.Flatten()(x)
  x = tf.keras.layers.Dense(num_units, activation='relu')(x)
  outputs = tf.keras.layers.Dense(1, activation='sigmoid')(x)
  model = tf.keras.Model(inputs, outputs)
  model.compile(
      loss='binary_crossentropy',
      optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=momentum),
      metrics=['accuracy'])
  return model
def main():
  args = get_args()
  train_data, validation_data = create_dataset()
  model = create_model(args.num_units, args.learning_rate, args.momentum)
  history = model.fit(train_data, epochs=NUM_EPOCHS, validation_data=validation_data)
  # DEFINE METRIC
  hp_metric = history.history['val_accuracy'][-1]
  hpt = hypertune.HyperTune()
  hpt.report_hyperparameter_tuning_metric(
      hyperparameter_metric_tag='accuracy',
      metric_value=hp_metric,
      global_step=NUM_EPOCHS)
if __name__ == "__main__":
    main()
在构建容器之前,我们来深入了解一下代码。有多个组件特定于使用超参数调节服务。
- 该脚本会导入 
hypertune库。请注意,第 1 步中的 Dockerfile 包含使用 pip install 命令来安装此库的说明。 get_args()函数为您要调节的每个超参数定义命令行参数。在此示例中,将要调节的超参数是学习速率、优化器中的动量值以及模型最后一个隐藏层中的单元数,但您可以随意尝试其他超参数。然后,在这些参数中传递的值将用于在代码中设置相应的超参数。- 在 
main()函数的末尾,hypertune库用于定义您要优化的指标。在 TensorFlow 中,Kerasmodel.fit方法会返回History对象。History.history特性是连续周期的训练损失值和指标值的记录。如果您将验证数据传递给model.fit,则History.history特性也将包含验证损失和指标值。例如,如果您使用验证数据将模型训练三个周期并提供accuracy作为指标,则History.history特性将类似于以下字典。 
{
 "accuracy": [
   0.7795261740684509,
   0.9471358060836792,
   0.9870933294296265
 ],
 "loss": [
   0.6340447664260864,
   0.16712145507335663,
   0.04546636343002319
 ],
 "val_accuracy": [
   0.3795261740684509,
   0.4471358060836792,
   0.4870933294296265
 ],
 "val_loss": [
   2.044623374938965,
   4.100203514099121,
   3.0728273391723633
 ]
如果您希望超参数调节服务发现使模型的验证准确率最大化的值,则可以将指标定义为 val_accuracy 列表的最后一个条目(或 NUM_EPOCS - 1)。然后,将此指标传递给 HyperTune 的实例。您可以为 hyperparameter_metric_tag 选择任何字符串,但稍后在启动超参数调节作业时,您需要再次使用该字符串。
第 3 步:构建容器
在终端中,运行以下命令为项目定义一个环境变量,务必注意将 your-cloud-project 替换为您的项目 ID:
PROJECT_ID='your-cloud-project'
使用 Google Container Registry 中容器映像的 URI 定义变量:
IMAGE_URI="gcr.io/$PROJECT_ID/horse-human:hypertune"
配置 Docker
gcloud auth configure-docker
然后,从 horses_or_humans 目录的根目录运行以下命令来构建容器:
docker build ./ -t $IMAGE_URI
最后,将其推送到 Google Container Registry:
docker push $IMAGE_URI
将容器推送到 Container Registry 后,您现在可以启动自定义模型超参数调节作业了。
5. 在 Vertex AI 上运行超参数调节作业
本实验通过 Google Container Registry 上的自定义容器使用自定义训练,但您也可以使用 Vertex AI 预构建容器运行超参数调优作业。
首先,请前往 Cloud 控制台的“Vertex”部分中的训练部分:

第 1 步:配置训练作业
点击创建,为超参数调节作业输入参数。
- 在数据集下,选择无代管式数据集
 - 然后选择自定义训练(高级)作为训练方法,并点击继续。
 - 在模型名称部分输入 
horses-humans-hyptertune(或您要调用模型的任何内容) - 点击继续
 
在“容器设置”步骤中,选择自定义容器:

在第一个框(容器映像)中,输入上一部分中的 IMAGE_URI 变量的值。它应该为 gcr.io/your-cloud-project/horse-human:hypertune(包含您自己的项目名称)。将其余字段留空,然后点击继续。
第 2 步:配置超参数调优作业
选择启用超参数调节。

配置超参数
接下来,您需要在训练应用代码中添加您设置为命令行参数的超参数。添加超参数时,您首先需要提供名称。此名称应与您传递给 argparse 的参数名称一致。

然后,您需要选择类型以及调整服务将尝试的值的边界。如果您选择双精度或整数类型,则需要提供最小值和最大值。如果您选择“分类”或“离散”,则需要提供相应的值。


对于双精度和整数类型,您还需要提供扩缩值。

添加 learning_rate 超参数后,再为 momentum 和 num_units 添加参数。


配置指标
添加超参数后,您需要提供要优化的指标以及目标。此值应与您在训练应用中设置的 hyperparameter_metric_tag 相同。

Vertex AI 超参数调节服务将使用前面步骤中配置的值运行训练应用的多次试验。您需要设置服务运行的试验次数的上限。更多的试验通常会带来更好的结果,但会有一个收益递减点,在该点之后,额外的试验对您尝试优化的指标几乎没有什么效果。最佳做法是从较少的试验开始,并在扩展到大量试验之前了解您选择的超参数的影响程度。
您还需要设置并行试验次数的上限。增加并行试验次数会减少超参数调节作业的运行时间;不过,这可能会降低作业的效率。这是因为默认调参策略使用先前试验的结果来告知后续试验中值的分配。如果您并行运行的试验过多,则有些试验会在未知晓仍在运行的试验的结果的情况下开始运行。
出于演示目的,您可以将试验次数设置为 15,将并行试验次数上限设置为 3。您可以实验不同的数值,但这可能会导致调整时间更长,费用也更高。

最后一步是选择“默认”作为搜索算法,该算法将使用 Google Vizier 对超参数调节执行贝叶斯优化。如需详细了解此算法,请点击此处。

点击继续。
第 3 步:配置计算
在计算和价格中,保留所选区域不变,然后按如下方式配置工作器池 0。

点击开始训练以启动超参数调节作业。在控制台的“训练”部分中的超参数调节作业标签页下,您会看到如下内容:

完成后,您将能够点击作业名称并查看调参试验的结果。

🎉 恭喜!🎉
您学习了如何使用 Vertex AI 执行以下操作:
- 为自定义容器中提供的训练代码启动超参数调节作业。您在此示例中使用了 TensorFlow 模型,但您可以使用自定义容器训练使用任何框架构建的模型。
 
如需详细了解 Vertex 的不同部分,请参阅相关文档。
6. [可选] 使用 Vertex SDK
上一部分介绍了如何通过界面启动超参数调节作业。在本部分中,您将看到使用 Vertex Python API 提交超参数调优作业的替代方法。
从启动器创建一个 TensorFlow 2 笔记本。

导入 Vertex AI SDK。
from google.cloud import aiplatform
from google.cloud.aiplatform import hyperparameter_tuning as hpt
如需启动超参数调节作业,您需要先定义以下规范。您需要将 image_uri 中的 {PROJECT_ID} 替换为您的项目。
# The spec of the worker pools including machine type and Docker image
# Be sure to replace PROJECT_ID in the `image_uri` with your project.
worker_pool_specs = [{
    "machine_spec": {
        "machine_type": "n1-standard-4",
        "accelerator_type": "NVIDIA_TESLA_V100",
        "accelerator_count": 1
    },
    "replica_count": 1,
    "container_spec": {
        "image_uri": "gcr.io/{PROJECT_ID}/horse-human:hypertune"
    }
}]
# Dictionary representing metrics to optimize.
# The dictionary key is the metric_id, which is reported by your training job,
# And the dictionary value is the optimization goal of the metric.
metric_spec={'accuracy':'maximize'}
# Dictionary representing parameters to optimize.
# The dictionary key is the parameter_id, which is passed into your training
# job as a command line argument,
# And the dictionary value is the parameter specification of the metric.
parameter_spec = {
    "learning_rate": hpt.DoubleParameterSpec(min=0.001, max=1, scale="log"),
    "momentum": hpt.DoubleParameterSpec(min=0, max=1, scale="linear"),
    "num_units": hpt.DiscreteParameterSpec(values=[64, 128, 512], scale=None)
}
接下来,创建一个 CustomJob。您需要将 {YOUR_BUCKET} 替换为项目中用于暂存的存储分区。
# Replace YOUR_BUCKET
my_custom_job = aiplatform.CustomJob(display_name='horses-humans-sdk-job',
                              worker_pool_specs=worker_pool_specs,
                              staging_bucket='gs://{YOUR_BUCKET}')
然后,创建并运行 HyperparameterTuningJob。
hp_job = aiplatform.HyperparameterTuningJob(
    display_name='horses-humans-sdk-job',
    custom_job=my_custom_job,
    metric_spec=metric_spec,
    parameter_spec=parameter_spec,
    max_trial_count=15,
    parallel_trial_count=3)
hp_job.run()
7. 清理
因为我们将笔记本配置为在空闲 60 分钟后超时,所以不必担心关停实例。如果您要手动关停实例,请点击控制台的 Vertex AI Workbench 部分中的“停止”按钮。如果您想完全删除该笔记本,请点击“删除”按钮。

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