调整自己的推理容器 - Amazon SageMaker
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

调整自己的推理容器

如果您无法将 使用预构建的 SageMaker Docker 镜像 Amazon 中列出的任何图像 SageMaker 用于您的用例,则可以构建自己的 Docker 容器,并在其中使用它 SageMaker 进行训练和推理。为了与之兼容 SageMaker,您的容器必须具有以下特性:

  • 您的容器必须在端口上列出 Web 服务器8080

  • 您的容器必须接受对/invocations/ping实时终端节点的POST请求。您发送到这些终端节点的请求必须在 60 秒后返回,并且最大大小为 6 MB。

要了解更多信息以及如何构建自己的 Docker 容器进行训练和推理的示例 SageMaker,请参阅构建自己的算法容器。

以下指南向您展示了如何在 Amazon SageMaker Studio Classic 中使用JupyterLab空间来调整推理容器以适应 SageMaker 托管。该示例使用 NGINX Web 服务器Gunicorn作为 Python Web 服务器网关接口和 Flask Web 应用程序框架。只要容器满足前面列出的要求,您就可以使用不同的应用程序来调整容器。有关使用自己的推理代码的更多信息,请参阅将您自己的推理代码用于托管服务

调整您的推理容器

使用以下步骤调整您自己的推理容器以适应 SageMaker 托管。以下步骤中显示的示例使用预先训练的命名实体识别 (NER) 模型,该模型使用 SpaCy 自然语言处理 (NLP) 库进行Python以下操作:

  • A Dockerfile 用于构建包含NER模型的容器。

  • 为NER模型提供服务的推理脚本。

