如何使用Amazon SageMaker,通过预先考虑,为生成式AI模型节省超过66%的成本

本文与Forethought Technologies, Inc.的工程总监Jad Chamoun和高级ML工程师Salina Wu共同撰写。

Forethought是一款领先的面向客户服务的生成AI套件。其核心是创新的SupportGPT™技术,利用机器学习改变客户支持生命周期,提高问题解决率,提高客户满意度,并提高代理商生产力。SupportGPT™利用最先进的信息检索(IR)系统和大型语言模型(LLMs),每年为超过3000万次客户互动提供支持。

SupportGPT的主要用途是增强客户支持交互和运营的质量和效率。通过使用由嵌入和排名模型支持的最先进的IR系统,SupportGPT可以快速搜索相关信息,为客户查询提供准确简洁的答案。Forethought使用针对每个客户的微调模型来检测客户意图,以便解决客户互动。大型语言模型的整合有助于将自动化代理人的互动人性化,创造更具吸引力和满意度的支持体验。

SupportGPT还通过提供自动完成建议和为客户票据制定适当的响应,以符合公司的先前回复,来协助客户支持代理人。通过使用先进的语言模型,代理人可以更快速和准确地解决客户问题,提高客户满意度。

此外,SupportGPT的架构可以检测支持知识库中的空缺,这有助于代理人向客户提供更准确的信息。一旦确定了这些空缺,SupportGPT可以自动生成文章和其他内容来填补这些知识空缺,确保支持知识库始终以客户为中心并保持最新。

在本文中,我们分享了Forethought如何在生成AI用例中使用Amazon SageMaker多模型端点,以节省超过66%的成本。

基础架构挑战

为了将这些能力带到市场上,Forethought有效地扩展其ML工作负载并提供针对每个客户的特定用例的超个性化解决方案。通过在客户数据上微调嵌入模型和分类器,实现了这种超个性化,确保准确的信息检索结果和满足每个客户独特需求的领域知识。还使用了经过客户数据微调的自动完成模型来进一步提高响应生成的准确性和相关性。

在AI处理中的一个重大挑战是高效利用GPU等硬件资源。为了解决这个挑战,Forethought选择使用SageMaker多模型端点(MMEs)在单个推理端点上运行多个AI模型并进行扩展。由于模型的超个性化要求必须训练和部署唯一的模型,因此模型数量随着客户数量呈线性增长,这可能会变得昂贵。

为了实现实时推理和成本的正确平衡,Forethought选择使用支持GPU加速的SageMaker MMEs。SageMaker MMEs使Forethought能够提供高性能、可扩展和具有成本效益的解决方案,具有亚秒级延迟,可扩展地解决多种客户支持场景。

SageMaker和Forethought

SageMaker是一项完全托管的服务,可以为开发人员和数据科学家提供快速构建、训练和部署ML模型的能力。SageMaker MMEs为实时推理部署大量模型提供了可扩展和具有成本效益的解决方案。MMEs使用共享服务容器和一组资源,可以使用加速实例(如GPU)托管所有模型。与使用单个模型端点相比,这减少了托管成本,最大化了端点利用率。它还减少了部署开销,因为SageMaker管理内存中的模型加载和卸载,并根据端点的流量模式进行扩展。此外,所有SageMaker实时端点都受益于管理和监视模型的内置功能,例如包括阴影变量、自动扩展和与Amazon CloudWatch的本机集成(有关更多信息,请参阅CloudWatch指标用于多模型端点部署)。

