在 Vertex AI 上执行自定义训练,以在云端运行您自己的机器学习 (ML) 训练代码,而不是使用 AutoML。本文档介绍了您在编写训练代码时需要考虑的最佳做法。
选择训练代码结构
首先,确定您希望机器学习训练代码采用的结构。您可以通过以下某种形式向 Vertex AI 提供训练代码:
用于预构建容器的 Python 脚本。 使用 Vertex AI SDK 创建自定义作业。此方法可让您以单个 Python 脚本的形式提供训练应用。
与预建容器一起使用的 Python 训练应用。使用用于训练机器学习模型的代码创建 Python 源分发并将其导出到 Cloud Storage。此训练应用可以使用您打算与预编译容器一起包含的任何预构建容器中包含的依赖项。
如果其中一个 Vertex AI 预构建容器包含训练所需的全部依赖项,请使用此选项。例如,如果您要使用 PyTorch、scikit-learn、TensorFlow 或 XGBoost 进行训练,则此选项可能是更好的选择。
如需了解特定于此选项的最佳实践,请参阅创建 Python 训练应用指南。
自定义容器映像。创建Docker 容器映像使用代码来训练机器学习模型并将其导出到 Cloud Storage。在容器映像中包含您的代码需要的任何依赖项。
如果您想使用某个 Vertex AI 预构建容器中未包含的依赖项,请使用此选项。例如,如果您想要使用预建容器中未提供的 Python ML 框架进行训练,或者要使用 Python 以外的编程语言进行训练,则此字段为更好的选择。
如需了解此选项特有的最佳做法,请阅读创建自定义容器映像指南。
本文档的其余部分介绍了与这两种训练代码结构相关的最佳做法。
所有自定义训练代码的最佳做法
为 Vertex AI 编写自定义训练代码时,请注意代码将在由 Google Cloud 管理的一个或多个虚拟机 (VM) 实例上运行。本部分介绍了适用于所有自定义训练代码的最佳做法。
在代码中访问 Google Cloud 服务
以下部分介绍了如何通过代码访问其他 Google Cloud 服务。如需访问 Google Cloud 服务,请编写训练代码以使用应用默认凭据 (ADC)。默认情况下,许多 Google Cloud 客户端库会通过 ADC 进行身份验证。您无需配置任何环境变量;Vertex AI 会自动配置 ADC,以作为您的项目的 Vertex AI Custom Code Service Agent(默认)或以自定义服务账号身份(如果您配置有该账号)进行身份验证。
但是,当您在代码中使用 Google Cloud 客户端库时,默认情况下,Vertex AI 可能无法始终连接到正确的 Google Cloud 项目。如果遇到权限错误,则连接错误的项目可能是问题所在。
出现此问题的原因是 Vertex AI 不直接在 Google Cloud 项目中运行代码。Vertex AI 会在由 Google 管理的多个单独项目中运行您的代码。Vertex AI 仅针对您的项目执行这些项目。因此,请勿尝试从训练或预测代码的环境中推断项目 ID;明确指定项目 ID。
如果您不想对训练代码中的项目 ID 进行硬编码,则可以引用 CLOUD_ML_PROJECT_ID
环境变量:Vertex AI 会在每个自定义训练容器中设置此环境变量,以包含启动自定义训练的项目的项目编号。许多能够接受项目 ID 的 Google Cloud 工具也都可以接受项目编号。
例如,如果您要使用适用于 Google BigQuery 的 Python 客户端访问同一项目中的 BigQuery 表,请勿尝试在训练代码中推理项目:
隐式选择项目
from google.cloud import bigquery
client = bigquery.Client()
请改为使用明确选择项目的代码:
明确项目选择
import os
from google.cloud import bigquery
project_number = os.environ["CLOUD_ML_PROJECT_ID"]
client = bigquery.Client(project=project_number)
如果您在以这种方式配置代码后遇到权限错误,请阅读以下部分,了解您的代码可以访问哪些资源以调整训练代码可用的权限。
您的代码可以访问哪些资源
默认情况下,您的训练应用可以访问您的项目的 Vertex AI Custom Code Service Agent (CCSA) 可用的任何 Google Cloud 资源。您可以按照授予 Vertex AI 服务代理对其他资源的访问权限中的说明向 CCSA 和您的训练应用授予对有限数量的其他资源的访问权限。 如果您的训练应用所需的权限不仅仅是对该页面中未列出的 Google Cloud 资源的读取级别的访问权限,则需要获取具有 https://www.googleapis.com/auth/cloud-platform 范围的 OAuth 2.0 访问令牌,而这只能通过自定义服务账号完成。
例如,请考虑训练代码对 Cloud Storage 资源的访问权限:
默认情况下,Vertex AI 可以访问要在其中执行自定义训练的 Google Cloud 项目中的任何 Cloud Storage 存储桶。您还可以向 Vertex AI 授予其他项目中的 Cloud Storage 存储桶访问权限,也可以使用自定义服务账号精确地自定义特定作业可以访问的存储桶。
使用 Cloud Storage FUSE 读取和写入 Cloud Storage 文件
在所有自定义训练作业中,Vertex AI 会在每个训练节点文件系统的 /gcs/
目录中装载您有权访问的 Cloud Storage 存储桶。作为使用 Cloud Storage 的 Python 客户端或其他库访问 Cloud Storage 的便捷替代方案,您可以直接在本地文件系统读写数据,以便从 Cloud Storage 读取数据或向 Cloud Storage 写入数据。例如,如需从 gs://BUCKET/data.csv
加载数据,您可以使用以下 Python 代码:
file = open('/gcs/BUCKET/data.csv', 'r')
Vertex AI 会使用 Cloud Storage FUSE 来装载存储桶。请注意,通过 Cloud Storage FUSE 装载的目录不符合 POSIX 标准。
您用于自定义训练的凭据决定了您可以通过这种方式访问哪些存储桶。上文中的代码可以访问哪些资源部分确切地介绍了默认情况下您可以访问哪些存储桶以及如何自定义此访问权限。
加载输入数据
机器学习模型通常利用训练数据来训练模型。无论是创建 Python 训练应用还是自定义容器映像,请勿将训练数据与代码一起存储。将代码存储在一起可能会导致精心组织的项目,因此很难在不同数据集上重复使用代码,以及为大型数据集导致错误。
您可以从 Vertex AI 代管式数据集加载数据,也可以编写自己的代码以从 Vertex AI 外部的来源(如 BigQuery 或 Cloud Storage)加载数据。
为了在从 Cloud Storage 加载数据时获得最佳性能,请使用执行自定义训练的区域中的存储桶。如需了解如何在 Cloud Storage 中存储数据,请参阅创建存储桶和上传对象。
如需了解您可以从哪些 Cloud Storage 存储桶加载数据,请参阅上一部分中有关代码可以访问哪些资源的内容。
如需通过训练代码从 Cloud Storage 加载数据,请使用上一部分介绍的 Cloud Storage FUSE 功能,或使用任何支持 ADC 的库。您无需在代码中明确提供任何身份验证凭据。
例如,您可以使用 Cloud Storage 下载对象指南中所示的任一客户端库。适用于 Cloud Storage 的 Python 客户端(尤其是)包含在预构建容器中。TensorFlow 的 tf.io.gfile.GFile
类也支持 ADC。
加载大型数据集
根据您计划在自定义训练期间使用的机器类型,您的虚拟机可能无法将大型数据集的完整加载到内存中。
如果需要读取太大而不适合内存的数据,请为数据流式插入或增量读取数据。不同的机器学习框架有不同的最佳做法。例如,TensorFlow 的 tf.data.Dataset
类可从 Cloud Storage 流式传输 TFRecord 或文本数据。
使用数据并行对多个虚拟机执行自定义训练,可降低每个虚拟机加载到内存的数据量。请参阅本文档的编写分布式训练代码部分。
导出经过训练的机器学习模型
机器学习代码通常在训练结束时以一个或多个模型工件的形式导出经过训练的模型。然后,您可以使用模型工件来获取预测结果。
自定义训练完成后,您将无法再访问运行训练代码的虚拟机。因此,您的训练代码必须将模型工件导出到 Vertex AI 之外的位置。
我们建议您将模型工件导出到 Cloud Storage 存储桶。如上一部分(关于您的代码可以访问哪些资源)所述,Vertex AI 可以访问您要执行自定义训练的 Google Cloud 项目中的任何 Cloud Storage 存储桶。使用支持 ADC 的库导出模型工件。例如,用于保存 Keras 模型的 TensorFlow API 可以将工件直接导出到 Cloud Storage 路径。
如果要使用经过训练的模型在 Vertex AI 上执行预测,则代码必须以与用于预测的预建容器兼容的格式导出模型工件。如需了解详情,请参阅导出用于预测的模型工件的相关指南。
特殊 Cloud Storage 目录的环境变量
如果指定 baseOutputDirectory
API 字段,则 Vertex AI 会在运行训练代码时设置以下环境变量:
AIP_MODEL_DIR
:用于保存模型工件的目录的 Cloud Storage URI。AIP_CHECKPOINT_DIR
:用于保存检查点的目录的 Cloud Storage URI。AIP_TENSORBOARD_LOG_DIR
:用于保存 TensorBoard 日志的目录的 Cloud Storage URI。查看将 Vertex AI TensorBoard 与自定义训练搭配使用。
这些环境变量的值略有不同,具体取决于您使用的是超参数调节。如需了解详情,请参阅 baseOutputDirectory
的 API 参考文档。
使用这些环境变量时,仅通过更改 baseOutputDirectory
API 字段即可更轻松地多次重复使用同一训练代码(例如,使用不同数据或配置选项),并将模型工件和检查点保存到不同位置。但是,如果您不想使用环境变量,则无需在代码中使用环境变量。例如,您可以通过其他硬编码位置保存检查点和导出模型工件。
此外,如果您使用 TrainingPipeline
进行自定义训练并且未指定 modelToUpload.artifactUri
字段,则 Vertex AI 会将 AIP_MODEL_DIR
环境变量的值用于 modelToUpload.artifactUri
。(对于超参数调节,Vertex AI 使用最佳试验中 AIP_MODEL_DIR
环境变量的值。)
确保对重启具有恢复能力
运行训练代码的虚拟机偶尔会重启。例如,出于维护原因,Google Cloud 可能需要重启虚拟机。虚拟机重启后,Vertex AI 会重新开始运行您的代码。
如果您预计训练代码运行的时间会超过 4 小时,请向代码添加几项行为,使其能够灵活地重启:
经常将训练进度导出到 Cloud Storage(至少每四小时一次),以免在虚拟机重启时丢失进度。
在训练代码开始时,检查导出位置是否已存在训练进度。如果是,请加载已保存的训练状态,而不是从头开始启动训练。
4 小时是一项准则,而不是硬性限制。如果需要优先确保弹性,那么即使您预计代码不会长时间运行,也请考虑向代码添加这些行为。
如何完成这些行为取决于您使用的机器学习框架。 例如,如果您使用 TensorFlow Keras,请了解如何将 ModelCheckpoint
回调用于此用途。
如需详细了解 Vertex AI 如何管理虚拟机,请参阅了解自定义训练服务。
可选自定义训练功能的最佳做法
如果您想使用某些可选的自定义训练功能,则可能需要对训练代码进行其他更改。本部分介绍超参数调节、GPU、分布式训练和 Vertex AI TensorBoard 的代码最佳做法。
编写代码以启用自动日志记录
您可以使用 Python 版 Vertex AI SDK 启用自动日志记录功能,以在提交自定义作业时自动捕获参数和性能指标。如需了解详情,请参阅使用实验跟踪运行训练作业。
编写代码以返回容器日志
从服务或作业中写入日志时,只要将日志写入以下任何位置,Cloud Logging 就会自动获取这些日志:
- 标准输出 (
stdout
) 或标准错误 (stderr
) 流 /var/log-storage/
中遵循output*.log
命名惯例的日志文件。- syslog (
/dev/log
) - 使用适用于众多主流语言的 Cloud Logging 客户端库编写的日志
大多数开发者都应该使用标准输出和标准错误来写入日志。
写入这些受支持位置的容器日志会自动与 Vertex AI 自定义训练服务、修订版本和位置,或与自定义训练作业相关联。Error Reporting 会捕获并报告这些日志中包含的异常。
在日志中使用简单文本与结构化 JSON
写入日志时,您可以发送一个简单文本字符串,也可以发送一行序列化 JSON(也称为“结构化”数据)。它由 Cloud Logging 获取并解析,并放入 jsonPayload
中。相反,简单文本消息会放在 textPayload
中。
写入结构化日志
您可以通过多种方式传递结构化 JSON 日志。最常见的方法是使用 Python Logging 库或使用 print
传递原始 JSON。
Python Logging 库
import json import logging from pythonjsonlogger import jsonlogger class CustomJsonFormatter(jsonlogger.JsonFormatter): """Formats log lines in JSON.""" def process_log_record(self, log_record): """Modifies fields in the log_record to match Cloud Logging's expectations.""" log_record['severity'] = log_record['levelname'] log_record['timestampSeconds'] = int(log_record['created']) log_record['timestampNanos'] = int( (log_record['created'] % 1) * 1000 * 1000 * 1000) return log_record def configure_logger(): """Configures python logger to format logs as JSON.""" formatter = CustomJsonFormatter( '%(name)s|%(levelname)s|%(message)s|%(created)f' '|%(lineno)d|%(pathname)s', '%Y-%m-%dT%H:%M:%S') root_logger = logging.getLogger() handler = logging.StreamHandler() handler.setFormatter(formatter) root_logger.addHandler(handler) root_logger.setLevel(logging.WARNING) logging.warning("This is a warning log")
原始 JSON
import json def log(severity, message): global_extras = {"debug_key": "debug_value"} structured_log = {"severity": severity, "message": message, **global_extras} print(json.dumps(structured_log)) def main(args): log("DEBUG", "Debugging the application.") log("INFO", "Info.") log("WARNING", "Warning.") log("ERROR", "Error.") log("CRITICAL", "Critical.")
消息中的特殊 JSON 字段
如果您以 JSON 字典形式提供结构化日志,则系统会从 jsonPayload
中删除某些特殊字段,并将这些字段写入所生成 LogEntry 中的相应字段(具体说明请参阅特殊字段文档)。
例如,如果您的 JSON 包含 severity
属性,则系统会将该属性从 jsonPayload
中移除,并将其显示为日志条目的 severity
。message
属性用作日志条目的主显示文本(如果存在)。
将容器日志与请求日志相关联(仅限服务)
在 Logs Explorer 中,以“父级-子级”格式可以查看由相同 trace
关联的日志:当您点击请求日志条目左侧的三角形图标时,与该请求相关的容器日志即会嵌套显示在请求日志下方。
如果不使用 Cloud Logging 客户端库,容器日志不会自动关联至请求日志。如需在不使用客户端库的情况下将容器日志与请求日志相关联,您可以使用结构化 JSON 日志行,其包含 logging.googleapis.com/trace
字段,该字段具有从 X-Cloud-Trace-Context
标头中提取的跟踪记录标识符。
查看日志
如需在 Google Cloud 控制台中查看容器日志,请执行以下操作:
在 Google Cloud 控制台中,进入 Vertex AI 自定义作业页面。
点击您要查看其日志的自定义作业的名称。
点击查看日志。
编写超参数调节代码
Vertex AI 可以对机器学习训练代码执行超参数调节。详细了解 Vertex AI 上的超参数调节的工作原理以及如何配置 HyperparameterTuningJob
资源。
如果您想要使用超参数调节,您的训练代码必须执行以下操作:
解析命令行参数(表示要调节的超参数),并使用解析值来设置训练的超参数。
向 Vertex AI 间接报告超参数调节指标。
解析命令行参数
对于超参数调节,Vertex AI 多次运行训练代码,每次都使用不同的命令行参数。您的训练代码必须解析这些命令行参数,并将其用作超参数以进行训练。例如,要调整优化工具的学习速率,您可能需要解析名为 --learning_rate
的命令行参数。了解如何配置 Vertex AI 提供的命令行参数。
我们建议您使用 Python 的 argparse
库来解析命令行参数。
报告超参数调节指标
您的训练代码必须间歇性地报告您尝试向 Vertex AI 优化的超参数指标。例如,如果您想要最大限度地提高模型的准确率,则可能需要在每个训练周期结束时报告此指标。Vertex AI 会使用此信息来确定用于下一个训练试验的超参数。详细了解如何选择和指定超参数调节指标。
使用 cloudml-hypertune
Python 库报告超参数调节指标。此库包含在所有预构建的容器中,您可以使用 pip
将其安装在自定义容器中。
要了解如何安装和使用此库,请参阅 cloudml-hypertune
GitHub 代码库,或者参阅“Vertex AI:超参数调节 Codelab”。
为 GPU 编写代码
您可以选择带图形处理单元 (GPU) 的虚拟机来运行自定义训练代码。详细了解如何配置自定义训练以使用支持 GPU 的虚拟机。
如果您想使用 GPU 进行训练,请确保您的训练代码能够利用它们。根据您使用的机器学习框架,这可能需要更改代码。例如,如果您使用的是 TensorFlow Keras,则只有在要使用多个 GPU 时才需要调整代码。某些机器学习框架根本不会使用 GPU。
此外,请确保您的容器支持 GPU:选择支持 GPU 的预构建容器,或安装 NVIDIA CUDA 工具包 和 NVIDIA cuDNN。一种方法是使用 nvidia/cuda
Docker 代码库中的基础映像;另一种方法是将 Deep Learning Containers 实例用作基础映像。
编写分布式训练代码
如需训练大型数据集,您可以在由 Vertex AI 管理的分布式集群中的多个虚拟机上运行代码。了解如何配置用于训练的多个虚拟机。
某些机器学习框架(如 TensorFlow 和 PyTorch)允许您在多台机器上运行相同的训练代码,这些代码会根据每台机器上设置的环境变量来自动划分工作。了解 Vertex AI 是否将环境变量设置为适用于您的机器学习框架。
或者,您也可以在多个工作器池上运行不同的容器。工作器池是您配置使用同一计算选项和容器的一组虚拟机。在这种情况下,您可能仍依靠 Vertex AI 设置的环境变量来协调虚拟机之间的通信。您可以自定义每个工作器池的训练代码,以执行所需的任何任务;具体如何操作取决于您的目标以及您使用的机器学习框架。
使用 Vertex AI TensorBoard 跟踪并直观呈现自定义训练实验
Vertex AI TensorBoard 是 TensorBoard 的代管式版本,它是用于直观呈现机器学习实验的 Google 开源项目。借助 Vertex AI TensorBoard,您可以跟踪、直观呈现和比较机器学习实验,然后分享给您的团队。您还可以使用 TensorBoard Profiler 找出并修复性能瓶颈,以更快的速度、更低的费用训练模型。
如需将 Vertex AI TensorBoard 与自定义训练搭配使用,您必须执行以下操作:
在项目中创建 Vertex AI TensorBoard 实例以存储实验(请参阅创建 TensorBoard 实例)。
配置服务账号以使用适当的权限运行自定义训练作业。
调整自定义训练代码,以便将与 TensorBoard 兼容的日志写入 Cloud Storage(请参阅训练脚本的更改)
如需查看分步指南,请参阅将 Vertex AI TensorBoard 与自定义训练搭配使用。
后续步骤
如果您不确定是否要执行自定义训练,请参阅自定义训练和 AutoML 的比较。