Estructura interna de Xinference#

Resumen#

Xinference utiliza el marco de programación de actores Xoscar que diseñamos como su componente central para gestionar máquinas, dispositivos y procesos de inferencia de modelos. Cada actor es la unidad básica de inferencia de modelos, y varios backends de inferencia pueden integrarse en los actores, lo que nos permite admitir múltiples motores de inferencia y hardware. Estos actores se alojan y programan en un grupo de actores, que funciona como un grupo de recursos. El diseño de los actores es asíncrono y no bloqueante.

actor

Los supervisores y los trabajadores son instancias de actores. Primero, es necesario crear un grupo de actores que funcione como un grupo de recursos en cada servidor; cada actor puede usar un núcleo de CPU o un dispositivo GPU. Cada servidor tiene su propia dirección (dirección IP o nombre de host), por lo que los actores en diferentes nodos de cómputo pueden comunicarse entre sí a través de estas direcciones. Para más información, consulte Actor.

RESTful API#

La API RESTful está implementada utilizando FastAPI, y el código específico se encuentra en api/restful_api.py.

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

Este es un ejemplo de una API. La API /status corresponde a la función get_status. Puede agregar la relación entre la API RESTful y la función de backend correspondiente en api/restful_api.py.

Línea de comandos#

La línea de comandos se implementa mediante Click, y el código específico está en deploy/cmdline.py, lo que permite a los usuarios interactuar directamente con Xinference en la terminal.

Punto de entrada#

Usando la línea de comandos que implementamos como ejemplo:

  • xinference: proporciona comandos para la gestión de modelos, incluyendo registrar/anular el registro de modelos, listar todos los modelos registrados/en ejecución, e iniciar o detener un modelo específico. También ofrece comandos interactivos como generación de lenguaje y chat para probar o interactuar con modelos implementados.

  • xinference-local: Inicia un servicio local de Xinference.

  • xinference-supervisor: inicia el proceso supervisor, gestiona y monitorea los actores trabajadores en un entorno distribuido.

  • xinference-worker: Inicia el proceso worker, utilizando los recursos computacionales disponibles para ejecutar las tareas asignadas por el supervisor.

Cada comando viene con option y flag para personalizar su comportamiento, como especificar el nivel de registro, la dirección del host, el número de puerto y otras configuraciones relacionadas.

Los proyectos de Python definen puntos de entrada de consola en setup.cfg o setup.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

La línea de comandos xinference puede consultar el código en xinference.deploy.cmdline:cli.

Click#

Usamos Click para implementar una línea de comandos específica:

@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.",
  )

Por ejemplo, el comando xinference-local le permite definir la dirección del host y el puerto.

Actor#

Xinference se basa en Xoscar, un framework de actores que gestiona recursos computacionales y procesos de Python, soportando programación concurrente escalable. El siguiente pseudocódigo demuestra el principio de funcionamiento del Worker Actor, aunque el Worker Actor real es mucho más complejo.

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
        ...

Tomamos WorkerActor como ejemplo para explicar cómo construir Xinference. Cada clase de actor es una clase estándar de Python que hereda de xoscar.Actor. Las instancias de esta clase son actores específicos dentro del pool de actores.

  • Definir el comportamiento del Actor: cada actor necesita definir ciertas acciones o comportamientos para completar tareas específicas. Por ejemplo, el WorkerActor de inferencia de modelos debe iniciar el modelo (launch_model), listar los modelos en ese actor (list_models) y finalizar el modelo (termininate_model). Hay dos métodos especiales que vale la pena destacar. __post_create__ se invoca antes de crear el actor para realizar la inicialización necesaria. Mientras que __pre_destroy____ se llama después de que el actor sea destruido para ejecutar tareas de limpieza.

  • Referencia al Actor e invocación de métodos: Cuando se crea un Actor, se genera una variable de referencia para que otros Actores puedan referenciarlo. Un Actor también se puede referenciar mediante una dirección IP. Suponiendo que se ha creado WorkerActor y la variable de referencia es worker_ref, entonces se puede invocar launch_model de la clase Actor mediante worker_ref.launch_model(). Incluso si el método en el actor era originalmente un método tradicional de bloqueo, cuando lo invocamos usando la variable de referencia, se convierte en un método asíncrono.

  • Motor de inferencia: Un Actor puede gestionar procesos, y un motor de inferencia también es un tipo de proceso. En la parte del modelo de inicio de WorkerActor, podemos inicializar diferentes motores de inferencia según las necesidades del usuario. Por lo tanto, Xinference puede soportar múltiples motores de inferencia y adaptarse fácilmente a nuevos motores de inferencia en el futuro.

Consulte la documentación de Xoscar para obtener más casos de uso de Actor.

Programación asíncrona#

Xinference y Xoscar dependen en gran medida de la biblioteca de programación asíncrona asyncio. La programación asíncrona es un paradigma de programación sin bloqueo. En comparación con las llamadas a funciones bloqueantes tradicionales, las solicitudes o llamadas a funciones en la programación asíncrona se ejecutan en segundo plano y los resultados se devuelven en un momento futuro. La ventaja de la programación asíncrona es que permite realizar muchas actividades o tareas diferentes de manera concurrente.