随着Forethought托管数百个还需要GPU资源的模型,我们看到了通过SageMaker MMEs创建更具成本效益、可靠和易于管理的架构的机会。在迁移到SageMaker MMEs之前,我们的模型部署在Amazon Elastic Kubernetes Service(Amazon EKS)上的Kubernetes上。尽管Amazon EKS提供了管理功能,但很明显,我们正在管理不专门针对推理的基础架构。Forethought必须自行管理Amazon EKS上的模型推理,这对工程效率是一种负担。例如,为了在多个模型之间共享昂贵的GPU资源,我们需要在部署期间指定分配给模型的刚性内存分数。我们希望解决现有基础架构中的以下关键问题:

  • 高成本 – 为确保每个模型都有足够的资源,我们在每个实例上适合的模型数量上非常保守。这导致模型托管的成本远高于必要的成本。
  • 低可靠性 – 尽管我们在内存分配上非常保守,但并不是所有模型都有相同的要求,有时一些模型会抛出内存不足(OOM)错误。
  • 低效管理 – 我们必须为每种类型的模型(如分类器、嵌入和自动完成)管理不同的部署清单,这是耗时且容易出错的。我们还必须维护用于确定不同模型类型的内存分配的逻辑。

最终,我们需要一个推理平台来承担在运行时管理我们的模型的重任,以改善我们服务模型的成本、可靠性和管理。SageMaker MMEs使我们能够解决这些需求。

通过其智能和动态的模型加载和卸载以及其扩展能力,SageMaker MME提供了一种成本更低、更可靠的解决方案,用于托管我们的模型。我们现在可以适合更多的模型,并且不必担心OOM错误,因为SageMaker MME处理动态加载和卸载模型。此外,部署现在只需调用Boto3 SageMaker API并附加适当的自动缩放策略。

以下图示了我们的传统架构。

为了开始我们的迁移到SageMaker MMEs,我们识别了MME的最佳用例以及我们的哪些模型会从这种变化中获益最多。MME最适合以下情况:

  • 预计具有低延迟但可以承受冷启动时间(首次加载)的模型
  • 经常和一致地调用的模型
  • 需要部分GPU资源的模型
  • 具有共同要求和推理逻辑的模型

我们将我们的嵌入模型和自动完成语言模型确定为迁移的最佳候选模型。为了将这些模型组织在MME下,我们将为每种模型类型或任务创建一个MME,一个用于我们的嵌入模型,另一个用于自动完成语言模型。

我们已经在模型上方有一个API层,用于模型管理和推理。我们的任务是重新设计这个API如何部署和处理推理模型的方式,以SageMaker为基础,同时最大限度地减少客户端和产品团队与API交互的更改。我们还需要将我们的模型和自定义推理逻辑打包,以与NVIDIA Triton推理服务器兼容,使用SageMaker MMEs。

以下图示了我们的新架构。

自定义推理逻辑

在迁移到SageMaker之前,Forethought的自定义推理代码(预处理和后处理)在调用模型时在API层中运行。目标是将此功能转移到模型本身,以明确责任的分离,模块化和简化它们的代码,并减轻API的负担。

嵌入

Forethought的嵌入模型由两个PyTorch模型工件组成,推理请求确定调用哪个模型。每个模型需要预处理的文本作为输入。主要挑战是集成预处理步骤并适应每个模型定义的两个模型工件。为了解决推理逻辑中需要多个步骤的需求,Forethought开发了一个Triton合奏模型,其中包括两个步骤:Python后端预处理过程和PyTorch后端模型调用。合奏模型允许定义和排序推理逻辑中的步骤,每个步骤由任何后端类型的Triton模型表示。为了确保与Triton PyTorch后端的兼容性,现有的模型工件已转换为TorchScript格式。为每个模型定义创建单独的Triton模型,并且Forethought的API层负责根据传入请求确定适当的TargetModel

自动完成

自动完成模型(序列到序列)提出了一组不同的要求。具体而言,我们需要启用通过多个模型调用循环并缓存每个调用的大量输入的功能,同时保持低延迟。此外,这些模型需要预处理和后处理步骤。为了满足这些要求并实现所需的灵活性,Forethought开发了使用Triton Python后端的自动完成MME模型,该后端提供了使用Python代码编写模型的优势。

基准测试

在确定 Triton 模型形状后,我们将模型部署到分段端点,并进行资源和性能基准测试。我们的主要目标是确定冷启动与内存模型的延迟,以及请求大小和并发性如何影响延迟。我们还想知道每个实例可以容纳多少个模型,多少个模型会导致实例按照自动缩放策略进行扩展,以及扩展的速度有多快。为了与我们已经使用的实例类型保持一致,我们使用 ml.g4dn.xlarge 和 ml.g4dn.2xlarge 实例进行基准测试。

