Xinference 的内部结构#

概述#

Xinference 利用我们设计的 actor 编程框架 Xoscar 作为其核心组件,以管理机器、设备和模型推理进程。每个 actor 都是模型推理的基本单元,各种推理后端可以集成到 actor 中,从而使我们能够支持多种推理引擎和硬件。这些 actor 在 actor 池中托管和调度,actor 池具有资源池的功能,actor 的设计是异步和非阻塞的,。

actor

supervisor 和 worker 都是 actor 实例。需要先在每台服务器上创建一个作为资源池的 actor 池;每个 actor 可以使用一个 CPU 内核或一块 GPU 设备。每台服务器都有自己的地址(IP 地址或主机名),因此不同计算节点上的 actor 可以通过这些地址相互通信。更多信息,请参阅 Actor

RESTful API#

RESTful API 是利用 FastAPI 实现的,具体代码在 api/restful_api.py

self._router.add_api_route("/status", self.get_status, methods=["GET"])

这是一个 API 的示例,API /status 对应函数 get_status。您可以在 api/restful_api.py 中添加 RESTful API 和对应后端函数之间的关系。

命令行#

命令行是通过 Click 实现的,具体代码在 deploy/cmdline.py,命令行允许用户直接在终端与 Xinference 进行交互。

入口点#

以我们实现的命令行为例:

  • xinference:提供命令用于模型管理,包括注册/取消注册模型、列出所有已注册/运行的模型,以及启动或终止特定模型。它还提供生成语言和聊天等交互式命令,用于测试或交互已部署的模型。

  • xinference-local:启动一个本地 Xinference 服务。

  • xinference-supervisor:启动 supervisor 进程,在分布式环境中管理和监控 worker actors。

  • xinference-worker:启动 worker 进程,利用可用计算资源,执行 supervisor 分配的任务。

每条命令都配有 optionflag,可自定义其行为,如指定日志级别、主机地址、端口号和其他相关设置。

Python 项目会在 setup.cfgsetup.py 中定义命令行控制台入口点。

console_scripts =
    xinference = xinference.deploy.cmdline:cli
    xinference-local = xinference.deploy.cmdline:local
    xinference-supervisor = xinference.deploy.cmdline:supervisor
    xinference-worker = xinference.deploy.cmdline:worker

命令行 xinference 可参考 xinference.deploy.cmdline:cli 中的代码。

Click#

我们使用 Click 来实现特定的命令行:

@click.option(
      "--host",
      "-H",
      default=XINFERENCE_DEFAULT_DISTRIBUTED_HOST,
      type=str,
      help="Specify the host address for the supervisor.",
  )
  @click.option(
      "--port",
      "-p",
      default=XINFERENCE_DEFAULT_ENDPOINT_PORT,
      type=int,
      help="Specify the port number for the Xinference web ui and service.",
  )

例如,xinference-local 命令允许您定义主机地址和端口。

Actor#

Xinference 以 Xoscar 为基础,Xoscar 是我们的 actor 框架,可以管理计算资源和 Python 进程,支持可扩展的并发编程。下面的伪代码演示了 Worker Actor 的工作原理,实际的 Worker Actor 要比这个复杂得多。

import xoscar as xo

class WorkerActor(xo.Actor):
    def __init__(self, *args, **kwargs):
        ...
    async def launch_model(self, model_id, n_gpu, ...):
        # launch an inference engine, use specific model class to load model checkpoints
        ...
    async def list_models(self):
        # list models on this actor
        ...
    async def terminate_model(self, model_id):
        # terminate the model
        ...
    async def __post_create__(self):
        # called after the actor instance is created
        ...
    async def __pre_destroy__(self):
        # called before the actor instance is destroyed
        ...

我们以 WorkerActor 为例,说明如何构建 Xinference。每个 actor 类都是继承自 xoscar.Actor 的标准 Python 类。该类的实例就是 actor 池中的一个特定的 actor。

  • 定义 Actor 的行为:每个 actor 都需要定义某些动作或行为来完成特定任务。例如,模型推理 WorkerActor 需要启动模型(launch_model)、列出该 actor 中的模型(list_models)、终止模型(termininate_model)。有两个特殊方法值得注意。__post_create__ 在创建 actor 之前调用,进行必要的初始化。而 __pre_destroy__ 会在 actor 被销毁后调用,执行清理任务。

  • 引用 Actor 和调用方法:当创建一个 Actor 时,它会产生一个引用变量,以便其他 Actor 可以引用它。Actor 也可以用 IP 地址来引用。假设创建了 WorkerActor,且引用变量为 worker_ref,那么就可以通过调用 worker_ref.launch_model() 来调用该 Actor 类的 launch_model。即使 actor 中的方法原来是一个传统的阻塞式的方法,当我们使用引用变量调用这个方法时,它也变成了一个异步方法。

  • 推理引擎:Actor 可以管理进程,而推理引擎也是一种进程。在 WorkerActor 的启动模型部分,我们可以根据用户的需要初始化不同的推理引擎。因此,Xinference 可以支持多种推理引擎,并能轻松适应未来的新推理引擎。

请参阅 Xoscar 文档 了解更多 Actor 用例。