Si no está familiarizado con asyncio de Python, puede consultar más tutoriales para obtener ayuda:

model#

Xinference admite diferentes tipos de modelos, incluidos modelos de lenguaje grande (LLM), modelos de imagen, modelos de audio, modelos de embedding, entre otros. Todos los modelos se implementan en la carpeta model/.

LLM#

Con model/llm/ como ejemplo, este principalmente gestiona e inicia LLM, incluyendo la carga, configuración y ejecución de modelos de lenguaje grandes.

Soportamos diferentes backends de inferencia, como GGML, PyTorch y vLLM. El contenido que generamos es compatible con el formato de OpenAI, por ejemplo, soportamos salida en streaming (stream), y el modelo de diálogo devuelve resultados en formato chat completion. Por lo tanto, después de que el modelo genere contenido, se deben realizar muchas adaptaciones. Estas tareas no son difíciles, pero requieren algo de tiempo. Al escribir esta parte del código, consulta la documentación de la API de OpenAI y la documentación de cada backend de inferencia para realizar las adaptaciones necesarias.

JSON#

En model/llm/llm_family.json , utilizamos archivos JSON para gestionar los metadatos de los nuevos modelos de código abierto que surgen. Agregar un nuevo modelo no requiere escribir código nuevo, solo es necesario añadir los nuevos metadatos al archivo JSON existente.

{
    "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>"]
    }
}

Aquí hay un ejemplo de cómo definir el modelo de chat Llama-2. model_specs define la información del modelo, ya que una familia de modelos generalmente tiene diferentes tamaños, métodos de cuantificación y formatos de archivo. Por ejemplo, model_format puede ser pytorch (usando Hugging Face Transformers o vLLM como backend), ggmlv3 (biblioteca de tensores relacionada con llama.cpp) o gptq (marco de cuantificación posterior al entrenamiento). model_id define el repositorio del centro de modelos, desde donde Xinference descarga los archivos de punto de control. Además, debido a diferentes procesos de ajuste de instrucciones, distintas familias de modelos tienen diferentes estilos de avisos. El prompt_style en el archivo JSON especifica el formato de aviso para ese modelo en particular. Por ejemplo, system_prompt y roles se utilizan para especificar las instrucciones y la personalidad del modelo.

Guía de código#

El código principal se encuentra en xinference/

  • api/restful_api.py es la parte central para configurar y ejecutar la API RESTful. Integra un servicio de autenticación (código específico ubicado en oauth2/), ya que algunos o todos los puertos requieren autenticación de usuario.

  • client/:Este es el cliente de Xinference.

    • oscar/ define el cliente Actor, que es una interfaz de cliente utilizada para interactuar con los modelos en Xinference.

    • restful/ implementa el cliente RESTful para interactuar con el servicio Xinference.

  • core/ : Esta es la parte central de Xinference.

    • metrics.py y resource.py definen un conjunto de herramientas para recopilar e informar métricas y el estado de los recursos del nodo, incluyendo el rendimiento del modelo, la latencia, el uso de CPU y GPU, el uso de memoria, etc.

    • image_interface.py y chat_interface.py implementan respectivamente la interfaz de Gradio para modelos de imagen y chat. Estas interfaces permiten a los usuarios interactuar con los modelos a través de una interfaz web, por ejemplo, generando imágenes o manteniendo conversaciones. El código utiliza el paquete Gradio para construir la interfaz de usuario y se comunica con los modelos del backend a través de nuestra API RESTful.

    • worker.py y supervisor.py definen la lógica del actor worker y del actor supervisor, respectivamente. El actor worker se encarga de ejecutar tareas específicas de cómputo de modelos, mientras que el actor supervisor gestiona el ciclo de vida de los nodos Worker y la programación de tareas, además de monitorear el estado del sistema.

    • status_guard.py implementa un monitor de estado para rastrear el estado del modelo (como creación, actualización, terminación, etc.). Permite consultar la información de estado de una instancia de modelo según su UID.

    • cache_tracker.py define un rastreador de caché utilizado para registrar y gestionar el estado de la caché y la información de la versión del modelo. Permite registrar el estado de la ubicación de la caché y la versión del modelo, y consultar la información de la versión del modelo según el nombre del modelo.

    • event.py define un recolector de eventos, utilizado para recopilar e informar diversos eventos de modelos en tiempo de ejecución, como información, advertencias y errores. model.py define un actor de modelo, que es un componente central que interactúa directamente con el modelo. El actor de modelo se encarga de ejecutar solicitudes de inferencia del modelo, gestionar los flujos de entrada y salida de datos, y admitir diversas operaciones del modelo. Ambas partes utilizan Xoscar para la ejecución concurrente y distribuida.

  • deploy/: Proporciona una interfaz de línea de comandos (CLI) para interactuar con el framework Xinference, permitiendo a los usuarios operar mediante comandos. Para más información, consulte Command Line.

  • locale/: admite la localización multilingüe. Solo necesita agregar y actualizar los archivos de traducción JSON para admitir más idiomas y mejorar la experiencia del usuario.

  • model/ :proporciona un marco para la descripción, creación y almacenamiento en caché de modelos. Consulte Model para obtener más información.

  • web/ui/: Código JavaScript del frontend (interfaz de usuario).