使用 Amazon SageMaker 轻松打包和部署经典 ML 和 LLM,第 1 部分:PySDK 改进 | 亚马逊网络服务

使用 Amazon SageMaker 轻松打包和部署经典 ML 和 LLM,第 1 部分:PySDK 改进 | 亚马逊网络服务

源节点: 2987135

亚马逊SageMaker 是一项完全托管的服务,使开发人员和数据科学家能够快速轻松地构建、训练和部署任何规模的机器学习 (ML) 模型。 SageMaker 可以通过对服务的 API 调用直接将模型部署到生产环境中。 模型被打包到容器中,以实现稳健且可扩展的部署。 尽管它提供了各种入口点,例如 SageMaker Python SDK、AWS SDK、SageMaker 控制台和 亚马逊SageMaker Studio 尽管笔记本电脑能够简化大规模训练和部署 ML 模型的过程,但客户仍在寻找更好的方法来部署模型以进行游乐场测试并优化生产部署。

我们正在推出两种新方法来简化使用 SageMaker 打包和部署模型的过程。

在这篇文章中,我们介绍新的 SageMaker Python SDK ModelBuilder 经验,旨在最大限度地减少数据科学家等新 SageMaker 用户的学习曲线,同时帮助经验丰富的 MLOps 工程师最大限度地利用 SageMaker 托管服务。 它降低了初始设置和部署的复杂性,并提供了利用 SageMaker 全部功能的最佳实践指导。 我们提供了有关这项新 SageMaker 功能的详细信息和 GitHub 示例。

另一项新发布是在 SageMaker Studio 中使用新的交互式部署体验。 我们将在第 2 部分讨论这一点。

将模型部署到 SageMaker 端点需要执行一系列步骤,以使模型准备好托管在 SageMaker 端点上。 这涉及以正确的格式和结构获取模型工件、创建推理代码以及指定基本细节,例如模型图像 URL、 亚马逊简单存储服务 (Amazon S3) 模型工件的位置、序列化和反序列化步骤以及必要的 AWS身份和访问管理 (IAM) 角色以促进适当的访问权限。 接下来,端点配置需要确定推理类型并配置相应的参数,例如实例类型、计数和模型变体之间的流量分配。

为了在使用 SageMaker 托管时进一步帮助我们的客户,我们推出了新的 ModelBuilder SageMaker Python SDK 中的类,在将模型部署到 SageMaker 端点时带来以下主要优势:

  • 统一跨框架的部署体验 – 新体验为部署使用 PyTorch、TensorFlow 和 XGBoost 等不同框架构建的模型提供了一致的工作流程。 这简化了部署过程。
  • 自动化模型部署 – 选择适当的容器、捕获依赖项以及处理序列化/反序列化等任务都是自动化的,从而减少了部署所需的手动工作量。
  • 提供从本地到 SageMaker 托管端点的平滑过渡 – 通过最少的代码更改,模型可以轻松地从本地测试过渡到 SageMaker 端点上的部署。 实时日志使调试变得无缝。

总体而言,SageMaker ModelBuilder 通过处理低级细节来简化 SageMaker 推理的模型打包流程,并提供用于测试、验证和优化端点的工具。 这提高了开发人员的工作效率并减少了错误。

在以下部分中,我们将深入探讨此新功能的详细信息。 我们还讨论了如何使用以下方法将模型部署到 SageMaker 托管 ModelBuilder,这简化了过程。 然后,我们将引导您了解不同框架的几个示例,以部署传统的 ML 模型和支持生成式 AI 用例的基础模型。

了解 SageMaker ModelBuilder

新的 ModelBuilder 是一个 Python 类,专注于采用使用 XGBoost 或 PyTorch 等框架构建的 ML 模型,并将其转换为准备在 SageMaker 上部署的模型。 ModelBuilder 提供了一个 build() 函数,根据模型服务器生成工件,以及 deploy() 用于在本地部署或部署到 SageMaker 端点的函数。 此功能的引入简化了模型与 SageMaker 环境的集成,优化了它们的性能和可扩展性。 下图显示了如何 ModelBuilder 在高水平上工作。

模型构建器类

建模者 类提供不同的定制选项。 然而,为了部署框架模型,模型构建者只需要模型、输入、输出和角色:

class ModelBuilder(
    model, # model id or model object
    role_arn, # IAM role
    schema_builder, # defines the input and output
    mode, # select between local deployment and depoy to SageMaker Endpoints
    ...
)

模式生成器

模式生成器 类使您能够定义端点的输入和输出。 它允许模式构建器生成相应的编组函数,用于序列化和反序列化输入和输出。 以下类文件提供了所有自定义选项:

class SchemaBuilder(
    sample_input: Any,
    sample_output: Any,
    input_translator: CustomPayloadTranslator = None,
    output_translator: CustomPayloadTranslator = None
)

然而,在大多数情况下,只需样本输入和输出就可以了。 例如:

input = "How is the demo going?"
output = "Comment la démo va-t-elle?"
schema = SchemaBuilder(input, output)

通过提供样本输入和输出, SchemaBuilder 可以自动确定必要的转换,使集成过程更加简单。 对于更高级的用例,可以灵活地为输入和输出提供自定义翻译函数,确保也可以有效地处理更复杂的数据结构。 我们在以下部分中通过使用各种框架部署不同的模型来演示这一点 ModelBuilder.

本地模式体验

在这个例子中,我们使用 ModelBuilder 在本地部署 XGBoost 模型。 您可以使用模式在本地测试和部署到 SageMaker 终端节点之间切换。 我们首先训练 XGBoost 模型(在本地或在 SageMaker 中)并将模型工件存储在工作目录中:

# Train the model
model = XGBClassifier()
model.fit(X_train, y_train)
model.save_model(model_dir + "/my_model.xgb")

然后我们通过传递实际的模型对象来创建一个 ModelBuilder 对象, SchemaBuilder 它使用样本测试输入和输出对象(与我们在训练和测试模型时使用的相同输入和输出)来推断所需的序列化。 请注意,我们使用 Mode.LOCAL_CONTAINER 指定本地部署。 之后,我们调用 建立 功能自动识别支持的框架容器镜像并扫描依赖项。 请看下面的代码:

model_builder_local = ModelBuilder(
    model=model,  
    schema_builder=SchemaBuilder(X_test, y_pred), 
    role_arn=execution_role, 
    mode=Mode.LOCAL_CONTAINER
)
xgb_local_builder = model_builder_local.build()

最后,我们可以调用 deploy 模型对象中的函数,它还提供实时日志记录以方便调试。 您不需要指定实例类型或数量,因为模型将在本地部署。 如果您提供了这些参数,它们将被忽略。 该函数将返回预测器对象,我们可以使用该对象对测试数据进行预测:

# note: all the serialization and deserialization is handled by the model builder.
predictor_local = xgb_local_builder.deploy(
# instance_type='ml.c5.xlarge',
# initial_instance_count=1
)

# Make prediction for test data. 
predictor_local.predict(X_test)

或者,您还可以使用以下命令控制模型的加载以及预处理和后处理 InferenceSpec。 我们将在本文后面提供更多详细信息。 使用 LOCAL_CONTAINER 是在部署到 SageMaker 终端节点之前在本地测试脚本的好方法。

参考 模型生成器-xgboost.ipynb 使用示例测试本地部署和 SageMaker 端点部署 ModelBuilder.

将传统模型部署到 SageMaker 端点

在以下示例中,我们展示了如何使用 ModelBuilder 部署传统的机器学习模型。

XGBoost 模型

与上一节类似,您可以通过更改将 XGBoost 模型部署到 SageMaker 终端节点 mode 创建时的参数 ModelBuilder 宾语:

model_builder = ModelBuilder(
    model=model,  
    schema_builder=SchemaBuilder(sample_input=sample_input, sample_output=sample_output), 
    role_arn=execution_role, 
    mode=Mode.SAGEMAKER_ENDPOINT
)
xgb_builder = model_builder.build()
predictor = xgb_builder.deploy(
    instance_type='ml.c5.xlarge',
    initial_instance_count=1
)

请注意,部署到 SageMaker 端点时,您需要在调用时指定实例类型和实例计数 deploy 功能。

参考 模型生成器-xgboost.ipynb 部署 XGBoost 模型的示例。

海卫一型号

您可以使用 ModelBuilder 为 PyTorch 模型提供服务 Triton 推理服务器。 为此,您需要指定 model_server 参数为 ModelServer.TRITON,传递一个模型,并有一个 SchemaBuilder 对象,它需要模型的样本输入和输出。 模型构建器将为您处理剩下的事情。