异步编程#

Xinference 和 Xoscar 非常依赖异步编程库 asyncio。异步编程是一种非阻塞的编程范式。相比于传统的阻塞式的函数调用,异步编程中的请求或函数调用在后台执行,运行结果在未来某个时刻返回。异步编程的优势是使得可以同时并发进行很多不同的活动或任务。

如果您不熟悉 Python 的 asyncio,可以查看更多教程以获得帮助:

模型#

Xinference 支持不同类型的模型,包括大型语言模型(LLM)、图像模型、音频模型、嵌入模型等。所有模型在 model/ 文件夹下实现。

LLM#

model/llm/ 为例,它主要管理和启动 LLM,包括加载、配置和运行大语言模型。

我们支持不同的推理后端,比如 GGML、PyTorch 和 vLLM。我们生成的内容与 OpenAI 的格式兼容,比如支持流式输出(stream),对话模型以 chat completion 格式返回。因此模型输出内容后要做很多适配工作。这些工作并不难,但需要一些时间。编写这部分代码时,请参考 OpenAI 的 API 文档 和各个推理后端的文档,做必要的适配。

JSON#

model/llm/llm_family.json 中,我们利用 JSON 文件来管理新出现的开源模型的元数据。添加一个新模型并不需要编写新代码,只需要将新的元数据添加到现有的 JSON 文件中即可。

{
    "model_name": "llama-2-chat",
    "model_ability": ["chat"],
    "model_specs": [
        {
            "model_format": "ggmlv3",
            "model_size_in_billions": 70,
            "quantization": ["q8_0", ...],
            "model_id": "TheBloke/Llama-2-70B-Chat-GGML",
        },
        ...
    ],
    "prompt_style": {
        "style_name": "LLAMA2",
        "system_prompt": "<s>[INST] <<SYS>>\nYou are a helpful AI assistant.\n<</SYS>>\n\n",
        "roles": ["[INST]", "[/INST]"],
        "stop_token_ids": [2],
        "stop": ["</s>"]
    }
}

这是一个如何定义 Llama-2 聊天模型的示例。model_specs 定义了模型的信息,因为一个模型系列通常有不同的尺寸、量化方法和文件格式。例如,model_format 可以是 pytorch (使用 Hugging Face Transformers 或 vLLM 作为后端)、 ggmlv3 (与 llama.cpp 相关的张量库)或 gptq (训练后量化框架)。 model_id 定义了模型中心的资源库,Xinference 从模型中心下载检查点文件。此外,由于不同的指令调整过程,不同的模型系列有不同的提示风格。JSON 文件中的 prompt_style 指定了该特定模型的提示格式。例如,system_promptroles 用于指定模型的指令和个性。

代码指南#

主要代码位于 xinference/

  • api/restful_api.py 是设置和运行 RESTful API 的核心部分。它集成了一个身份验证服务(具体代码位于 oauth2/),因为部分或所有端口需要用户身份验证。

  • client/:这是 Xinference 的客户端。

    • oscar/ 定义了 Actor 客户端,它是一个客户端接口,用于与 Xinference 中的模型交互。

    • restful/ 实现与 Xinference 服务交互的 RESTful 客户端。

  • core/:这是 Xinference 的核心部分。

    • metrics.pyresource.py 定义了一套用于收集和报告指标以及节点资源状态的工具,包括模型吞吐量、延迟、CPU 和 GPU 的使用率、内存使用率等。

    • image_interface.pychat_interface.py 分别为图像和聊天模型实现了 Gradio 接口。这些接口允许用户通过 Web 界面与模型进行交互,例如生成图像或进行聊天。代码使用 Gradio 软件包构建用户界面,并通过我们的 RESTful API 与后端模型通信。

    • worker.pysupervisor.py 分别定义了 worker actor 和 supervisor actor 的逻辑。worker actor 负责执行特定的模型计算任务,而 supervisor actor 则管理 Worker 节点的生命周期和任务调度,并监控系统状态。

    • status_guard.py 实现了一个状态监视器,用于跟踪模型的状态(如创建、更新、终止等)。它允许根据模型的 UID 查询模型实例的状态信息

    • cache_tracker.py 定义了一个缓存跟踪器,用于记录和管理缓存状态和模型版本信息。它支持记录缓存位置和模型版本的状态,并根据模型名称查询模型版本信息。

    • event.py 定义了一个事件收集器,用于收集和报告各种运行时模型的事件,如信息、警告和错误。model.py 定义了一个模型 Actor,它是与模型直接交互的核心组件。模型 actor 负责执行模型推理请求,处理输入和输出数据流,并支持各种模型操作。这两个部分都使用 Xoscar 用于并发和分布式执行。

  • deploy/:它提供了一个命令行界面(CLI),用于与 Xinference 框架进行交互,允许用户通过命令行进行操作。更多信息,请参见 Command Line

  • locale/:它支持多语言本地化。只需添加和更新 JSON 翻译文件,就可以支持更多语言,改善用户体验。

  • model/:它为模型描述、创建和缓存提供了一个框架。请参见 Model 以获取更多信息。

  • web/ui/:前端(用户界面)的js代码。