全部产品
Search
文档中心

人工智能平台 PAI:通过镜像使用TensorFlow Serving部署模型服务

更新时间:Nov 11, 2024

TensorFlow Serving是一个适用于深度学习模型的推理服务引擎,支持将TensorFlow标准的SavedModel格式的模型部署为在线服务,并支持模型热更新与模型版本管理等功能。本文为您介绍如何使用镜像部署的方式部署TensorFlow Serving模型服务。

前提准备

模型文件

使用TensorFlow Serving镜像部署要求模型文件存储在OSS,且模型文件的存储目录需满足以下要求:

  • 模型版本目录:每个模型至少包含一个模型版本目录,且必须以数字命名,作为模型版本号,数字越大版本号越新。

  • 模型文件:模型版本目录下存放导出的SavedModel格式的模型文件,服务会自动加载最大模型版本号目录下的模型文件。

操作步骤如下:

  1. 在OSS存储空间中创建模型存储目录(例:oss://examplebucket/models/tf_serving/),详情请参见管理目录

  2. 将模型文件上传到步骤1创建的目录(您可以下载本文案例TensorFlowServing模型文件使用),得到模型存储目录格式如下:

    tf_serving 
    ├── modelA
    │   └── 1
    │       ├── saved_model.pb
    │       └── variables
    │           ├── variables.data-00000-of-00001
    │           └── variables.index
    │
    ├── modelB
    │   ├── 1
    │   │   └── ...
    │   └── 2
    │       └── ...
    │
    └── modelC
        ├── 1
        │   └── ...
        ├── 2
        │   └── ...
        └── 3
            └── ...

模型配置文件

通过配置文件的方式可以实现让一个服务同时运行多个模型。如果您只需要部署单模型服务,可跳过本小节。

请根据如下说明创建配置文件并上传到OSS(模型文件准备小节中给出的案例文件里已有一个模型配置文件model_config.pbtxt,您可直接使用或按需修改)。本案例中将模型配置文件上传到目录oss://examplebucket/models/tf_serving/

模型配置文件model_config.pbtxt内容示例如下:

model_config_list {
  config {
    name: 'modelA'
    base_path: '/models/modelA/'
    model_platform: 'tensorflow'
    model_version_policy{
        all: {}
    }
  }
  config {
    name: 'modelB'
    base_path: '/models/modelB/'
    model_platform: 'tensorflow'
    model_version_policy{
        specific {
            versions: 1
            versions: 2
        }
    }
    version_labels {
      	key: 'stable'
      	value: 1
    }
    version_labels {
      	key: 'canary'
      	value: 2
    }
  }
  config {
    name: 'modelC'
    base_path: '/models/modelC/'
    model_platform: 'tensorflow'
    model_version_policy{
        latest {
            num_versions: 2
        }
    }
  }
}

其中关键配置说明如下:

参数

是否必选

描述

name

自定义配置模型名称。建议配置该参数,如果不配置模型名称,则model_name为空,后续无法调用该模型服务。

base_path

配置模型存储目录在实例中的路径,后续部署服务时用于读取模型文件。例如:挂载目录为/models,要加载的模型目录为/models/modelA,则该参数配置为/models/modelA

model_version_policy

表示模型版本加载策略。

  • 不配置该参数:表示默认加载模型最新版本。

  • all{}:表示加载该模型所有版本。示例中modelA模型加载所有版本。

  • latest{}:示例中modelC配置为num_versions: 2,表示加载最新的2个版本,即版本2和3。

  • specific{}:表示加载指定版本。示例中modelB模型加载版本1和2。

version_labels

为模型版本配置自定义标签。如果没有配置version_labels,那么就只能通过版本号来区分模型版本,请求路径为:/v1/models/<model name>/versions/<version number>:predict

如果设置了version_labels,那么就可以请求version label来指向指定的版本号/v1/models/<model name>/labels/<version label>:predict

说明

标签默认只能分配给已成功加载并启动为服务的模型版本。若想要预先为尚未加载的模型版本分配标签,需要在运行命令中设置启动参数--allow_version_labels_for_unavailable_models=true。当前场景化模型部署不支持设置运行命令,请选择自定义模型部署。

部署服务

可以通过以下两种方法来进行TensorFlow Serving的镜像部署。

  • 场景化模型部署:适用于基本场景部署。您只需配置几个参数,即可一键部署TensorFlow Serving模型服务。

  • 自定义模型部署:适用于在特定环境下运行的服务。您可以根据自身业务的具体需求来调整更多配置选项,从而实现更灵活的服务配置。

重要

TensorFlow Serving模型服务支持配置8501和8500两种端口。

  • 8501:支持HTTP请求,在8501端口启动HTTP或REST服务。

  • 8500:支持gRPC请求,在8500端口启动gRPC服务。

场景化模型部署默认使用8501端口,无法修改。如果您需要配置8500端口来使用gRPC服务请选择自定义模型部署。

以下示例中单模型部署以部署modelA为例进行说明。

场景化模型部署

具体操作步骤如下:

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 模型在线服务(EAS)页面,单击部署服务。然后在场景化模型部署区域,单击TFServing部署

  3. TFServing部署页面配置参数。关键参数说明如下,其他参数配置说明,请参见服务部署:控制台

    参数

    描述

    部署方式

    支持以下两种部署方式:

    • 标准模型部署:用于部署单模型服务。

    • 配置文件部署:用于部署多模型服务。

    模型配置

    部署方式选择标准模型部署时,您需要配置模型文件所在的OSS路径。

    部署方式选择配置文件部署时,您需要配置以下参数:

    • OSS:选择模型文件所在的OSS路径。

    • 挂载路径:挂载服务实例中的目标路径,用来读取模型文件。

    • 配置文件:选择模型配置文件所在的OSS路径。

    参数配置示例:

    参数

    单模型示例(部署modelA)

    多模型示例

    服务名称

    modela_scene

    multi_scene

    部署方式

    选择标准模型部署

    选择配置文件部署

    模型配置

    OSSoss://examplebucket/models/tf_serving/modelA/

    • OSS:oss://examplebucket/models/tf_serving/

    • 挂载路径:/models

    • 配置文件: oss://examplebucket/models/tf_serving/model_config.pbtxt

  4. 参数配置完成后,单击部署

自定义模型部署

具体操作步骤如下:

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 模型在线服务(EAS)页面,单击部署服务。然后在自定义模型部署区域,单击自定义部署

  3. 新建服务页面配置参数,关键参数说明如下,其他参数配置说明,请参见服务部署:控制台

    参数

    描述

    镜像选择

    PAI平台镜像列表中选择tensorflow-serving和对应的镜像版本。建议选择最新版本。

    说明

    如果服务需要使用GPU,则镜像版本必须选择x.xx.x-gpu

    模型配置

    单击填写模型配置,进行模型配置。支持多种方式配置模型文件,这里采用OSS。

    • OSS路径:选择模型文件所在的OSS路径。

    • 挂载路径:挂载服务实例中的目标路径,用来读取模型文件。

    运行命令

    tensorflow-serving的启动参数,选择tensorflow-serving镜像会自动加载命令:/usr/bin/tf_serving_entrypoint.sh,还需设置的参数说明如下。

    单模型部署启动参数:

    • --model_name:模型名称,用于服务请求中的URL。如果不配置,默认名称为model。

    • --model_base_path:用于指定模型存储目录在实例中的路径。如果不配置,默认路径为/models/model

    多模型部署启动参数:

    • --model_config_file:必选。用来指定模型配置文件。

    • --model_config_file_poll_wait_seconds:选填。如果您希望在服务启动后修改模型配置文件的内容,需要配置轮询模型文件的周期,单位为秒。服务会按照配置的时间定期读取模型配置文件的内容。例如--model_config_file_poll_wait_seconds=30表示服务每隔30秒读取一次模型配置文件内容。

      说明

      当模型服务读取新的模型配置文件时,只会执行新配置文件中的内容。例如:旧配置文件中包含模型A,而新配置文件将模型A删除并增加了模型B的配置,那么服务会卸载模型A并加载模型B。

    • --allow_version_labels_for_unavailable_models:选填。默认为false,如果您想预先为尚未加载的模型版本分配标签,需要将该参数配置为true。例如--allow_version_labels_for_unavailable_models=true

    参数配置示例:

    参数

    单模型示例(部署modelA)

    多模型示例

    部署方式

    选择镜像部署服务

    镜像选择

    选择PAI平台镜像tensorflow-serving,版本选择2.14.1。

    模型配置

    模型配置选择OSS

    OSS路径:oss://examplebucket/models/tf_serving/

    挂载路径:配置为/models

    运行命令

    /usr/bin/tf_serving_entrypoint.sh --model_name=modelA --model_base_path=/models/modelA

    /usr/bin/tf_serving_entrypoint.sh --model_config_file=/models/model_config.pbtxt --model_config_file_poll_wait_seconds=30 --allow_version_labels_for_unavailable_models=true

    端口号默认为8501,服务将在8501端口启动HTTP或REST服务,支持HTTP请求。若您需要该服务支持gRPC请求,需要执行以下操作:

    • 模型服务信息运行命令将端口号修改为8500。

    • 服务功能配置是否使用GRPC协议打开开关。

    • 对应配置编辑中添加以下配置

      "networking": {
          "path": "/"
      }
  4. 单击部署

发送服务请求

根据服务部署时运行命令中配置的端口号,分别支持HTTP和gRPC两种请求协议。下面给出modelA的服务请求示例。

  1. 测试数据准备

    modelA是一个图片分类模型,训练数据集为Fashion-MNIST,样本是28x28的灰度图片,模型输出是样本属于10个分类的概率值。为了测试方便,我们使用[[[[1.0]] * 28] * 28]作为modelA服务请求的测试数据。

  2. 请求示例:

    HTTP请求

    端口号配置为8501,服务支持HTTP请求。下表总结了单模型和多模型部署情况下的HTTP请求路径:

    单模型

    多模型

    路径格式:<service_url>/v1/models/<model_name>:predict

    其中:

    • 场景化部署:<model_name> 不支持自定义配置,默认为model。

    • 自定义部署:<model_name>运行命令中配置的模型名称,如果没有配置,默认为model。

    支持不指定版本和指定模型版本两种请求方式,路径格式如下:

    • 不指定版本(默认加载最大版本):

      <service_url>/v1/models/<model_name>:predict

    • 指定模型版本:

      <service_url>/v1/models/<model_name>/versions/<version_num>:predict

    • 如果设置了version_labels:

      /v1/models/<model name>/labels/<version label>:predict

    其中,<model_name> 为模型配置文件中配置的模型名称。

    其中:<service_url> 是您部署的服务访问地址。您可以在模型在线服务(EAS)页面,单击待调用服务服务方式列下的调用信息,在公网地址调用页签查看服务访问地址。在使用控制台在线调试的时候,页面已自动填充该部分路径。

    以场景化部署单模型modelA为例,HTTP请求路径为:<service_url>/v1/models/model:predict

    下面以此为例为您具体说明如何通过控制台发送服务请求和通过Python代码发送服务请求:

    通过控制台发送服务请求

    服务部署完成后,单击服务操作列下的在线调试在线调试请求参数中已填充<service_url>,在地址后增加路径/v1/models/model:predictBody中配置服务请求数据:

    {"signature_name": "serving_default", "instances": [[[[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]]]}

    参数配置完成后,单击发送请求,输出如下类似结果。

    image

    通过Python代码发送服务请求

    Python代码示例如下:

    from urllib import request
    import json
    
    # 请替换为你的服务访问地址和Token。
    # 您可以在推理服务列表的服务方式列单击调用信息,在公网地址调用页签查看。
    service_url = '<service_url>'
    token = '<test-token>'
    # 场景化单模型部署为 model,其他情况参照上面路径说明表格
    model_name = "model"
    url = "{}/v1/models/{}:predict".format(service_url, model_name)
    
    # 创建HTTP请求。
    req = request.Request(url, method="POST")
    req.add_header('authorization', token)
    data = {
        'signature_name': 'serving_default',
        'instances': [[[[1.0]] * 28] * 28]
    }
    
    # 请求服务。
    response = request.urlopen(req, data=json.dumps(data).encode('utf-8')).read()
    
    # 查看返回结果。
    response = json.loads(response)
    print(response)

    gRPC请求

    端口号配置为8500,并添加gRPC相关配置后,服务支持发送gRPC请求。Python代码示例如下:

    import grpc
    import tensorflow as tf
    from tensorflow_serving.apis import predict_pb2
    from tensorflow_serving.apis import prediction_service_pb2_grpc
    from tensorflow.core.framework import tensor_shape_pb2
    
    # 服务访问地址。格式参见下面的host参数说明。
    host = "tf-serving-multi-grpc-test.166233998075****.cn-hangzhou.pai-eas.aliyuncs.com:80"
    # test-token替换为服务Token。您可以在公网地址调用页签查看Token。
    token = "<test-token>"
    
    # 模型名称。参见下面的name参数说明。
    name = "<model_name>"
    signature_name = "serving_default"
    # 配置为模型版本号。每次只能对单个模型版本发送请求。
    version = "<version_num>"
    
    # 创建gRPC请求。
    request = predict_pb2.PredictRequest()
    request.model_spec.name = name
    request.model_spec.signature_name = signature_name
    request.model_spec.version.value = version
    request.inputs["keras_tensor"].CopyFrom(tf.make_tensor_proto([[[[1.0]] * 28] * 28]))
    
    # 请求服务。
    channel = grpc.insecure_channel(host)
    stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
    metadata = (("authorization", token),)
    response, _ = stub.Predict.with_call(request, metadata=metadata)
    
    print(response)
    

    其中关键参数配置如下:

    参数

    描述

    host

    需要配置为服务访问地址,服务访问地址需要省略http://并在末尾添加:80。您可以在模型在线服务(EAS)页面,单击待调用服务服务方式列下的调用信息,在公网地址调用页签查看服务访问地址。

    name

    • 单模型发送gRPC请求

      • 场景化部署:配置为model。

      • 自定义部署:配置为运行命令中配置的模型名称,如果没有配置,默认为model。

    • 多模型发送gRPC请求

      配置为模型配置文件中配置的模型名称。

    version

    配置为模型版本号。每次只能对单个模型版本发送请求。

    metadata

    配置为服务Token。您可以在公网地址调用页签查看Token。

相关文档