31、优化机器学习模型部署:多模型端点、弹性推理与模型编译

优化机器学习模型部署:多模型端点、弹性推理与模型编译

1. 多模型端点部署

在处理大量模型时,将每个模型部署到单独的端点可能并不合理。例如,一家SaaS公司为其10,000个客户分别构建回归模型,管理和支付10,000个端点的成本是巨大的。此时,多模型端点就派上了用场。

1.1 多模型端点理解

多模型端点可以基于CPU为存储在S3中的任意数量的模型提供预测服务(撰写本文时暂不支持GPU)。在每个预测请求中传递要使用的模型工件的路径。模型会根据使用情况和端点上的可用内存动态加载和卸载。通过在S3中复制或删除工件,还可以向端点添加或删除模型。

为了服务多个模型,推理容器必须实现一组特定的API,端点将调用这些API,包括:LOAD MODEL、LIST MODEL、GET MODEL、UNLOAD MODEL和INVOKE MODEL。详情可查看:https://docs.aws.amazon.com/sagemaker/latest/dg/mms-container-apis.html。

目前,最新的scikit - learn、TensorFlow、Apache MXNet和PyTorch内置容器原生支持这些API。XGBoost、kNN、线性学习器和随机切割森林等内置算法也支持。对于其他算法和框架,最佳选择是构建包含SageMaker推理工具包的自定义容器,因为它已经实现了所需的API(https://github.com/aws/sagemaker - inference - toolkit)。该工具包基于多模型服务器(https://github.com/awslabs/multi - model - server),也可以直接从CLI使用它来为多个模型提供预测服务。更多信息可查看:https://docs.aws.amazon.com/sagemaker/latest/dg/build - multi - model - build - container.html。

1.2 使用scikit - learn构建多模型端点

以下是使用scikit - learn构建多模型端点的步骤,该端点托管在波士顿住房数据集上训练的模型,此功能仅支持scikit - learn 0.23 - 1及以上版本:
1. 上传数据集到S3

import sagemaker, boto3
sess = sagemaker.Session()
bucket = sess.default_bucket()
prefix = 'sklearn - boston - housing - mme'
training = sess.upload_data(path='housing.csv', 
                            key_prefix=prefix + 
                            '/training')
output = 's3://{}/{}/output/'.format(bucket,prefix)
  1. 使用不同的测试大小训练三个模型,并将它们的名称存储在字典中
from sagemaker.sklearn import SKLearn
jobs = {}
for test_size in [0.2, 0.1, 0.05]:
    sk = SKLearn(entry_point=
                'sklearn - boston - housing.py',
        role=sagemaker.get_execution_role(),
        framework_version='0.23 - 1',
        instance_count=1,
        instance_type='ml.m5.large',
        output_path=output,
        hyperparameters={ 'normalize': True,
                          'test - size': test_size }
    )
    sk.fit({'training':training}, wait=False)
    jobs[sk.latest_training_job.name] = {}
    jobs[sk.latest_training_job.name]['test - size'] = test_size
  1. 查找模型工件的S3 URI及其前缀
import boto3
sm = boto3.client('sagemaker')
for j in jobs.keys():
    job = sm.describe_training_job(TrainingJobName=j)
    jobs[j]['artifact'] = job['ModelArtifacts']['S3ModelArtifacts']
    jobs[j]['key'] = '/'.join(
        job['ModelArtifacts']['S3ModelArtifacts']
        .split('/')[3:])
  1. 删除S3中之前存储的任何模型
%%sh -s "$bucket" "$prefix"
aws s3 rm --recursive s3://$1/$2/models
  1. 将三个模型工件复制到指定位置
s3 = boto3.client('s3')
for j in jobs.keys():
    copy_source = { 'Bucket': bucket, 
                    'Key': jobs[j]['key'] }
    s3.copy_object(CopySource=copy_source,  
                   Bucket=bucket, 
                   Key=prefix+'/models/'+j+'.tar.gz')
response = s3.list_objects(Bucket=bucket, 
                           Prefix=prefix+'/models/')
for o in response['Contents']:
    print(o['Key'])

列出的模型工件如下:

sklearn - boston - housing - mme/models/sagemaker - scikit - learn - 2021 - 09 - 01 - 07 - 52 - 22 - 679
sklearn - boston - housing - mme/models/sagemaker - scikit - learn - 2021 - 09 - 01 - 07 - 52 - 26 - 399
sklearn - boston - housing - mme/models/sagemaker - scikit - learn - 2021 - 09 - 01 - 08 - 05 - 33 - 229
  1. 定义脚本名称和将上传代码存档的S3位置
script = 'sklearn - boston - housing.py'
script_archive = 's3://{}/{}/source/source.tar.gz'.format(bucket, prefix)
  1. 创建代码存档并将其上传到S3
%%sh -s "$script" "$script_archive"
tar cvfz source.tar.gz $1
aws s3 cp source.tar.gz $2
  1. 使用create_model() API创建多模型端点并相应设置Mode参数
import time
model_name = prefix+'-'+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
response = sm.create_model(
  ModelName = model_name,
  ExecutionRoleArn = role,
  Containers = [{
    'Image': sk.image_uri,
    'ModelDataUrl':'s3://{}/{}/models/'.format(bucket, 
                    prefix),
    'Mode': 'MultiModel',
    'Environment': {
        'SAGEMAKER_PROGRAM' : script,
        'SAGEMAKER_SUBMIT_DIRECTORY' : script_archive
    }
  }]
)
  1. 像往常一样创建端点配置
epc_name = prefix+'-epc'+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
response = sm.create_endpoint_config(
    EndpointConfigName = epc_name,
    ProductionVariants=[{
        'InstanceType': 'ml.m5.large',
        'InitialInstanceCount': 1,
        'InitialVariantWeight': 1,
        'ModelName': model_name,
        'VariantName': 'variant - 1'}]
)
  1. 像往常一样创建端点
ep_name = prefix+'-ep'+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
response = sm.create_endpoint(
    EndpointName=ep_name,
    EndpointConfigName=epc_name)
  1. 端点投入使用后,从数据集中加载样本并将其转换为numpy数组
import pandas as pd
import numpy as np
from io import BytesIO
data = pd.read_csv('housing.csv')
payload = data[:10].drop(['medv'], axis=1)
buffer = BytesIO()
np.save(buffer, payload.values)
  1. 使用所有三个模型对这些样本进行预测,在每个预测请求中传递要使用的模型名称
smrt = boto3.client('runtime.sagemaker')
for j in jobs.keys():
    model_name=j+'.tar.gz'
    response = smrt.invoke_endpoint(
        EndpointName=ep_name,
        TargetModel=model_name,
        Body=buffer.getvalue(),
        ContentType='application/x - npy')
    print(response['Body'].read())
  1. 可以训练更多模型,将其工件复制到相同的S3位置,无需重新创建端点即可直接使用。也可以删除不需要的模型
  2. 完成后,删除端点
sm.delete_endpoint(EndpointName=ep_name)
sm.delete_endpoint_config(EndpointConfigName=epc_name)
2. 使用Amazon Elastic Inference部署模型

在部署模型时,需要决定是在CPU实例还是GPU实例上运行。有些算法无法从GPU加速中受益,应部署到CPU实例;而计算机视觉或自然语言处理等复杂的深度学习模型在GPU上运行效果最佳。但在很多情况下,情况并非如此明确。首先要了解应用程序的最大预测延迟。例如,实时广告技术应用中预测点击率时,每毫秒都很重要;而在后台办公应用中预测客户流失率时,对延迟的要求则没那么高。此外,即使可以从GPU加速中受益的模型,也可能不够大或复杂,无法充分利用现代GPU上的数千个核心。在这种情况下,部署在CPU上可能速度不够,而部署在GPU上又不划算。

2.1 Amazon Elastic Inference简介

Amazon Elastic Inference旨在解决这个问题(https://aws.amazon.com/machine - learning/elastic - inference/)。它允许将部分GPU加速功能附加到任何EC2实例,包括笔记本实例和端点实例。加速器有三种不同的尺寸(中、大、特大),可以为应用程序找到最佳的性价比。

Elastic Inference适用于TensorFlow、PyTorch和Apache MXNet。借助深度学习AMI中可用的AWS扩展,可以在运行于EC2实例上的代码中使用它,也可以与深度学习容器一起使用。更多信息可查看:https://docs.aws.amazon.com/elastic - inference/latest/developerguide/working - with - ei.html。

当然,Elastic Inference也可在SageMaker上使用。可以在创建笔记本实例时附加加速器,并使用内置的conda环境。也可以将加速器附加到端点,下面通过示例展示如何操作。

2.2 使用Amazon Elastic Inference部署模型示例

我们复用之前在猫狗图像上训练的图像分类模型,该模型基于18层ResNet模型,在卷积神经网络中算是比较小的:
1. 模型训练完成后,将其像往常一样部署到两个端点 :一个由ml.c5.large实例支持,另一个由ml.g4dn.xlarge实例支持(SageMaker上最具成本效益的GPU实例):

import time
endpoint_name = 'c5 - '+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
c5_predictor = ic.deploy(initial_instance_count=1,
                         instance_type='ml.c5.large',
                         endpoint_name=endpoint_name,
                         wait=False)
endpoint_name = 'g4 - '+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
g4_predictor = ic.deploy(
    initial_instance_count=1,
    instance_type='ml.g4dn.xlarge',
    endpoint_name=endpoint_name,
    wait=False)
  1. 下载测试图像,对其进行1000次预测,并测量总时间
with open(file_name, 'rb') as f:
    payload = f.read()
    payload = bytearray(payload)
def predict_images(predictor, iterations=1000):
    total = 0
    for i in range(0, iterations):
        tick = time.time()
        response = runtime.invoke_endpoint(
            EndpointName=predictor.endpoint_name,                                 
            ContentType='application/x - image',
            Body=payload)
        tock = time.time()
        total += tock - tick
    return total/iterations
predict_images(c5_predictor)
predict_images(g4_predictor)
  1. 结果如下表所示(us - east - 1价格)
    | 实例类型 | 速度 | 成本 |
    | ---- | ---- | ---- |
    | ml.c5.large | 相对较慢 | 低 |
    | ml.g4dn.xlarge | 约为CPU实例的两倍快 | 高 |

可以看出,GPU实例速度约为CPU实例的两倍,但CPU实例更具成本效益,其成本不到GPU实例的四分之一。也就是说,可以用四个CPU实例代替一个GPU实例运行端点,在相同成本下获得更高的吞吐量。这表明了解应用程序的延迟要求非常重要,“快”和“慢”是相对的概念。

  1. 将相同的模型部署到另外三个由ml.c5.large实例支持的端点,并分别使用中、大、特大的Elastic Inference加速器进行加速 。只需在deploy() API中添加一个额外的参数。以下是中型端点的代码:
endpoint_name = 'c5 - medium - '+time.strftime("%Y-%m-%d-%H-%M-%S", time.gmtime())
c5_medium_predictor = ic.deploy(
    initial_instance_count=1,
    instance_type='ml.c5.large',
    accelerator_type='ml.eia2.medium',
    endpoint_name=endpoint_name,
    wait=False)
predict_images(c5_medium_predictor)

与裸CPU端点相比,速度最多可提高20%,且成本低于使用GPU实例。

  1. 考虑数据类型对模型的影响 :之前的表格中包含了32位和16位浮点值的teraFLOP值。实际上,可以使用这两种数据类型之一来存储模型参数。查看图像分类算法的文档,发现可以使用precision_dtype参数选择数据类型,默认值为float32。那么,在float16模式下训练模型,结果会有不同吗?我们来测试一下:
ic.set_hyperparameters(
    num_layers=18,                       
    use_pretrained_model=0,
    num_classes=2,
    num_training_samples=22500,
    mini_batch_size=128,
    precision_dtype='float16',
    epochs=10)                   
  1. 再次训练模型,发现与float32模式下的模型准确率相同。再次进行部署和基准测试,得到以下结果
    在裸实例上没有明显差异。在大型和特大型加速器上使用FP - 16模型进行预测,与FP - 32模型相比,速度可提高约10%。与裸CPU实例相比,性能有了很大提升,且与GPU实例相比更具成本效益。实际上,将单个端点实例从ml.g4dn.xlarge切换到ml.c5.large + ml.eia2.large,每月可节省($0.736 - $0.438) x 24 x 30 = $214美元。

综上所述,Amazon Elastic Inference非常易于使用,为模型部署提供了更多选择。一旦确定了应用程序的预测延迟要求,就可以快速进行实验,找到最佳的性价比。

下面我们将介绍另一个SageMaker功能:Amazon Neo,它可以为特定的硬件架构编译模型。

3. 使用Amazon SageMaker Neo编译模型

嵌入式软件开发人员早已学会如何编写高度优化的代码,既能快速运行又能节省硬件资源。理论上,同样的技术也可应用于优化机器学习预测。但实际上,由于机器学习库和模型的复杂性,这是一项艰巨的任务。Amazon SageMaker Neo旨在解决这个问题。

3.1 理解Amazon SageMaker Neo

Amazon Neo有两个组件:一个模型编译器,用于为底层硬件优化模型;一个名为深度学习运行时(DLR)的小型运行时,用于加载优化后的模型并进行预测(https://aws.amazon.com/sagemaker/neo)。

Amazon SageMaker Neo可以编译使用以下算法和框架训练的模型:
- 内置算法 :XGBoost和图像分类。
- 内置框架 :TensorFlow、PyTorch和Apache MXNet,以及ONNX格式的模型。支持许多运算符,完整列表可查看:https://aws.amazon.com/releasenotes/sagemaker - neo - supported - frameworks - and - operators。

训练过程与往常一样,使用选择的估计器。然后,使用compile_model() API,可以轻松为以下硬件目标之一编译模型:
- Amazon EC2实例家族 :c4、c5、m4、m5、p2、p3和inf1(本章后面会讨论),以及Lambda。
- AI相机 :AWS DeepLens和Acer aiSage。
- NVIDIA Jetson平台 :TX1、TX2、Nano和Xavier。
- Raspberry Pi
- 来自Rockchip、Qualcomm、Ambarella等的片上系统平台

模型编译会进行架构优化(如融合层)和代码优化(用硬件优化版本替换机器学习运算符)。生成的工件存储在S3中,包含原始模型及其优化形式。

DLR用于加载模型并进行预测。当然,它也可以独立使用,例如在Raspberry Pi上。安装说明可查看:https://neo - ai - dlr.readthedocs.io。由于DLR是开源的(https://github.com/neo - ai/neo - ai - dlr),还可以从源代码构建它,并根据自己的硬件平台进行定制!

在SageMaker中使用DLR时,事情要简单得多。SageMaker提供了支持Neo的内置容器,应该使用这些容器来部署使用Neo编译的模型(如前所述,训练容器保持不变)。支持Neo的容器列表可查看:https://docs.aws.amazon.com/sagemaker/latest/dg/neo - deployment - hosting - services - cli.html。

最后,DLR的一个优点是其体积小。例如,p2和p3实例的Python包只有5.4 MB,比典型的深度学习库及其依赖项小几个数量级。这对于嵌入式环境显然至关重要,在SageMaker上也很受欢迎,因为容器也会更小。

3.2 在SageMaker上编译和部署图像分类模型

为了让Neo有更多的工作可做,这次我们训练一个50层的ResNet模型。然后编译它,将其部署到端点,并与原始模型进行比较:
1. 将num_layers设置为50,训练模型30个周期。然后像往常一样将其部署到ml.c5.4xlarge实例

ic_predictor = ic.deploy(initial_instance_count=1,
    instance_type='ml.c5.4xlarge',                         
    endpoint_name=ic_endpoint_name)
  1. 使用Neo编译模型,目标是EC2 c5实例家族。还定义模型的输入形状:一张图像,三个通道(红、绿、蓝),224 x 224像素(图像分类算法的默认值)。由于内置算法是用Apache MXNet实现的,相应地设置框架
output_path = 's3://{}/{}/output - neo/'.format(bucket, prefix)
ic_neo_model = ic.compile_model(
    target_instance_family='ml_c5',
    input_shape={'data':[1, 3, 224, 224]},
    role=role,
    framework='mxnet',
    framework_version='1.5.1',
    output_path=output_path)
  1. 像往常一样部署编译后的模型,明确将预测容器设置为支持Neo的图像分类版本
ic_neo_model.image = get_image_uri(
    session.boto_region_name, 
    'image - classification - neo', 
    repo_version='latest')
ic_neo_predictor = ic_neo_model.deploy(
    endpoint_name=ic_neo_endpoint_name,
    initial_instance_count=1,
    instance_type='ml.c5.4xlarge')
  1. 下载测试图像,并使用之前用于Amazon Elastic Inference的相同基准测试函数,测量预测1000张图像所需的时间
predict_images(ic_predictor)
predict_images(ic_neo_predictor)

原始模型预测需要87秒,而经过Neo优化的模型预测只需要28.5秒,速度提高了三倍!而且编译Neo模型是免费的,所以没有理由不尝试一下。

综上所述,多模型端点、Amazon Elastic Inference和Amazon SageMaker Neo为机器学习模型的部署提供了强大的工具和优化方案,可以根据不同的需求和场景选择合适的方法,以达到最佳的性能和成本效益。

优化机器学习模型部署:多模型端点、弹性推理与模型编译

4. 总结与对比

为了更清晰地展示多模型端点、Amazon Elastic Inference和Amazon SageMaker Neo这三种技术的特点和适用场景,我们将它们的关键信息总结在以下表格中:
| 技术 | 适用场景 | 优势 | 操作步骤 |
| ---- | ---- | ---- | ---- |
| 多模型端点 | 处理大量模型,无需为每个模型部署单独端点 | 节省管理成本和资源,可动态加载和卸载模型 | 上传数据集到S3;训练多个模型;查找模型工件S3 URI;删除旧模型;复制模型工件;定义脚本和存档位置;创建代码存档并上传;创建多模型端点;创建端点配置;创建端点;进行预测;完成后删除端点 |
| Amazon Elastic Inference | 模型需要一定GPU加速,但又不足以充分利用完整GPU资源 | 提供部分GPU加速,有多种尺寸可选,可找到最佳性价比 | 部署模型到不同实例;下载测试图像进行预测并测量时间;尝试不同加速器尺寸;考虑不同数据类型训练模型并对比结果 |
| Amazon SageMaker Neo | 为特定硬件架构优化模型,提高预测速度 | 进行架构和代码优化,生成优化后的模型工件,DLR体积小 | 训练模型;使用Neo编译模型;部署编译后的模型;进行基准测试对比 |

下面是这三种技术的使用流程的mermaid流程图:

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px

    A([开始]):::startend --> B{选择技术}:::decision
    B -->|多模型端点| C(上传数据集到S3):::process
    B -->|Amazon Elastic Inference| D(部署模型到不同实例):::process
    B -->|Amazon SageMaker Neo| E(训练模型):::process
    C --> F(训练多个模型):::process
    F --> G(查找模型工件S3 URI):::process
    G --> H(删除旧模型):::process
    H --> I(复制模型工件):::process
    I --> J(定义脚本和存档位置):::process
    J --> K(创建代码存档并上传):::process
    K --> L(创建多模型端点):::process
    L --> M(创建端点配置):::process
    M --> N(创建端点):::process
    N --> O(进行预测):::process
    O --> P([结束]):::startend
    D --> Q(下载测试图像进行预测并测量时间):::process
    Q --> R(尝试不同加速器尺寸):::process
    R --> S(考虑不同数据类型训练模型并对比结果):::process
    S --> P
    E --> T(使用Neo编译模型):::process
    T --> U(部署编译后的模型):::process
    U --> V(进行基准测试对比):::process
    V --> P
5. 实际应用建议

在实际应用中,我们可以根据具体的业务需求和模型特点来选择合适的技术:
- 多模型端点 :当你有大量模型需要管理,且这些模型的使用频率相对较低或者不需要实时响应时,多模型端点是一个很好的选择。例如,一家电商公司为不同地区的用户群体训练了多个推荐模型,这些模型可以通过多模型端点进行统一管理和调用。
- Amazon Elastic Inference :如果你的模型需要一定的GPU加速,但又不想承担使用完整GPU实例的高昂成本,那么Amazon Elastic Inference是一个理想的解决方案。比如,一个小型的图像识别应用,其模型规模不足以充分利用GPU资源,但又需要比CPU更快的预测速度,就可以使用Elastic Inference。
- Amazon SageMaker Neo :当你需要将模型部署到特定的硬件设备上,并且希望提高模型的预测性能时,Amazon SageMaker Neo可以帮助你实现这一目标。例如,将模型部署到边缘设备(如AI相机、嵌入式系统等)上,通过Neo进行优化可以显著提高预测速度。

6. 未来展望

随着机器学习技术的不断发展,模型的规模和复杂度越来越高,对部署效率和成本的要求也越来越严格。多模型端点、Amazon Elastic Inference和Amazon SageMaker Neo等技术为我们提供了有效的解决方案,但未来可能还会有更多创新的技术和方法出现。

例如,可能会出现更加智能的资源分配算法,能够根据模型的实时使用情况自动调整资源配置,进一步提高资源利用率和成本效益。同时,随着硬件技术的不断进步,可能会有更适合机器学习模型部署的新型硬件设备出现,这也将促使相关技术不断优化和升级。

总之,我们需要密切关注这些技术的发展动态,不断探索和尝试新的方法,以满足日益增长的机器学习应用需求。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值