Guide de serving LoRAX : adapters LoRA sur Kubernetes à grande échelle

Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.

Un modèle de base et de nombreux adapters LoRA créent un problème de serving inhabituel. Les poids de base sont partagés, mais chaque requête peut nécessiter un ensemble différent de poids d’adapter. Une architecture conventionnelle avec un déploiement par variante gaspille de la mémoire GPU lorsque la plupart des variantes sont inactives.

LoRAX répond à cette longue traîne. Il charge les adapters à la demande, regroupe dans des batches les requêtes associées à différents adapters et déplace les poids des adapters entre la mémoire GPU et la mémoire CPU. La promesse séduisante est de servir « des milliers de modèles fine-tunés sur un seul GPU ». La question d’ingénierie est plus précise : compte tenu de votre ensemble d’adapters actifs, de votre profil d’arrivée et de votre objectif de latence, la planification des échanges apporte-t-elle un gain suffisant pour justifier un runtime de serving supplémentaire ?

Ce guide s’adresse aux ingénieurs inference et plateforme qui doivent servir de nombreuses variantes LoRA à partir d’un seul modèle de base. Il montre comment tester les APIs documentées, dimensionner le working set et transformer le chart Helm de démarrage en plan de production explicite.

Résumé. Choisissez LoRAX lorsque de nombreux adapters LoRA compatibles partagent un même modèle de base et que le trafic est clairsemé ou à longue traîne. La capacité dépend du working set actif, et non de la taille du catalogue. Épinglez le runtime, authentifiez et allowlistez les IDs d’adapters, ajoutez un cache durable pour les artifacts ainsi que des probes, routez en tenant compte de la localité du cache et mesurez séparément les chemins cold et warm.


Le problème de serving est celui du working set

LoRA gèle un modèle de base et apprend des mises à jour low-rank pour certaines matrices de poids. L’adapter obtenu est généralement bien plus petit qu’un checkpoint complet, mais sa taille dépend toujours du rank, des modules ciblés, du nombre de couches et du dtype. Des affirmations fixes telles que « chaque adapter fait 100 Mo » constituent de mauvaises données d’entrée pour le dimensionnement.

Pour le serving, distinguez trois quantités :

  • Taille du catalogue : tous les adapters qu’une plateforme peut résoudre depuis le stockage
  • Working set actif : adapters recevant des requêtes pendant la fenêtre de rétention du cache
  • Ensemble concurrent : adapters représentés dans des batches au même instant

Un catalogue peut contenir des milliers d’adapters sans que des milliers d’entre eux puissent tenir en VRAM. Ce qui compte, c’est la fréquence de renouvellement de l’ensemble actif, la taille de ces adapters et la possibilité de regrouper utilement dans des batches les requêtes associées à différents adapters.

LoRAX combine quatre mécanismes :

  1. Le modèle de base reste résident pour tous les adapters compatibles.
  2. Une requête nomme un adapter, qui peut être résolu depuis Hugging Face, Predibase ou un système de fichiers.
  3. La planification des échanges d’adapters précharge et décharge les poids entre la mémoire GPU et la mémoire CPU.
  4. Le continuous batching hétérogène regroupe les requêtes ciblant différents adapters.

Chemin de déploiement LoRAX durci, avec une gateway externe qui autorise les IDs d’adapters avant que LoRAX ne les résolve et ne les serveChemin de déploiement LoRAX durci, avec une gateway externe qui autorise les IDs d’adapters avant que LoRAX ne les résolve et ne les serve

Le projet LoRAX indique que le batching hétérogène maintient le throughput et la latence presque constants lorsque le nombre d’adapters concurrents augmente dans ses benchmarks. Considérez ce résultat du fournisseur comme une hypothèse à vérifier sur votre workload. La longueur des prompts, la longueur des sorties, le rank, les modules ciblés, l’occupation des batches, le churn du cache et la génération du GPU peuvent modifier le résultat.

