亚马逊AWS官方博客

使用 Amazon SageMaker 高效地训练、调整和部署自定义集成

人工智能(AI)已成为科技界一个重要而热门的话题。随着人工智能的发展,我们看到出现了不同类型的机器学习(ML)模型。一种称为集成建模的方法已迅速受到数据科学家和从业人员的关注。在这篇文章中,我们将讨论什么是集成模型以及使用集成模型的好处。然后,我们将举例说明如何使用 Amazon SageMaker 训练、优化和部署自定义集成。

集成学习是指使用多种学习模型和算法来获得比任何单一的、单独的学习算法更准确的预测。事实证明,这种集成在各种应用和学习环境中都非常有效,例如网络安全 [1] 和欺诈检测、遥感、预测财务决策的最佳下一步行动、医学诊断,甚至是计算机视觉和自然语言处理(NLP)任务。我们倾向于根据用于训练集成的技术、集成的组成以及将不同预测合并为单一推理的方式对集成进行分类。这些类别包括:

  • 提升 – 依次训练多个弱学习器,在此序列中,前一个学习器的每个错误预测都会被赋予更高的权重,并输入到下一个学习器中,从而形成一个更强的学习器。示例包括 AdaBoost、Gradient Boosting 和 XGBoost。
  • 装袋 – 使用多个模型来减少单个模型的方差。示例包括 Random Forest 和 Extra Trees。
  • 堆叠(混合)– 通常使用异构模型,将每个单独估计器的预测结果堆叠在一起,作为处理预测的最终估计器的输入。这种最终估计器的训练过程通常使用交叉验证。

有多种方法可将预测结果合并为模型最终产生的单一预测结果,例如,使用线性学习器等元估计器、基于分类任务的多数票表决法(使用多个模型进行预测)或用于回归的集成平均法。

虽然有几个库和框架提供了集成模型的实现,如 XGBoost、CatBoost 或 scikit-learn 的 random forest,但在这篇文章中,我们将重点介绍自带模型并将这些模型用作堆叠集成。不过,我们没有为每个模型使用专用资源(专用训练和调整作业以及每个模型的托管端点),而是使用单个 SageMaker 训练作业和单个调整作业来训练、调整和部署自定义集成(多个模型),然后部署到单个端点,从而降低可能的成本和运营开销。

BYOE:自带集成

使用 SageMaker 训练和部署异构集成模型有多种方法:可以在单独的训练作业中训练每个模型,并使用 Amazon SageMaker 自动模型调优分别优化每个模型。在托管这些模型时,SageMaker 提供了各种经济实惠的方式来在同一租户基础设施上托管多个模型。有关此类设置的详细部署模式,请参阅 Amazon SageMaker 中的模型托管模式,第 1 部分:在 Amazon SageMaker 上构建机器学习应用程序的常见设计模式。这些模式包括使用多个端点(针对每个经过训练的模型)或单个多模型端点,甚至是单个多容器端点,其中容器可以单独调用,也可以在管道中连锁调用。所有这些解决方案都包括一个元估计器(例如 AWS Lambda 函数中的元估计器),元估计器调用每个模型并实现混合或投票功能。

然而,运行多个训练作业可能会带来运营和成本开销,特别是当您的集成需要在相同的数据上进行训练时。同样,在不同的端点或容器上托管不同的模型,并将模型的预测结果结合起来以提高准确性,也需要多次调用,因此会带来额外的管理、成本和监控工作。例如,SageMaker 支持使用 Triton Inference Server 的集成机器学习模型,但该解决方案要求模型或模型集成由 Triton 后端支持。此外,客户还需要付出额外的努力来设置 Triton 服务器,并进一步学习以了解不同的 Triton 后端的工作原理。因此,客户更喜欢采用更直接的方式来实现解决方案,即他们只需要向端点发送一次调用,并且可以灵活地控制如何聚合结果以生成最终输出。

解决方案概览

为了解决这些问题,我们将举例说明如何使用单个训练作业进行集成训练、优化模型的超参数并使用单个容器将模型部署到无服务器端点。我们为集成堆栈使用两个模型:CatBoost 和 XGBoost(两者都是增强集成)。在数据方面,我们使用 scikit-learn 库中的糖尿病数据集 [2]:该数据集由 10 个特征(年龄、性别、体重、血压和六个血清测量值)组成,我们的模型预测基线特征收集后 1 年的疾病进展情况(回归模型)。

完整的代码存储库可以在 GitHub 上找到。

在单个 SageMaker 作业中训练多个模型