结果

以下表格总结了我们的结果。

请求大小 冷启动延迟 缓存推断延迟 并发延迟(5个请求)
小(30个标记) 12.7秒 0.03秒 0.12秒
小猪AI(250个标记) 12.7秒 0.05秒 0.12秒
大(550个标记) 12.7秒 0.13秒 0.12秒

值得注意的是,冷启动请求的延迟明显高于缓存推断请求的延迟。这是因为当进行冷启动请求时,需要从磁盘或 Amazon Simple Storage Service (Amazon S3) 加载模型。并发请求的延迟也高于单个请求的延迟。这是因为模型需要在并发请求之间共享,可能会导致争用。

以下表格比较了传统模型和 SageMaker 模型的延迟。

请求大小 传统模型 SageMaker 模型
小(30个标记) 0.74秒 0.24秒
小猪AI(250个标记) 0.74秒 0.24秒
大(550个标记) 0.80秒 0.32秒

总的来说,SageMaker 模型比传统模型更适合托管自动完成模型。它们提供更低的延迟、可扩展性、可靠性和安全性。

资源使用情况

为了确定每个实例可以容纳的最佳模型数量,我们进行了一系列测试。我们的实验涉及使用 ml.g4dn.xlarge 实例类型将模型加载到我们的端点中,没有任何自动缩放策略。

这些特定的实例提供了 15.5 GB 的内存,我们的目标是每个实例达到大约 80% 的 GPU 内存使用率。考虑到每个编码器模型工件的大小,我们设法找到了在一个实例上加载的最佳 Triton 编码器数量,以达到我们的目标 GPU 内存使用率。此外,鉴于我们的每个嵌入模型对应两个 Triton 编码器模型,我们能够在每个实例上容纳一定数量的嵌入模型。因此,我们计算出了为服务所有嵌入模型所需的总实例数。这个实验对于优化我们的资源使用和增强模型的效率非常重要。

我们对自动完成模型进行了类似的基准测试。这些模型的大小约为292.0 MB。当我们测试单个 ml.g4dn.xlarge 实例可以适合多少个模型时,我们注意到尽管这些模型很小,但我们只能适合四个模型,然后我们的实例就开始卸载模型了。我们的主要关注点是:

  • CPU 内存利用率急剧上升的原因
  • 当我们试图加载一个以上模型而不是仅加载最近最少使用的(LRU)模型时,模型被卸载的原因

我们能够确定内存利用率飙升的根本原因是我们在 Python 模型中初始化 CUDA 运行时环境,这是将模型和数据移动到 GPU 设备上和从 GPU 设备上移动模型和数据所必需的。当运行时初始化时,CUDA 会将许多外部依赖项加载到 CPU 内存中。由于 Triton PyTorch 后端处理和抽象了在 GPU 设备上移动数据,所以我们在嵌入式模型中没有遇到这个问题。为了解决这个问题,我们尝试使用 ml.g4dn.2xlarge 实例,这些实例具有相同数量的 GPU 内存,但是 CPU 内存是 GPU 内存的两倍。此外,我们在 Python 后端代码中添加了几个小优化,包括在使用后删除张量,清空缓存,禁用渐变和进行垃圾回收。使用更大的实例类型,我们能够每个实例适合 10 个模型,CPU 和 GPU 内存利用率变得更加一致。

以下图表说明了这种架构。

自动扩展

我们为嵌入式和自动完成 MME 均附加了自动缩放策略。我们的嵌入式端点策略针对平均 GPU 内存利用率 80% 使用自定义指标。我们发现,我们的自动完成模型在商业时间内流量很大,在夜间流量很小。因此,我们创建了一个基于 InvocationsPerInstance 的自动缩放策略,以便我们可以根据流量模式进行缩放,节省成本而不牺牲可靠性。根据我们的资源使用基准测试,我们将缩放策略配置为每个实例的目标为 225 InvocationsPerInstance

