原文:
towardsdatascience.com/hosting-multiple-llms-on-a-single-endpoint-32eda0201832
图片来自Unsplash的**Michael Dziedzic
过去一年,大型语言模型(LLM)领域经历了爆炸式增长,出现了许多新的模型,以及各种技术和工具来帮助训练、托管和评估这些模型。特别是,托管/推理是这些 LLM 和机器学习的一般力量得到认可的地方,因为没有推理,这些模型就没有可视结果或目的。
如我过去所记录的,托管这些 LLM由于模型的大小以及高效利用模型背后的相关硬件,可能会相当具有挑战性。虽然我们已经与模型托管技术如DJL Serving、文本生成推理(TGI)和Triton合作,并使用如 Amazon SageMaker 之类的模型/基础设施托管平台来托管这些 LLM,但在我们尝试将 LLM 用例投入生产时,又出现了另一个问题。我们如何为多个 LLM 做到这一点?
为什么这个问题甚至会出现?当我们达到生产级用例时,通常会有多个可能被使用的模型。例如,可能有一个 Llama 模型用于你的摘要用例,而 Falcon 模型则用于你的聊天机器人。虽然我们可以将这些模型各自托管在它们自己的持久端点上,但这会导致高昂的成本。需要一个同时考虑成本和性能/资源分配及优化的解决方案。
在本文中,我们将探讨如何利用一种高级托管选项,即SageMaker 推理组件,来解决这个问题,并构建一个示例,其中我们在单个端点上托管了Flan和Falcon模型。
注意:本文文章假设您对 Python、LLMs 和 Amazon SageMaker 推理有中级理解。我建议您阅读这篇文章以开始使用 Amazon SageMaker 推理。
免责声明:我是 AWS 的机器学习架构师,我的观点仅代表个人。
目录
-
推理组件简介
-
其他多模型 SageMaker 推理托管选项
-
多个 LLM 托管示例
-
其他资源与结论
1. 推理组件简介
SageMaker 推理组件是 SageMaker 实时推理(SageMaker Real-Time Inference)中引入的一种新型多模型托管选项。对于 SageMaker 实时推理,您有一个持久端点,并配备了一组专用硬件,您可以为它启用自动扩展。创建纯 SageMaker 端点的一般流程如下:
纯 SageMaker 端点流程(作者截图)
对于我们新的端点,我们可以添加多个推理组件,我们的创建/编排过程可能如下所示:
推理组件流程(作者截图)
现在什么是推理组件?推理组件与纯 SageMaker 模型对象 非常相似。您可以指定模型数据和您要托管模型的容器。关键区别在于您可以指定托管此模型所需的计算资源。您可以在类似于以下 API 调用中定义它:
"ComputeResourceRequirements": {
"NumberOfAcceleratorDevicesRequired": 1,
"NumberOfCpuCoresRequired": 1,
"MinMemoryRequiredInMb": 1024
}
加速器数量可以是 GPU 或基于 Inferentia 的设备,同时您也可以根据需要指定 CPU 核心和内存。另外,在运行时,您可以设置一个初始副本数量。这意味着模型副本已经加载并准备好服务请求(有助于缓解冷启动并保持模型活跃)。
在推理组件中,您还可以根据每个模型副本接收的平均调用次数应用自动扩展。例如,如果您有一个模型副本,并且它达到了您在自动扩展策略中指定的某个调用阈值,您可以根据您指定的限制增加副本数量。请注意,每个副本将保留您指定的计算资源需求,因此您需要确保您在端点后面有适当数量的实例,以便能够托管您可能创建的副本数量。
在本质上,在推理组件中,我们需要牢记的主要特性如下:
-
每个推理组件都有自己的容器和模型数据,它是模型服务器/容器无关的。例如,您可以在同一个端点上有一个使用 TGI 容器的 IC 和一个使用 DJL Serving 容器的 IC。
-
您可以将 n 个推理组件添加到您的端点,这是此托管选项的多模型角度。
-
您可以为每个推理组件/模型指定计算资源需求。
-
您可以为正在处理的推理组件/模型指定一个初始副本数量。
-
您可以按推理组件/模型进行扩展,为您的 IC 配置应用程序自动扩展以配置副本数量。使用此功能,您还可以将您的 IC 扩展到 0 个副本。
在今天的示例中,我们不会探索扩展部分,但我们将看看如何将两个独立的 LLM 作为推理组件添加到 SageMaker 端点。在那之前,让我们了解 SageMaker 推理中的其他多模型选项,以避免在使用哪个选项时产生混淆。
2. 其他多模型 SageMaker 推理托管选项
虽然推理组件引入了在 SageMaker 实时推理上托管多个模型的新方法,但根据您的用例,已经有一些先前选项也可以使用。
要了解这两种选项之间的全部差异,请参考我的入门文章这里。在本质上,当您有多个类似口味的模型时,可以使用 MME,这意味着相同的容器/模型框架。使用 MME,您可以在专用端点上为单个容器挂载多个模型。当您有不同的容器/模型框架时,可以使用 MCE。使用 MCE,您在单个端点上拥有多个容器。推理组件可以被视为这两种选项之间的一种结合,但对于特定的用例,最好利用这两种现有选项之一。
多模型选项(作者截图)
在每个高级推理选项中,您还需要考虑一些特定的注意事项。例如,在 MME 中,根据流量自动加载和卸载模型。在 MCE 中,有选项将这些容器链接在一起形成一个单一的 推理管道。在您确实想要控制每个模型背后的硬件并在每个模型级别进行扩展的使用案例中,最好利用推理组件。
每个选项都有其自身的优缺点,因此评估您的性能要求和模型推理模式,以做出 正确的托管决策 非常重要。
3. 多个 LLM 托管示例
在我们的示例中,我们将使用推理组件在单个端点上托管 FlanT5XXL 和 Falcon7B 模型。对于我们的编排,我们将使用 SageMaker Python SDK 和 AWS Python SDK (Boto3) 中的某些相同工具。
import boto3
import sagemaker
import time
from time import gmtime, strftime
#Setup
client = boto3.client(service_name="sagemaker")
runtime = boto3.client(service_name="sagemaker-runtime")
boto_session = boto3.session.Session()
s3 = boto_session.resource('s3')
region = boto_session.region_name
sagemaker_session = sagemaker.Session()
role = sagemaker.get_execution_role()
print(f"Role ARN: {role}")
print(f"Region: {region}")
传统的 SageMaker 模型对象是 SageMaker 实时推理创建的第一步。在这种情况下,我们跳过这个步骤,因为我们创建推理组件时已经定义了模型数据和容器工件。我们直接进入 SageMaker 端点配置 的创建,其中有一些关键变化。
# Container Parameters, increase health check for LLMs:
variant_name = "AllTraffic"
instance_type = "ml.g5.24xlarge"
model_data_download_timeout_in_seconds = 3600
container_startup_health_check_timeout_in_seconds = 3600
# Setting up managed AutoScaling
initial_instance_count = 1
max_instance_count = 2
print(f"Initial instance count: {initial_instance_count}")
print(f"Max instance count: {max_instance_count}")
# Endpoint Config Creation
endpoint_config_response = client.create_endpoint_config(
EndpointConfigName=epc_name,
ExecutionRoleArn=role,
ProductionVariants=[
{
"VariantName": variant_name,
"InstanceType": instance_type,
"InitialInstanceCount": 1,
"ModelDataDownloadTimeoutInSeconds": model_data_download_timeout_in_seconds,
"ContainerStartupHealthCheckTimeoutInSeconds": container_startup_health_check_timeout_in_seconds,
"ManagedInstanceScaling": {
"Status": "ENABLED",
"MinInstanceCount": initial_instance_count,
"MaxInstanceCount": max_instance_count,
},
# can set to least outstanding or random: https://aws.amazon.com/blogs/machine-learning/minimize-real-time-inference-latency-by-using-amazon-sagemaker-routing-strategies/
"RoutingConfig": {"RoutingStrategy": "LEAST_OUTSTANDING_REQUESTS"},
}
],
)
传统的端点配置中,我们只需定义初始实例数量和类型。然而,在这种情况下,我们添加了一些关键参数:
-
托管实例自动扩展:使用推理组件,您可以在模型/容器级别进行扩展。在这里,您可以根据接收到的流量调整模型副本的数量。然而,您端点背后的硬件也必须扩展,以便有足够的资源来托管这些额外的副本。SageMaker 根据这些推理组件的扩展情况提供托管实例自动扩展,我们在端点配置中启用了这一功能。
-
路由策略:此外,我们还可以配置请求的 路由策略。这里的默认值是随机路由,但我们启用了 Least Outstanding Requests (LOR) 路由。在这种情况下,请求会被路由到具有最多容量来处理请求的实例,这是监控每个实例上的推理组件数量和流量的组合/公式。
一旦我们定义了端点配置,我们就可以以常规方式 创建一个端点。
#Endpoint Creation
endpoint_name = "ic-ep" + strftime("%Y-%m-%d-%H-%M-%S", gmtime())
create_endpoint_response = client.create_endpoint(
EndpointName=endpoint_name,
EndpointConfigName=epc_name,
)
一旦端点被创建,我们就专注于添加推理组件的新流程。再次将推理组件视为与 SageMaker 模型对象非常相似。你需要定义相同的模型数据和容器规范。在这种情况下,我们甚至可以直接创建一个 SageMaker 模型对象,并让我们的推理组件继承所需的元数据。
对于我们的 Flan 和 Falcon 模型,我们使用 文本生成推理(TGI)容器 直接获取这些模型工件(TGI 将作为模型服务器)。
from sagemaker.huggingface import HuggingFaceModel, get_huggingface_llm_image_uri
import json
# utilizing huggingface TGI container
image_uri = get_huggingface_llm_image_uri("huggingface",version="1.1.0")
print(f"TGI Image: {image_uri}")
# Flan T5 TGI Model
flant5_model = {"Image": image_uri, "Environment": {"HF_MODEL_ID": "google/flan-t5-xxl"}}
flant5_model_name = "flant5-model" + strftime("%Y-%m-%d-%H-%M-%S", gmtime())
print(f"Flan Model Name: {flant5_model_name}")
#note: falcon 7b takes just one GPU, sharding is not supported
falcon7b_model = {"Image": image_uri, "Environment": {'HF_MODEL_ID':'tiiuae/falcon-7b'}}
falcon7b_model_name = "falcon7b-model" + strftime("%Y-%m-%d-%H-%M-%S", gmtime())
print(f"Falcon Model Name: {falcon7b_model_name}")
我们随后创建一个 SageMaker 模型对象,并将模型元数据传递给我们的 推理组件。
# create model object for flan t5
create_flan_model_response = client.create_model(
ModelName=flant5_model_name,
ExecutionRoleArn=role,
Containers=[flant5_model],
)
print("Flan Model Arn: " + create_flan_model_response["ModelArn"])
# flan inference component reaction, inherit SageMaker Model object
create_flan_ic_response = client.create_inference_component(
InferenceComponentName=flant5_ic_name,
EndpointName=endpoint_name,
VariantName=variant_name,
Specification={
"ModelName": flant5_model_name,
"ComputeResourceRequirements": {
# enables tensor parallel via TGI, reserving 2 GPUs (g5.24xlarge has 4 GPUs)
"NumberOfAcceleratorDevicesRequired": 2,
"NumberOfCpuCoresRequired": 1,
"MinMemoryRequiredInMb": 1024,
},
},
# can setup autoscaling for copies
RuntimeConfig={"CopyCount": 1},
)
print("IC Flan Arn: " + create_flan_ic_response["InferenceComponentArn"])
注意,我们将包含容器和模型数据信息的 SageMaker 模型名称传递给我们的推理组件。我们的推理组件(IC)将继承这些信息,然后我们定义使 IC 与我们的传统 SageMaker 实时推理流程不同的组件。
推理组件的关键差异在于我们定义的硬件和扩展参数。注意,我们定义了 NumberOfAcceleratorDevicesRequired,此参数可以指代 GPU 或 Inferentia 基于的实例。请注意,你需要考虑你有多少硬件可用。在我们的案例中,我们使用 ml.g5.24xlarge 实例作为我们的端点,这个实例有 4 个 GPU 可用。通过说我们需要 2 个设备,我们 为每个创建的模型副本分配 2 个 GPU。再次强调,我们也在运行时配置中定义了 CopyCount。在这种情况下,我们定义了 2 个 GPU,因为与 Flan 一样,我们希望通过 TGI 容器启用 Tensor Parallel。对于某些模型,可能甚至不需要多个加速器。例如,对于我们的 Falcon 推理组件,Tensor Parallel 对于 Falcon 7B 没有启用,所以我们只定义了 1 个 GPU 的需求,如下所示:
# create falcon model object
create_falcon_model_response = client.create_model(
ModelName=falcon7b_model_name,
ExecutionRoleArn=role,
Containers=[falcon7b_model],
)
print("Falcon Model Arn: " + create_falcon_model_response["ModelArn"])
falcon_ic_name = "falcon-ic" + strftime("%Y-%m-%d-%H-%M-%S", gmtime())
variant_name = "AllTraffic"
# falcon inference component requirement
create_falcon_ic_response = client.create_inference_component(
InferenceComponentName=falcon_ic_name,
EndpointName=endpoint_name,
VariantName=variant_name,
Specification={
"ModelName": falcon7b_model_name,
"ComputeResourceRequirements": {
# For falcon 7b only one GPU is needed: https://github.com/huggingface/text-generation-inference/issues/418#issuecomment-1579186709
"NumberOfAcceleratorDevicesRequired": 1,
"NumberOfCpuCoresRequired": 1,
"MinMemoryRequiredInMb": 1024,
},
},
# can setup autoscaling for copies
RuntimeConfig={"CopyCount": 1},
)
print("IC Falcon Arn: " + create_falcon_ic_response["InferenceComponentArn"])
一旦你的推理组件被创建,你可以在 Studio UI 中看到你的端点上启用了多个模型:
推理组件(创建)
调用这些推理组件与调用传统的 MME 或 MCE 端点非常相似,我们指定推理组件名称作为其自己的参数。
import json
# sample request
payload = "What is the capitol of the United States?"
response = runtime.invoke_endpoint(
EndpointName=endpoint_name,
InferenceComponentName=falcon_ic_name, #specify IC name
ContentType="application/json",
Accept="application/json",
Body=json.dumps(
{
"inputs": payload,
"parameters": {
"early_stopping": True,
"length_penalty": 2.0,
"max_new_tokens": 50,
"temperature": 1,
"min_length": 10,
"no_repeat_ngram_size": 3,
},
}
),
)
result = json.loads(response["Body"].read().decode())
result
4. 其他资源与结论
SageMaker-Deployment/LLM-Hosting/Inference-Components/falcon-flan-tgi-ic.ipynb at master ·…
整个示例的代码可以在上面的链接中找到。有关推理组件的更多资源,请参阅官方 AWS 博客这里和SageMaker LLM 研讨会。推理组件提供了一种生产级别的视角,不仅可以帮助您扩展 LLM 模型,还可以以性能和成本最优的方式扩展您可能拥有的任何其他 ML 模型。
请继续关注更多关于 GenAI/AWS 的文章以及对我们上述讨论的主题的深入探讨。一如既往,感谢您的阅读,欢迎留下任何反馈。
1万+

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