model_builder = ModelBuilder(
    model=model,  
    schema_builder=SchemaBuilder(sample_input=sample_input, sample_output=sample_output), 
    role_arn=execution_role,
    model_server=ModelServer.TRITON, 
    mode=Mode.SAGEMAKER_ENDPOINT
)

triton_builder = model_builder.build()

predictor = triton_builder.deploy(
    instance_type='ml.g4dn.xlarge',
    initial_instance_count=1
)

请参阅 模型构建器-triton.ipynb 使用 Triton 部署模型。

拥抱脸模型

在此示例中,我们向您展示如何将 Hugging Face 提供的预训练 Transformer 模型部署到 SageMaker。 我们想使用拥抱脸 pipeline 加载模型,因此我们创建一个自定义推理规范 ModelBuilder:

# custom inference spec with hugging face pipeline
class MyInferenceSpec(InferenceSpec):
    def load(self, model_dir: str):
        return pipeline("translation_en_to_fr", model="t5-small")
        
    def invoke(self, input, model):
        return model(input)
    
inf_spec = MyInferenceSpec()

我们还通过定义推理工作负载的输入和输出 SchemaBuilder 基于模型输入和输出的对象:

schema = SchemaBuilder(value,output)

然后我们创建 ModelBuilder 对象并按照与其他示例中所示相同的逻辑将模型部署到 SageMaker 端点上:

builder = ModelBuilder(
    inference_spec=inf_spec,
    mode=Mode.SAGEMAKER_ENDPOINT,  # you can change it to Mode.LOCAL_CONTAINER for local testing
    schema_builder=schema,
    image_uri=image,
)
model = builder.build(
    role_arn=execution_role,
    sagemaker_session=sagemaker_session,
)
predictor = model.deploy(
    initial_instance_count=1,
    instance_type='ml.g5.2xlarge'
)

请参阅 模型生成器-huggingface.ipynb 部署 Hugging Face 管道模型。

将基础模型部署到 SageMaker 端点

在以下示例中,我们展示了如何使用 ModelBuilder 部署基础模型。 就像前面提到的型号一样,所需要的只是型号 ID。

拥抱脸中心

如果您想从以下位置部署基础模型 拥抱脸中心,您所需要做的就是传递预训练的模型ID。 例如,以下代码片段部署 元美洲驼/美洲驼-2-7b-hf 本地模型。 您可以将模式更改为 Mode.SAGEMAKER_ENDPOINT 部署到 SageMaker 端点。

model_builder = ModelBuilder(
    model="meta-llama/Llama-2-7b-hf",
    schema_builder=SchemaBuilder(sample_input, sample_output),
    model_path="/home/ec2-user/SageMaker/LoadTestResources/meta-llama2-7b", #local path where artifacts will be saved
    mode=Mode.LOCAL_CONTAINER,
    env_vars={
        # Llama 2 is a gated model and requires a Hugging Face Hub token.
        "HUGGING_FACE_HUB_TOKEN": "<YourHuggingFaceToken>"
 
    }
)
model = model_builder.build()
local_predictor = model.deploy()

对于 Hugging Face Hub 上的门控模型,您需要通过 Hugging Face Hub 请求访问,并通过将其作为环境变量传递来使用关联的密钥 HUGGING_FACE_HUB_TOKEN。 某些 Hugging Face 模型可能需要信任远程代码。 它可以设置为环境变量以及使用 HF_TRUST_REMOTE_CODE。 默认, ModelBuilder 将使用拥抱面部文本生成推理(TGI) 容器作为 Hugging Face 模型的底层容器。 如果您想使用 AWS 大型模型推理 (LMI) 集装箱,您可以设置 model_server 参数为 ModelServer.DJL_SERVING 当您配置 ModelBuilder 目的。

一个整洁的特点 ModelBuilder 是在使用时对容器参数进行本地调整的能力 LOCAL_CONTAINER 模式。 只需运行即可使用此功能 tuned_model = model.tune().

请参阅 演示模型构建器-huggingface-llama2.ipynb 部署 Hugging Face Hub 模型。

SageMaker 快速启动

亚马逊SageMaker JumpStart 还提供了许多预先训练的基础模型。 就像从 Hugging Face Hub 部署模型的过程一样,需要模型 ID。 将 SageMaker JumpStart 模型部署到 SageMaker 端点就像运行以下代码一样简单:

model_builder = ModelBuilder(
    model="huggingface-llm-falcon-7b-bf16",
    schema_builder=SchemaBuilder(sample_input, sample_output),
    role_arn=execution_role
)

sm_ep_model = model_builder.build()

predictor = sm_ep_model.deploy()

有关所有可用的 SageMaker JumpStart 模型 ID,请参阅 带有预训练模型表的内置算法。 参考 模型生成器-jumpstart-falcon.ipynb 部署 SageMaker JumpStart 模型。

推理部分

ModelBulder 允许您使用 SageMaker 中的新推理组件功能来部署模型。 有关推理组件的更多信息,请参阅 使用 SageMaker 的最新功能将模型部署成本平均降低 50%。 您可以使用推理组件进行部署 ModelBuilder 通过指定 endpoint_type=EndpointType.INFERENCE_COMPONENT_BASED ,在 deploy() 方法。 您还可以使用 tune() 方法,获取最佳加速器数量,并根据需要进行修改。

resource_requirements = ResourceRequirements(
    requests={
        "num_accelerators": 4,
        "memory": 1024,
        "copies": 1,
    },
    limits={},
)

goldfinch_predictor_2 = model_2.deploy(
    mode=Mode.SAGEMAKER_ENDPOINT,
    endpoint_type=EndpointType.INFERENCE_COMPONENT_BASED,
    ...
	
)

请参阅 模型构建器推理组件.ipynb 将模型部署为推理组件。

自定义模型构建器类

ModelBuilder 类允许您使用自定义模型加载 InferenceSpec.

此外,您可以控制有效负载和响应序列化和反序列化,并使用自定义预处理和后处理 CustomPayloadTranslator。 此外,当您需要扩展我们预构建的容器以在 SageMaker 上部署模型时,您可以使用 ModelBuilder 处理模型打包过程。 在以下部分中,我们将提供这些功能的更多详细信息。

推理规范

推理规范 提供额外的定制层。 它允许您定义模型的加载方式以及它将如何处理传入的推理请求。 通过 InferenceSpec,您可以为模型定义自定义加载过程,绕过默认加载​​机制。 当使用非标准模型或自定义推理管道时,这种灵活性特别有用。 可以自定义调用方法,使您能够定制模型处理传入请求的方式(预处理和后处理)。 这种定制对于确保推理过程符合模型的特定需求至关重要。 请看下面的代码:

class InferenceSpec(abc.ABC):
    @abc.abstractmethod
    def load(self, model_dir: str):
        pass

    @abc.abstractmethod
    def invoke(self, input_object: object, model: object):
        pass

以下代码显示了使用此类的示例:

class MyInferenceSpec(InferenceSpec):
    def load(self, model_dir: str):
        return // model object

    def invoke(self, input, model):
        return model(input)

自定义PayloadTranslator

调用 SageMaker 端点时,数据通过具有不同 MIME 类型的 HTTP 负载发送。 例如,发送到端点进行推理的图像需要在客户端转换为字节并通过 HTTP 负载发送到端点。 当端点接收到有效负载时,它需要将字节字符串反序列化回模型期望的数据类型(也称为 服务器端反序列化)。 模型完成预测后,需要将结果序列化为可以通过 HTTP 负载发送回用户或客户端的字节。 当客户端收到响应的字节数据时,需要进行客户端反序列化,将字节数据转换回期望的数据格式,例如JSON。 最低限度, 您需要转换以下数据 (如下图编号):

  1. 推理请求序列化(由客户端处理)
  2. 推理请求反序列化(由服务器或算法处理)
  3. 针对负载调用模型
  4. 发回响应负载
  5. 推理响应序列化(由服务器或算法处理)
  6. 推理响应反序列化(由客户端处理)

下图展示了调用过程中序列化和反序列化的过程。

在下面的代码片段中,我们展示了一个示例 CustomPayloadTranslator 当需要额外的自定义来分别处理客户端和服务器端的序列化和反序列化时:

from sagemaker.serve import CustomPayloadTranslator

