【发布时间】:2021-09-02 04:01:47
【问题描述】:
有关如何使用 SageMaker 估算器的文档分散在各处,有时甚至是过时的、不正确的。是否有一站式位置提供有关如何使用 SageMaker SDK Estimator 训练和保存模型的全面视图?
【问题讨论】:
标签: amazon-web-services amazon-sagemaker
有关如何使用 SageMaker 估算器的文档分散在各处,有时甚至是过时的、不正确的。是否有一站式位置提供有关如何使用 SageMaker SDK Estimator 训练和保存模型的全面视图?
【问题讨论】:
标签: amazon-web-services amazon-sagemaker
AWS 中没有这样一种资源可以全面了解如何使用 SageMaker SDK Estimator 来训练和保存模型。
我放了一张图表和简要说明,以大致了解 SageMaker Estimator 如何运行培训。
SageMaker 为训练作业设置 docker 容器,其中:
/opt/ml/input/data下。/opt/ml/code下。/opt/ml/model 和 /opt/ml/output 目录用于存储训练输出。/opt/ml
├── input
│ ├── config
│ │ ├── hyperparameters.json <--- From Estimator hyperparameter arg
│ │ └── resourceConfig.json
│ └── data
│ └── <channel_name> <--- From Estimator fit method inputs arg
│ └── <input data>
├── code
│ └── <code files> <--- From Estimator src_dir arg
├── model
│ └── <model files> <--- Location to save the trained model artifacts
└── output
└── failure <--- Training job failure logs
SageMaker Estimator fit(inputs) 方法执行训练脚本。估计器hyperparameters 和fit 方法inputs 作为其命令行参数提供。
训练完成后,训练脚本会将模型工件保存在 /opt/ml/model 中。
SageMaker 将 /opt/ml/model 下的工件存档到 model.tar.gz 中,并将其保存到为 output_path Estimator 参数指定的 S3 位置。
您可以设置 Estimator metric_definitions 参数以从训练日志中提取模型指标。然后,您可以在 SageMaker 控制台指标中监控训练进度。
我认为 AWS 需要停止大量生产冗长、冗余、冗长、分散和过时的文档。 AWS 需要理解一张图胜过千言万语。
以图表为中心,在上下文下将各个部分拼凑在一起,以实现明确的目标。
AWS 文档需要认真重新设计和重新构建。仅仅为了理解如何训练和保存模型,我们就不得不经历几十个分散、支离破碎、冗长、冗余的文档,这些文档通常是过时的、不完整的,有时甚至是不正确的。
在Why I think GCP is better than AWS中有很好的总结:
并不是说 AWS 比 GCP 更难使用,而是 它太难了;基础设施原语的杂乱无章的蔓延,它们之间的凝聚力很差。
挑战是好的,混乱的混乱不是,AWS 的问题是你的大部分工作时间都花在整理他们的文档和筛选功能和产品以找到你想要的东西,而不是专注于酷有趣的挑战。
尤其是 SageMaker 团队不断更改实施而不更新文档。它的推出也不一致,例如SDK 版本 2 已在 SageMaker Studio 中推出,使得 Github 中的 AWS 示例不兼容,但并未公布。而 SageMaker 实例仍然有 SDK 1,因此代码在 Instance 中工作,但在 Studio 中没有。
令人难以置信,甚至是疯狂,我们必须通过下面的这些文档来了解如何使用 SageMaker SDK Estimator 进行训练。 AWS 想浪费多少开发人员的时间?
本文档提供了 20,000 英尺的 SageMaker 培训概述,但没有提供任何线索。
本文档概述了 SageMaker 的培训方式。但是,这不是最新的,因为它基于已过时的 SageMaker Containers。
警告:此软件包已被弃用。请使用 SageMaker 训练工具包进行模型训练,使用 SageMaker 推理工具包进行模型服务。
本文档列出了培训的步骤。
Amazon SageMaker Python 开发工具包提供框架估计器和通用估计器来训练您的模型,同时编排机器学习 (ML) 生命周期,访问 SageMaker 功能进行训练和 AWS 基础设施
要使用 SageMaker Python SDK 训练模型,您:
- 准备训练脚本
- 创建估算器
- 调用估计器的 fit 方法
最后,本文档给出了具体的步骤和想法。但是仍然缺少有关环境变量、SageMaker docker 容器中的目录结构**、用于上传代码、放置数据的 S3、保存训练模型的 S3 等的全面详细信息。
本文档重点介绍 TensorFlow Estimator 实施步骤。使用Training a Tensorflow Model on MNIST Github 例子来配合实际实现。
本部分说明 SageMaker 如何使训练信息(例如训练数据、超参数和其他配置信息)可用于您的 Docker 容器。
本文档最终给出了如何传递参数和数据的想法,但同样不全面。
本文档被标记为已弃用,但它是唯一解释 SageMaker 环境变量的文档。
重要的环境变量
- SM_MODEL_DIR
- SM_CHANNELS
- SM_CHANNEL_{频道名称}
- SM_HPS
- SM_HP_{hyperparameter_name}
- SM_CURRENT_HOST
- SM_HOSTS
- SM_NUM_GPUS
SageMaker Containers 提供的环境变量列表
- SM_NUM_CPUS
- SM_LOG_LEVEL
- SM_NETWORK_INTERFACE_NAME
- SM_USER_ARGS
- SM_INPUT_DIR
- SM_INPUT_CONFIG_DIR
- SM_OUTPUT_DATA_DIR
- SM_RESOURCE_CONFIG
- SM_INPUT_DATA_CONFIG
- SM_TRAINING_ENV
/opt/ml
├── input
│ ├── config
│ │ ├── hyperparameters.json
│ │ └── resourceConfig.json
│ └── data
│ └── <channel_name>
│ └── <input data>
├── model
│ └── <model files>
└── output
└── failure
本文档解释了每个目录的目录结构和用途。
输入
- /opt/ml/input/config 包含控制程序运行方式的信息。 hyperparameters.json 是一个 JSON 格式的超参数名称到值的字典。这些值将始终是字符串,因此您可能需要对其进行转换。 resourceConfig.json 是一个 JSON 格式的文件,描述了用于分布式训练的网络布局。由于 scikit-learn 不支持分布式训练,我们将在此处忽略它。
- /opt/ml/input/data/
/(对于文件模式)包含该通道的输入数据。通道是基于对 CreateTrainingJob 的调用创建的,但通道与算法的预期相匹配通常很重要。每个通道的文件将从 S3 复制到此目录,并保留 S3 密钥结构指示的树结构。 - /opt/ml/input/data/
_ (用于管道模式)是给定纪元的管道。时期从零开始,每次阅读时增加一。您可以运行的 epoch 数量没有限制,但您必须在读取下一个 epoch 之前关闭每个管道。 输出
- /opt/ml/model/ 是您编写算法生成的模型的目录。您的模型可以是您想要的任何格式。它可以是单个文件或整个目录树。 SageMaker 会将此目录中的所有文件打包成一个压缩的 tar 存档文件。该文件将在 DescribeTrainingJob 结果中返回的 S3 位置提供。
- /opt/ml/output 是算法可以在其中写入描述作业失败原因的文件失败的目录。该文件的内容将在 DescribeTrainingJob 结果的 FailureReason 字段中返回。对于成功的作业,没有理由写入此文件,因为它将被忽略。
但是,这不是最新的,因为它基于已过时的 SageMaker Containers。
关于已训练模型的保存位置和格式的信息从根本上缺失。训练脚本需要将模型保存在/opt/ml/model 下,格式和子目录结构取决于框架,例如TensorFlow、Pytorch。这是因为 SageMaker 部署使用依赖于框架的模型服务,例如TensorFlow Serving 用于 TensorFlow 框架。
这没有明确记录并导致混淆。开发者需要指定使用哪种格式以及保存在哪个子目录下。
使用 TensorFlow Estimator 训练和部署:
因为我们使用 TensorFlow Serving 进行部署,我们的训练脚本将模型保存为 TensorFlow 的 SavedModel 格式。
# Save the model
# A version number is needed for the serving container
# to load the model
version = "00000000"
ckpt_dir = os.path.join(args.model_dir, version)
if not os.path.exists(ckpt_dir):
os.makedirs(ckpt_dir)
model.save(ckpt_dir)
代码将模型保存在 /opt/ml/model/00000000 中,因为这是用于 TensorFlow 服务的。
保存路径遵循 TensorFlow Serving 使用的约定,其中最后一个路径组件(此处为 1/)是模型的版本号 - 它允许 Tensorflow Serving 等工具推断相对新鲜度。
要将我们训练好的模型加载到 TensorFlow Serving 中,我们首先需要将其保存为 SavedModel 格式。这将在定义良好的目录层次结构中创建一个 protobuf 文件,并将包含一个版本号。 TensorFlow Serving 允许我们在发出推理请求时选择要使用的模型版本或“可服务”版本。每个版本都将导出到给定路径下的不同子目录。
基本上,SageMaker SDK Estimator 为训练部分实现了CreateTrainingJob API。因此,更好地了解它是如何设计的以及需要定义哪些参数。否则,在 Estimator 上工作就像在黑暗中行走。
import sagemaker
from sagemaker import get_execution_role
sagemaker_session = sagemaker.Session()
role = get_execution_role()
bucket = sagemaker_session.default_bucket()
metric_definitions = [
{"Name": "train:loss", "Regex": ".*loss: ([0-9\\.]+) - accuracy: [0-9\\.]+.*"},
{"Name": "train:accuracy", "Regex": ".*loss: [0-9\\.]+ - accuracy: ([0-9\\.]+).*"},
{
"Name": "validation:accuracy",
"Regex": ".*step - loss: [0-9\\.]+ - accuracy: [0-9\\.]+ - val_loss: [0-9\\.]+ - val_accuracy: ([0-9\\.]+).*",
},
{
"Name": "validation:loss",
"Regex": ".*step - loss: [0-9\\.]+ - accuracy: [0-9\\.]+ - val_loss: ([0-9\\.]+) - val_accuracy: [0-9\\.]+.*",
},
{
"Name": "sec/sample",
"Regex": ".* - \d+s (\d+)[mu]s/sample - loss: [0-9\\.]+ - accuracy: [0-9\\.]+ - val_loss: [0-9\\.]+ - val_accuracy: [0-9\\.]+",
},
]
import uuid
checkpoint_s3_prefix = "checkpoints/{}".format(str(uuid.uuid4()))
checkpoint_s3_uri = "s3://{}/{}/".format(bucket, checkpoint_s3_prefix)
from sagemaker.tensorflow import TensorFlow
# --------------------------------------------------------------------------------
# 'trainingJobName' msut satisfy regular expression pattern: ^[a-zA-Z0-9](-*[a-zA-Z0-9]){0,62}
# --------------------------------------------------------------------------------
base_job_name = "fashion-mnist"
hyperparameters = {
"epochs": 2,
"batch-size": 64
}
estimator = TensorFlow(
entry_point="fashion_mnist.py",
source_dir="src",
metric_definitions=metric_definitions,
hyperparameters=hyperparameters,
role=role,
input_mode='File',
framework_version="2.3.1",
py_version="py37",
instance_count=1,
instance_type="ml.m5.xlarge",
base_job_name=base_job_name,
checkpoint_s3_uri=checkpoint_s3_uri,
model_dir=False
)
estimator.fit()
import os
import argparse
import json
import multiprocessing
import numpy as np
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Flatten, BatchNormalization
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.layers.experimental.preprocessing import Normalization
from tensorflow.keras import backend as K
print("TensorFlow version: {}".format(tf.__version__))
print("Eager execution is: {}".format(tf.executing_eagerly()))
print("Keras version: {}".format(tf.keras.__version__))
image_width = 28
image_height = 28
def load_data():
fashion_mnist = tf.keras.datasets.fashion_mnist
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()
number_of_classes = len(set(y_train))
print("number_of_classes", number_of_classes)
x_train = x_train / 255.0
x_test = x_test / 255.0
x_full = np.concatenate((x_train, x_test), axis=0)
print(x_full.shape)
print(type(x_train))
print(x_train.shape)
print(x_train.dtype)
print(y_train.shape)
print(y_train.dtype)
# ## Train
# * C: Convolution layer
# * P: Pooling layer
# * B: Batch normalization layer
# * F: Fully connected layer
# * O: Output fully connected softmax layer
# Reshape data based on channels first / channels last strategy.
# This is dependent on whether you use TF, Theano or CNTK as backend.
# Source: https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py
if K.image_data_format() == 'channels_first':
x = x_train.reshape(x_train.shape[0], 1, image_width, image_height)
x_test = x_test.reshape(x_test.shape[0], 1, image_width, image_height)
input_shape = (1, image_width, image_height)
else:
x_train = x_train.reshape(x_train.shape[0], image_width, image_height, 1)
x_test = x_test.reshape(x_test.shape[0], image_width, image_height, 1)
input_shape = (image_width, image_height, 1)
return x_train, y_train, x_test, y_test, input_shape, number_of_classes
# tensorboard --logdir=/full_path_to_your_logs
validation_split = 0.2
verbosity = 1
use_multiprocessing = True
workers = multiprocessing.cpu_count()
def train(model, x, y, args):
# SavedModel Output
tensorflow_saved_model_path = os.path.join(args.model_dir, "tensorflow/saved_model/0")
os.makedirs(tensorflow_saved_model_path, exist_ok=True)
# Tensorboard Logs
tensorboard_logs_path = os.path.join(args.model_dir, "tensorboard/")
os.makedirs(tensorboard_logs_path, exist_ok=True)
tensorboard_callback = tf.keras.callbacks.TensorBoard(
log_dir=tensorboard_logs_path,
write_graph=True,
write_images=True,
histogram_freq=1, # How often to log histogram visualizations
embeddings_freq=1, # How often to log embedding visualizations
update_freq="epoch",
) # How often to write logs (default: once per epoch)
model.compile(
optimizer='adam',
loss=tf.keras.losses.sparse_categorical_crossentropy,
metrics=['accuracy']
)
history = model.fit(
x,
y,
shuffle=True,
batch_size=args.batch_size,
epochs=args.epochs,
validation_split=validation_split,
use_multiprocessing=use_multiprocessing,
workers=workers,
verbose=verbosity,
callbacks=[
tensorboard_callback
]
)
return history
def create_model(input_shape, number_of_classes):
model = Sequential([
Conv2D(
name="conv01",
filters=32,
kernel_size=(3, 3),
strides=(1, 1),
padding="same",
activation='relu',
input_shape=input_shape
),
MaxPooling2D(
name="pool01",
pool_size=(2, 2)
),
Flatten(), # 3D shape to 1D.
BatchNormalization(
name="batch_before_full01"
),
Dense(
name="full01",
units=300,
activation="relu"
), # Fully connected layer
Dense(
name="output_softmax",
units=number_of_classes,
activation="softmax"
)
])
return model
def save_model(model, args):
# Save the model
# A version number is needed for the serving container
# to load the model
version = "00000000"
model_save_dir = os.path.join(args.model_dir, version)
if not os.path.exists(model_save_dir):
os.makedirs(model_save_dir)
print(f"saving model at {model_save_dir}")
model.save(model_save_dir)
def parse_args():
# --------------------------------------------------------------------------------
# https://docs.python.org/dev/library/argparse.html#dest
# --------------------------------------------------------------------------------
parser = argparse.ArgumentParser()
# --------------------------------------------------------------------------------
# hyperparameters Estimator argument are passed as command-line arguments to the script.
# --------------------------------------------------------------------------------
parser.add_argument('--epochs', type=int, default=10)
parser.add_argument('--batch-size', type=int, default=64)
# /opt/ml/model
# sagemaker.tensorflow.estimator.TensorFlow override 'model_dir'.
# See https://sagemaker.readthedocs.io/en/stable/frameworks/tensorflow/\
# sagemaker.tensorflow.html#sagemaker.tensorflow.estimator.TensorFlow
parser.add_argument('--model_dir', type=str, default=os.environ['SM_MODEL_DIR'])
# /opt/ml/output
parser.add_argument("--output_dir", type=str, default=os.environ["SM_OUTPUT_DIR"])
args = parser.parse_args()
return args
if __name__ == "__main__":
args = parse_args()
print("---------- key/value args")
for key, value in vars(args).items():
print(f"{key}:{value}")
x_train, y_train, x_test, y_test, input_shape, number_of_classes = load_data()
model = create_model(input_shape, number_of_classes)
history = train(model=model, x=x_train, y=y_train, args=args)
print(history)
save_model(model, args)
results = model.evaluate(x_test, y_test, batch_size=100)
print("test loss, test accuracy:", results)
2021-09-03 03:02:04 Starting - Starting the training job...
2021-09-03 03:02:16 Starting - Launching requested ML instancesProfilerReport-1630638122: InProgress
......
2021-09-03 03:03:17 Starting - Preparing the instances for training.........
2021-09-03 03:04:59 Downloading - Downloading input data
2021-09-03 03:04:59 Training - Downloading the training image...
2021-09-03 03:05:23 Training - Training image download completed. Training in progress.2021-09-03 03:05:23.966037: W tensorflow/core/profiler/internal/smprofiler_timeline.cc:460] Initializing the SageMaker Profiler.
2021-09-03 03:05:23.969704: W tensorflow/core/profiler/internal/smprofiler_timeline.cc:105] SageMaker Profiler is not enabled. The timeline writer thread will not be started, future recorded events will be dropped.
2021-09-03 03:05:24.118054: W tensorflow/core/profiler/internal/smprofiler_timeline.cc:460] Initializing the SageMaker Profiler.
2021-09-03 03:05:26,842 sagemaker-training-toolkit INFO Imported framework sagemaker_tensorflow_container.training
2021-09-03 03:05:26,852 sagemaker-training-toolkit INFO No GPUs detected (normal if no gpus installed)
2021-09-03 03:05:27,734 sagemaker-training-toolkit INFO Installing dependencies from requirements.txt:
/usr/local/bin/python3.7 -m pip install -r requirements.txt
WARNING: You are using pip version 21.0.1; however, version 21.2.4 is available.
You should consider upgrading via the '/usr/local/bin/python3.7 -m pip install --upgrade pip' command.
2021-09-03 03:05:29,028 sagemaker-training-toolkit INFO No GPUs detected (normal if no gpus installed)
2021-09-03 03:05:29,045 sagemaker-training-toolkit INFO No GPUs detected (normal if no gpus installed)
2021-09-03 03:05:29,062 sagemaker-training-toolkit INFO No GPUs detected (normal if no gpus installed)
2021-09-03 03:05:29,072 sagemaker-training-toolkit INFO Invoking user script
Training Env:
{
"additional_framework_parameters": {},
"channel_input_dirs": {},
"current_host": "algo-1",
"framework_module": "sagemaker_tensorflow_container.training:main",
"hosts": [
"algo-1"
],
"hyperparameters": {
"batch-size": 64,
"epochs": 2
},
"input_config_dir": "/opt/ml/input/config",
"input_data_config": {},
"input_dir": "/opt/ml/input",
"is_master": true,
"job_name": "fashion-mnist-2021-09-03-03-02-02-305",
"log_level": 20,
"master_hostname": "algo-1",
"model_dir": "/opt/ml/model",
"module_dir": "s3://sagemaker-us-east-1-316725000538/fashion-mnist-2021-09-03-03-02-02-305/source/sourcedir.tar.gz",
"module_name": "fashion_mnist",
"network_interface_name": "eth0",
"num_cpus": 4,
"num_gpus": 0,
"output_data_dir": "/opt/ml/output/data",
"output_dir": "/opt/ml/output",
"output_intermediate_dir": "/opt/ml/output/intermediate",
"resource_config": {
"current_host": "algo-1",
"hosts": [
"algo-1"
],
"network_interface_name": "eth0"
},
"user_entry_point": "fashion_mnist.py"
}
Environment variables:
SM_HOSTS=["algo-1"]
SM_NETWORK_INTERFACE_NAME=eth0
SM_HPS={"batch-size":64,"epochs":2}
SM_USER_ENTRY_POINT=fashion_mnist.py
SM_FRAMEWORK_PARAMS={}
SM_RESOURCE_CONFIG={"current_host":"algo-1","hosts":["algo-1"],"network_interface_name":"eth0"}
SM_INPUT_DATA_CONFIG={}
SM_OUTPUT_DATA_DIR=/opt/ml/output/data
SM_CHANNELS=[]
SM_CURRENT_HOST=algo-1
SM_MODULE_NAME=fashion_mnist
SM_LOG_LEVEL=20
SM_FRAMEWORK_MODULE=sagemaker_tensorflow_container.training:main
SM_INPUT_DIR=/opt/ml/input
SM_INPUT_CONFIG_DIR=/opt/ml/input/config
SM_OUTPUT_DIR=/opt/ml/output
SM_NUM_CPUS=4
SM_NUM_GPUS=0
SM_MODEL_DIR=/opt/ml/model
SM_MODULE_DIR=s3://sagemaker-us-east-1-316725000538/fashion-mnist-2021-09-03-03-02-02-305/source/sourcedir.tar.gz
SM_TRAINING_ENV={"additional_framework_parameters":{},"channel_input_dirs":{},"current_host":"algo-1","framework_module":"sagemaker_tensorflow_container.training:main","hosts":["algo-1"],"hyperparameters":{"batch-size":64,"epochs":2},"input_config_dir":"/opt/ml/input/config","input_data_config":{},"input_dir":"/opt/ml/input","is_master":true,"job_name":"fashion-mnist-2021-09-03-03-02-02-305","log_level":20,"master_hostname":"algo-1","model_dir":"/opt/ml/model","module_dir":"s3://sagemaker-us-east-1-316725000538/fashion-mnist-2021-09-03-03-02-02-305/source/sourcedir.tar.gz","module_name":"fashion_mnist","network_interface_name":"eth0","num_cpus":4,"num_gpus":0,"output_data_dir":"/opt/ml/output/data","output_dir":"/opt/ml/output","output_intermediate_dir":"/opt/ml/output/intermediate","resource_config":{"current_host":"algo-1","hosts":["algo-1"],"network_interface_name":"eth0"},"user_entry_point":"fashion_mnist.py"}
SM_USER_ARGS=["--batch-size","64","--epochs","2"]
SM_OUTPUT_INTERMEDIATE_DIR=/opt/ml/output/intermediate
SM_HP_BATCH-SIZE=64
SM_HP_EPOCHS=2
PYTHONPATH=/opt/ml/code:/usr/local/bin:/usr/local/lib/python37.zip:/usr/local/lib/python3.7:/usr/local/lib/python3.7/lib-dynload:/usr/local/lib/python3.7/site-packages
Invoking script with the following command:
/usr/local/bin/python3.7 fashion_mnist.py --batch-size 64 --epochs 2
TensorFlow version: 2.3.1
Eager execution is: True
Keras version: 2.4.0
---------- key/value args
epochs:2
batch_size:64
model_dir:/opt/ml/model
output_dir:/opt/ml/output
【讨论】: