Struttura interna di Xinference#

Panoramica#

Xinference utilizza il framework di programmazione actor da noi progettato, Xoscar, come suo componente principale per gestire macchine, dispositivi e processi di inferenza dei modelli. Ogni actor è l’unità base dell’inferenza dei modelli, e vari backend di inferenza possono essere integrati negli actor, permettendoci di supportare molteplici motori di inferenza e hardware. Questi actor sono ospitati e schedulati in un pool di actor, che funge da pool di risorse. Il design degli actor è asincrono e non bloccante.

actor

Sia supervisor che worker sono istanze di actor. È necessario prima creare un pool di actor come pool di risorse su ciascun server; ogni actor può utilizzare un core della CPU o un dispositivo GPU. Ogni server ha il proprio indirizzo (indirizzo IP o nome host), quindi gli actor su diversi nodi di calcolo possono comunicare tra loro tramite questi indirizzi. Per maggiori informazioni, consulta Actor.

RESTful API#

RESTful API è implementato utilizzando FastAPI, con il codice specifico in api/restful_api.py.

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

Questo è un esempio di API, dove l’API /status corrisponde alla funzione get_status. È possibile aggiungere la relazione tra l’API RESTful e la funzione backend corrispondente in api/restful_api.py.

Riga di comando#

La riga di comando è implementata tramite Click, con il codice specifico in deploy/cmdline.py. La riga di comando consente agli utenti di interagire direttamente con Xinference dal terminale.

Punto di ingresso#

Prendiamo come esempio la riga di comando che abbiamo implementato:

  • xinference:fornisce comandi per la gestione dei modelli, inclusi registrazione/annullamento della registrazione dei modelli, elenco di tutti i modelli registrati/in esecuzione, nonché avvio o terminazione di modelli specifici. Offre inoltre comandi interattivi come generazione del linguaggio e chat per testare o interagire con i modelli distribuiti.

  • xinference-local: avvia un servizio Xinference locale.

  • xinference-supervisor:avvia il processo supervisor, che gestisce e monitora gli attori worker in un ambiente distribuito.

  • xinference-worker: avvia il processo worker, utilizzando le risorse di calcolo disponibili per eseguire i compiti assegnati dal supervisor.

Ogni comando è dotato di option e flag, che ne consentono la personalizzazione del comportamento, ad esempio specificando il livello di log, l’indirizzo host, il numero di porta e altre impostazioni correlate.

I progetti Python definiscono i punti di ingresso della console a riga di comando in 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 riga di comando xinference può fare riferimento al codice in xinference.deploy.cmdline:cli.

Click#

Utilizziamo Click per implementare comandi specifici da riga di comando.

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

Ad esempio, il comando xinference-local consente di definire l’indirizzo host e la porta.

Actor#

Xinference si basa su Xoscar, un nostro framework ad attori in grado di gestire risorse di calcolo e processi Python, supportando una programmazione concorrente scalabile. Il seguente pseudocodice illustra il principio di funzionamento dell’Actor Worker, sebbene l’Actor Worker reale sia molto più complesso.

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

Prendiamo WorkerActor come esempio per illustrare come creare Xinference. Ogni classe actor è una classe Python standard che eredita da xoscar.Actor. Le istanze di questa classe sono attori specifici all’interno del pool di attori.

  • Definire il comportamento di un Actor: ogni actor deve definire determinate azioni o comportamenti per svolgere compiti specifici. Ad esempio, il WorkerActor per l’inferenza del modello deve avviare il modello (launch_model), elencare i modelli nell’actor (list_models) e terminare il modello (termininate_model). Ci sono due metodi speciali da notare. __post_create__ viene chiamato prima della creazione dell’actor per eseguire le inizializzazioni necessarie. Mentre __pre_destroy__ viene chiamato dopo la distruzione dell’actor per eseguire le attività di pulizia.

  • Riferimento all’Actor e chiamata di metodi: Quando si crea un Actor, viene generata una variabile di riferimento, in modo che altri Actor possano farvi riferimento. Un Actor può anche essere referenziato tramite indirizzo IP. Supponendo che sia stato creato WorkerActor e che la variabile di riferimento sia worker_ref, è possibile chiamare il metodo launch_model della classe Actor tramite worker_ref.launch_model(). Anche se il metodo nell’Actor era originariamente un metodo bloccante tradizionale, quando lo si chiama utilizzando una variabile di riferimento, diventa un metodo asincrono.

  • Motore di inferenza: Actor può gestire processi, e un motore di inferenza è anch’esso un processo. Nella parte del modello di avvio di WorkerActor, possiamo inizializzare diversi motori di inferenza in base alle esigenze dell’utente. Pertanto, Xinference può supportare molteplici motori di inferenza e adattarsi facilmente a nuovi motori di inferenza futuri.

Consulta la documentazione di Xoscar per ulteriori casi d’uso degli Actor.

Programmazione asincrona#

Xinference e Xoscar dipendono fortemente dalla libreria di programmazione asincrona asyncio. La programmazione asincrona è un paradigma di programmazione non bloccante. Rispetto alle tradizionali chiamate di funzione bloccanti, le richieste o le chiamate di funzione nella programmazione asincrona vengono eseguite in background e il risultato viene restituito in un momento futuro. Il vantaggio della programmazione asincrona è che consente di eseguire contemporaneamente molte attività o compiti diversi.

Se non hai familiarità con asyncio di Python, puoi consultare ulteriori tutorial per assistenza:

