跳至主要内容

使用 Amazon SageMaker 在 AWS 上进行机器学习的完整指南

本综合教程将教您如何使用 AWS SageMaker 来构建、训练和部署机器学习模型。我们将引导您完成完整工作流,从设置 AWS 环境和创建 SageMaker notebook 实例,到准备数据、训练模型并将其部署为端点。
· 15分钟 阅读

使用 AI 探索

尽管大多数人提到 Amazon 会想到 Prime 送货和内容服务,这家公司在开发者中同样备受推崇。具体来说,Amazon Web Services(AWS)为超过 100 万活跃用户提供云技术,覆盖企业级公司以及中小型企业。

本文将聚焦 AWS 生态中的 AWS SageMaker——一个用于大规模执行端到端机器学习(ML)工作流的专用平台。读完本文后,我们将部署一个模型,可用于发送请求以生成分类任务的预测。

什么是 Amazon SageMaker?

Amazon SageMaker 是一项全托管服务,提供用于构建、训练和部署机器学习模型的工具与基础设施。它简化了机器学习工作流,使数据科学家和开发者能够快速高效地创建、训练和部署模型。

在一个典型的机器学习项目中,您会经历多个阶段:

  • 数据摄取、清洗与探索。
  • 特征工程与选择。
  • 模型训练与超参数调优。
  • 部署与模型监控。

每个阶段都需要不同的工具组合以及一支技术熟练的团队来无缝协同。Amazon SageMaker 将这一切整合到单一平台。以下是其优势:

  • 端到端机器学习服务:为机器学习生命周期的每个阶段提供全面工具集,从数据准备到模型部署与监控。
  • 集成开发环境:SageMaker Studio 提供 IDE,简化整个 ML 工作流,用户可在单一界面中构建、训练和部署模型。
  • 易用性:借助 SageMaker Autopilot 等特性,无需编写代码也能通过几次点击生成高质量模型。
  • 可扩展性:自动为训练和部署扩展基础设施,无需人工干预即可确保最佳性能。
  • 成本效益:提供 Spot Training 和多模型端点等选项,降低训练与托管成本。
  • 支持主流 ML 框架:允许数据科学家使用偏好的框架,包括 TensorFlow、PyTorch 和 XGBoost。
  • 自动化超参数调优:通过自动调参寻找表现最佳的模型配置。
  • 稳健的模型部署:简化将模型部署为实时 API 或批量预测服务,便于将 ML 模型集成到应用中。
  • 全面监控:SageMaker Model Monitor 持续跟踪模型性能,检测生产中的数据漂移与异常。
  • 模型注册表:管理机器学习模型的生命周期,包括版本控制、审批流程与部署历史。
  • 无缝集成:轻松与其他 AWS 服务集成,例如用于数据存储的 Amazon S3、用于数据准备的 AWS Glue、用于事件驱动处理的 AWS Lambda,以及用于监控与日志的 Amazon CloudWatch。
  • 安全与合规:通过包括 VPC 支持、加密与 IAM 策在内的强大安全功能,确保数据保护并符合行业标准。

这些优势使 Amazon SageMaker 成为在规模化场景下构建、训练与部署机器学习模型的强大而灵活的平台。

为 SageMaker 设置您的 AWS 控制台与环境

如前所述,SageMaker 是 AWS 生态的一部分,其中包含众多云计算方案,提供存储、网络、数据库、分析与机器学习能力。所有这些解决方案都可与 SageMaker 协同工作,统一您的机器学习工作流。

因此,如果您还没有 AWS 账户,SageMaker 需要您先创建一个。

要创建账户,请访问 https://aws.amazon.com/,并按照流程创建新账户。

注册过程包括添加账单信息和验证您的电话号码。别担心——在您实际开始使用某些 SageMaker 功能之前(通常也较为经济),不会产生费用。

完成后,您将进入 AWS 控制台,界面通常如下图所示。请注意,您的控制台小部件可能与下图有所不同。