Le graphique restauré ci-dessous provient du rapport de lancement de LoRAX de Predibase, publié en novembre 2023. Predibase a benchmarké Llama 2 7B avec des requêtes réparties sur 1 à 128 adapters, sur un seul NVIDIA A10G. Ce graphique présente la comparaison des coûts pour 1 à 32 adapters, afin de traiter un million de tokens, répartis équitablement entre les adapters.

Comparaison archivée par Predibase du coût par million de tokens de LoRAX, des déploiements dédiés et de GPT-3.5 Turbo fine-tuné, lorsque le nombre de modèles passe de 1 à 32

Comparaison archivée du projet, reproduite à partir du rapport LoRAX 2023 de Predibase. Les coûts LoRAX et dédiés utilisent les heures GPU de cette expérience. Au niveau de détail fourni par la source, la courbe GPT-3.5 Turbo utilise le coût par token du modèle fine-tuné.

Interprétez les barres orange plates comme le résultat de ce protocole de benchmark, et non comme une promesse de prix actuelle. Le test envoyait un total fixe d’un million de tokens et permettait à différents adapters de partager un batch. La référence dédiée supposait un compute hébergé séparé pour chaque modèle ; son coût augmentait donc avec le nombre de modèles.

Predibase n’a pas publié les paramètres de benchmark suivants :

  • répartition des tokens entre prompt et sortie
  • longueurs des requêtes
  • taille des batches
  • rank des adapters
  • prix de l’heure GPU
  • modèle exact d’utilisation des déploiements dédiés
  • prix historiques exacts des tokens d’entrée et de sortie de GPT-3.5

Le rapport ne contient donc pas suffisamment d’informations pour reproduire indépendamment les valeurs en dollars du graphique.

Le graphique ne couvre pas non plus le trafic clairsemé, les téléchargements cold, les prix actuels du cloud, les GPU plus récents, les autres modèles de base ni les adapters utilisant des ranks différents. Il justifie la mise en place d’un replay du workload, mais ne peut pas le remplacer.

Quand LoRAX est un choix plausible

LoRAX mérite un benchmark lorsque toutes les conditions suivantes sont réunies :

  • les adapters ont été entraînés sur le même modèle de base supporté et avec le même contrat de tokenizer
  • le trafic couvre de nombreux adapters, avec une longue traîne significative
  • le chargement à la demande d’un adapter cold est préférable à la réservation d’un déploiement dédié
  • le routage par tenant ou par tâche existe déjà à la frontière applicative
  • l’équipe sait exploiter un runtime d’inference spécialisé et le comportement de son cache

Les cas courants incluent les assistants spécifiques à chaque tenant, de nombreuses variantes par domaine et les expérimentations en ligne partageant un même checkpoint de base.

LoRAX est moins adapté lorsque quelques adapters concentrent l’essentiel du trafic ou lorsque les modèles ne partagent pas de base commune. Il l’est également lorsque des objectifs de latence stricts ne tolèrent pas les chargements cold, ou lorsque la plateforme ne peut pas contrôler de manière sûre les artifacts chargés par le serveur. Dans ces situations, un déploiement vLLM ou TGI standard avec un ensemble fixe d’adapters peut être plus simple.

N’utilisez pas Kubernetes uniquement parce que le catalogue est volumineux. Validez d’abord le runtime et la compatibilité des adapters sur un seul GPU.


Tester localement un modèle de base et un adapter

Le README de LoRAX recommande son conteneur préconstruit. Dans un environnement réel, utilisez un digest d’image immuable. main n’apparaît ici que parce qu’il s’agit du tag de quick start documenté dans le dépôt. Ces exemples sont illustratifs et n’ont pas été vérifiés localement dans ce checkout. Validez-les avec les versions de l’image et des dépendances que vous déployez.

mkdir -p data

docker run --rm --gpus all --shm-size 1g \
    -p 8080:80 \
    -v "$PWD/data:/data" \
    ghcr.io/predibase/lorax:main \
    --model-id mistralai/Mistral-7B-Instruct-v0.1