modello#

Xinference supporta diversi tipi di modelli, inclusi modelli linguistici di grandi dimensioni (LLM), modelli di immagini, modelli audio, modelli di embedding e altri. Tutti i modelli sono implementati nella cartella model/.

LLM#

Prendendo come esempio model/llm/, gestisce e avvia principalmente i LLM, inclusi il caricamento, la configurazione e l’esecuzione di modelli linguistici di grandi dimensioni.

Supportiamo diversi backend di inferenza, come GGML, PyTorch e vLLM. Il contenuto generato è compatibile con il formato di OpenAI, ad esempio supporta output in streaming (stream) e il modello di dialogo restituisce risposte nel formato chat completion. Pertanto, dopo che il modello ha prodotto il contenuto, è necessario eseguire molto lavoro di adattamento. Questi compiti non sono difficili, ma richiedono un po” di tempo. Quando si scrive questa parte del codice, si prega di fare riferimento alla documentazione dell’API di OpenAI e alla documentazione dei vari backend di inferenza per eseguire gli adattamenti necessari.

JSON#

In model/llm/llm_family.json utilizziamo file JSON per gestire i metadati dei nuovi modelli open source. Per aggiungere un nuovo modello non è necessario scrivere nuovo codice, basta aggiungere i nuovi metadati al file JSON esistente.

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

Ecco un esempio di come definire il modello di chat Llama-2. model_specs definisce le informazioni del modello, poiché una famiglia di modelli ha spesso diverse dimensioni, metodi di quantizzazione e formati di file. Ad esempio, model_format può essere pytorch (usando Hugging Face Transformers o vLLM come backend), ggmlv3 (relativo alla libreria di tensori llama.cpp) o gptq (framework di quantizzazione post-addestramento). model_id definisce il repository nel modello hub, da cui Xinference scarica i file dei checkpoint. Inoltre, a causa di diversi processi di fine-tuning delle istruzioni, diverse famiglie di modelli hanno stili di prompt differenti. prompt_style nel file JSON specifica il formato del prompt per quel particolare modello. Ad esempio, system_prompt e roles vengono utilizzati per specificare le istruzioni e la personalità del modello.

Guida al codice#

Il codice principale si trova in xinference/:

  • api/restful_api.py è la parte centrale per la configurazione e l’esecuzione delle API RESTful. Integra un servizio di autenticazione (il codice specifico si trova in oauth2/), poiché alcune o tutte le porte richiedono l’autenticazione dell’utente.

  • client/: Questo è il client di Xinference.

    • oscar/ definisce il client Actor, un’interfaccia client utilizzata per interagire con i modelli in Xinference.

    • restful/ implementa il client RESTful per interagire con il servizio Xinference.

  • core/:Questa è la parte principale di Xinference.

    • metrics.py e resource.py definiscono un insieme di strumenti per la raccolta e la segnalazione di metriche e dello stato delle risorse dei nodi, inclusi la produttività del modello, la latenza, l’utilizzo di CPU e GPU, l’utilizzo della memoria e altro.

    • image_interface.py e chat_interface.py implementano rispettivamente l’interfaccia Gradio per i modelli di immagini e chat. Queste interfacce consentono agli utenti di interagire con i modelli tramite un’interfaccia web, ad esempio per generare immagini o effettuare chat. Il codice utilizza il pacchetto Gradio per creare l’interfaccia utente e comunica con i modelli backend tramite la nostra API RESTful.

    • worker.py e supervisor.py definiscono rispettivamente la logica dell’attore worker e dell’attore supervisor. L’attore worker è responsabile dell’esecuzione di specifici compiti di calcolo del modello, mentre l’attore supervisor gestisce il ciclo di vita dei nodi Worker e la pianificazione dei compiti, oltre a monitorare lo stato del sistema.

    • status_guard.py implementa un monitor di stato utilizzato per tracciare lo stato del modello (come creazione, aggiornamento, terminazione, ecc.). Consente di interrogare le informazioni sullo stato dell’istanza del modello in base al suo UID.

    • cache_tracker.py definisce un tracciatore di cache, utilizzato per registrare e gestire lo stato della cache e le informazioni sulla versione del modello. Supporta la registrazione della posizione della cache e dello stato della versione del modello, nonché la query delle informazioni sulla versione del modello in base al nome del modello.

    • event.py definisce un raccoglitore di eventi per raccogliere e segnalare vari eventi del modello in esecuzione, come informazioni, avvisi ed errori. model.py definisce un attore del modello, che è il componente centrale per interagire direttamente con il modello. L’attore del modello è responsabile dell’esecuzione delle richieste di inferenza del modello, della gestione dei flussi di dati in input e output e del supporto di varie operazioni del modello. Entrambe le parti utilizzano Xoscar per l’esecuzione concorrente e distribuita.

  • deploy/: fornisce un’interfaccia a riga di comando (CLI) per interagire con il framework Xinference, consentendo agli utenti di operare tramite riga di comando. Per maggiori informazioni, consultare Command Line.

  • locale/: supporta la localizzazione multilingua. È sufficiente aggiungere e aggiornare i file JSON di traduzione per supportare più lingue, migliorando l’esperienza utente.

  • model/: fornisce un framework per la descrizione, creazione e memorizzazione nella cache dei modelli. Vedere Model per maggiori informazioni.

  • web/ui/: codice JavaScript del frontend (interfaccia utente).