The screenshot of the AWS Console

现在,我们将创建一个 JupyterLab 会话,在 Amazon 云端机器上执行代码。

在 AWS 控制台顶部搜索栏中搜索 SageMaker 仪表盘:

A GIF that shows how to switch to SageMaker console from AWS Console

然后向下滚动找到“Notebooks”选项卡,并在其中选择“Notebooks instances”。您将看不到任何实例,因此需要创建一个:

A GIF that shows how to create a SAgeMaker notebook instance

要创建新 notebook,请点击屏幕右上角的“Create notebook instance”。然后填写以下字段:

  • 为您的 notebook 命名。本例中我将其命名为 sagemaker-test。
  • “Notebook instance type”字段指定您希望 notebook 内核使用的机器类型。我选择 ml.t3.large,它有 8 GB 内存和 2 个 CPU 核心。撰文时费用约为每小时 $0.12(请参阅 SageMaker 价格页面获取最新信息)。
  • 在“Permissions and encryption”部分,选择“Create new user”选项,这将自动创建一个具有运行 notebook 实例所需权限的 IAM 用户。

填写完所有必填信息后,点击“Create notebook instance”。

当状态从“Pending”变为“inService”时,您可以点击 notebook 旁的“Open in JupyterLab”链接启动 JupyterLab:

A GIF that shows how to create a notebook in SageMaker jupyterlab

实例进入服务后,计时和计费就开始了。如果您决定休息,请不要忘记点击“Actions”,然后点击“Stop”停止实例,以避免不必要的费用。默认闲置关机时间为两小时:

A GIF that shows how to stop a notebook instance in SageMaker

如果您停止了 notebook 实例,之后可以再次重启。您的环境会被保留。

在 AWS SageMaker 中上传要使用的数据集

本节我们将学习如何将本地文件上传到 Amazon 服务器,这是 SageMaker 的前置要求。

我们将使用来自 UCI ML 仓库的Dry Bean 数据集构建一个多分类器。它包含 1.3 万条豆类样本及其 15 个数值型测量。

任务是将其分类为七种豆类:Seker、Barbunya、Bombay、Cali、Dermosan、Horoz 和 Sira。

七种豆类:Seker、Barbunya、Bombay、Cali、Dermosan、Horoz 和 Sira。

下载后,将 ZIP 中的 Excel 文件保存到 JupyterLab 的本地 data 目录中,然后将以下代码添加到您的 notebook:

from pathlib import Path

cwd = Path.cwd()
data_path = cwd / "data" / "Dry_Bean_Dataset.xlsx"

接着,添加以下代码,我们可以用 pandas 读取 Excel 文件并将其保存为 CSV:

import pandas as pd

beans = pd.read_excel(data_path)
beans.to_csv(cwd / "data" / "dry_bean.csv", index=False)

我们的数据集必须位于 S3 存储桶中,SageMaker 才能访问。AWS S3 是一种云存储解决方案,允许您将与项目相关的对象保存在 Amazon 服务器托管的存储桶中。

要创建存储桶,请按以下步骤操作:

  • 前往 S3 控制台。您可以在搜索栏中搜索“S3”。
  • 点击“Create bucket”。
  • 为您的存储桶命名。
    • 存储桶名称必须全局唯一,因此您无法使用我所用的 dry-bean-bucket。
  • 其余字段保持默认配置。
  • 点击“Create bucket”。

A GIF that shows how to create an AWS S3 bucket

创建完成后,点击该存储桶上传之前保存的 CSV 文件:

  • 在存储桶中,点击“Upload”,然后点击“Add files”。
  • 选择该 CSV 文件。
  • 再次点击“Upload”。

A GIF that shows how to upload a local file to S3 bucket

之后,您可以返回 JupyterLab 界面,因为我们终于要运行一些代码了!

在 AWS SageMaker 中的端到端模型训练与部署工作流

本节我们将走一遍端到端流程:从 CSV 数据集开始,到将模型部署到端点。但在深入技术细节之前,先从高层面了解一下我们要做什么。

SageMaker 中 ML 工作流的高层概览

在 SageMaker 中进行机器学习与在本地机器上略有不同。我们已经完成了前期步骤:

  • 创建 AWS 账户。
  • 创建 notebook 实例。
  • 为项目创建 S3 存储桶。

与本地 notebook 一样,SageMaker 的 notebook 实例用于探索性分析——数据清洗、探索、摄取和基础实验。

一旦进入模型训练阶段,SageMaker 期望您按其“规则”行事。具体而言,要让 SageMaker 将您的自定义模型部署为端点,它需要一个训练脚本,其功能包括:

  • 从数据源加载数据。
  • 在其上训练单个模型。
  • 保存模型及其工件。
  • 报告其指标。

脚本还必须能够动态地以命令行参数的形式接收变量。因此,将来希望在脚本内更改的任何内容(模型超参数、文件路径等)都必须设置为命令行参数。

脚本准备就绪后,会传入 SageMaker 提供的特殊估计器中。它们与主流 ML 库(如 scikit-learn、XGBoost、TensorFlow、PyTorch 等)配套。借助这些估计器,SageMaker 会围绕您的脚本构建 Docker 容器,并将其部署为可大规模服务的端点。

随着我们逐步实践,上述要点将变得更加清晰。

首先,在您先前创建的 sagemaker-test notebook 中导入必要的库:

import boto3
import numpy as np
import pandas as pd
import sagemaker
from sagemaker import get_execution_role
from sklearn.model_selection import train_test_split

部分导入说明如下:

  • sagemaker 是官方 Python SDK,用于在 Amazon SageMaker 上训练和部署机器学习模型。通过该 SDK,您可以使用主流深度学习框架、Amazon 提供的算法,或内置于 SageMaker 兼容 Docker 镜像中的自定义算法来训练与部署模型。
  • boto3 是另一个官方 Python SDK,但它连接您的代码与所有 AWS 服务,而不仅是 SageMaker。您可以通过这门课程了解更多 boto3。

导入完成后,创建本教程中将用到的一些资源:

sm_boto3 = boto3.client("sagemaker")
sess = sagemaker.Session()
  • 上述代码中,我们首先使用 boto3 创建一个 SageMaker 客户端对象。
  • 然后创建一个 SageMaker 会话。

这两个对象都允许我们以不同方式与 SageMaker 平台交互。

现在,定义一些需要的全局变量:

region = sess.boto_session.region_name
BUCKET_URI = "s3://dry-bean-bucket"
BUCKET_NAME = "dry-bean-bucket"
DATASET_PATH = f"{BUCKET_URI}/dry_bean.csv"
TARGET_NAME = "Class"

这些变量一目了然,我们将在整个教程中使用它们。

在大多数数据项目中,训练数据集会存放在云存储解决方案中,如 BigQuery、GCS 或 AWS S3 存储桶。我们将数据存放在 S3,因此用 pandas 加载。将以下代码添加到 notebook 并执行:

dry_bean = pd.read_csv(DATASET_PATH)
dry_bean.head()

The first five rows of the dry bean dataset

在 Sagemaker notebook 中工作的一个优势是,我们可以直接用 pandas 从 S3 读取文件。所有 SageMaker 环境都已配置了您的凭证,允许 pandas 从存储桶下载 CSV。如果在本地运行,这段代码将无法工作。

在每个项目中花时间进行EDA(探索性数据分析)至关重要,本项目也不例外。不过这里我们不做深入分析,以免偏离本文主题。

我们仅打印一些概要统计和图表:

dry_bean.info()
<class 'pandas.core.frame.DataFrame'>

RangeIndex: 13611 entries, 0 to 13610

Data columns (total 17 columns):

#   Column           Non-Null Count  Dtype 

---  ------           --------------  ----- 

0   Area             13611 non-null  int64 

1   Perimeter        13611 non-null  float64

2   MajorAxisLength  13611 non-null  float64

