SageMaker Python SDK v3 可在 Notebook 内完成生成式 AI 端点基准测试,并基于数据给出部署配置建议。开发者还能直接部署推荐配置,无需切换工作流。
优化生成式 AI 推理部署,需要对端点进行基准测试、评估实例配置,并反复调整部署设置。Amazon SageMaker Python SDK v3 现在可以直接在 notebook 工作流中提供 Amazon SageMaker AI 的生成式 AI 推理建议。你也可以通过 Amazon SageMaker AI UI 和 Boto3 API 获取这些建议。借助此版本,你可以使用 Amazon SageMaker Python SDK v3,直接在 notebook 中对端点进行基准测试、生成由数据驱动的部署建议,并部署推荐的配置。
本文将演示如何使用新的 SDK 接口完成端到端工作流,以优化生成式 AI 推理部署。
Amazon SageMaker AI 中的生成式 AI 推理建议可通过以下方式自动完成推理优化:
使用合成工作负载或真实流量工作负载对在线 Amazon SageMaker 端点进行基准测试,测量吞吐量、首个 token 响应时间(TTFT)、端到端延迟等指标。
根据你的实际使用模式,生成按成本与性能权衡排序的部署建议。
将排名最高的配置直接部署到 Amazon SageMaker 实时端点。
过去,要使用这些功能,需要通过 Amazon SageMaker Studio 操作或构造 AWS SDK for Python(Boto3)API 调用。此次发布后,这些功能成为 Python SDK 操作,可以自然地融入现有的 notebook 和流水线工作流。
新功能从 3.17.0 版本开始在 sagemaker.serve.ai_inference_recommender 包中提供,并公开以下主要操作:
确认已安装最新版 Amazon SageMaker Python SDK:
pip install --upgrade sagemaker >= 3.17.0
需要一个 AWS 账户,以及一个具备 Amazon SageMaker 执行权限的 AWS Identity and Access Management(IAM)角色。
需要一个已部署的 Amazon SageMaker 实时端点(或者一个待部署的 JumpStart 模型;请参阅下一节)。
考虑一个常见场景:你有一个已准备好投入生产的生成式 AI 模型,需要确定最优的实例类型、框架配置和服务参数。传统上,这需要在多种实例类型、容器版本和并发设置之间手动反复试错。借助 Amazon SageMaker Python SDK 集成,你可以在一个 notebook 中自动完成整个工作流。以下演练将使用此 notebook,引导你完成端到端流程:
生成部署建议:让服务根据你的工作负载画像探索实例和框架配置,并返回经过排序的选项。
解读并选择:查看排序后的结果,理解其中的权衡,并选择最合适的配置。
部署:将胜出的配置推送到在线 Amazon SageMaker 端点。
基准测试:在贴近真实情况的负载条件下验证已部署的端点。
比较框架:可选择让 LMI 与 vLLM 进行直接对比,以找出最佳服务技术栈。
第一步是为你的模型和工作负载找到最佳部署配置。你无需手动在多种实例类型上进行部署,只需调用 mb.generate_deployment_recommendations(…),即可让服务根据你的工作负载画像探索实例类型和框架配置。服务会在每个候选配置上部署你的模型,运行与流量模式相匹配的负载测试,然后返回一份配置排名列表,并针对你选择的性能目标进行优化。
import time, uuid
from sagemaker.core.jumpstart.configs import JumpStartConfig
from sagemaker.serve import ModelBuilder
from sagemaker.train.configs import Compute
from sagemaker.serve import InferenceFramework, PerformanceTarget
uid = f"{int(time.time())}-{uuid.uuid4().hex[:8]}"
src_model_name = f"demo-rec-source-{uid}"
rec_ep_name = f"demo-rec-ep-{uid}"
mb = ModelBuilder.from_jumpstart_config(
jumpstart_config=JumpStartConfig(model_id=MODEL_ID),
compute=Compute(instance_type=INSTANCE_TYPE),
role_arn=ROLE,
)
source_model = mb.build(model_name=src_model_name)
rec_job = mb.generate_deployment_recommendations(
tokenizer="google/gemma-4-e2b-it",
concurrency=1,
request_count=10,
prompt_input_tokens_mean=32,
output_tokens_mean=32,
streaming=True,
performance_target=PerformanceTarget.TTFT_MS
instance_types=[INSTANCE_TYPE],
advanced_optimization=False,
framework=InferenceFramework.LMI,
role_arn=ROLE,
wait=True,
)
#Comparative table across all returned recommendations
print(mb.recommendations)
# .best is the top-ranked row
top = mb.recommendations.best
print(f"Throughput avg: {top.expected_performance.request_throughput.avg}")
print(f"TTFT p99: {top.expected_performance.time_to_first_token.p99}")
# auto_approve=True bypasses the ModelPackage approval-status check
rec_endpoint = mb.deploy(
endpoint_name=rec_ep_name,
role=ROLE,
wait=True,
auto_approve=True,
)
print(f"Deployed: {rec_endpoint.endpoint_name} ({rec_endpoint.endpoint_status})")
建议结果也可以表示为 Python 数据帧。
import pandas as pd
pd.set_option("display.width", 200)
pd.set_option("display.max_columns", None)
pd.set_option("display.max_colwidth", 60)
rows = []
for i, rec in enumerate(mb.recommendations):
raw = rec._raw
spec = raw.model_details.inference_specification_name
for m in raw.expected_performance:
rows.append({
"rank": i,
"spec_name": spec,
"instance": raw.deployment_configuration.instance_type,
"metric": m.metric, # ← attribute, not subscript
"stat": m.stat,
"value": float(m.value),
"unit": m.unit,
})
df_long = pd.DataFrame(rows)
print(df_long)
rank spec_name instance metric stat value unit
0 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge RequestThroughput avg 112.7664 Requests/Second
1 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge OutputTokenThroughput avg 3608.5300 Tokens/Second
2 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge RequestLatency p50 462.1300 Milliseconds
3 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge RequestLatency p90 999.5400 Milliseconds
4 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge RequestLatency p99 1069.8400 Milliseconds
5 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge TimeToFirstToken p50 438.5300 Milliseconds
6 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge TimeToFirstToken p90 983.3300 Milliseconds
7 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge InterTokenLatency p50 0.7900 Milliseconds
8 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge InterTokenLatency p90 2.8700 Milliseconds
9 0 low-ttft-on-g6-2xlarge-lmi-26-0-0 ml.g6.2xlarge ClientSideConcurrency 64.0000 Count
10 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge RequestThroughput avg 96.8522 Requests/Second
11 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge OutputTokenThroughput avg 3099.2700 Tokens/Second
12 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge RequestLatency p50 541.1600 Milliseconds
13 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge RequestLatency p90 1122.2000 Milliseconds
14 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge RequestLatency p99 1162.5300 Milliseconds
15 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge TimeToFirstToken p50 502.9000 Milliseconds
16 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge TimeToFirstToken p90 1088.4800 Milliseconds
17 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge InterTokenLatency p50 1.0000 Milliseconds
18 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge InterTokenLatency p90 3.6100 Milliseconds
19 1 low-ttft-on-g6-2xlarge-lmi-27-0-0 ml.g6.2xlarge ClientSideConcurrency 64.0000 Count
建议表展示了两个候选配置(排名 0 和排名 1),二者均使用 ml.g6.2xlarge,但采用不同版本的 LMI 容器。下面介绍如何理解关键指标,并在二者之间做出选择。
需要比较的关键指标:
RequestThroughput (avg):端点每秒能够处理的请求数。越高越好。
OutputTokenThroughput (avg):所有并发请求每秒生成的 token 总数。越高越好。
RequestLatency (p50/p90/p99):从发出请求到获得完整响应的端到端耗时。越低越好。
TimeToFirstToken (p50/p90):用户看到首个流式 token 的速度。越低越好。
InterTokenLatency(p50/p90):流式传输期间连续 token 之间的延迟。越低越好。
在本示例的两种配置之间进行选择:
排名第 0(lmi-26-0-0)的配置可实现 112.8 req/s 的吞吐量和 3,609 tokens/s 的 token 吞吐量,p90 TTFT 为 983 ms,p90 延迟为 1,000 ms。排名第 1(lmi-27-0-0)的配置可实现 96.9 req/s 的吞吐量和 3,099 tokens/s 的 token 吞吐量,p90 TTFT 为 1,088 ms,p90 延迟为 1,122 ms。排名第 0 的配置在所有维度上都胜出:吞吐量约高 16%,延迟约低 10%。该服务将其排在首位,是因为任务配置了 performance_target=PerformanceTarget.TTFT_MS,这意味着优化器会优先选择能够最大限度缩短首 token 时间的配置。
延迟敏感型应用(聊天机器人、交互式 UI):优先考虑较低的 TTFT(p90/p99),让用户感知到更快的响应速度。
吞吐量敏感型工作负载(批量摘要、离线处理):优先考虑较高的 RequestThroughput 和 OutputTokenThroughput,以最大化每一美元可处理的 token 数量。
如果两种配置在主要指标上表现接近,可以使用次要指标作为决胜依据,然后再考虑成本(在性能相近的情况下,较小的实例可以节省费用)。
在本示例中,排名最高的配置(lmi-26-0-0)显然是最佳选择,因为在相同并发级别(64)下,它在所有指标上都占据优势。
在生产工作流中,通常会在一个会话中生成推荐结果,然后在另一个会话中完成部署。例如,数据科学家可能会在实验阶段运行推荐任务,而 MLOps 流水线则在发布周期中部署推荐结果。使用 ModelBuilder.from_recommendation_job(job_name) 可以根据已完成的任务初始化 ModelBuilder:
from sagemaker.serve import ModelBuilder
# Hydrate a fresh ModelBuilder from a completed recommendation job
mb = ModelBuilder.from_recommendation_job("my-rec-job-name")
print(f"Loaded {len(mb.recommendations)} recommendations")
print(mb.recommendations)
# Deploy the top-ranked recommendation
endpoint = mb.deploy(
role=ROLE,
wait=True,
auto_approve=True,
)
部署推荐配置后,下一步是在受控条件下验证其性能。基准测试可以在端点开始承载生产流量之前,确认它是否满足延迟和吞吐量要求。SDK 让这一过程变得非常简单:只需几行代码,即可部署 JumpStart 模型并运行合成负载测试。
import time, uuid
from sagemaker.core.jumpstart.configs import JumpStartConfig
from sagemaker.serve import ModelBuilder, start_benchmark
from sagemaker.train.configs import Compute
from sagemaker.serve import InferenceFramework, PerformanceTarget
uid = f"{int(time.time())}-{uuid.uuid4().hex[:8]}"
ep_name = f"demo-bench-ep-{uid}"
model_name = f"demo-bench-model-{uid}"
# Build and deploy a JumpStart endpoint
mb = ModelBuilder.from_jumpstart_config(
jumpstart_config=JumpStartConfig(model_id=MODEL_ID),
compute=Compute(instance_type=INSTANCE_TYPE),
role_arn=ROLE,
)
core_model = mb.build(model_name=model_name)
core_endpoint = mb.deploy(endpoint_name=ep_name)
# Benchmark with a synthetic workload
job = start_benchmark(
endpoint=core_endpoint,
tokenizer="google/gemma-4-e2b-it",
concurrency=1,
request_count=10,
prompt_input_tokens_mean=32,
output_tokens_mean=32,
streaming=True,
role=ROLE,
wait=True,
)
result = job.show_result()
基准测试完成后,你需要判断端点是否满足服务级别目标。基准测试会返回一个带类型的结果对象,其中包含一个指标访问器,让你能够通过编程方式访问吞吐量、延迟百分位数以及 token 级时间指标。所有字段均支持 IDE 自动补全。
# Well-known shortcuts — fully typed, IDE autocomplete works
print(f"Throughput avg: {result.metrics.request_throughput.avg} req/sec")
print(f"TTFT p99: {result.metrics.time_to_first_token.p99} ms")
print(f"E2E latency p90: {result.metrics.request_latency.p90} ms")
# Any metric AIPerf produced, by raw key
ott = result.metrics.get("output_token_throughput")
if ott:
print(f"Output token throughput p90: {ott.p90} {ott.unit}")
基准测试结果同样可以表示为 Python 数据帧。
import pandas as pd
pd.set_option("display.width", 200)
pd.set_option("display.max_columns", None)
pd.set_option("display.max_colwidth", 60)
result = job.show_result()
rows = []
for name, m in result.metrics.all_metrics.items():
rows.append({
"metric": name,
"unit": m.unit,
"avg": m.avg,
"p50": m.p50,
"p90": m.p90,
"p99": m.p99,
})
df = pd.DataFrame(rows).set_index("metric")
print(df)
unit avg p50 p90 p99
metric
request_throughput requests/sec 3.841439 NaN NaN NaN
request_latency ms 256.288407 206.586304 257.917252 656.735558
request_count requests 10.000000 NaN NaN NaN
time_to_first_token ms 91.770545 23.977319 93.899204 636.935233
time_to_second_token ms 3.035445 3.400013 3.611624 3.670301
inter_token_latency ms 4.987234 5.518454 5.689482 5.695788
output_token_throughput tokens/sec 130.608919 NaN NaN NaN
output_token_throughput_per_user tokens/sec/user 1036.753582 181.210181 1041.238702 7969.310858
output_sequence_length tokens 34.000000 34.000000 35.000000 35.000000
input_sequence_length tokens 32.000000 32.000000 32.000000 32.000000
output_token_count tokens 34.000000 34.000000 35.000000 35.000000
inter_chunk_latency ms 5.307028 5.921907 6.133938 6.599335
total_output_tokens tokens 340.000000 NaN NaN NaN
benchmark_duration sec 2.603191 NaN NaN NaN
total_isl tokens 320.000000 NaN NaN NaN
total_osl tokens 340.000000 NaN NaN NaN
http_req_sending ms 0.504841 0.230543 0.527351 2.798082
http_req_waiting ms 91.130199 23.709262 93.193301 633.795432
http_req_data_received KB 7.954883 7.956055 7.970801 7.971592
http_req_connecting ms 0.257011 0.000000 0.257011 2.338797
http_req_connection_reused ratio 0.900000 1.000000 1.000000 1.000000
http_req_blocked ms 0.000000 0.000000 0.000000 0.000000
http_req_dns_lookup ms 0.042871 0.000000 0.042871 0.390126
http_req_chunks_received count 32.600000 33.000000 34.000000 34.000000
http_req_chunks_sent count 1.000000 1.000000 1.000000 1.000000
time_to_first_output_token ms 91.770545 23.977319 93.899204 636.935233
http_req_duration ms 258.657125 207.090978 260.330959 674.649103
http_req_data_sent KB 0.270020 0.269043 0.279687 0.282852
http_req_receiving ms 167.022085 182.795457 183.395102 183.933767
e2e_output_token_throughput tokens/sec/user 152.770842 164.113321 168.852245 169.710317
osl_mismatch_diff_pct % 6.250000 6.250000 9.375000 9.375000
prefill_throughput_per_user tokens/sec/user 1183.218498 1334.595197 1370.633072 1376.052063
http_req_connection_overhead ms 0.299882 0.000000 0.299882 2.728923
http_req_total ms 258.957006 207.090978 260.630841 677.378026
osl_mismatch_count requests 8.000000 NaN NaN NaN
total_token_throughput tokens/sec 253.534960 NaN NaN NaN
下表详细说明了基准测试服务报告的各项指标。
不确定哪种推理框架更适合你的模型?可以并行运行两个推荐任务,一个使用 LMI,另一个使用 vLLM,然后比较两者排名最高的结果,并部署表现更优的方案。
import time, uuid
from concurrent.futures import ThreadPoolExecutor, as_completed
from sagemaker.core.jumpstart.configs import JumpStartConfig
from sagemaker.serve import ModelBuilder
from sagemaker.train.configs import Compute
def build_for(framework):
bid = f"{int(time.time())}-{uuid.uuid4().hex[:6]}"
mb = ModelBuilder.from_jumpstart_config(
jumpstart_config=JumpStartConfig(model_id=MODEL_ID),
compute=Compute(instance_type=INSTANCE_TYPE),
role_arn=ROLE,
)
mb.build(model_name=f"demo-fw-{framework.lower()}-{bid}")
return mb
mb_lmi = build_for("LMI")
mb_vllm = build_for("VLLM")
def run_rec(mb, framework):
return mb.generate_deployment_recommendations(
tokenizer="google/gemma-4-e2b-it",
concurrency=1, request_count=10,
prompt_input_tokens_mean=32, output_tokens_mean=32,
streaming=True,
performance_target=PerformanceTarget.TTFT_MS,
instance_types=[INSTANCE_TYPE],
advanced_optimization=False,
framework=framework,
role_arn=ROLE, wait=True,
)
# Run both recommendation jobs in parallel
with ThreadPoolExecutor(max_workers=2) as ex:
futures = {
ex.submit(run_rec, mb_lmi, "LMI"): InferenceFramework.LMI,
ex.submit(run_rec, mb_vllm, "VLLM"): InferenceFramework.VLLM,
}
for fut in as_completed(futures):
print(f"[{futures[fut]}] rec job complete.")
print("\n=== LMI ==="); print(mb_lmi.recommendations)
print("\n=== vLLM ==="); print(mb_vllm.recommendations)
# Pick the framework whose top recommendation has the higher throughput
winner_fw, winner_mb = max(
[("LMI", mb_lmi), ("VLLM", mb_vllm)],
key=lambda x: x[1].recommendations.best.expected_performance.request_throughput.avg or 0,
)
print(f"Winner: {winner_fw} recipe={winner_mb.recommendations.best.recommendation_spec_name}")
fw_endpoint = winner_mb.deploy(
endpoint_name=f"demo-fw-winner-ep-{uuid.uuid4().hex[:8]}",
role=ROLE, wait=True, auto_approve=True,
)
print(f"Deployed: {fw_endpoint.endpoint_name} ({fw_endpoint.endpoint_status})")
GitHub 上提供了可端到端运行的 notebook:
为避免持续产生费用,请删除本演练中创建的端点。在 notebook 中运行以下命令:
import boto3
sm = boto3.client("sagemaker")
# Delete benchmark endpoint
sm.delete_endpoint(EndpointName=ep_name)
sm.delete_endpoint_config(EndpointName=ep_name)
# Delete recommendation endpoint
sm.delete_endpoint(EndpointName=rec_ep_name)
sm.delete_endpoint_config(EndpointName=rec_ep_name)
有关 Amazon SageMaker 实时推理实例的定价详情,请参阅 Amazon SageMaker AI 定价。
本文带你完整实践了推理优化的全过程。你生成了部署建议,以探索不同的实例和框架配置;解读了排序后的结果,并部署了最优配置。你还使用基准测试验证了配置在真实负载下的性能,并对不同服务框架进行了正面对比。借助 Amazon SageMaker Python SDK 对生成式 AI 推理建议的集成,整个工作流都可以在一个 notebook 中完成,无须再在控制台 UI 和 API 调用之间来回切换。
要开始使用,请升级到 Amazon SageMaker Python SDK v3,并探索本文附带的示例 notebook。
Amazon SageMaker AI 中的生成式 AI 推理建议。
Amazon SageMaker AI 中的生成式 AI 推理基准测试。
SageMaker Python SDK 文档。
SageMaker JumpStart 入门指南。
GitHub 上的示例 notebook。
有任何反馈或问题?欢迎前往 Amazon SageMaker 讨论论坛与我们交流。