自动化机器学习工作流:从AWS CDK到Step Functions
1. 使用AWS CDK自动化
AWS CDK是一个多语言SDK,允许你通过编写代码来定义AWS基础设施(https://github.com/aws/aws-cdk )。借助CDK CLI,你可以在底层使用CloudFormation来配置这些基础设施。
1.1 安装CDK
CDK原生使用Node.js实现,因此请确保你的机器上安装了npm工具(https://www.npmjs.com/get-npm )。安装CDK非常简单,只需执行以下命令:
$ npm i -g aws-cdk
$ cdk --version
1.114.0 (build 7e41b6b)
1.2 创建CDK应用程序
我们将部署与使用CloudFormation部署的相同模型。这里使用Python,当然你也可以使用JavaScript、TypeScript、Java和.NET。API文档可在https://docs.aws.amazon.com/cdk/api/latest/python/ 查看。具体步骤如下:
1. 创建一个名为endpoint的Python应用程序:
$ mkdir cdk
$ cd cdk
$ cdk init --language python --app endpoint
- 这将自动创建一个虚拟环境,我们需要激活它:
$ source .venv/bin/activate
- 同时会创建一个默认的app.py文件用于编写CDK代码,一个cdk.json文件用于应用程序配置,以及一个requirements.txt文件用于安装依赖项。我们将使用GitHub仓库中的文件。
- 在requirements.txt文件中,安装S3和SageMaker的CDK包。每个服务需要不同的包,例如,为S3添加aws_cdk.aws_s3:
-e .
aws_cdk.aws_s3
aws_cdk.aws_sagemaker
- 像往常一样安装依赖项:
$ pip install -r requirements.txt
- 在cdk.json文件中,存储应用程序上下文,即应用程序可以读取的键值对,用于配置(https://docs.aws.amazon.com/cdk/latest/guide/context.html ):
{
"app": "python3 app.py",
"context": {
"role_arn": "arn:aws:iam::123456789012:role/Sagemaker-fullaccess",
"model_name": "tf2-fmnist",
"epc_name": "tf2-fmnist-epc",
"ep_name": "tf2-fmnist-ep",
"image": "763104351884.dkr.ecr.us-east-1.amazonaws.com/tensorflow-inference:2.1-cpu",
"model_data_url": "s3://sagemaker-us-east-1-123456789012/keras2-fashion-mnist/output/tensorflow-training-2020-06-08-07-46-04-367/output/model.tar.gz",
"instance_type": "ml.t2.xlarge",
"instance_count": 1
}
}
这是向应用程序传递值的首选方式。你应该使用版本控制来管理此文件,以便跟踪堆栈的构建方式。
7. 可以使用
cdk context
命令查看应用程序的上下文。
1.3 编写CDK应用程序
所有代码都放在app.py文件中,具体实现步骤如下:
1. 导入所需的包:
import time
from aws_cdk import (
aws_sagemaker as sagemaker,
core
)
-
扩展
core.Stack类来创建我们自己的堆栈:
class SagemakerEndpoint(core.Stack):
def __init__(self, app: core.App, id: str, **kwargs) -> None:
timestamp = '-'+time.strftime("%Y-%m-%d-%H-%M-%S",time.gmtime())
super().__init__(app, id, **kwargs)
- 添加一个CfnModel对象,读取适当的上下文值:
model = sagemaker.CfnModel(
scope = self,
id="my_model",
execution_role_arn= self.node.try_get_context("role_arn"),
containers=[{
"image": self.node.try_get_context("image"),
"modelDataUrl": self.node.try_get_context("model_data_url")
}],
model_name= self.node.try_get_context("model_name")+timestamp
)
-
添加一个CfnEndpointConfig对象,使用内置的
get_att()函数将其与模型关联起来。这会创建一个依赖关系,CloudFormation将使用该关系按正确顺序构建资源:
epc = sagemaker.CfnEndpointConfig(
scope=self,
id="my_epc",
production_variants=[{
"modelName": core.Fn.get_att(model.logical_id, 'ModelName').to_string(),
"variantName": "variant-1",
"initialVariantWeight": 1.0,
"initialInstanceCount": 1,
"instanceType": self.node.try_get_context("instance_type")
}],
endpoint_config_name=self.node.try_get_context("epc_name")+timestamp
)
-
添加一个CfnEndpoint对象,使用内置的
get_att()函数将其与端点配置关联起来:
ep = sagemaker.CfnEndpoint(
scope=self,
id="my_ep",
endpoint_config_name=core.Fn.get_att(epc.logical_id, 'EndpointConfigName').to_string(),
endpoint_name=self.node.try_get_context("ep_name")+timestamp
)
- 最后,实例化应用程序:
app = core.App()
SagemakerEndpoint(
app,
"SagemakerEndpoint",
env={'region': 'eu-west-1'}
)
app.synth()
1.4 部署CDK应用程序
现在可以部署端点了,具体步骤如下:
1. 列出可用的堆栈:
$ cdk list
SagemakerEndpointEU
- 查看实际的CloudFormation模板,它应该与上一节中编写的模板非常相似:
$ cdk synth SagemakerEndpointEU
- 部署堆栈同样简单。
- 查看CloudFormation,会发现堆栈是使用变更集创建的。几分钟后,端点即可投入使用。
- 编辑app.py,将初始实例计数设置为2。然后要求CDK部署堆栈,但不执行变更集。
-
如果你对变更集满意,可以在CloudFormation控制台中执行它,或者再次运行之前的命令,但不使用
--no-execute。如预期的那样,端点将被更新。 - 完成后,可以销毁堆栈:
$ cdk destroy SagemakerEndpointEU
1.5 工作流示意图
graph LR
A[安装CDK] --> B[创建CDK应用程序]
B --> C[编写CDK应用程序]
C --> D[部署CDK应用程序]
2. 使用AWS Step Functions构建端到端工作流
AWS Step Functions允许你基于状态机定义和运行工作流(https://aws.amazon.com/step-functions/ )。状态机是步骤的组合,这些步骤可以是顺序的、并行的或有条件的。每个步骤从其前一个步骤接收输入,执行操作,并将输出传递给其后一个步骤。Step Functions与许多AWS服务集成,如Amazon SageMaker、AWS Lambda、容器服务、Amazon DynamoDB、Amazon EMR、AWS Glue等。
状态机可以使用JSON和Amazon States Language定义,并且可以在服务控制台中可视化。状态机的执行是完全托管的,因此你无需配置任何基础设施来运行。
对于SageMaker,Step Functions有一个专门的Python SDK,名为Data Science SDK(https://github.com/aws/aws-step-functions-data-science-sdk-python )。
2.1 设置权限
首先,请确保你的用户或笔记本实例的IAM角色具有调用Step Functions API的权限。如果没有,请将
AWSStepFunctionsFullAccess
托管策略添加到该角色。
然后,需要为Step Functions创建一个服务角色,允许它代表我们调用AWS API,具体步骤如下:
1. 从IAM控制台(https://console.aws.amazon.com/iam/home#/roles )开始,点击“Create role”。
2. 选择AWS服务和Step Functions。
3. 点击后续屏幕,直到可以输入角色名称。将其命名为
StepFunctionsWorkflowExecutionRole
,然后点击“Create role”。
4. 选择此角色,点击其“Permission”选项卡,然后点击“Add inline policy”。
5. 选择JSON选项卡,将空策略替换为
Chapter12/step_functions/service-role-policy.json
文件的内容,然后点击“Review policy”。
6. 将策略命名为
StepFunctionsWorkflowExecutionPolicy
,然后点击“Create policy”。
7. 记录角色的ARN,然后关闭IAM控制台。
2.2 实现第一个工作流
在这个工作流中,我们将按以下步骤顺序进行:训练模型、创建模型、使用模型进行批量转换、创建端点配置,并将模型部署到端点。具体步骤如下:
1. 将训练集和去除目标属性的测试集上传到S3,后者用于批量转换:
import sagemaker
import pandas as pd
sess = sagemaker.Session()
bucket = sess.default_bucket()
prefix = 'sklearn-boston-housing-stepfunc'
training_data = sess.upload_data(
path='housing.csv',
key_prefix=prefix + "/training")
data = pd.read_csv('housing.csv')
data.drop(['medv'], axis=1, inplace=True)
data.to_csv('test.csv', index=False, header=False)
batch_data = sess.upload_data(
path='test.csv',
key_prefix=prefix + "/batch")
- 像往常一样配置估算器:
from sagemaker.sklearn import SKLearn
output = 's3://{}/{}/output/'.format(bucket,prefix)
sk = SKLearn(
entry_point='sklearn-boston-housing.py',
role=sagemaker.get_execution_role(),
framework_version='0.23-1',
train_instance_count=1,
train_instance_type='ml.m5.large',
output_path=output,
hyperparameters={
'normalize': True,
'test-size': 0.1
}
)
- 定义用于批量转换的转换器:
sk_transformer = sk.transformer(
instance_count=1,
instance_type='ml.m5.large')
- 导入工作流所需的Step Functions对象。API文档可在https://aws-step-functions-data-science-sdk.readthedocs.io/en/latest/ 查看:
import stepfunctions
from stepfunctions import steps
from stepfunctions.steps import TrainingStep, ModelStep, TransformStep
from stepfunctions.inputs import ExecutionInput
from stepfunctions.workflow import Workflow
- 定义工作流的输入,传递训练作业名称、模型名称和端点名称:
execution_input = ExecutionInput(schema={
'JobName': str,
'ModelName': str,
'EndpointName': str}
)
- 工作流的第一步是训练步骤,传递估算器、S3中数据集的位置和训练作业名称:
from sagemaker.inputs import TrainingInput
training_step = TrainingStep(
'Train Scikit-Learn on the Boston Housing dataset',
estimator=sk,
data={'training': TrainingInput(training_data,content_type='text/csv')},
job_name=execution_input['JobName']
)
- 下一步是模型创建步骤,传递上一步训练的模型的位置和模型名称:
model_step = ModelStep(
'Create the model in SageMaker',
model=training_step.get_expected_model(),
model_name=execution_input['ModelName']
)
- 接下来是对测试数据集进行批量转换的步骤,传递转换器对象、S3中测试数据集的位置及其内容类型:
transform_step = TransformStep(
'Transform the dataset in batch mode',
transformer=sk_transformer,
job_name=execution_input['JobName'],
model_name=execution_input['ModelName'],
data=batch_data,
content_type='text/csv'
)
- 下一步是创建端点配置:
endpoint_config_step = EndpointConfigStep(
"Create an endpoint configuration for the model",
endpoint_config_name=execution_input['ModelName'],
model_name=execution_input['ModelName'],
initial_instance_count=1,
instance_type='ml.m5.large'
)
- 最后一步是创建端点:
endpoint_step = EndpointStep(
"Create an endpoint hosting the model",
endpoint_name=execution_input['EndpointName'],
endpoint_config_name=execution_input['ModelName']
)
- 定义好所有步骤后,按顺序将它们链接起来:
workflow_definition = Chain([
training_step,
model_step,
transform_step,
endpoint_config_step,
endpoint_step
])
- 使用工作流定义和输入定义构建工作流:
import time
timestamp = time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
workflow_execution_role = "arn:aws:iam::0123456789012:role/StepFunctionsWorkflowExecutionRole"
workflow = Workflow(
name='sklearn-boston-housing-workflow1-{}'.format(timestamp),
definition=workflow_definition,
role=workflow_execution_role,
execution_input=execution_input
)
- 可视化状态机,这是检查是否按预期构建的简单方法:
workflow.render_graph(portrait=True)
- 创建工作流:
workflow.create()
- 在Step Functions控制台中可以看到它,既可以看到其图形表示,也可以看到基于Amazon States Language的JSON定义。如有需要,还可以编辑工作流。
- 运行工作流:
execution = workflow.execute(
inputs={
'JobName': 'sklearn-boston-housing-{}'.format(timestamp),
'ModelName': 'sklearn-boston-housing-{}'.format(timestamp),
'EndpointName': 'sklearn-boston-housing-{}'.format(timestamp)
}
)
-
可以使用
render_progress()和list_events()API跟踪其进度,也可以在控制台中查看。注意,还可以看到每个步骤的输入和输出,这是排查问题的好方法。 - 工作流完成后,可以像往常一样测试端点。完成后,别忘了在SageMaker控制台中删除它。
2.3 工作流步骤表格
| 步骤 | 描述 |
|---|---|
| 训练步骤 | 使用估算器在指定数据集上进行训练 |
| 模型创建步骤 | 根据训练结果创建模型 |
| 批量转换步骤 | 对测试数据集进行批量转换 |
| 端点配置步骤 | 创建端点配置 |
| 端点创建步骤 | 将模型部署到端点 |
2.4 工作流示意图
graph LR
A[设置权限] --> B[实现工作流]
B --> C[训练模型]
C --> D[创建模型]
D --> E[批量转换]
E --> F[创建端点配置]
F --> G[部署到端点]
3. 为工作流添加并行执行
接下来要构建的工作流,其步骤与之前相同,只是修改了步骤的链接方式。
3.1 分支定义
工作流有两个分支,一个用于批量转换,一个用于端点:
batch_branch = Chain([
transform_step
])
endpoint_branch = Chain([
endpoint_config_step,
endpoint_step
])
3.2 创建并行步骤
创建一个并行步骤,以允许这两个分支并行执行:
parallel_step = Parallel('Parallel execution')
parallel_step.add_branch(batch_branch)
parallel_step.add_branch(endpoint_branch)
3.3 整合工作流
将所有步骤整合在一起:
workflow_definition = Chain([
training_step,
model_step,
parallel_step
])
现在可以像之前的示例一样创建和运行这个工作流。查看Step Functions控制台,会发现工作流确实会并行运行这两个分支。不过存在一个小问题,端点创建步骤显示已完成,但端点仍在创建中。在SageMaker控制台中可以看到端点状态为“Creating”,如果客户端应用程序在工作流完成后立即尝试调用端点,可能会出现问题。
3.4 并行工作流示意图
graph LR
A[训练步骤] --> B[模型创建步骤]
B --> C{并行步骤}
C --> D[批量转换分支]
C --> E[端点创建分支]
4. 向工作流添加Lambda函数
AWS Lambda是无服务器架构的核心,你可以编写和部署在完全托管的基础设施上运行的短函数。这些函数可以由各种AWS事件触发,也可以按需调用。
4.1 设置权限
创建Lambda函数的前提是创建一个执行角色,即一个IAM角色,该角色授予函数调用其他AWS服务的权限。这里只需要
DescribeEndpoint
API的权限以及在CloudWatch中创建日志的权限。使用boto3 API进行操作,具体步骤如下:
1. 定义角色的信任策略,允许Lambda服务承担该角色:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}
- 创建角色并附加信任策略:
import boto3
iam = boto3.client('iam')
with open('trust-policy.json') as f:
policy = f.read()
role_name = 'lambda-role-sagemaker-describe-endpoint'
response = iam.create_role(
RoleName=role_name,
AssumeRolePolicyDocument=policy,
Description='Allow function to invoke all SageMaker APIs'
)
role_arn = response['Role']['Arn']
- 定义允许的API列表的策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sagemaker:DescribeEndpoint",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
- 创建策略并将其添加到角色:
with open('policy.json') as f:
policy = f.read()
policy_name = 'Sagemaker-describe-endpoint'
response = iam.create_policy(
PolicyName=policy_name,
PolicyDocument=policy,
Description='Allow the DescribeEndpoint API'
)
policy_arn = response['Policy']['Arn']
response = iam.attach_role_policy(
RoleName=role_name,
PolicyArn=policy_arn
)
4.2 编写Lambda函数
编写一个短的Lambda函数,它接收一个JSON事件作为输入,该事件存储了由
EndpointStep
步骤创建的端点的ARN。函数从ARN中提取端点名称,创建一个boto3等待器,并等待端点投入使用。
# lambda.py
import boto3
def lambda_handler(event, context):
endpoint_arn = event['EndpointArn']
endpoint_name = endpoint_arn.split('/')[-1]
sagemaker_client = boto3.client('sagemaker')
waiter = sagemaker_client.get_waiter('endpoint_in_service')
waiter.wait(EndpointName=endpoint_name)
return {
'statusCode': 200,
'body': f'Endpoint {endpoint_name} is in service.'
}
4.3 部署Lambda函数
- 创建Lambda函数的部署包并上传到S3:
$ zip -9 lambda.zip lambda.py
$ aws s3 cp lambda.zip s3://my-bucket
- 创建函数,设置超时时间为15分钟(Lambda函数的最长运行时间):
lambda_client = boto3.client('lambda')
response = lambda_client.create_function(
FunctionName='sagemaker-wait-for-endpoint',
Role=role_arn,
Runtime='python3.6',
Handler='lambda.lambda_handler',
Code={
'S3Bucket': bucket_name,
'S3Key': 'lambda.zip'
},
Description='Wait for endpoint to be in service',
Timeout=900,
MemorySize=128
)
4.4 将Lambda函数添加到工作流
定义一个Lambda步骤并将其添加到端点分支,其有效负载是从
EndpointStep
输出中提取的端点ARN:
lambda_step = LambdaStep(
'Wait for endpoint to be in service',
parameters={
'FunctionName': 'sagemaker-wait-for-endpoint',
'Payload': {"EndpointArn.$": "$.EndpointArn"}
},
timeout_seconds=900
)
endpoint_branch = steps.Chain([
endpoint_config_step,
endpoint_step,
lambda_step
])
再次运行工作流,可以看到新步骤接收端点ARN作为输入,并等待端点投入使用。
4.5 包含Lambda函数的工作流示意图
graph LR
A[训练步骤] --> B[模型创建步骤]
B --> C{并行步骤}
C --> D[批量转换分支]
C --> E[端点创建分支]
E --> F[等待端点就绪(Lambda)]
通过以上步骤,我们可以看到如何利用AWS CDK和Step Functions实现机器学习工作流的自动化,并且可以根据需求灵活调整工作流的执行方式,如添加并行执行和使用Lambda函数进行额外的控制和等待。
超级会员免费看
21

被折叠的 条评论
为什么被折叠?