3   MinorAxisLength  13611 non-null  float64

4   AspectRation     13611 non-null  float64

5   Eccentricity     13611 non-null  float64

6   ConvexArea       13611 non-null  int64 

7   EquivDiameter    13611 non-null  float64

8   Extent           13611 non-null  float64

9   Solidity         13611 non-null  float64

10  roundness        13611 non-null  float64

11  Compactness      13611 non-null  float64

12  ShapeFactor1     13611 non-null  float64

13  ShapeFactor2     13611 non-null  float64

14  ShapeFactor3     13611 non-null  float64

15  ShapeFactor4     13611 non-null  float64

16  Class            13611 non-null  object

dtypes: float64(14), int64(2), object(1)

memory usage: 1.8+ MB

上面的信息告诉我们,有 16 个数值特征和一个名为 Class 的目标列。数据集很小,只有 1.3 万条样本,因此我们不需要高性能机器或花费大量费用来训练模型。

接下来,使用 seaborn 绘制几个重要特征的配对图:

import seaborn as sns

sns.pairplot(
   dry_bean,
   vars=["Area", "MajorAxisLength", "MinorAxisLength", "Eccentricity", "roundness"],
   hue="Class",
);

A pair plot of some numeric features of the dry bean dataset

上述图表显示,豆类在物理测量方面差异明显。绿色的 Bombay 豆尤其区分度高。

我们还可以绘制相关矩阵来查看特征之间的关系:

import matplotlib.pyplot as plt

correlation = dry_bean.corr(numeric_only=True)

# Create a square heatmap with center at 0
sns.heatmap(correlation, center=0, square=True, cmap="coolwarm", vmin=-1, vmax=1)
plt.show()

A feature correlation plot of the features of dry beans dataset

图中显示许多特征呈完全(正或负)相关,这很合理,因为这些特征都与物理测量相关。

此处起,我将探索的部分留给您继续进行。

完成探索后,您需要在训练前处理数据集中的质量或逻辑问题,并在必要时进行特征工程。

Dry Bean 数据集相当干净,所以我们的工作很简单。我们只需对目标列进行编码,因为它包含文本。我们将使用 scikit-learn 来完成:

from sklearn.preprocessing import LabelEncoder

# For preprocessing
df = dry_bean.copy(deep=True)

# Encode the target
le = LabelEncoder()
df[TARGET_NAME] = le.fit_transform(df[TARGET_NAME])

预处理完成后,我们需要划分数据集:

from sklearn.model_selection import train_test_split

# Split the data into two sets
train, test = train_test_split(df, random_state=1, test_size=0.2)

如果您愿意,也可以进一步划分出第三个验证集,但这里我们先保持简单。

现在,将数据集保存为 CSV:

train.to_csv("dry-bean-train.csv")
test.to_csv("dry-bean-test.csv")

并将它们上传到我们的 S3 存储桶:

# Send data to S3. SageMaker will take training data from s3
trainpath = sess.upload_data(
   path="dry-bean-train.csv",
   bucket=BUCKET_NAME,
   key_prefix="sagemaker/sklearncontainer",
)

testpath = sess.upload_data(
   path="dry-bean-test.csv",
   bucket=BUCKET_NAME,
   key_prefix="sagemaker/sklearncontainer",
)

会话对象的 .upload_data() 函数会返回上传文件的完整路径,分别存储在 trainpath 和 testpath 变量中。后续我们会用到这些路径。

在新的 notebook 单元中,粘贴以下代码:

%%writefile script.py

import argparse
import os
import joblib
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import balanced_accuracy_score