为了训练模型,我们在脚本模式下使用 SageMaker 训练作业。在脚本模式下,您可以在使用 SageMaker 框架容器的同时编写自定义训练(以及随后的推理代码)。框架容器使您能够使用由 AWS 管理的现成环境,其中包括所有必要的配置和模块。为了演示如何自定义框架容器,我们以预构建的 SKLearn 容器为例,其中不包括 XGBoost 和 CatBoost 软件包。添加这些软件包有两种选择:要么扩展内置容器以安装 CatBoost 和 XGBoost(然后作为自定义容器部署),要么使用 SageMaker 训练作业脚本模式功能,该功能允许您在创建训练估计器时提供 requirements.txt 文件。SageMaker 训练作业会在运行时安装 requirements.txt 文件中列出的库。这样,您就不需要管理自己的 Docker 映像存储库,还能更灵活地运行需要其他 Python 软件包的训练脚本。

下面的代码块显示了我们用来开始训练的代码。entry_point 参数指向我们的训练脚本。我们还使用了 SageMaker SDK API 的两项引人注目的功能:

  • 首先,我们在 source_dirdependencies 参数中分别指定源目录的本地路径和依赖项。SDK 会压缩这些目录并将其上传到 Amazon Simple Storage Service(Amazon S3),SageMaker 会在训练实例的工作目录 /opt/ml/code 下提供这些目录。
  • 其次,我们将 SDK SKLearn 估计器对象与我们首选的 Python 和框架版本一起使用,这样 SageMaker 就会提取相应的容器。我们还定义了一个自定义训练指标“validation:rmse”,该指标将在训练日志中发出并由 SageMaker 捕获。稍后,我们在调整作业中使用此指标作为目标指标。
hyperparameters = {"num_round": 6, "max_depth": 5}
estimator_parameters = {
    "entry_point": "multi_model_hpo.py",
    "source_dir": "code",
    "dependencies": ["my_custom_library"],
    "instance_type": training_instance_type,
    "instance_count": 1,
    "hyperparameters": hyperparameters,
    "role": role,
    "base_job_name": "xgboost-model",
    "framework_version": "1.0-1",
    "keep_alive_period_in_seconds": 60,
    "metric_definitions":[
       {'Name': 'validation:rmse', 'Regex': 'validation-rmse:(.*?);'}
    ]
}
estimator = SKLearn(**estimator_parameters)

接下来,我们编写训练脚本(multi_model_hpo.py)。我们的脚本遵循一个简单的流程:捕获超参数(配置作业时使用的超参数),并训练 CatBoost 模型XGBoost 模型。我们还实现了 k-fold 交叉验证函数。请参阅以下代码:

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

    # Sagemaker specific arguments.Defaults are set in the environment variables.
    parser.add_argument("--output-data-dir", type=str, default=os.environ["SM_OUTPUT_DATA_DIR"])
    parser.add_argument("--model-dir", type=str, default=os.environ["SM_MODEL_DIR"])
    parser.add_argument("--train", type=str, default=os.environ["SM_CHANNEL_TRAIN"])
    parser.add_argument("--validation", type=str, default=os.environ["SM_CHANNEL_VALIDATION"])
    .
    .
    .
    
    """
    Train catboost
    """
    
    K = args.k_fold    
    catboost_hyperparameters = {
        "max_depth": args.max_depth,
        "eta": args.eta,
    }
    rmse_list, model_catboost = cross_validation_catboost(train_df, K, catboost_hyperparameters)
    .
    .
    .
    
    """
    Train the XGBoost model
    """

    hyperparameters = {
        "max_depth": args.max_depth,
        "eta": args.eta,
        "objective": args.objective,
        "num_round": args.num_round,
    }

    rmse_list, model_xgb = cross_validation(train_df, K, hyperparameters)

模型训练完成后,我们会计算 CatBoost 和 XGBoost 预测结果的平均值。pred_mean 结果就是集成的最终预测结果。然后,我们根据验证集确定 mean_squared_errorval_rmse 用于在训练期间评估整个集成。请注意,我们还以符合我们在 metric_definitions 中使用的正则表达式的模式打印 RMSE 值。稍后,SageMaker Automatic Model Tuning 将使用此值来捕获目标指标。请参阅以下代码:

pred_mean = np.mean(np.array([pred_catboost, pred_xgb]), axis=0)
val_rmse = mean_squared_error(y_validation, pred_mean, squared=False)
print(f"Final evaluation result: validation-rmse:{val_rmse}")

最后,我们的脚本将两个模型构件保存到位于 /opt/ml/model 的输出文件夹。