Le minimum documenté est Linux, Docker, un GPU NVIDIA Ampere ou plus récent, ainsi que des drivers compatibles avec CUDA 11.8. Les licences des modèles et les dépôts gated peuvent également nécessiter un token Hugging Face.

Commencez par le modèle de base :

curl http://127.0.0.1:8080/generate \
    -H 'Content-Type: application/json' \
    -d '{
      "inputs": "[INST] Give one reason to measure cold-adapter latency. [/INST]",
      "parameters": {"max_new_tokens": 64}
    }'

Envoyez ensuite un adapter compatible :

curl http://127.0.0.1:8080/generate \
    -H 'Content-Type: application/json' \
    -d '{
      "inputs": "[INST] Solve: Natalia sold 48 clips in April and half as many in May. What is the total? [/INST]",
      "parameters": {
        "max_new_tokens": 64,
        "adapter_id": "vineetsharma/qlora-adapter-Mistral-7B-Instruct-v0.1-gsm8k"
      }
    }'

La première requête peut télécharger et charger l’adapter. Les requêtes suivantes peuvent utiliser les artifacts en cache et les poids résidents. Mesurez les deux chemins. Une seule requête warm ne dit pas grand-chose sur le comportement en longue traîne.

Utiliser un client compatible OpenAI

LoRAX expose un endpoint de chat compatible OpenAI. Le champ model identifie l’adapter :

from openai import OpenAI

client = OpenAI(
    api_key="EMPTY",
    base_url="http://127.0.0.1:8080/v1",
)

response = client.chat.completions.create(
    model="alignment-handbook/zephyr-7b-dpo-lora",
    messages=[
        {"role": "user", "content": "Explain cache locality in two sentences."},
    ],
    max_tokens=100,
)

print(response.choices[0].message.content)

Par défaut, le serveur n’exige pas de clé API. C’est pratique pour localhost, mais dangereux pour une configuration exposée sur Internet. Placez devant lui une couche d’authentification, d’autorisation par tenant, de quotas et d’allowlisting des adapters.

Définir une gate de compatibilité

Avant qu’un adapter n’entre dans le catalogue, vérifiez au minimum :

  • le modèle de base et la révision déclarés
  • la compatibilité du tokenizer et du chat-template
  • le rank LoRA et les modules ciblés supportés par le runtime
  • le format des artifacts et les formes des tenseurs
  • la licence, la provenance et le digest d’intégrité
  • une petite suite de tests comportementaux et de régression

Rejetez les artifacts incompatibles lors de leur enregistrement, plutôt qu’à la première requête d’un utilisateur.


Comprendre la résidence avant le déploiement

Stockage des artifacts d’adapters et résidence dans le runtime LoRAXStockage des artifacts d’adapters et résidence dans le runtime LoRAX

Le modèle de base consomme la plus grande part fixe de la mémoire GPU. Les poids des adapters, le KV cache, l’espace de travail des batches et les kernels du runtime se disputent le reste. La RAM CPU peut contenir les adapters déchargés, tandis que /data stocke les artifacts téléchargés.

Ces niveaux ne sont pas interchangeables. Un artifact provenant du disque ou du Hub doit être lu et matérialisé avant de devenir un adapter résident en CPU ou en GPU. Mesurez séparément les transitions :

CheminCe qu’il comprendMétrique à relever
GPU hitAdapter déjà résidenttemps en file et time to first token
CPU hitTransfert ou rematérialisation vers le GPUdélai de chargement de l’adapter et latence end-to-end
Artifact hitLecture depuis le cache local /datadélai de lecture/chargement et octets du cache
Remote missTéléchargement, validation et chargementdurée du téléchargement, erreurs et latence cold totale

Le capacity planning doit rejouer la distribution réelle de popularité des adapters. Des IDs d’adapters tirés uniformément au hasard créent un problème de cache différent de celui d’un workload de tenants suivant une distribution de type Zipf.


Déployer le chart du dépôt avec précaution

Le dépôt contient charts/lorax ; le point de départ reproductible est donc une révision épinglée du dépôt et un chart local :

git clone https://github.com/predibase/lorax.git
cd lorax
git checkout <reviewed-commit-or-release>

helm dependency update charts/lorax
helm template lorax charts/lorax -f values.production.yaml
helm upgrade --install lorax charts/lorax \
    --namespace inference \
    --create-namespace \
    -f values.production.yaml

Au 15 juillet 2026, les values du chart et le template du Deployment présentent des valeurs par défaut qui méritent votre attention :

  • le tag de l’image est latest
  • /data est un emptyDir ; le remplacement du pod supprime donc les artifacts téléchargés
  • les probes de liveness et de readiness sont vides
  • le token Hugging Face est modélisé comme une valeur d’environnement littérale
  • un GPU est demandé par défaut

Le chart constitue une bonne base de travail. Ce n’est pas une politique de production.

Partir de la structure réelle des values du chart

Le chart imbrique la configuration du runtime sous deployment, et les arguments du launcher sont une liste de paires nom/valeur. Un overlay minimal ressemble à ceci :

deployment:
    replicas: 1

    image:
        repository: ghcr.io/predibase/lorax
        tag: "<tested-release-tag>"

    args:
        - name: "--model-id"
          value: "mistralai/Mistral-7B-Instruct-v0.1"
        - name: "--max-input-length"
          value: "2048"
        - name: "--max-total-tokens"
          value: "3072"
        - name: "--max-batch-total-tokens"
          value: "8192"
        - name: "--max-batch-prefill-tokens"
          value: "4096"

    resources:
        requests:
            nvidia.com/gpu: "1"
        limits:
            nvidia.com/gpu: "1"

    env:
        - name: HUGGING_FACE_HUB_TOKEN
          valueFrom:
              secretKeyRef:
                  name: lorax-hub
                  key: token

    readinessProbe:
        httpGet:
            path: /health
            port: http
        periodSeconds: 5
        failureThreshold: 600

service:
    serviceType: ClusterIP
    port: 80

Les limites de tokens ci-dessus sont des valeurs initiales d’exemple, et non des recommandations de dimensionnement. Déduisez-les de prompts représentatifs, de la concurrence attendue, de la mémoire GPU et de load tests.

Corriger explicitement les lacunes

Le template actuel du chart transmet deployment.env via toYaml ; un overlay values normal peut donc utiliser valueFrom.secretKeyRef pour les credentials du Hub, comme ci-dessus. Il encode en dur les volumes emptyDir, ne possède aucun hook startupProbe et formate toujours l’image sous la forme repository:tag. Un fichier values seul ne peut donc pas fournir un /data durable, une startup probe ou l’épinglage d’un digest d’image ; utilisez pour ces changements un fork du chart révisé, un patch post-render ou une couche de manifests de niveau supérieur. Cette couche de production doit inclure :

  • un PersistentVolumeClaim ou un cache d’artifacts local au nœud monté sur /data
  • une startup probe avant une liveness probe agressive
  • une politique de disruption des pods et une répartition topologique pour plusieurs replicas
  • une NetworkPolicy, des restrictions sur le service account et une gateway authentifiée
  • l’épinglage du digest de l’image et des contrôles d’intégrité des artifacts

Ne placez jamais un token Hub directement dans un fichier values versionné. Deux replicas n’offrent une haute disponibilité utile que si tous deux peuvent charger le modèle de base. Le routeur doit également éviter d’envoyer chaque adapter cold aux deux pods.

Router en privilégiant la localité du cache

Un équilibrage round-robin peut transformer chaque replica en cache cold. Un routeur utile associe un ID d’adapter autorisé à un replica par hash ou selon une autre règle stable. Il conserve un chemin de failover lorsque ce replica est indisponible.