if __name__ == "__main__":
   print("extracting arguments")
   parser = argparse.ArgumentParser()

   # Hyperparameters sent by the client are passed as command-line arguments to the script.
   parser.add_argument("--n-estimators", type=int, default=10)
   parser.add_argument("--min-samples-leaf", type=int, default=3)

   # Data, model, and output directories
   parser.add_argument("--model-dir", type=str, default=os.environ.get("SM_MODEL_DIR"))
   parser.add_argument("--train", type=str, default=os.environ.get("SM_CHANNEL_TRAIN"))
   parser.add_argument("--test", type=str, default=os.environ.get("SM_CHANNEL_TEST"))
   parser.add_argument("--train-file", type=str, default="dry-bean-train.csv")
   parser.add_argument("--test-file", type=str, default="dry-bean-test.csv")
   args, _ = parser.parse_known_args()
   
   print("reading data")

   train_df = pd.read_csv(os.path.join(args.train, args.train_file))
   test_df = pd.read_csv(os.path.join(args.test, args.test_file))

   print("building training and testing datasets")

   X_train = train_df.drop("Class", axis=1)
   X_test = test_df.drop("Class", axis=1)
   y_train = train_df[["Class"]]
   y_test = test_df[["Class"]]

   # Train model
   print("training model")

   model = RandomForestClassifier(
       n_estimators=args.n_estimators,
       min_samples_leaf=args.min_samples_leaf,
       n_jobs=-1,
   )

   model.fit(X_train, y_train)

   # Print abs error
   print("validating model")

   bal_acc_train = balanced_accuracy_score(y_train, model.predict(X_train))
   bal_acc_test = balanced_accuracy_score(y_test, model.predict(X_test))

   print(f"Train balanced accuracy: {bal_acc_train:.3f}")
   print(f"Test balanced accuracy: {bal_acc_test:.3f}")

   # Persist model
   path = os.path.join(args.model_dir, "model.joblib")
   joblib.dump(model, path)
   print("model persisted at " + path)

上述脚本中的 %%writefile script.py 魔法命令会将单元格内容写入 Python 脚本文件,而不是直接运行。

现在逐步说明脚本中发生的事情:

import argparse
import os
import joblib
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import balanced_accuracy_score

首先导入一些必要的库。

if __name__ == "__main__":
   print("extracting arguments")
   parser = argparse.ArgumentParser()
   
   # Hyperparameters sent by the client are passed as command-line arguments to the script.
   parser.add_argument("--n-estimators", type=int, default=10)
   parser.add_argument("--min-samples-leaf", type=int, default=3)

   # Data, model, and output directories
   parser.add_argument("--model-dir", type=str, default=os.environ.get("SM_MODEL_DIR"))
   parser.add_argument("--train", type=str, default=os.environ.get("SM_CHANNEL_TRAIN"))
   parser.add_argument("--test", type=str, default=os.environ.get("SM_CHANNEL_TEST"))
   parser.add_argument("--train-file", type=str, default="dry-bean-train.csv")
   parser.add_argument("--test-file", type=str, default="dry-bean-test.csv")
   args, _ = parser.parse_known_args()

接着,我们创建一个参数解析器对象以读取命令行参数。正如我所说,将来可能需要更新的任何值都应通过 CLI 参数传入。本节定义了这些参数:

  • Random Forest 分类器对象的两个超参数。
  • 五个输入与输出文件路径,包括模型工件和数据集文件。

如果您注意到 model-dir、train 和 test 目录的默认值,会看到一些环境变量。它们来自 SageMaker SDK 文档,其中也提供了编写此类脚本文件的说明。

print("reading data")
train_df = pd.read_csv(os.path.join(args.train, args.train_file))
test_df = pd.read_csv(os.path.join(args.test, args.test_file))
print("building training and testing datasets")
X_train = train_df.drop("Class", axis=1)
X_test = test_df.drop("Class", axis=1)
y_train = train_df[["Class"]]
y_test = test_df[["Class"]]

定义完动态值后,我们读取并构建训练与测试数据集。可以看到,我们没有传递硬编码路径,而是使用 args.argument 的值。

# Train model
print("training model")
   
model = RandomForestClassifier(
    n_estimators=args.n_estimators,
    min_samples_leaf=args.min_samples_leaf,
    n_jobs=-1,
)

model.fit(X_train, y_train)

