使用 Amazon SageMaker 大规模训练和调整深度学习模型

在本教程中,您将学习如何使用 Amazon SageMaker 构建、训练和调整 TensorFlow 深度学习模型。

Amazon SageMaker 是一项全托管服务,为机器学习 (ML) 开发人员和数据科学家提供快速构建、训练和部署 ML 模型的能力。Amazon SageMaker 为您提供大规模训练和调整模型所需的一切资源,而无需管理基础设施。使用首个用于机器学习的集成开发环境 (IDE),即 Amazon SageMaker Studio,可以将实验快速可视化并跟踪训练进度,而无需离开熟悉的 Jupyter 笔记本界面。在 Amazon SageMaker Studio 中,您可以使用 Amazon SageMaker Experiments 轻松跟踪、评估和组织实验。

在本教程中,您将学习如何:

  1. 设置 Amazon SageMaker Studio
  2. 使用 Amazon SageMaker Studio 笔记本下载公共数据集,并将其上传到 Amazon S3
  3. 创建用来跟踪和管理训练作业的 Amazon SageMaker 实验
  4. 使用 Amazon SageMaker 的点击式训练在全托管的 GPU 实例上运行 TensorFlow 训练作业
  5. 运行大规模 Amazon SageMaker Automatic Model Tuning 作业以查找最佳的模型超参数,从而提高准确性
  6. 将训练结果可视化

您将使用 CIFAR-10 数据集来训练 TensorFlow 中的模型,以将图像分为 10 类。此数据集由 60,000 个 32x32 彩色图像组成,这些图像分为 40,000 个用于训练的图像、10,000 个用于验证的图像和 10,000 个用于测试的图像。

关于本教程
时间 1 小时
成本 大约 100 美元
使用场景 机器学习
产品 Amazon SageMaker
受众 开发人员
级别 中级
上次更新时间 2021 年 7 月 1 日

步骤 1:创建 AWS 账户

大约 100 美元即可完成本教程。

已经有账户?登录

步骤 2:设置 Amazon SageMaker Studio

通过完成以下步骤来登录 Amazon SageMaker Studio 并设置 Amazon SageMaker Studio 控制面板。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的 Amazon SageMaker Studio 入门


a. 登录 Amazon SageMaker 控制台。 

注意:确保在右上角选择可提供 SageMaker Studio 的 AWS 区域。有关区域列表,请参见登录 Amazon SageMaker Studio


b. 在 Amazon SageMaker 导航窗格中,选择 Amazon SageMaker Studio

注意:首次使用 Amazon SageMaker Studio 时,您必须完成 Studio 登录过程。登录时,您可以选择使用 AWS 单点登录 (AWS SSO) 或 AWS 身份与访问管理 (IAM) 作为身份验证方法。如果使用 IAM 身份验证,您可以选择 Quick start(快速入门)或 Standard setup(标准设置)过程。如果您不确定要使用哪个选项,请参见登录 Amazon SageMaker Studio 并向 IT 管理员寻求帮助。为简单起见,本教程使用 Quick start(快速入门)过程。


c. 在 Get started(入门)对话框中,选择 Quick start(快速入门)并指定用户名。


d. 对于 Execution role(执行角色),选择 Create an IAM role(创建 IAM 角色)。在出现的对话框中,选择 Any S3 bucket(任意 S3 存储桶),然后点击 Create role(创建角色)。

Amazon SageMaker 会创建一个具有所需权限的角色并将该角色分配给您的实例。


e. 点击 Submit(提交)。

步骤 3:下载数据集

Amazon SageMaker Studio 笔记本是点击式 Jupyter 笔记本,其中包含构建和测试训练脚本所需的一切资源。SageMaker Studio 还包括实验跟踪和可视化功能,可让您在某个位置轻松管理整个机器学习工作流。

通过完成以下步骤来创建 SageMaker 笔记本、下载数据集、将数据集转换为 TensorFlow 支持的 TFRecord 格式并将数据集上传到 Amazon S3。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的使用 Amazon SageMaker Studio 笔记本


a. 在 Amazon SageMaker Studio Control Panel(Amazon SageMaker Studio 控制面板)中,选择 Open Studio(打开 Studio)。