训练作业完成后,SageMaker 会将 /opt/ml/model 目录中的内容打包,并以压缩 TAR 格式将打包内容作为单一对象复制到作业配置中指定的 S3 位置。在我们的例子中,SageMaker 将这两个模型捆绑在一个 TAR 文件中,并在训练作业结束时将此文件上传到 Amazon S3。请参阅以下代码:

model_file_name = 'catboost-regressor-model.dump'
   
    # Save CatBoost model
    path = os.path.join(args.model_dir, model_file_name)
    print('saving model file to {}'.format(path))
    model.save_model(path)
   .
   .
   .
   # Save XGBoost model
   model_location = args.model_dir + "/xgboost-model"
   pickle.dump(model, open(model_location, "wb"))
   logging.info("Stored trained model at {}".format(model_location))

总之,您应该注意到,在这个过程中,我们下载了一次数据,并使用单个训练作业训练了两个模型。

自动集成模型调整

因为我们正在构建机器学习模型集合,所以探索所有可能的超参数排列是不切实际的。SageMaker 提供自动模型调整(AMT),通过在您指定的范围内关注最有希望的值组合,寻找最佳模型超参数(由您来定义要探索的正确范围)。SageMaker 支持多种优化方法供您选择。

我们首先定义优化过程的两个部分:目标指标和要调整的超参数。在我们的示例中,我们使用验证 RMSE 作为目标指标,并调整 etamax_depth(其他超参数请参考 XGBoost 超参数CatBoost 超参数):

from sagemaker.tuner import (
    IntegerParameter,
    ContinuousParameter,
    HyperparameterTuner,
)

hyperparameter_ranges = {
    "eta": ContinuousParameter(0.2, 0.3),
    "max_depth": IntegerParameter(3, 4)
}
metric_definitions = [{"Name": "validation:rmse", "Regex": "validation-rmse:([0-9\\.]+)"}]
objective_metric_name = "validation:rmse"

我们还需要确保训练脚本中的超参数不是硬编码的,而是从 SageMaker 运行时参数中提取的:

catboost_hyperparameters = {
    "max_depth": args.max_depth,
    "eta": args.eta,
}

SageMaker 还会将超参数写入 JSON 文件,可以从训练实例上的 /opt/ml/input/config/hyperparameters.json 读取此文件。

像 CatBoost 一样,我们还捕获 XGBoost 模型的超参数(请注意,objectivenum_round 没有调整):

catboost_hyperparameters = {
    "max_depth": args.max_depth,
    "eta": args.eta,
}

最后,我们使用以下配置启动超参数调整作业:

tuner = HyperparameterTuner(
    estimator, 
    objective_metric_name,
    hyperparameter_ranges, 
    max_jobs=4, 
    max_parallel_jobs=2, 
    objective_type='Minimize'
)
tuner.fit({"train": train_location, "validation": validation_location}, include_cls_metadata=False)

作业完成后,可以检索最佳训练作业的值(RMSE 最小):

job_name=tuner.latest_tuning_job.name
attached_tuner = HyperparameterTuner.attach(job_name)
attached_tuner.describe()["BestTrainingJob"]

有关 AMT 的更多信息,请参阅使用 SageMaker 执行自动模型调整

部署

要部署自定义集成,我们需要提供一个脚本来处理推理请求并配置 SageMaker 托管。在此示例中,我们使用了一个包含训练和推理代码的文件(multi_model_hpo.py)。SageMaker 使用 if _ name _ == "_ main _" 下的代码进行训练,并在部署和处理模型时使用函数 model_fninput_fnpredict_fn

推理脚本

与训练一样,我们结合使用 SageMaker SKLearn 框架容器与自己的推理脚本。该脚本将实现 SageMaker 所需的三种方法。

首先,model_fn 方法读取我们保存的模型构件文件,并将这些文件加载到内存中。在我们的例子中,该方法以 all_model 的形式返回集成,这是一个 Python 列表,但也可以使用以模型名称作为键的字典。

def model_fn(model_dir):
    catboost_model = CatBoostRegressor()
    catboost_model.load_model(os.path.join(model_dir, model_file_name))
    
    model_file = "xgboost-model"
    model = pickle.load(open(os.path.join(model_dir, model_file), "rb"))
    
    all_model = [catboost_model, model]
    return all_model

其次,input_fn 方法对要传递给推理处理程序的请求输入数据进行反序列化。有关输入处理程序的更多信息,请参阅自适应推理容器

def input_fn(input_data, content_type):
    dtype=None
    payload = StringIO(input_data)
    return np.genfromtxt(payload, dtype=dtype, delimiter=",")

第三,predict_fn 方法负责从模型中获取预测结果。该方法将模型以及从 input_fn 返回的数据作为参数,并返回最终预测结果。在我们的示例中,我们从模型列表的第一个成员(model[0])获得 CatBoost 结果,从第二个成员(model[1])获得 XGBoost 结果,并使用混合函数来返回两个预测结果的平均值:

def predict_fn(input_data, model):
    predictions_catb = model[0].predict(input_data)
    
    dtest = xgb.DMatrix(input_data)
    predictions_xgb = model[1].predict(dtest,
                                          ntree_limit=getattr(model, "best_ntree_limit", 0),
                                          validate_features=False)
    
    return np.mean(np.array([predictions_catb, predictions_xgb]), axis=0)

现在我们已经有了经过训练的模型和推理脚本,可以配置环境来部署集成了。

SageMaker 无服务器推理

尽管 SageMaker 中有许多托管选项,但在本示例中,我们使用的是无服务器端点。无服务器端点会自动启动计算资源,并根据流量扩展和缩减资源。这消除了管理服务器的无差别繁重工作。此选项非常适合在流量激增之间有空闲时间并且可以容忍冷启动的工作负载。

配置无服务器端点非常简单,因为我们不需要选择实例类型或管理扩展策略。我们只需要提供两个参数:内存大小和最大并发量。无服务器端点会自动根据您选择的内存量按比例分配计算资源。如果选择较大的内存,则容器有权访问更多的 vCPU。应始终根据模型大小选择端点的内存大小。我们需要提供的第二个参数是最大并发量。对于单个端点,该参数最多可设置为 200(截至本文撰写时,一个区域中的无服务器端点总数限制为 50)。请注意,单个端点的最大并发量会阻止该端点占用账户允许的所有调用,因为任何超出最大值的端点调用都会被节流(有关每个区域所有无服务器端点总并发量的更多信息,请参阅 Amazon SageMaker 端点和限额)。

from sagemaker.serverless.serverless_inference_config import ServerlessInferenceConfig
serverless_config = ServerlessInferenceConfig(
    memory_size_in_mb=6144,
    max_concurrency=1,
) 

配置好端点后,我们就可以部署超参数优化作业中选择的模型了:

estimator=attached_tuner.best_estimator()
predictor = estimator.deploy(serverless_inference_config=serverless_config)

清理

尽管无服务器端点在不使用时成本为零,但在运行完本示例后,应确保删除端点:

predictor.delete_endpoint(predictor.endpoint)

总结

在这篇文章中,我们介绍了一种训练、优化和部署自定义集成的方法。我们详细介绍了使用单个训练作业训练多个模型的过程、如何使用自动模型调整来优化集成超参数,以及如何部署一个融合多个模型推理的无服务器端点。

使用这种方法可以解决潜在的成本和运营问题。训练作业的成本取决于您在使用期间使用的资源。通过仅下载一次数据来训练这两个模型,我们将作业的数据下载阶段和存储数据的已用量减少了一半,从而降低了训练作业的总体成本。此外,AMT 作业运行了四项训练作业,每项作业都减少了上述时间和存储空间,因此节省了 4 倍的成本! 关于在无服务器端点上部署模型,由于您也要为处理的数据量付费,因此只需为两个模型调用一次端点,就能支付一半的 I/O 数据费用。

尽管这篇文章只展示了使用两个模型时的好处,但您可以使用这种方法来训练、调整和部署大量集成模型,以获得更大的效果。

参考

[1] Raj Kumar、P. Arun;Selvakumar, S.(2011)。“利用神经分类器集成检测分布式拒绝服务攻击”。计算机通信。34 (11): 1328–1341。doi:10.1016/j.comcom.2011.01.012。

[2] Bradley Efron、Trevor Hastie、Iain Johnstone 和 Robert Tibshirani(2004)“最小角度回归”,统计年鉴(附讨论),407-499。(https://web.stanford.edu/~hastie/Papers/LARS/LeastAngle_2002.pdf


Original URL: https://aws.amazon.com/blogs/machine-learning/efficiently-train-tune-and-deploy-custom-ensembles-using-amazon-sagemaker/

关于作者

Melanie Li 博士是设于澳大利亚悉尼的 AWS 的高级人工智能/机器学习专家 TAM。她协助企业客户利用 AWS 上最先进的人工智能/机器学习工具构建解决方案,并就如何利用最佳实践架构和实施机器学习解决方案提供指导。在业余时间,她喜欢在户外探索大自然,与家人和朋友共度时光。

Uri Rosenberg 是欧洲、中东和非洲地区的人工智能和机器学习专家技术经理。Uri 常驻以色列,致力于让企业客户能够大规模设计、构建和运营机器学习工作负载。在业余时间,他喜欢骑自行车、徒步旅行和尽量减少 RMSE。