快速上手

最后更新于

这里以 Pytorch ResNet50 为例,介绍一下模型部署的流程。所有的代码可以在 openbayes-serving-examples/pytorch/image-classifier-resnet50/ 获取。

准备模型

import torch
from torchvision import models

# 加载预训练模型
model = models.resnet50(pretrained=True)

# 保存到本地
torch.save(model.state_dict(), "resnet50.pt")

编写模型部署脚本(predict.py)

目前 OpenBayes 模型部署对 ONNX 模型文件有特别的支持(ONNXPredictor),其他则可以使用通用的 PythonPredictor。这里先介绍适合于 PyTorch 的 .pt / .pth 格式文件的 PythonPredictor。

predictor.py 文件需要包含一个名为 PythonPredictor 的类,其结构如下:

class PythonPredictor:
    def __init__(self):
        """
        负责加载相应的模型以及对元数据的初始化
        """
        pass

    def predict(self, payload, query_params, headers):
        """
        接受 HTTP 请求的内容(`payload`、`query_params`、`headers`)进行必要的预处理(preprocess)后预测结果,最终将结果进行后处理(postprocess)并返回给调用方
        """
        pass

具体的参数和介绍可以在Serving Predictor找到。

而对于 PyTorch ResNet50 其 predictor.py 的内容如下所示:

import torch
import cv2
import numpy as np
import json
import requests
from torchvision import models, transforms


def get_url_image(url_image):
    resp = requests.get(url_image, stream=True).raw
    image = np.asarray(bytearray(resp.read()), dtype="uint8")
    image = cv2.imdecode(image, cv2.IMREAD_COLOR)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    return image


class PythonPredictor:
    def __init__(self, config):
        # 加载分类元数据
        classes = json.load(open('classes.json'))
        self.idx2label = [classes[str(k)][1] for k in range(len(classes))]

        # 指定模型文件名称
        model_name = 'resnet50.pt'

        # 加载模型
        self.device = "cuda" if torch.cuda.is_available() else "cpu"
        self.model = models.resnet50()
        self.model.load_state_dict(torch.load(model_name))
        self.model.eval()
        self.model = self.model.to(self.device)

        # 图像预处理,包括 normalization 和 resize
        normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        self.transform = transforms.Compose(
            [
                transforms.ToPILImage(),
                transforms.Resize([224, 224]),
                transforms.ToTensor(),
                normalize,
            ]
        )

    def predict(self, payload):
        # 从 payload.url 获取图片的 url,即要求传递的 HTTP 请求内容为 {"url": "xxx"}
        imageurl = payload["url"]
        # 获取图片内容
        image = get_url_image(imageurl)
        # 图片预处理
        image = self.transform(image)
        image = torch.tensor(image.numpy()[np.newaxis, ...])

        # 推理
        results = self.model(image.to(self.device))

        # 获取结果前五
        top5_idx = results[0].sort()[1][-5:]

        # 获取前五分类的名称
        top5_labels = [self.idx2label[idx] for idx in top5_idx]
        top5_labels = top5_labels[::-1]

        return top5_labels

上传到数据仓库

将已经准备好的 .pt 文件与 predictor.py 文件以及其他需要的文件放置于同一个目录下,目录中必须包含的文件如下所示:

.
├── classes.json
├── predictor.py
└── resnet50.pt

其中

  • resnet50.pt 为模型文件。
  • predictor.pyPythonPredictor 文件,目前 OpenBayes 模型部署要求其文件名称固定为 predictor.py 使用其他的文件名将会导致部署失败。 `
  • classes.json 包含 resnet 分类的元数据,在展示分类的名称时使用。

创建一个模型仓库:

将以上三个文件上传:

上传文件

查看上传结果:

创建 Serving

左侧导航栏「算力容器」->「模型部署」创建一个新的 Serving,选择镜像类型为「Python」:

绑定模型目录,选择刚才上传的模型版本目录:

点击部署等待部署标记为「运行中」:

点击当前最新的部署版本(也是目前唯一的部署版本)查看日志:

测试

在模型展示为「运行中」后可以看到「概览」页面展示了当前部署的 http 访问地址。

通过 curl 工具可以对该当前部署的服务进行测试:

curl -X POST \
    <服务地址> \
    -H "Content-Type: application/json" \
    --data-raw '{ "url": "http://openbayes-public.cn-bj.ufileos.com/cat.jpg" }'

「模型部署」默认使用的请求为 JSON 格式,其中 url 字段与我们上文 predictor.pypayload 中获取的 url 相对应。

可以获得相应的结果:

["tabby", "Egyptian_cat", "tiger_cat", "lynx", "tiger"]