Structure interne de Xinference#
Aperçu#
Xinference utilise le framework de programmation d’acteurs que nous avons conçu, Xoscar, comme composant central pour gérer les machines, les périphériques et les processus d’inférence de modèles. Chaque acteur est une unité de base de l’inférence de modèles, et divers moteurs d’inférence peuvent être intégrés dans les acteurs, ce qui nous permet de prendre en charge plusieurs moteurs d’inférence et matériels. Ces acteurs sont hébergés et ordonnancés dans un pool d’acteurs, qui fonctionne comme un pool de ressources. La conception des acteurs est asynchrone et non bloquante.
supervisor et worker sont tous deux des instances d’acteurs. Vous devez d’abord créer un pool d’acteurs servant de pool de ressources sur chaque serveur ; chaque acteur peut utiliser un cœur de CPU ou un dispositif GPU. Chaque serveur possède sa propre adresse (adresse IP ou nom d’hôte), ce qui permet aux acteurs situés sur différents nœuds de calcul de communiquer entre eux via ces adresses. Pour plus d’informations, veuillez consulter Actor.
RESTful API#
L’API RESTful est implémentée en utilisant FastAPI, et le code spécifique se trouve dans api/restful_api.py.
self._router.add_api_route("/status", self.get_status, methods=["GET"])
Ceci est un exemple d’API, où l’API /status correspond à la fonction get_status. Vous pouvez ajouter la relation entre les API RESTful et les fonctions backend correspondantes dans api/restful_api.py.
ligne de commande#
La ligne de commande est implémentée via Click, avec le code spécifique dans deploy/cmdline.py, la ligne de commande permet aux utilisateurs d’interagir directement avec Xinference dans le terminal.
Point d’entrée#
Prenons notre implémentation en ligne de commande comme exemple :
xinference: fournit des commandes pour la gestion des modèles, y compris l’enregistrement/la désinscription des modèles, la liste de tous les modèles enregistrés/en cours d’exécution, ainsi que le démarrage ou l’arrêt de modèles spécifiques. Il propose également des commandes interactives telles que la génération de langage et le chat, permettant de tester ou d’interagir avec les modèles déployés.xinference-local : Démarre un service Xinference local.
xinference-supervisor: Démarrer le processus supervisor pour gérer et surveiller les acteurs worker dans un environnement distribué.xinference-worker: Démarre le processus worker, utilise les ressources de calcul disponibles pour exécuter les tâches assignées par le superviseur.
Chaque commande est accompagnée d’une option et d’un flag, permettant de personnaliser son comportement, comme spécifier le niveau de journalisation, l’adresse hôte, le numéro de port et d’autres paramètres connexes.
Les projets Python définissent les points d’entrée de la console de commande dans setup.cfg ou 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 ligne de commande xinference peut être consultée dans le code de xinference.deploy.cmdline:cli.
Click#
Nous utilisons Click pour implémenter une ligne de commande spécifique.
@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.",
)
Par exemple, la commande xinference-local vous permet de définir l’adresse hôte et le port.
Actor#
Xinference est basé sur Xoscar, un framework d’acteurs qui permet de gérer les ressources de calcul et les processus Python, tout en supportant la programmation concurrente évolutive. Le pseudo-code ci-dessous illustre le fonctionnement d’un Worker Actor, bien que l’implémentation réelle soit beaucoup plus complexe.
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
...
Nous prenons WorkerActor comme exemple pour illustrer comment construire Xinference. Chaque classe d’acteur est une classe Python standard héritant de xoscar.Actor. L’instance de cette classe est un acteur spécifique dans le pool d’acteurs.
Définir le comportement de l’acteur : chaque acteur doit définir certaines actions ou comportements pour accomplir des tâches spécifiques. Par exemple, le
WorkerActord’inférence de modèle doit lancer le modèle (launch_model), lister les modèles dans cet acteur (list_models) et terminer le modèle (terminate_model). Deux méthodes spéciales méritent attention.__post_create__est appelée avant la création de l’acteur pour effectuer une initialisation nécessaire, tandis que__pre_destroy__est appelée après la destruction de l’acteur pour exécuter les tâches de nettoyage.Référence à un Acteur et appel de méthode : Lors de la création d’un acteur, une variable de référence est générée afin que d’autres acteurs puissent le référencer. Un acteur peut également être référencé par une adresse IP. Supposons que
WorkerActorsoit créé avec la variable de référenceworker_ref, on peut alors appelerworker_ref.launch_model()pour invoquer la méthodelaunch_modelde la classe de l’acteur. Même si la méthode dans l’acteur était à l’origine une méthode bloquante traditionnelle, lorsqu’elle est appelée via la variable de référence, elle devient une méthode asynchrone.Moteur d’inférence : Un Actor peut gérer des processus, et un moteur d’inférence est également un type de processus. Dans la partie modèle de démarrage de
WorkerActor, nous pouvons initialiser différents moteurs d’inférence selon les besoins de l’utilisateur. Ainsi, Xinference peut prendre en charge plusieurs moteurs d’inférence et s’adapter facilement à de nouveaux moteurs d’inférence à l’avenir.
Veuillez vous référer à la Documentation Xoscar pour plus d’exemples d’utilisation d’Actor.
Programmation asynchrone#
Xinference et Xoscar dépendent fortement de la bibliothèque de programmation asynchrone asyncio. La programmation asynchrone est un paradigme de programmation non bloquant. Contrairement aux appels de fonctions bloquants traditionnels, les requêtes ou appels de fonctions en programmation asynchrone s’exécutent en arrière-plan, et les résultats sont renvoyés à un moment ultérieur. L’avantage de la programmation asynchrone est qu’elle permet d’effectuer simultanément de nombreuses activités ou tâches différentes.
Si vous n’êtes pas familier avec asyncio de Python, vous pouvez consulter davantage de tutoriels pour obtenir de l’aide :
Modèle#
Xinference prend en charge différents types de modèles, notamment les grands modèles de langage (LLM), les modèles d’image, les modèles audio, les modèles d’embedding, etc. Tous les modèles sont implémentés dans le dossier model/.
LLM#
Voici la traduction demandée :
Nous prenons en charge différents backends d’inférence, tels que GGML, PyTorch et vLLM. Le contenu que nous générons est compatible avec le format d’OpenAI, par exemple en prenant en charge la sortie en flux (stream), et les modèles de dialogue renvoient les résultats au format chat completion. Par conséquent, après la sortie du contenu du modèle, beaucoup de travail d’adaptation est nécessaire. Ce travail n’est pas difficile, mais il demande un certain temps. Lors de la rédaction de cette partie du code, veuillez vous référer à la documentation API d’OpenAI et à la documentation de chaque backend d’inférence pour effectuer les adaptations nécessaires.
JSON#
Dans model/llm/llm_family.json, nous utilisons des fichiers JSON pour gérer les métadonnées des modèles open source émergents. Ajouter un nouveau modèle ne nécessite pas d’écrire de nouveau code, il suffit d’ajouter les nouvelles métadonnées au fichier JSON existant.
{
"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>"]
}
}
Voici un exemple de définition du modèle de chat Llama-2. model_specs définit les informations du modèle, car une famille de modèles peut avoir différentes tailles, méthodes de quantification et formats de fichier. Par exemple, model_format peut être pytorch (utilisant Hugging Face Transformers ou vLLM comme backend), ggmlv3 (bibliothèque tensorielle associée à llama.cpp) ou gptq (cadre de quantification post-entraînement). model_id définit le dépôt dans le hub de modèles, à partir duquel Xinference télécharge les fichiers de points de contrôle. De plus, en raison des différents processus d’ajustement des instructions, les différentes familles de modèles ont des styles de prompt distincts. prompt_style dans le fichier JSON spécifie le format du prompt pour ce modèle particulier. Par exemple, system_prompt et roles sont utilisés pour spécifier les instructions et la personnalité du modèle.
Guide du code#
Le code principal se trouve dans xinference/ :
api/ : restful_api.py est la partie centralisée pour configurer et exécuter l’API RESTful. Il intègre un service d’authentification (dont le code spécifique se trouve dans oauth2/), car une partie ou la totalité des ports nécessite une authentification utilisateur.
client/ : C’est le client de Xinference.
core/ : il s’agit de la partie centrale de Xinference.
metrics.py et resource.py définissent un ensemble d’outils pour collecter et signaler les métriques ainsi que l’état des ressources des nœuds, notamment le débit du modèle, la latence, l’utilisation du CPU et du GPU, l’utilisation de la mémoire, etc.
image_interface.py et chat_interface.py implémentent respectivement les interfaces Gradio pour les modèles d’image et de chat. Ces interfaces permettent aux utilisateurs d’interagir avec les modèles via une interface Web, par exemple pour générer des images ou effectuer des conversations. Le code utilise le package Gradio pour construire l’interface utilisateur et communique avec les modèles backend via notre API RESTful.
worker.py et supervisor.py définissent respectivement la logique des acteurs worker et supervisor. L’acteur worker est responsable de l’exécution des tâches de calcul spécifiques d’un modèle, tandis que l’acteur supervisor gère le cycle de vie des nœuds Worker, la planification des tâches et surveille l’état du système.
status_guard.py implémente un moniteur d’état permettant de suivre l’état d’un modèle (tel que la création, la mise à jour, la terminaison, etc.). Il permet de consulter les informations d’état d’une instance de modèle en fonction de son UID.
cache_tracker.py définit un tracker de cache utilisé pour enregistrer et gérer l’état du cache ainsi que les informations de version de modèle. Il prend en charge l’enregistrement de l’emplacement du cache et de l’état des versions de modèle, et permet de consulter les informations de version de modèle en fonction du nom du modèle.
event.py définit un collecteur d’événements utilisé pour collecter et signaler divers événements liés au modèle en cours d’exécution, tels que des informations, des avertissements et des erreurs. model.py définit un acteur de modèle, qui est un composant central interagissant directement avec le modèle. L’acteur du modèle est responsable de l’exécution des requêtes d’inférence du modèle, du traitement des flux de données d’entrée et de sortie, et de la prise en charge de diverses opérations du modèle. Ces deux parties utilisent Xoscar pour l’exécution concurrente et distribuée.
deploy/ : Il fournit une interface en ligne de commande (CLI) pour interagir avec le framework Xinference, permettant aux utilisateurs d’effectuer des opérations via la ligne de commande. Pour plus d’informations, veuillez consulter la Ligne de commande.
locale/ : Il prend en charge la localisation multilingue. Il suffit d’ajouter et de mettre à jour les fichiers de traduction JSON pour prendre en charge davantage de langues et améliorer l’expérience utilisateur.
model/ : il fournit un cadre pour la description, la création et la mise en cache des modèles. Voir Model pour plus d’informations.
web/ui/ : le code JavaScript de l’interface utilisateur (front-end).