41个版本

0.8.6 2024年6月16日
0.8.4 2024年2月27日
0.8.3 2024年1月1日
0.8.2 2023年12月5日
0.2.1 2021年10月23日

#3 in 机器学习

Apache-2.0

175KB
3K SLoC

Python 1.5K SLoC // 0.3% comments Rust 1.5K SLoC // 0.1% comments

MOSEC

discord invitation link PyPI version conda-forge Python Version PyPi monthly Downloads License Check status

在云中高效实现模型服务。

简介

MOSEC

Mosec是一个高性能且灵活的模型服务框架,用于构建支持机器学习模型的后端和微服务。它弥合了您刚刚训练的任何机器学习模型和高效在线服务API之间的差距。

  • 高性能:使用Rust 🦀 构建的Web层和任务协调,除了异步I/O提供的有效CPU利用率外,还提供了闪电般的速度
  • 易于使用:纯Python 🐍 用户界面,用户可以使用与离线测试相同的代码以ML框架无关的方式提供他们的模型
  • 动态批处理:聚合来自不同用户的请求进行批量推理,并将结果分发给用户
  • 管道阶段:为管道阶段启动多个进程,以处理CPU/GPU/IO混合工作负载
  • 云友好:设计用于在云中运行,具有模型预热、优雅关闭和Prometheus监控指标,可由Kubernetes或任何容器编排系统轻松管理
  • 专注于一项任务:专注于在线服务部分,用户可以关注模型优化和业务逻辑

安装

Mosec需要Python 3.7或更高版本。使用以下命令安装最新版本的PyPI软件包

pip install -U mosec
# or install with conda
conda install conda-forge::mosec

要从源代码构建,安装 Rust 并运行以下命令

make package

您将在 dist 文件夹中获得一个mosec wheel文件。

用法

我们展示了Mosec如何帮助您轻松地将预训练的稳定扩散模型作为服务托管。您需要安装 diffuserstransformers 作为先决条件

pip install --upgrade diffusers[torch] transformers

编写服务器

点击此处获取带解释的服务器代码。

首先,我们导入库并设置一个基本的日志记录器,以便更好地观察发生了什么。

from io import BytesIO
from typing import List

import torch  # type: ignore
from diffusers import StableDiffusionPipeline  # type: ignore

from mosec import Server, Worker, get_logger
from mosec.mixin import MsgpackMixin

logger = get_logger()

然后,我们构建一个API,供客户端查询文本提示,并基于stable-diffusion-v1-5模型在3个步骤内获取图像。

  1. 将您的服务定义为一个类,该类继承自mosec.Worker。在这里,我们还继承MsgpackMixin来使用msgpack序列化格式(a)

  2. __init__方法内部,初始化您的模型并将其放置到相应的设备上。可选地,您可以将self.example分配一些数据来预热(b)模型。请注意,数据应与您的处理器的输入格式兼容,我们将在下面详细说明。

  3. 重写forward方法来编写您的服务处理器(c),签名如下:forward(self, data: Any | List[Any]) -> Any | List[Any]。接收/返回单个项或元组取决于是否配置了动态批量处理(d)

class StableDiffusion(MsgpackMixin, Worker):
    def __init__(self):
        self.pipe = StableDiffusionPipeline.from_pretrained(
            "runwayml/stable-diffusion-v1-5", torch_dtype=torch.float16
        )
        device = "cuda" if torch.cuda.is_available() else "cpu"
        self.pipe = self.pipe.to(device)
        self.example = ["useless example prompt"] * 4  # warmup (batch_size=4)

    def forward(self, data: List[str]) -> List[memoryview]:
        logger.debug("generate images for %s", data)
        res = self.pipe(data)
        logger.debug("NSFW: %s", res[1])
        images = []
        for img in res[0]:
            dummy_file = BytesIO()
            img.save(dummy_file, format="JPEG")
            images.append(dummy_file.getbuffer())
        return images

[!NOTE]

(a) 在此示例中,我们以二进制格式返回图像,JSON不支持(除非使用base64编码,这会使有效载荷更大)。因此,msgpack更适合我们的需求。如果我们不继承MsgpackMixin,则默认使用JSON。换句话说,服务请求/响应的协议可以是msgpack、JSON或任何其他格式(请参阅我们的混合)。

(b) 预热通常有助于提前分配GPU内存。如果指定了预热示例,则服务将在示例通过处理器转发后才能准备就绪。然而,如果没有提供示例,则第一个请求的延迟预期会更长。《code>example应根据forward期望接收的内容设置为单个项或元组。此外,如果您想要使用多个不同的示例进行预热,则可以设置multi_examples(示例见此处)。

(c) 此示例显示了一个单阶段服务,其中StableDiffusion工作直接接收客户端的提示请求并响应图像。因此,forward可以被视为完整的服务处理器。但是,我们也可以设计多阶段服务,其中工作在不同阶段的作业(例如,下载图像、模型推理、后处理)在管道中执行。在这种情况下,整个管道被视为服务处理器,第一个工作接收请求,最后一个工作发送响应。工作之间的数据流通过进程间通信完成。

(d) 在本例中启用了动态批处理,因此 forward 方法将希望接收一个字符串 列表,例如,['a cute cat playing with a red ball', 'a man sitting in front of a computer', ...],这是从不同的客户端聚合用于 批推理 的,从而提高了系统吞吐量。

最后,我们将工作进程添加到服务器中,构建一个 单阶段 工作流程(可以通过 流水线 将多个阶段进一步串联以提高吞吐量,见 此示例),并指定并行运行的进程数(num=1),以及最大批处理大小(max_batch_size=4,动态批处理在超时之前将累积的最大请求数量;超时通过 max_wait_time=10(毫秒)定义,这意味着 Mosec 等待将批处理发送给 Worker 的最长时间)。