b. 在 JupyterLab 中的 File(文件)菜单中,选择 New Launcher(创建启动器)。在 Notebooks and compute resources(笔记本和计算资源)部分中,为 Select a SageMaker image(选择 SageMaker 图像)选择 TensorFlow 1.15 Python 3.6 (optimized for CPU)(TensorFlow 1.15 Python 3.6 (针对 CPU 优化))。然后,对于 Notebook(笔记本),选择 Python 3

注意:在此步骤中,您将选择用来运行 SageMaker 笔记本的 CPU 实例,SageMaker 笔记本会下载数据集、构建训练脚本、提交 Amazon SageMaker 训练作业并将结果可视化。训练作业本身就是在一个单独的实例类型上运行,您可以指定实例类型(例如在步骤 5 中看到的 GPU 实例)。


c. 复制以下代码块并将其粘贴到代码单元格中,然后选择 Run(运行)。

此代码下载 generate_cifar10_tfrecords.py 脚本,下载 CIFAR-10 数据集,然后将该数据集转换为 TFRecord 格式。

注意:此代码运行时,方括号之间会出现一个特殊符号 (*)。此代码将在几秒后执行完毕,该符号 (*) 会替换成某个数字。

https://github.com/aws/amazon-sagemaker-examples/blob/master/advanced_functionality/tensorflow_bring_your_own/utils/generate_cifar10_tfrecords.py

复制以下代码并将其粘贴到代码单元格中,然后选择 Run(运行)。

!pip install ipywidgets
!python generate_cifar10_tfrecords.py --data-dir cifar10

d. 将该数据集上传到默认的 Amazon SageMaker Amazon S3 存储桶。复制以下代码并将其粘贴到代码单元格中,然后选择 Run(运行)。

您应当会看到输出结果是该数据集的 Amazon S3 位置。

import time, os, sys
import sagemaker, boto3
import numpy as np
import pandas as pd

sess = boto3.Session()
sm   = sess.client('sagemaker')
role = sagemaker.get_execution_role()
sagemaker_session = sagemaker.Session(boto_session=sess)

datasets = sagemaker_session.upload_data(path='cifar10', key_prefix='datasets/cifar10-dataset')
datasets 

步骤 4:创建一个 Amazon SageMaker 实验

现在您已下载数据集并将其暂存在 Amazon S3 中,可以创建 Amazon SageMaker 实验了。实验是与同一机器学习项目相关的处理和训练作业的集合。Amazon SageMaker Experiments 自动为您管理和跟踪所运行的训练。

通过完成以下步骤来创建新实验。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的实验


a. 在 Jupyter 笔记本中,复制以下代码块并将其粘贴到代码单元格中,然后选择 Run(运行)。

此代码使用 smexperiments python 软件包创建名为 sagemaker-training-experiments 的实验。此软件包会预先安装在 Amazon SageMaker Studio 笔记本上。您可以自定义实验名称和说明。

from smexperiments.experiment import Experiment
from smexperiments.trial import Trial
from smexperiments.trial_component import TrialComponent

training_experiment = Experiment.create(
                                experiment_name = "sagemaker-training-experiments", 
                                description     = "Experiment to track cifar10 training trials", 
                                sagemaker_boto_client=sm)

b. 在左侧工具栏中,选择 Components and registries(组件和注册表)(三角形图标),然后选择 Experiments and trials(实验和试验)。新实验 sagemaker-training-experiments 将出现在列表中。

步骤 5:创建试验和训练脚本

若要训练 CIFAR-10 数据集上的分类器,您需要一个训练脚本。在此步骤中,您将为 TensorFlow 训练作业创建试验和训练脚本。每个试验都是端到端训练作业的一次迭代。除了训练作业之外,试验还可以跟踪预处理作业、后处理作业、数据集和其他元数据。单个实验中可以包括多个试验,这使您可以在 Amazon SageMaker Studio Experiments 窗格中轻松跟踪一段时间内的多次迭代。

通过完成以下步骤来为 TensorFlow 训练作业新建试验和训练脚本。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的将 TensorFlow 与 Amazon SageMaker 结合使用