# request translator
class MyRequestTranslator(CustomPayloadTranslator):
    # This function converts the payload to bytes - happens on client side
    def serialize_payload_to_bytes(self, payload: object) -> bytes:
        # converts the input payload to bytes
        ... ...
        return  //return object as bytes
        
    # This function converts the bytes to payload - happens on server side
    def deserialize_payload_from_stream(self, stream) -> object:
        # convert bytes to in-memory object
        ... ...
        return //return in-memory object
        
# response translator 
class MyResponseTranslator(CustomPayloadTranslator):
    # This function converts the payload to bytes - happens on server side
    def serialize_payload_to_bytes(self, payload: object) -> bytes:
        # converts the response payload to bytes
        ... ...
        return //return object as bytes
    
    # This function converts the bytes to payload - happens on client side
    def deserialize_payload_from_stream(self, stream) -> object:
        # convert bytes to in-memory object
        ... ...
        return //return in-memory object

演示模型构建器-pytorch.ipynb 笔记本中,我们演示了如何使用以下命令轻松将 PyTorch 模型部署到 SageMaker 端点 ModelBuilderCustomPayloadTranslatorInferenceSpec 类。

部署阶段模型

如果您想暂存模型以进行推理或将模型存储在模型注册表中,您可以使用 model.create() or model.register()。 已在服务上创建启用的模型,然后您可以稍后进行部署。 请看下面的代码:

model_builder = ModelBuilder(
    model=model,  
    schema_builder=SchemaBuilder(X_test, y_pred), 
    role_arn=execution_role, 
)
deployable_model = model_builder.build()

deployable_model.create() # deployable_model.register() for model registry

使用自定义容器

SageMaker 提供 预构建的 Docker 镜像 因其内置算法以及支持的用于训练和推理的深度学习框架。 如果预构建的 SageMaker 容器不能满足您的所有要求,您可以扩展现有映像以满足您的需求。 通过扩展预构建的图像,您可以使用附带的深度学习库和设置,而无需从头开始创建图像。 有关如何扩展预构建容器的更多详细信息,请参阅 SageMaker 文档。 ModelBuilder 支持使用从我们预构建的 Docker 容器扩展的您自己的容器时的用例。

要在这种情况下使用您自己的容器映像,您需要设置字段 image_urimodel_server 定义时 ModelBuilder:

model_builder = ModelBuilder(
    model=model,  # Pass in the actual model object. its "predict" method will be invoked in the endpoint.
    schema_builder=SchemaBuilder(X_test, y_pred), # Pass in a "SchemaBuilder" which will use the sample test input and output objects to infer the serialization needed.
    role_arn=execution_role, 
    image_uri=image_uri, # REQUIRED FOR BYOC: Passing in image hosted in personal ECR Repo
    model_server=ModelServer.TORCHSERVE, # REQUIRED FOR BYOC: Passing in model server of choice
    mode=Mode.SAGEMAKER_ENDPOINT,
    dependencies={"auto": True, "custom": ["protobuf==3.20.2"]}
)

在这里, image_uri 将是存储在您帐户的容器映像 ARN Amazon Elastic Container注册 (Amazon ECR) 存储库。 举例如下:

# Pulled the xgboost:1.7-1 DLC and pushed to personal ECR repo
image_uri = "<your_account_id>.dkr.ecr.us-west-2.amazonaws.com/my-byoc:xgb"

当。。。的时候 image_uri 被设置,期间 ModelBuilder 构建过程中,当提供图像 URI 时,它将跳过图像的自动检测。 如果 model_server 未在模型构建器中设置,您将收到验证错误消息,例如:

ValueError: Model_server must be set when image_uri is set. Supported model servers: {<ModelServer.TRITON: 5>, <ModelServer.DJL_SERVING: 4>, <ModelServer.TORCHSERVE: 1>}

截至本文发布时, ModelBuilder 支持携带您自己的容器,这些容器是从我们的扩展而来的 预构建的 DLC 容器镜像 或使用模型服务器构建的容器,例如 深度Java库(DJL), 文本生成推理 (TGI), 火炬服务Triton 推理服务器.

自定义依赖项

跑步时 ModelBuilder.build(),默认情况下它会自动将您的 Python 环境捕获到 requirements.txt 文件并在容器中安装相同的依赖项。 但是,有时你本地的Python环境会与容器中的环境发生冲突。 ModelBuilder 为您提供了一种简单的方法来修改捕获的依赖项以修复此类依赖项冲突,允许您将自定义配置提供到 ModelBuilder。 请注意,这仅适用于 TorchServe 和 Triton InferenceSpec。 例如,您可以在 ModelBuilder 中指定输入参数依赖项,即 Python 字典,如下所示:

dependency_config = {
   "auto" = True,
   "requirements" = "/path/to/your/requirements.txt"
   "custom" = ["module>=1.2.3,<1.5", "boto3==1.16.*", "some_module@http://some/url"]
}
  
ModelBuilder(
    # Other params
    dependencies=dependency_config,
).build()

我们定义以下字段:

  • 汽车 – 是否尝试自动捕获环境中的依赖关系。
  • – 你自己的路径字符串 requirements.txt 文件。 (这是可选的。)
  • 习俗 – 您想要添加或修改的任何其他自定义依赖项的列表。 (这是可选的。)

如果在多个地方指定同一个模块, custom 将具有最高优先级,那么 requirementsauto 将具有最低优先级。 例如,假设在自动检测期间, ModelBuilder 检测 numpy==1.25和一个 requirements.txt 提供的文件指定 numpy>=1.24,<1.26。 此外,还有一个自定义依赖项: custom = ["numpy==1.26.1"]。 在这种情况下, numpy==1.26.1 当我们在容器中安装依赖项时将被选择。

清理

完成模型测试后,作为最佳实践,如果不再需要端点,请删除该端点以节省成本。 您可以按照 清理 每个演示笔记本中的部分或使用以下代码删除演示创建的模型和端点:

predictor.delete_model()
predictor.delete_endpoint()

结论

新的 SageMaker ModelBuilder 功能简化了在 SageMaker 上将 ML 模型部署到生产中的过程。 通过处理幕后的许多复杂细节,模型构建器缩短了新用户的学习曲线,并最大限度地提高了经验丰富的用户的利用率。 只需几行代码,您就可以将具有 XGBoost、PyTorch、Triton 和 Hugging Face 等内置框架的模型以及 SageMaker JumpStart 提供的模型部署到 SageMaker 上强大、可扩展的端点中。

我们鼓励所有 SageMaker 用户通过参考以下内容来尝试此新功能 建模者 文档页面。 ModelBuilder 现已向所有 SageMaker 用户开放,无需额外付费。 利用这种简化的工作流程来更快地部署您的模型。 我们期待听到 ModelBuilder 如何加速您的模型开发生命周期!

特别感谢 Sirisha Upadhyayala、Raymond Liu、Gary Wang、Dhawal Patel、Deepak Garg 和 Ram Vegiraju。


关于作者

李梅兰妮博士,是澳大利亚悉尼 AWS 的高级 AI/ML 专家 TAM。 她帮助企业客户使用 AWS 上最先进的 AI/ML 工具构建解决方案,并提供有关通过最佳实践构建和实施 ML 解决方案的指导。 在业余时间,她喜欢探索大自然并与家人和朋友共度时光。

马克卡普 是 Amazon SageMaker 服务团队的 ML 架构师。 他专注于帮助客户大规模设计、部署和管理 ML 工作负载。 在业余时间,他喜欢旅行和探索新的地方。

山姆爱德华兹是 AWS 悉尼的一名云工程师 (AI/ML),专门从事机器学习和 Amazon SageMaker。 他热衷于帮助客户解决与机器学习工作流程相关的问题并为他们创建新的解决方案。 工作之余,他喜欢球拍运动和旅行。

拉古·拉梅沙 是 Amazon SageMaker Service 团队的高级 ML 解决方案架构师。 他专注于帮助客户大规模构建、部署 ML 生产工作负载并将其迁移到 SageMaker。 他专注于机器学习、人工智能和计算机视觉领域,并拥有 UT 达拉斯分校计算机科学硕士学位。 空闲时间,他喜欢旅行和摄影。

湿婆拉吉科蒂尼 担任 Amazon SageMaker 推理产品组合的首席产品经理。 他专注于 SageMaker 中的模型部署、性能调整和推理优化。

莫汉甘地 是 AWS 的高级软件工程师。 在过去的 10 年里,他一直在 AWS 工作,并从事过各种 AWS 服务,例如 EMR、EFA 和 RDS。 目前,他专注于改进 SageMaker 推理体验。 在业余时间,他喜欢远足和马拉松。

时间戳记:

更多来自 AWS机器学习