# Print abs error
print("validating model")
   
bal_acc_train = balanced_accuracy_score(y_train, model.predict(X_train))
bal_acc_test = balanced_accuracy_score(y_test, model.predict(X_test))

print(f"Train balanced accuracy: {bal_acc_train:.3f}")
print(f"Test balanced accuracy: {bal_acc_test:.3f}")

我们拟合模型,并在训练集与测试集上进行评估,然后打印性能。

# Persist model
path = os.path.join(args.model_dir, "model.joblib")
joblib.dump(model, path)
print("model persisted at " + path)

最后,我们使用 joblib 将模型保存到模型目录。

当您在 notebook 中运行此单元时,script.py 文件会出现在 JupyterLab 环境中。您可以通过如下方式执行它来检查是否正常工作:

! python script.py --n-estimators 100 \

                  --min-samples-leaf 2 \

                  --model-dir ./ \

                  --train ./ \

                  --test ./ \
extracting arguments

reading data

building training and testing datasets

validating model

Train balanced accuracy: 1.000

Test balanced accuracy: 0.998

model persisted at ./modejoblib

很好,我们获得了 99% 的平衡准确率——相当出色!这意味着我们终于可以启动训练作业了。

现在,我们将把脚本传递给 SageMaker 的估计器对象。在 AWS 的语境中,训练作业是一个过程,即使用指定的算法、数据集和 AWS SageMaker 提供的计算资源来训练模型。与在本地机器或 notebook 上训练不同,该作业在云端可扩展基础设施上运行,提供更强算力、更快处理速度,并可处理更大的数据集。

在 notebook 的新单元中添加以下代码:

# We use the Estimator from the SageMaker Python SDK
from sagemaker.sklearn.estimator import SKLearn
FRAMEWORK_VERSION = "0.23-1"

sklearn_estimator = SKLearn(
   entry_point="script.py",
   role=get_execution_role(),
   instance_count=1,
   instance_type="ml.c5.xlarge",
   framework_version=FRAMEWORK_VERSION,
   base_job_name="rf-scikit",
   hyperparameters={
       "n-estimators": 100,
       "min-samples-leaf": 3,
   },
)

上述代码中,SKLearn 类用于处理 scikit-learn 脚本。它包含一些需要说明的关键参数:

  • entry_point:脚本的路径。
  • role:有权访问 SageMaker 的用户名。
  • instance_count:要启动的机器数量。
  • instance_type:机器类型。

对于超参数,只传入您在训练脚本中定义的那些。否则会出错。

现在,我们可以将该估计器拟合到数据上:

# Launch training job, with asynchronous call
sklearn_estimator.fit({"train": trainpath, "test": testpath}, wait=True)

回忆本教程前文,trainpath 和 testpath 是指向我们 CSV 文件的 S3 存储桶路径。

运行上述单元后,训练开始:

INFO:sagemaker:Creating training-job with name: rf-scikit-2024-06-04-10-36-31-142

2024-06-04 10:36:31 Starting - Starting the training job...

2024-06-04 10:36:46 Starting - Preparing the instances for training...

2024-06-04 10:37:13 Downloading - Downloading input data...

2024-06-04 10:37:53 Training - Training image download completed. Training in progress...

SageMaker 会下载运行 scikit-learn 代码所需的 Docker 镜像并执行您的脚本。完成后,您将看到如下消息:

2024-06-04 10:38:17 Uploading - Uploading generated training model

2024-06-04 10:38:17 Completed - Training job completed

Training seconds: 65

Billable seconds: 65

如果您进入 SageMaker 控制台的 “SageMaker” > “Traning” > “Training Jobs” 部分,也会看到该训练作业的列表。

由于数据集较小,我们可以使用低成本、低配置的机器进行训练:ml.c5.xlarge。但在您的项目中,可能需要处理包含数百万记录或海量图像的数据集。在这种情况下,您必须选择提供更强算力和 GPU 的机器。