if __name__ == "__main__":
    server = Server()
    # 1) `num` specifies the number of processes that will be spawned to run in parallel.
    # 2) By configuring the `max_batch_size` with the value > 1, the input data in your
    # `forward` function will be a list (batch); otherwise, it's a single item.
    server.append_worker(StableDiffusion, num=1, max_batch_size=4, max_wait_time=10)
    server.run()

运行服务器

点击此处查看如何运行和查询服务器。

上述片段已合并到我们的示例文件中。您可以直接在项目根目录下运行。我们首先查看 命令行参数(解释见 此处

python examples/stable_diffusion/server.py --help

然后以调试日志启动服务器

python examples/stable_diffusion/server.py --log-level debug --timeout 30000

在浏览器中打开 http://127.0.0.1:8000/openapi/swagger/ 以获取 OpenAPI 文档。

然后在另一个终端中测试它

python examples/stable_diffusion/client.py --prompt "a cute cat playing with a red ball" --output cat.jpg --port 8000

您将在当前目录中获得一个名为 "cat.jpg" 的图像。

您可以检查指标

curl http://127.0.0.1:8000/metrics

这就完成了!您刚刚将您的 stable-diffusion 模型 作为服务托管!😉

示例

更多现成示例可以在 示例 部分找到。它包括

配置

  • 动态批处理
    • 在调用 append_worker 时配置 max_batch_sizemax_wait_time (毫秒)
    • 确保使用 max_batch_size 值进行推理时不会导致 GPU 内存溢出。
    • 通常情况下,max_wait_time 应该小于批量推理时间。
    • 如果启用,则在累积请求数量达到 max_batch_sizemax_wait_time 已经过去时,会收集一个批次。在流量高峰时,该服务将受益于此功能。
  • 有关其他配置,请参阅参数文档

部署

  • 如果您正在寻找已安装 mosec 的 GPU 基础镜像,可以查看官方镜像mosecorg/mosec。对于复杂用例,请参阅envd
  • 此服务不需要 Gunicorn 或 NGINX,但在必要时当然可以使用入口控制器。
  • 由于它控制多个进程,因此此服务应该是容器中的 PID 1 进程。如果您需要在单个容器中运行多个进程,则需要一个监督器。您可以选择SupervisorHorust
  • 请记住收集 指标
    • mosec_service_batch_size_bucket 显示批量大小的分布。
    • mosec_service_batch_duration_second_bucket 显示每个阶段(从接收第一个任务开始)中每个连接的动态批处理持续时间。
    • mosec_service_process_duration_second_bucket 显示每个阶段中每个连接的处理持续时间(包括 IPC 时间,但不包括 mosec_service_batch_duration_second_bucket)。
    • mosec_service_remaining_task 显示当前正在处理的任务数量。
    • mosec_service_throughput 显示服务吞吐量。
  • 由于具有优雅的关闭逻辑,请使用 SIGINTCTRL+C)或 SIGTERMkill {PID})停止服务。

性能调整

  • 找出适合您的推理服务的最佳 max_batch_sizemax_wait_time。指标将显示实际批大小和批处理持续时间的直方图。这些是调整这两个参数的关键信息。
  • 尝试将整个推理过程分成独立的 CPU 和 GPU 阶段(参考 DistilBERT)。不同的阶段将在 数据管道 中运行,这将保持 GPU 忙碌。
  • 您还可以调整每个阶段的工人数。例如,如果您的管道由一个用于预处理的 CPU 阶段和一个用于模型推理的 GPU 阶段组成,则增加 CPU 阶段工人的数量可以帮助在 GPU 阶段生成更多要批量处理的数据;增加 GPU 阶段工人的数量可以充分利用 GPU 内存和计算能力。这两种方法都可能有助于提高 GPU 利用率,从而提高服务吞吐量。
  • 对于多阶段服务,请注意,通过不同阶段传递的数据将由 serialize_ipc/deserialize_ipc 方法进行序列化/反序列化,因此非常大的数据可能会使整个管道变慢。默认情况下,序列化数据通过 rust 传递到下一阶段,您可以通过启用共享内存来潜在地降低延迟(参考 RedisShmIPCMixin)。
  • 您应选择适当的 序列化//反序列化 方法,这些方法用于解码用户请求并编码响应。默认情况下,两者都使用 JSON。然而,JSON 对图像和嵌入物的支持并不好。您可以选择 msgpack,它速度更快且与二进制兼容(参考 Stable Diffusion)。
  • 配置 OpenBLAS 或 MKL 的线程。它可能无法选择当前 Python 进程使用的最合适的 CPU。您可以通过使用 环境变量(参考 自定义 GPU 分配)为每个工作进程进行配置。

采用者

以下是使用 Mosec 的一些公司和个人用户

引用

如果您发现此软件对您的研究有用,请考虑引用

@software{yang2021mosec,
  title = {{MOSEC: Model Serving made Efficient in the Cloud}},
  author = {Yang, Keming and Liu, Zichen and Cheng, Philip},
  url = {https://github.com/mosecorg/mosec},
  year = {2021}
}

贡献

我们欢迎任何形式的贡献。请通过 提出问题 或在 Discord 上讨论来给我们反馈。您还可以直接 贡献 代码和拉取请求!

要开始开发,您可以使用 envd 创建一个隔离且干净的 Python & Rust 环境。有关更多信息,请查看 envd 文档build.envd

依赖关系

~12–23MB
~303K SLoC