a. 在 Jupyter 笔记本中,复制以下代码块并将其粘贴到代码单元格中,然后选择 Run(运行)。

此代码会创建一个新试验并将其与步骤 4 中创建的实验相关联。

single_gpu_trial = Trial.create(
    trial_name = 'sagemaker-single-gpu-training', 
    experiment_name = training_experiment.experiment_name,
    sagemaker_boto_client = sm,
)

trial_comp_name = 'single-gpu-training-job'
experiment_config = {"ExperimentName": training_experiment.experiment_name, 
                       "TrialName": single_gpu_trial.trial_name,
                       "TrialComponentDisplayName": trial_comp_name}

每个试验都是端到端训练作业的一次迭代。除了训练作业之外,试验还可以跟踪预处理作业、后处理作业、数据集和其他元数据。单个实验中可以包括多个试验,这使您可以在 Amazon SageMaker Studio Experiments 窗格中轻松跟踪一段时间内的多次迭代。


b. 在左侧工具栏中,选择 Components and registries(组件和注册表)(三角形图标)。双击 sagemaker-training-experiments 以显示相关试验。新试验 sagemaker-single-gpu-training 将出现在列表中。


c. 在 File(文件)菜单中,选择 New(新建),然后选择 Text File(文本文件)。在代码编辑器中,复制以下 TensorFlow 代码并将其粘贴到新创建的文件中。

该脚本执行 TensorFlow 代码,用于读取 CIFAR-10 数据集并训练 resnet50 模型。

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.callbacks import ModelCheckpoint
from tensorflow.keras.layers import Input, Dense, Flatten
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.optimizers import Adam, SGD
import argparse
import os
import re
import time

HEIGHT = 32
WIDTH = 32
DEPTH = 3
NUM_CLASSES = 10

def single_example_parser(serialized_example):
    """Parses a single tf.Example into image and label tensors."""
    # Dimensions of the images in the CIFAR-10 dataset.
    # See http://www.cs.toronto.edu/~kriz/cifar.html for a description of the
    # input format.
    features = tf.io.parse_single_example(
        serialized_example,
        features={
            'image': tf.io.FixedLenFeature([], tf.string),
            'label': tf.io.FixedLenFeature([], tf.int64),
        })
    image = tf.decode_raw(features['image'], tf.uint8)
    image.set_shape([DEPTH * HEIGHT * WIDTH])

    # Reshape from [depth * height * width] to [depth, height, width].
    image = tf.cast(
        tf.transpose(tf.reshape(image, [DEPTH, HEIGHT, WIDTH]), [1, 2, 0]),
        tf.float32)
    label = tf.cast(features['label'], tf.int32)
    
    image = train_preprocess_fn(image)
    label = tf.one_hot(label, NUM_CLASSES)
    
    return image, label

def train_preprocess_fn(image):

    # Resize the image to add four extra pixels on each side.
    image = tf.image.resize_with_crop_or_pad(image, HEIGHT + 8, WIDTH + 8)

    # Randomly crop a [HEIGHT, WIDTH] section of the image.
    image = tf.image.random_crop(image, [HEIGHT, WIDTH, DEPTH])

    # Randomly flip the image horizontally.
    image = tf.image.random_flip_left_right(image)
    return image

def get_dataset(filenames, batch_size):
    """Read the images and labels from 'filenames'."""
    # Repeat infinitely.
    dataset = tf.data.TFRecordDataset(filenames).repeat().shuffle(10000)

    # Parse records.
    dataset = dataset.map(single_example_parser, num_parallel_calls=tf.data.experimental.AUTOTUNE)

    # Batch it up.
    dataset = dataset.batch(batch_size, drop_remainder=True)
    return dataset

def get_model(input_shape, learning_rate, weight_decay, optimizer, momentum):
    input_tensor = Input(shape=input_shape)
    base_model = keras.applications.resnet50.ResNet50(include_top=False,
                                                          weights='imagenet',
                                                          input_tensor=input_tensor,
                                                          input_shape=input_shape,
                                                          classes=None)
    x = Flatten()(base_model.output)
    predictions = Dense(NUM_CLASSES, activation='softmax')(x)
    model = Model(inputs=base_model.input, outputs=predictions)
    return model