按需使用这些机器可能会非常昂贵。为降低成本,Amazon 提供 Spot Training 实例。使用这些实例,您可以以较低价格选择高性能计算资源,但有一个前提——训练不会立即开始。SageMaker 会等待到需求较低且您请求的机器可用时再启动训练。

要启用 Spot Training,您只需在上一段代码中添加几行:

spot_sklearn_estimator = SKLearn(
   entry_point="script.py",
   role=get_execution_role(),
   instance_count=1,
   instance_type="ml.c5.xlarge",
   framework_version=FRAMEWORK_VERSION,
   base_job_name="rf-scikit",
   hyperparameters={
       "n-estimators": 100,
       "min-samples-leaf": 3,
   },
   use_spot_instances=True,
   max_wait=7200,
   max_run=3600,
)

将 use_spot_instances 设为 True 后,spot_sklearn_estimator 将使用 Spot 训练。您还可以设置等待 Spot 实例可用的最长时间,以及训练运行的最长时间。

当您再次调用 .fit() 时,训练才会真正开始:

# Launch training job, with asynchronous call
spot_sklearn_estimator.fit({"train": trainpath, "test": testpath}, wait=True)

使用同一个训练脚本,我们可以通过类似流程得到一个进行过超参数调优的模型。

首先,从 sagemaker.tuner 导入 IntegerParameter 类:

from sagemaker.tuner import IntegerParameter

IntegerParameter 类允许 SageMaker 为接收整数值的参数定义调优范围。tuner 模块还包含其他类型参数的类:

  • CategoricalParameter:定义取离散类别值的超参数。
  • ContinuousParameter:为接收连续值的超参数定义调优范围。

但本教程中我们只使用 IntegerParameter,因为我们仅调 Random Forest 的两个参数:

# We use the Hyperparameter Tuner
from sagemaker.tuner import IntegerParameter

# Define exploration boundaries
hyperparameter_ranges = {
   "n-estimators": IntegerParameter(20, 100),
   "min-samples-leaf": IntegerParameter(2, 6),
}

然后,创建一个调优器对象:

# Create Optimizer
Optimizer = sagemaker.tuner.HyperparameterTuner(
   estimator=sklearn_estimator,
   hyperparameter_ranges=hyperparameter_ranges,
   base_tuning_job_name="RF-tuner",
   objective_type="Maximize",
   objective_metric_name="balanced-accuracy",
   metric_definitions=[
       {"Name": "balanced-accuracy", "Regex": "Test balanced accuracy: ([0-9.]+).*$"}
   ],  # Extract tracked metric from logs with regexp
   max_jobs=10,
   max_parallel_jobs=2,
)

Optimizer 的关键参数包括:

  • estimator:与您的训练脚本兼容的估计器对象。
  • hyperparameter_ranges:使用 tuner 模块中的类定义的参数范围。
  • objective_type:是最大化还是最小化给定指标。平衡准确率应被最大化。
  • metric_definitions:要优化的指标名称,以及如何从训练日志中提取它们。

metric_definitions 参数稍微复杂一些,因为它在定义指标的方式上提供了很高的灵活性。

在训练脚本中,我们通过以下日志消息报告平衡准确率:

print(f"Train balanced accuracy: {bal_acc_train:.3f}")
print(f"Test balanced accuracy: {bal_acc_test:.3f}")

metric_definitions 要求我们用正则表达式捕获该消息。因此,我们提供了如下字典,为以“Test balanced accuracy”开头的捕获指标赋予一个自定义名称。

{
   "Name": "balanced-accuracy",
   "Regex": "Test balanced accuracy: ([0-9.]+).*$",
}

其余代码就很熟悉了:

Optimizer.fit({"train": trainpath, "test": testpath})
INFO:sagemaker:Creating hyperparameter tuning job with name: RF-tuner-240604-1049

调优器完成后,您可以将结果以 DataFrame 展示:

# Get tuner results in a df
results = Optimizer.analytics().dataframe()