部署逻辑和流程管道

在 SageMaker 上创建 MME 是简单的,与在 SageMaker 上创建任何其他端点类似。创建端点后,将其他模型添加到端点的方法就像将模型工件移动到端点目标的 S3 路径一样简单;此时,我们可以对我们的新模型进行推理请求。

我们定义了一个逻辑,该逻辑将接收模型元数据,根据元数据确定性地格式化端点,并检查端点是否存在。如果不存在,则创建端点并将 Triton 模型工件添加到端点的 S3 路径(也是确定性格式化的)。例如,如果模型元数据指示它是自动完成模型,则会为自动完成模型创建一个端点,并为自动完成模型工件创建一个关联的 S3 路径。如果端点已存在,则将模型工件复制到 S3 路径。

既然我们已经拥有了 MME 模型的模型形状和部署模型的功能,我们需要一种自动化部署的方式。我们的用户必须指定要部署的模型;我们处理模型的打包和部署。打包在模型附带的自定义推理代码被版本化并推送到 Amazon S3 中进行;在打包步骤中,我们根据指定的版本(或最新版本)提取推理代码,并使用指示 Triton 模型文件结构的 YAML 文件。

我们的一个要求是,我们的所有 MME 模型都将加载到内存中,以避免在生产推理请求期间出现任何冷启动延迟来加载模型。为了实现这一点,我们分配足够的资源来适合我们的所有模型(根据前面的基准测试),并以每小时的节奏调用我们的 MME 中的每个模型。

以下图表说明了模型部署管道。

以下图表说明了模型预热管道。

模型调用

我们现有的 API 层为调用者提供了一个抽象层,使其能够对我们所有的 ML 模型进行推断。这意味着我们只需要在 API 层中添加功能,以便根据推断请求调用 SageMaker MME 并使用正确的目标模型,而无需更改调用代码。SageMaker 推断代码接受推断请求,格式化我们 Triton 模型中定义的 Triton 输入,并使用 Boto3 调用 MME。

成本效益

由于迁移到 SageMaker MME,Forethought 在降低模型托管成本和减轻模型 OOM 错误方面取得了重大进展。在此更改之前,我们使用 Amazon EKS 上运行的 ml.g4dn.xlarge 实例。通过过渡到 MME,我们发现每个实例可以容纳 12 个嵌入模型,并且可以实现 80% 的 GPU 内存利用率。这导致了我们每月支出的显着下降。为了进一步提高流量管理能力,我们考虑扩展副本。假设我们使用三个副本的情况下,即使在这种情况下,我们的成本节约仍然相当可观,约为 43%。

使用 SageMaker MME 的过程证明了它的财务效益,可以降低我们的支出,同时确保模型性能最优。以前,我们的自动完成语言模型部署在 Amazon EKS 上,需要根据每个模型的内存分配数量使用不同数量的 ml.g4dn.xlarge 实例。这导致了相当大的月度成本。然而,通过最近迁移到 SageMaker MME,我们已经能够大幅降低这些成本。我们现在使用 ml.g4dn.2xlarge 实例托管所有模型,能够更加高效地打包模型。这显著地降低了我们的月度支出,我们现在实现了 66-74% 的成本节约。此举证明了有效的资源利用可以通过 SageMaker MME 实现显著的财务节约。

结论

在本文中,我们回顾了 Forethought 如何使用 SageMaker 多模型端点降低实时推断的成本。SageMaker 承担了无差别的重活,因此 Forethought 可以提高工程效率。它还允许 Forethought 大幅降低实时推断的成本,同时保持业务关键操作所需的性能。通过这样做,Forethought 可以使用超个性化模型为其客户提供差异化的产品。使用 SageMaker MME 托管您的模型并通过提高端点利用率降低托管成本。它还通过管理内存中的模型加载和基于流量模式对端点进行扩展来减少部署开销。您可以在 GitHub 上找到使用 SageMaker MME 托管多个模型的代码示例。