def main(args):
    # Hyper-parameters
    epochs       = args.epochs
    lr           = args.learning_rate
    batch_size   = args.batch_size
    momentum     = args.momentum
    weight_decay = args.weight_decay
    optimizer    = args.optimizer

    # SageMaker options
    training_dir   = args.training
    validation_dir = args.validation
    eval_dir       = args.eval

    train_dataset = get_dataset(training_dir+'/train.tfrecords',  batch_size)
    val_dataset   = get_dataset(validation_dir+'/validation.tfrecords', batch_size)
    eval_dataset  = get_dataset(eval_dir+'/eval.tfrecords', batch_size)
    
    input_shape = (HEIGHT, WIDTH, DEPTH)
    model = get_model(input_shape, lr, weight_decay, optimizer, momentum)
    
    # Optimizer
    if optimizer.lower() == 'sgd':
        opt = SGD(lr=lr, decay=weight_decay, momentum=momentum)
    else:
        opt = Adam(lr=lr, decay=weight_decay)

    # Compile model
    model.compile(optimizer=opt,
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    
    # Train model
    history = model.fit(train_dataset, steps_per_epoch=40000 // batch_size,
                        validation_data=val_dataset, 
                        validation_steps=10000 // batch_size,
                        epochs=epochs)
                        
    
    # Evaluate model performance
    score = model.evaluate(eval_dataset, steps=10000 // batch_size, verbose=1)
    print('Test loss    :', score[0])
    print('Test accuracy:', score[1])
    
    # Save model to model directory
    model.save(f'{os.environ["SM_MODEL_DIR"]}/{time.strftime("%m%d%H%M%S", time.gmtime())}', save_format='tf')


#%%
if __name__ == "__main__":
    
    parser = argparse.ArgumentParser()
    # Hyper-parameters
    parser.add_argument('--epochs',        type=int,   default=10)
    parser.add_argument('--learning-rate', type=float, default=0.01)
    parser.add_argument('--batch-size',    type=int,   default=128)
    parser.add_argument('--weight-decay',  type=float, default=2e-4)
    parser.add_argument('--momentum',      type=float, default='0.9')
    parser.add_argument('--optimizer',     type=str,   default='sgd')

    # SageMaker parameters
    parser.add_argument('--model_dir',        type=str)
    parser.add_argument('--training',         type=str,   default=os.environ['SM_CHANNEL_TRAINING'])
    parser.add_argument('--validation',       type=str,   default=os.environ['SM_CHANNEL_VALIDATION'])
    parser.add_argument('--eval',             type=str,   default=os.environ['SM_CHANNEL_EVAL'])
    
    args = parser.parse_args()
    main(args)

d. 在 File(文件)菜单中,选择 Rename File(重命名文件)。New Name(新名称)文本框中,复制并粘贴 cifar10-training-sagemaker.py,然后选择 Rename(重命名)。(请确保新扩展名是 .py,而不是 .txt)。然后依次选择 File(文件)和 Save Python File(保存 Python 文件)。

步骤 6:运行 TensorFlow 训练作业并将结果可视化

在此步骤中,您将使用 Amazon SageMaker 运行 TensorFlow 训练作业。使用 Amazon SageMaker 可以轻松训练模型。在您指定数据集在 Amazon S3 中的位置以及训练实例的类型之后,Amazon SageMaker 会为您管理训练基础设施。


通过完成以下步骤来运行 TensorFlow 训练作业并将结果可视化。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的将 TensorFlow 与 Amazon SageMaker 结合使用


a. 在 Jupyter 笔记本中,复制以下代码块并将其粘贴到代码单元格中,然后选择 Run(运行)。接下来仔细查看代码。

注意:如果出现 ResourceLimitExceeded,请将实例类型更改为 ml.c5.xlarge

注意:您可以放心忽略任何弃用警告(如 sagemaker.deprecations:train_instance_type has been renamed 已重命名...)。此类警告因版本变更而触发,不会导致任何训练失败。

from sagemaker.tensorflow import TensorFlow

hyperparams={'epochs'       : 30,
             'learning-rate': 0.01,
             'batch-size'   : 256,
             'weight-decay' : 2e-4,
             'momentum'     : 0.9,
             'optimizer'    : 'adam'}

bucket_name = sagemaker_session.default_bucket()
output_path = f's3://{bucket_name}/jobs'
metric_definitions = [{'Name': 'val_acc', 'Regex': 'val_acc: ([0-9\\.]+)'}]

tf_estimator = TensorFlow(entry_point          = 'cifar10-training-sagemaker.py', 
                          output_path          = f'{output_path}/',
                          code_location        = output_path,
                          role                 = role,
                          train_instance_count = 1, 
                          train_instance_type  = 'ml.g4dn.xlarge',
                          framework_version    = '1.15.2', 
                          py_version           = 'py3',
                          script_mode          = True,
                          metric_definitions   = metric_definitions,
                          sagemaker_session    = sagemaker_session,
                          hyperparameters      = hyperparams)

job_name=f'tensorflow-single-gpu-{time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())}'
tf_estimator.fit({'training'  : datasets,
                  'validation': datasets,
                  'eval'      : datasets},
                 job_name = job_name,
                 experiment_config=experiment_config)

此代码包括三个部分:

  • 指定训练作业超参数
  • 调用 Amazon SageMaker Estimator 函数并提供训练作业详细信息(如训练脚本的名称、要在其上进行训练的实例类型、框架版本等)
  • 调用 fit 函数以启动训练作业

Amazon SageMaker 会自动配置所请求的实例、下载数据集、拉取 TensorFlow 容器、下载训练脚本并开始训练。

在此示例中,您将提交一个 Amazon SageMaker 训练作业,该作业将在 ml.g4dn.xlarge(GPU 实例)上运行。深度学习训练是计算密集型训练,建议使用 GPU 实例以便更快获取结果。

训练完成后,您应当会看到最终的准确性结果、训练时间和可计费时间。  


b. 查看训练摘要。在左侧工具栏中,选择 Components and registries(组件和注册表)(三角形图标)。双击 sagemaker-training-experiments 以及 sagemaker-single-gpu-training,然后双击为您的训练作业新创建的 single-gpu-training-job 试验组件。选择 Metrics(指标)。


c. 将结果可视化。依次选择 Charts(图表)和 Add chart(添加图表)。在 Chart Properties(图表属性)窗格中,选择以下选项:

  • Chart type(图表类型):Line(线形图)
  • X-axis dimension(X 轴维度):Epoch
  • Y-axis(Y 轴):val_acc_EVAL_avg

您应当会看到一张显示评估准确性随训练进行而发生变化的趋势图,并以步骤 6a 中的最终准确性结束。

步骤 7:使用 Amazon SageMaker 提供的模型自动调整功能来调整模型

在此步骤中,您将运行 Amazon SageMaker 模型自动调整作业,以找到最佳超参数并提高步骤 6 中获得的训练准确性。若要运行模型调整作业,需要为 Amazon SageMaker 提供超参数范围(而不是固定值),这样 Amazon SageMaker 就可以探查超参数空间并自动为您找到最佳值。

通过完成以下步骤来运行模型自动调整作业。

注意:有关详细信息,请参见 Amazon SageMaker 文档中的执行模型自动调整


a. 在 Jupyter 笔记本中,复制以下代码块并将其粘贴到代码单元格中,然后选择 Run(运行)。接下来仔细查看代码。

注意:如果出现 ResourceLimitExceeded,请将实例类型更改为 ml.c5.xlarge

注意:您可以放心忽略任何弃用警告(如 sagemaker.deprecations:train_instance_type 已重命名...)。此类警告因版本变更而触发,不会导致任何训练失败。

from sagemaker.tuner import IntegerParameter, CategoricalParameter, ContinuousParameter, HyperparameterTuner

hyperparameter_ranges = {
    'epochs'        : IntegerParameter(5, 30),
    'learning-rate' : ContinuousParameter(0.001, 0.1, scaling_type='Logarithmic'), 
    'batch-size'    : CategoricalParameter(['128', '256', '512']),
    'momentum'      : ContinuousParameter(0.9, 0.99),
    'optimizer'     : CategoricalParameter(['sgd', 'adam'])
}

objective_metric_name = 'val_acc'
objective_type = 'Maximize'
metric_definitions = [{'Name': 'val_acc', 'Regex': 'val_acc: ([0-9\\.]+)'}]

tf_estimator = TensorFlow(entry_point          = 'cifar10-training-sagemaker.py', 
                          output_path          = f'{output_path}/',
                          code_location        = output_path,
                          role                 = role,
                          train_instance_count = 1, 
                          train_instance_type  = 'ml.g4dn.xlarge',
                          framework_version    = '1.15', 
                          py_version           = 'py3',
                          script_mode          = True,
                          metric_definitions   = metric_definitions,
                          sagemaker_session    = sagemaker_session)

tuner = HyperparameterTuner(estimator             = tf_estimator,
                            objective_metric_name = objective_metric_name,
                            hyperparameter_ranges = hyperparameter_ranges,
                            metric_definitions    = metric_definitions,
                            max_jobs              = 16,
                            max_parallel_jobs     = 8,
                            objective_type        = objective_type)

job_name=f'tf-hpo-{time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())}'
tuner.fit({'training'  : datasets,
           'validation': datasets,
           'eval'      : datasets},
            job_name = job_name)

此代码包括四个部分:

  • 指定超参数值的范围。该范围可以是整数范围(如迭代次数)、连续范围(如学习率)或分类值(如优化器类型 sgd 或 adam)。
  • 调用与步骤 6 中相似的 Estimator 函数
  • 创建一个 HyperparameterTuner 对象,该对象具有超参数范围、最大作业数以及要运行的并行作业数
  • 调用 fit 函数以启动超参数调整作业

注意:您可以将 max_jobs 变量从 16 改为更小的值以节省作业调整成本。但是,调整作业数越少,找到更好模型的概率越小。您还可以将 max_parallel_jobs 变量减小到不大于 max_jobs 的值。当 max_parallel_jobs 等于 max_jobs 时,您可以更快地获取结果。请确保 max_parallel_jobs 小于 AWS 账户的实例限值,以免遇到资源错误。


b. 查看最佳超参数。打开 Amazon SageMaker 控制台,然后在左侧导航窗格中的 Training(训练)下面,选择 Hyperparameter tuning jobs(超参数调整作业)。在显示的页面中,选择所需调整作业,然后选择 Best training job(最佳训练作业)。您将看到训练准确性 (80%) 比步骤 6 中的结果 (60%) 有所提高。

注意:您的结果可能有所不同。可以通过增加 max_jobs、放宽超参数范围并探查其他模型架构来进一步改进结果。

步骤 8:清除

在此步骤中,您将终止本实验中使用的资源。

重要说明:终止当前未使用的资源可降低成本,这是一种最佳做法。如果不终止资源,则系统会继续从您的账户中扣费。


停止训练作业:

  1. 打开 Amazon SageMaker 控制台
  2. 在左侧导航窗格的 Training(训练)下面,选择 Training Jobs(训练作业)。
  3. 确认没有状态为 In Progress(进行中)的训练作业。对于任何正在进行中的训练作业,您可以等待其完成训练,也可以选择训练作业名称并点击 Stop(停止)。

(可选)清除所有训练构件:若要清除所有训练构件(如模型、预处理的数据集等),请在 Jupyter 笔记本中,复制并粘贴以下代码,然后选择 Run(运行)。

注意:请确保将 ACCOUNT_NUMBER 替换为您的账号。

!aws s3 rm --recursive s3://sagemaker-us-west-2-ACCOUNT_NUMBER/datasets/cifar10-dataset
!aws s3 rm --recursive s3://sagemaker-us-west-2-ACCOUNT_NUMBER/jobs

结论

恭喜您!您已经使用 Amazon SageMaker 创建、训练和调整了 TensorFlow 深度学习模型。

可以通过下面的后续步骤部分继续您的 SageMaker 机器学习之旅。

后续步骤