while results.empty:
   time.sleep(1)
   results = Optimizer.analytics().dataframe()

results.head()

The results of a hyperparameter tuning job from SageMaker

然后,您可以获取最优估计器:

best_estimator = Optimizer.best_estimator()

一旦确定用于实验的模型,您可以将其与所有工件一起上传到 SageMaker。此任务可使用 boto3 完成:

artifact_path = sm_boto3.describe_training_job(
   TrainingJobName=best_estimator.latest_training_job.name
)["ModelArtifacts"]["S3ModelArtifacts"]

print("Model artifact persisted at " + artifact)
Model artifact persisted at s3://sagemaker-us-east-1-496320894061/RF-tuner-240604-1049-009-38d75f35/output/model.tar.gz

describe_training_job() 函数接收一个估计器对象并将其推送到 SageMaker 提供的默认 S3 位置。您可以看到,该函数在上传前将模型打包为 tarball。

上传的模型可以在 SageMaker 控制台的 “Inference” > “Models” 部分看到:

A list of custom models uploaded to sagemaker

点击任意模型,您会在右上角看到“Create endpoint”按钮:

A screenshot of one of the models listed inside SageMaker

您可以通过该按钮部署模型,或返回 notebook 实例使用 .deploy() 方法通过代码部署:

from sagemaker.sklearn.model import SKLearnModel

model = SKLearnModel(
   model_data=artifact_path,
   role=get_execution_role(),
   entry_point="script.py",
   framework_version=FRAMEWORK_VERSION,
)

这次我们使用 SKLearnModel 类,它用于已训练的模型。它需要存放工件的 S3 位置以及用于训练的脚本。提供这些信息后,您可以运行代码进行部署:

predictor = model.deploy(instance_type="ml.c5.large", initial_instance_count=1)
INFO:sagemaker:Creating model with name: sagemaker-scikit-learn-2024-06-04-11-03-54-465
INFO:sagemaker:Creating endpoint-config with name sagemaker-scikit-learn-2024-06-04-11-03-54-960
INFO:sagemaker:Creating endpoint with name sagemaker-scikit-learn-2024-06-04-11-03-54-960

随后,您应能在 SageMaker 控制台的 “Inference” > “Endpoints” 部分看到该端点:

A list of endpoints created with SageMaker

要用于预测,您可以在 notebook 中使用端点对象的 .predict() 方法:

preds = predictor.predict(test.sample(4).drop("Class", axis=1))
preds
array([3, 0, 1, 2])

恭喜,您刚刚用 SageMaker 部署了第一个模型!

使用完毕后,请不要忘记删除端点,因为它会持续产生费用:

sm_boto3.delete_endpoint(EndpointName=predictor.endpoint)

别忘了,您在 SageMaker 中创建的每个 notebook 实例都带有一个示例 notebook 的独立标签页,展示 SageMaker 的不同功能:

A list of pre-made notebooks offered by SageMaker to all users to learn more about the platform.

另请查看 SageMaker Python SDK 的官方文档。

总结

今天,我们学习了如何使用最受欢迎的企业级机器学习平台之一:AWS SageMaker。我们涵盖了从创建 AWS 账户到使用 SageMaker 将 ML 模型部署为端点的全部流程。

与任何平台一样,我们目前只是浅尝辄止。如果在项目中将其与其他 AWS 技术结合,您可以做到更多。以下是一些相关资源可供参考:

Bexruz (Bex) Tuychiev

作者

Bexruz (Bex) Tuychiev

我是一名数据科学内容创作者,拥有超过 2 年的经验,并在 Medium 上拥有较大的读者群。我喜欢用略带讽刺的笔调撰写有关 AI 和机器学习的深度文章——毕竟总得想点办法让这些内容不那么枯燥。我已发表 130 多篇文章,并制作了一门 DataCamp 课程,另有一门正在筹备中。我的内容已被超过 500 万人次阅读,其中有 2 万人在 Medium 和 LinkedIn 上成为关注者。 

主题