La clé de routage doit provenir de l’état applicatif authentifié, et non d’un paramètre arbitraire d’URL publique. Dans le cas contraire, un appelant peut forcer des téléchargements distants, provoquer du churn dans les caches ou sonder des noms d’adapters privés.


LoRAX ou vLLM ?

La documentation actuelle de vLLM décrit des modules LoRA déclarés au démarrage ainsi que leur chargement dynamique via des endpoints ou des plugins de resolver. Elle avertit que la mise à jour des adapters à chaud comporte des risques de sécurité et ne doit pas être utilisée en production en dehors d’un environnement isolé et de confiance.

La comparaison utile est opérationnelle plutôt que numérique :

QuestionLoRAXvLLM
Comment les adapters de la longue traîne sont-ils découverts ?L’ID de l’adapter peut résoudre à la demande des artifacts Hugging Face, Predibase ou du système de fichiersModules statiques, endpoints de gestion dynamiques ou plugins de resolver
Comment la résidence est-elle gérée ?Planification explicite des échanges d’adapters entre GPU et CPULimites configurées pour les LoRA actifs et CPU, plus comportement du resolver
Quelle est l’interface de requête ?/generate de style TGI, client Python et chat compatible OpenAIServing compatible OpenAI et APIs Python natives
Qu’est-ce qui doit guider le choix ?Latence cold/warm, churn du cache, throughput du batching hétérogène et adéquation opérationnelleLe même replay du workload et les mêmes critères opérationnels

Évitez les règles telles que « LoRAX pour 1 000 adapters, vLLM pour dix ». La taille du catalogue ne détermine pas à elle seule les performances. Benchmarkez les deux solutions avec la même base, les mêmes adapters, prompts, ranks, trace d’arrivée et hardware.

Test d’acceptation pour la production

Avant d’élargir le catalogue, exécutez un replay comprenant :

  1. Un ensemble hot fixe pour établir le throughput et la latence warm.
  2. Une distribution à longue traîne pour mesurer les hits CPU et les hits du cache d’artifacts.
  3. Un burst d’adapters jusque-là inconnus mais allowlistés.
  4. Le remplacement d’un pod pour mesurer la récupération du modèle de base et des adapters.
  5. Un adapter indisponible ou corrompu pour vérifier l’isolation et le fallback.
  6. Des tenants concurrents pour vérifier l’authentification, les quotas et les labels des métriques.

Suivez ces métriques :

  • taux de requêtes
  • temps en file
  • time to first token
  • latence inter-tokens
  • latence totale
  • temps de chargement de l’adapter
  • catégorie de cache hit
  • mémoire GPU
  • mémoire CPU
  • octets téléchargés
  • erreurs par motif

N’utilisez pas les IDs d’adapters dans des labels de métriques non bornés. Associez-les à des dimensions contrôlées ou à des traces échantillonnées.

Définissez les seuils d’acceptation avant le test. Fixez un taux maximal d’erreurs sur le chemin cold et une cible P99 warm. Définissez également une cible de cache hit pour la distribution de popularité observée, ainsi qu’un objectif de temps de récupération après la perte d’un pod.

Conclusion

LoRAX transforme le problème de nombreux fine-tunes compatibles : au lieu d’une flotte de copies du modèle de base, on obtient un problème de placement des adapters. Cela peut constituer une excellente architecture pour les workloads à longue traîne, mais ne rend pas par définition les coûts ou la latence constants. Le working set actif, le chemin d’échange, la composition des batches et la couche de stockage déterminent toujours le résultat.

Prouvez d’abord ces mécanismes sur un seul GPU. Prenez ensuite Kubernetes au sérieux : épinglez les artifacts, préservez le cache, protégez les credentials, autorisez les adapters, routez en privilégiant la localité et mesurez chaque chemin de résidence. Si LoRAX surpasse une configuration vLLM actuelle lors du même replay, la décision de déploiement reposera sur des éléments probants.

Références