如果您根据自己的用例调整此示例,则必须使用部署Dockerfile和提供模型所需的和推理脚本。

  1. 使用 Amazon SageMaker Studio Classic(可选)创建 JupyterLab 空间。

    您可以使用任何笔记本来运行脚本,以使您的推理容器适应 SageMaker 托管。此示例向您展示如何使用 Amazon SageMaker Studio Classic 中的JupyterLab空间来启动带有 SageMaker分发映像的JupyterLab应用程序。有关更多信息,请参阅 SageMaker JupyterLab

  2. 上传Docker文件和推理脚本。

    1. 在您的主目录中创建一个新文件夹。如果您使用的是JupyterLab,请在左上角选择 “新建文件夹” 图标,然后输入包含您的文件夹的名称。Dockerfile在此示例中,该文件夹名为docker_test_folder

    2. 将Dockerfile文本文件上传到您的新文件夹。以下示例使用来自 SpaCy Dockerfile 的预训练命名实体识别 (NER) 模型、运行该示例所需的应用程序和环境变量来创建Docker容器:

      FROM python:3.8 RUN apt-get -y update && apt-get install -y --no-install-recommends \ wget \ python3 \ nginx \ ca-certificates \ && rm -rf /var/lib/apt/lists/* RUN wget https://bootstrap.pypa.io/get-pip.py && python3 get-pip.py && \ pip install flask gevent gunicorn && \ rm -rf /root/.cache #pre-trained model package installation RUN pip install spacy RUN python -m spacy download en # Set environment variables ENV PYTHONUNBUFFERED=TRUE ENV PYTHONDONTWRITEBYTECODE=TRUE ENV PATH="/opt/program:${PATH}" COPY NER /opt/program WORKDIR /opt/program

      在前面的代码示例中,环境变PYTHONUNBUFFERED量不会Python缓冲标准输出流,这样可以更快地向用户传送日志。环境变量PYTHONDONTWRITEBYTECODE可以Python防止写入已编译的字节码.pyc文件,而对于这个用例来说,这些文件是不必要的。调用容器时,环境变量PATH用于标识trainserve程序的位置。

    3. 在新文件夹中创建一个新目录,以包含为模型提供服务的脚本。此示例使用名为的目录NER,其中包含运行此示例所需的以下脚本:

      • predictor.py— 一个Python脚本,其中包含用于加载模型并对模型执行推理的逻辑。

      • nginx.conf— 用于配置 Web 服务器的脚本。

      • serve— 启动推理服务器的脚本。

      • wsgi.py— 用于为模型提供服务的辅助脚本。

      重要

      如果您将推理脚本复制到以结尾的笔记本中.ipynb并对其进行重命名,则您的脚本可能包含格式化字符,从而阻止您的端点部署。而是创建一个文本文件并对其进行重命名。

    4. 上传脚本以使您的模型可用于推理。以下是一个名为的脚本示例predictor.py,Flask用于提供/ping/invocations端点:

      from flask import Flask import flask import spacy import os import json import logging #Load in model nlp = spacy.load('en_core_web_sm') #If you plan to use a your own model artifacts, #your model artifacts should be stored in /opt/ml/model/ # The flask app for serving predictions app = Flask(__name__) @app.route('/ping', methods=['GET']) def ping(): # Check if the classifier was loaded correctly health = nlp is not None status = 200 if health else 404 return flask.Response(response= '\n', status=status, mimetype='application/json') @app.route('/invocations', methods=['POST']) def transformation(): #Process input input_json = flask.request.get_json() resp = input_json['input'] #NER doc = nlp(resp) entities = [(X.text, X.label_) for X in doc.ents] # Transform predictions to JSON result = { 'output': entities } resultjson = json.dumps(result) return flask.Response(response=resultjson, status=200, mimetype='application/json')

      上一个脚本示例中的/ping端点返回的200状态代码为:模型404是否正确加载以及模型加载不正确。/invocations端点处理格式化为的请求JSON,提取输入字段,并使用NER模型识别和存储变量实体中的实体。Flask应用程序返回包含这些实体的响应。有关这些必需的运行状况请求的更多信息,请参阅容器应如何响应运行状况检查 (Ping) 请求

    5. 上传脚本以启动推理服务器。以下脚本示例serve使用Gunicorn作为应用程序服务器和作Nginx为 Web 服务器进行调用:

      #!/usr/bin/env python # This file implements the scoring service shell. You don't necessarily need to modify it for various # algorithms. It starts nginx and gunicorn with the correct configurations and then simply waits until # gunicorn exits. # # The flask server is specified to be the app object in wsgi.py # # We set the following parameters: # # Parameter Environment Variable Default Value # --------- -------------------- ------------- # number of workers MODEL_SERVER_WORKERS the number of CPU cores # timeout MODEL_SERVER_TIMEOUT 60 seconds import multiprocessing import os import signal import subprocess import sys cpu_count = multiprocessing.cpu_count() model_server_timeout = os.environ.get('MODEL_SERVER_TIMEOUT', 60) model_server_workers = int(os.environ.get('MODEL_SERVER_WORKERS', cpu_count)) def sigterm_handler(nginx_pid, gunicorn_pid): try: os.kill(nginx_pid, signal.SIGQUIT) except OSError: pass try: os.kill(gunicorn_pid, signal.SIGTERM) except OSError: pass sys.exit(0) def start_server(): print('Starting the inference server with {} workers.'.format(model_server_workers)) # link the log streams to stdout/err so they will be logged to the container logs subprocess.check_call(['ln', '-sf', '/dev/stdout', '/var/log/nginx/access.log']) subprocess.check_call(['ln', '-sf', '/dev/stderr', '/var/log/nginx/error.log']) nginx = subprocess.Popen(['nginx', '-c', '/opt/program/nginx.conf']) gunicorn = subprocess.Popen(['gunicorn', '--timeout', str(model_server_timeout), '-k', 'sync', '-b', 'unix:/tmp/gunicorn.sock', '-w', str(model_server_workers), 'wsgi:app']) signal.signal(signal.SIGTERM, lambda a, b: sigterm_handler(nginx.pid, gunicorn.pid)) # Exit the inference server upon exit of either subprocess pids = set([nginx.pid, gunicorn.pid]) while True: pid, _ = os.wait() if pid in pids: break sigterm_handler(nginx.pid, gunicorn.pid) print('Inference server exiting') # The main routine to invoke the start function. if __name__ == '__main__': start_server()

      前面的脚本示例定义了一个信号处理函数sigterm_handler,该函数在收到信号时会关闭Nginx和Gunicorn子进程。SIGTERMstart_server函数启动信号处理程序,启动和监视Nginx和Gunicorn子进程,并捕获日志流。

    6. 上传脚本来配置您的 Web 服务器。以下名nginx.conf为的脚本示例配置了一个Gunicorn用作应用服务器的 Nginx Web 服务器,以便为您的模型提供推理:

      worker_processes 1; daemon off; # Prevent forking pid /tmp/nginx.pid; error_log /var/log/nginx/error.log; events { # defaults } http { include /etc/nginx/mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log combined; upstream gunicorn { server unix:/tmp/gunicorn.sock; } server { listen 8080 deferred; client_max_body_size 5m; keepalive_timeout 5; proxy_read_timeout 1200s; location ~ ^/(ping|invocations) { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://gunicorn; } location / { return 404 "{}"; } } }

      前面的脚本示例配置Nginx为在前台运行,设置捕获位置error_log,并定义upstream为Gunicorn服务器的套接字插槽。服务器将服务器块配置为监听端口8080,设置客户端请求正文大小和超时值的限制。服务器块会将包含/ping/invocations路径的请求转发到该路径 Gunicornserver http://gunicorn,并返回其他路径的404错误。

    7. 上传为模型提供服务所需的任何其他脚本。此示例需要调用以下示例脚本wsgi.py来帮助Gunicorn查找您的应用程序:

      import predictor as myapp # This is just a simple wrapper for gunicorn to find your app. # If you want to change the algorithm file, simply change "predictor" above to the # new file. app = myapp.app

    在该文件夹中docker_test_folder,您的目录结构应包含Dockerfile和文件夹NER。该NER文件夹应包含文件nginx.confpredictor.py、和servewsgi.py如下所示:

    The Dockerfile structure has inference scripts under the NER directory next to the Dockerfile.

  3. 建造自己的容器。

    从该文件夹docker_test_folder中构建您的Docker容器。以下示例命令将生成在您中配置的Docker容器Dockerfile:

    ! docker build -t byo-container-test .

    前面的命令将在当前工作目录byo-container-test中构建一个名为的容器。有关Docker编译参数的更多信息,请参阅生成参数

    注意

    如果您收到以下Docker无法找到的错误消息Dockerfile,请确保名称正确且已保存到目录中。Dockerfile

    unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /home/ec2-user/SageMaker/docker_test_folder/Dockerfile: no such file or directory

    Docker在当前目录中查找一个专门调用的Dockerfile不带任何扩展名的文件。如果您将其命名为其他名称,则可以使用-f 标志手动传入文件名。例如,如果您将自己的命名Dockerfile为Dockerfile-text.txt,请使用-f标志(后面跟着您的文件)来构建Docker容器,如下所示:

    ! docker build -t byo-container-test -f Dockerfile-text.txt .
  4. 将您的Docker图片推送到亚马逊弹性容器注册表 (Amazon ECR) Container Registry

    在笔记本单元中,将您的Docker图像推送到 ECR。以下代码示例向您展示了如何在本地构建容器、登录并将其推送到 ECR:

    %%sh # Name of algo -> ECR algorithm_name=sm-pretrained-spacy #make serve executable chmod +x NER/serve account=$(aws sts get-caller-identity --query Account --output text) # Region, defaults to us-west-2 region=$(aws configure get region) region=${region:-us-east-1} fullname="${account}.dkr.ecr.${region}.amazonaws.com/${algorithm_name}:latest" # If the repository doesn't exist in ECR, create it. aws ecr describe-repositories --repository-names "${algorithm_name}" > /dev/null 2>&1 if [ $? -ne 0 ] then aws ecr create-repository --repository-name "${algorithm_name}" > /dev/nullfi # Get the login command from ECR and execute it directly aws ecr get-login-password --region ${region}|docker login --username AWS --password-stdin ${fullname} # Build the docker image locally with the image name and then push it to ECR # with the full name. docker build -t ${algorithm_name} . docker tag ${algorithm_name} ${fullname} docker push ${fullname}

    在前面的示例中,展示了如何执行将示例 Docker 容器推送到 ECR 所需的以下步骤:

    1. 将算法名称定义为sm-pretrained-spacy

    2. 使NER文件夹内的serve文件可执行。

    3. 设置 Amazon Web Services 区域.

    4. 如果 ECR 尚不存在,请将其创建。

    5. 登录 ECR。

    6. 在本地构建Docker容器。

    7. 将Docker图像推送到 ECR。

  5. 设置 SageMaker 客户端

    如果要使用 SageMaker 托管服务进行推理,则必须创建模型、创建端点配置并创建终端点。为了从您的端点获取推论,您可以使用 SageMaker boto3 Runtime 客户端来调用您的终端节点。以下代码向您展示了如何使用 SageMaker boto3 SageMaker 客户端设置客户端和 SageMaker 运行时客户端:

    import boto3 from sagemaker import get_execution_role sm_client = boto3.client(service_name='sagemaker') runtime_sm_client = boto3.client(service_name='sagemaker-runtime') account_id = boto3.client('sts').get_caller_identity()['Account'] region = boto3.Session().region_name #used to store model artifacts which SageMaker will extract to /opt/ml/model in the container, #in this example case we will not be making use of S3 to store the model artifacts #s3_bucket = '<S3Bucket>' role = get_execution_role()

    在前面的代码示例中,未使用 Amazon S3 存储桶,而是作为注释插入以展示如何存储模型工件。

    如果您在运行前面的代码示例后收到权限错误,则可能需要为您的 IAM 角色添加权限。有关 IAM 角色的更多信息,请参阅 Amazon SageMaker 角色管理器。有关为当前角色添加权限的更多信息,请参阅Amazon 适用于亚马逊的托管政策 SageMaker

  6. 创建您的模型。

    如果要使用 SageMaker 托管服务进行推理,则必须在中创建模型。 SageMaker以下代码示例向您展示了如何在内部创建spaCyNER模型 SageMaker:

    from time import gmtime, strftime model_name = 'spacy-nermodel-' + strftime("%Y-%m-%d-%H-%M-%S", gmtime()) # MODEL S3 URL containing model atrifacts as either model.tar.gz or extracted artifacts. # Here we are not #model_url = 's3://{}/spacy/'.format(s3_bucket) container = '{}.dkr.ecr.{}.amazonaws.com/sm-pretrained-spacy:latest'.format(account_id, region) instance_type = 'ml.c5d.18xlarge' print('Model name: ' + model_name) #print('Model data Url: ' + model_url) print('Container image: ' + container) container = { 'Image': container } create_model_response = sm_client.create_model( ModelName = model_name, ExecutionRoleArn = role, Containers = [container]) print("Model Arn: " + create_model_response['ModelArn'])

    前面的代码示例演示了如何model_url使用步骤 5 中注释中的 Amazon S3 存储桶,并定义容器映像的 ECR URI。s3_bucket前面的代码示例定义ml.c5d.18xlarge为实例类型。您也可以选择不同的实例类型。有关可用实例类型的更多信息,请参阅 Amazon EC2 实例类型

    在前面的代码示例中,Image关键指向容器镜像 URI。该create_model_response定义使用创建模型,并返回模型名称、角色和包含容器信息的列表。create_model method

    上一个脚本的输出示例如下:

    Model name: spacy-nermodel-YYYY-MM-DD-HH-MM-SS Model data Url: s3://spacy-sagemaker-us-east-1-bucket/spacy/ Container image: 123456789012.dkr.ecr.us-east-2.amazonaws.com/sm-pretrained-spacy:latest Model Arn: arn:aws:sagemaker:us-east-2:123456789012:model/spacy-nermodel-YYYY-MM-DD-HH-MM-SS
    1. 配置和创建端点

      要使用 SageMaker 托管进行推理,还必须配置和创建终端节点。 SageMaker 将使用此端点进行推理。以下配置示例显示如何使用您之前定义的实例类型和型号名称生成和配置终端节点:

      endpoint_config_name = 'spacy-ner-config' + strftime("%Y-%m-%d-%H-%M-%S", gmtime()) print('Endpoint config name: ' + endpoint_config_name) create_endpoint_config_response = sm_client.create_endpoint_config( EndpointConfigName = endpoint_config_name, ProductionVariants=[{ 'InstanceType': instance_type, 'InitialInstanceCount': 1, 'InitialVariantWeight': 1, 'ModelName': model_name, 'VariantName': 'AllTraffic'}]) print("Endpoint config Arn: " + create_endpoint_config_response['EndpointConfigArn'])

      在前面的配置示例中,将model_name与使用时间戳创建的唯一端点配置名称create_endpoint_config_responseendpoint_config_name相关联。

      上一个脚本的输出示例如下:

      Endpoint config name: spacy-ner-configYYYY-MM-DD-HH-MM-SS Endpoint config Arn: arn:aws:sagemaker:us-east-2:123456789012:endpoint-config/spacy-ner-config-MM-DD-HH-MM-SS

      有关终端节点错误的更多信息,请参阅创建或更新 SageMaker 终端节点时,为什么我的 Amazon 终端节点会进入故障状态?

    2. 创建终端节点并等待终端节点投入使用。

      以下代码示例使用上一个配置示例中的配置创建端点并部署模型:

      %%time import time endpoint_name = 'spacy-ner-endpoint' + strftime("%Y-%m-%d-%H-%M-%S", gmtime()) print('Endpoint name: ' + endpoint_name) create_endpoint_response = sm_client.create_endpoint( EndpointName=endpoint_name, EndpointConfigName=endpoint_config_name) print('Endpoint Arn: ' + create_endpoint_response['EndpointArn']) resp = sm_client.describe_endpoint(EndpointName=endpoint_name) status = resp['EndpointStatus'] print("Endpoint Status: " + status) print('Waiting for {} endpoint to be in service...'.format(endpoint_name)) waiter = sm_client.get_waiter('endpoint_in_service') waiter.wait(EndpointName=endpoint_name)

      在前面的代码示例中,该create_endpoint方法使用在上一个代码示例中创建的生成的终端节点名称创建终端节点,并打印该终端节点的 Amazon 资源名称。该describe_endpoint方法返回有关端点及其状态的信息。 SageMaker 服务员等待端点投入使用。

  7. 测试您的终端节点。

    终端节点投入使用后,向您的终端节点发送调用请求。以下代码示例显示了如何向您的终端节点发送测试请求:

    import json content_type = "application/json" request_body = {"input": "This is a test with NER in America with \ Amazon and Microsoft in Seattle, writing random stuff."} #Serialize data for endpoint #data = json.loads(json.dumps(request_body)) payload = json.dumps(request_body) #Endpoint invocation response = runtime_sm_client.invoke_endpoint( EndpointName=endpoint_name, ContentType=content_type, Body=payload) #Parse results result = json.loads(response['Body'].read().decode())['output'] result

    在前面的代码示例中,该方法将json.dumps序列化request_body为采用 JSON 格式的字符串,并将其保存在变量 payload 中。然后, SageMaker 运行时客户端使用调用端点方法向您的终端节点发送有效负载。结果包含提取输出字段后来自终端节点的响应。

    前面的代码示例应返回以下输出:

    [['NER', 'ORG'], ['America', 'GPE'], ['Amazon', 'ORG'], ['Microsoft', 'ORG'], ['Seattle', 'GPE']]
  8. 删除您的终端节点

    完成调用后,请删除您的终端节点以节省资源。以下代码示例向您展示了如何删除终端节点:

    sm_client.delete_endpoint(EndpointName=endpoint_name) sm_client.delete_endpoint_config(EndpointConfigName=endpoint_config_name) sm_client.delete_model(ModelName=model_name)

    有关包含此示例中代码的完整笔记本,请参阅 BYOC-Single-model。

对容器部署进行故障排除

如果您的终端节点未部署,请按如下方式查看 Amazon CloudWatch 事件日志:

  1. https://console.aws.amazon.com/sagemaker/ SageMaker 控制台导航窗格中,选择 Inference。

  2. 推理下面,选择端点

  3. 在 “名称” 下找到您的终端节点,然后单击终端节点的名称。在此示例中,名称将遵循命名约定spacy-ner-configYYYY-MM-DD-HH-MM-SS

  4. 在 “端点摘要” 下,选择 “模型容器日志” 下的链接。

  5. 日志流框中选择最新的日志流

使用以下列表对部署终端节点进行故障排除。如果您需要进一步的帮助,请联系 Su Amazon pp or Amazon t 或亚马逊开发者论坛 SageMaker

主题

  • 姓名错误

  • 配额不足

  • 上游超时错误

姓名错误

如果日志处于状态NameError: name 'null' is not defined,请确保您的脚本不是在以结尾的笔记本中创建的,.ipnyb然后重命名为其他文件名,例如Dockerfile。创建笔记本时,格式化字符可能会阻止部署终端节点。如果您遇到此错误并更改脚本来修复它,则可能需要重新启动内核才能使更改生效。

配额不足

如果您收到ResourceLimitExceeded错误消息,则必须按以下方式申请额外的配额:

申请提高 Amazon 服务配额
  1. 从屏幕上的错误消息中检索实例名称、当前配额和必要的配额。例如,在以下示例错误中:

    • 实例名称是ml.c5d.18xlarge

    • 以下数字中的当前配额current utilization1 instances

    • 以下数字中需要的额外配额request delta1 instances

    示例错误如下:

    ResourceLimitExceeded: An error occurred (ResourceLimitExceeded) when calling the CreateEndpoint operation: The account-level service limit 'ml.c5d.18xlarge for endpoint usage' is 1 Instances, with current utilization of 1 Instances and a request delta of 1 Instances. Please use Amazon Service Quotas to request an increase for this quota. If Amazon Service Quotas is not available, contact Amazon support to request an increase for this quota.
  2. 登录 Amazon Web Services Management Console 并打开 S ervice Quotas 控制台

  3. 在导航窗格的管理配额下,输入 Amazon SageMaker。

  4. 选择查看配额

  5. 服务配额下的搜索栏中,输入步骤 1 中的实例名称。例如,使用步骤 1 中错误消息中包含的信息进行输入ml.c5d.18xlarge

  6. 选择显示在您的实例名称旁边并以结尾的终端节点使用配额名称。例如,使用步骤 1 中错误消息中包含的信息,选择终端节点ml.g5.12xlarge的使用。

  7. 选择在账户层面申请加薪

  8. 在 “增加配额值” 下,根据步骤 1 的错误消息中提供的信息输入所需的必要配额。输入current utilization和的request delta。在前面的示例错误中1 Instancescurrent utilization是,request delta1 Instances。在此示例中,请求配额为2以提供所需的配额。

  9. 选择请求

  10. 从导航窗格中选择配额申请历史记录

  11. 状态从 “待定” 更改为已批准” 时,请重新运行您的作业。您可能需要刷新浏览器才能看到更改。

有关申请增加配额的更多信息,请参阅申请增加配额

上游超时错误

如果您收到upstream timed out (110: Connection timed out)错误,可以尝试以下方法:

  • 减少容器的延迟或增加容器的超时限制。 SageMaker 要求您的容器在 60 秒内响应请求。

  • 增加 Web 服务器等待模型响应之前的时间。

有关超时错误的更多信息,请参阅如何解决 Amazon SageMaker 推理错误 “从上游读取响应标头时上游超时(110:连接超时)”?