FLUX.1 Schnell est adapté aux générations rapides, mais un notebook n’est pas un service de production. Il faut charger les poids une seule fois, borner la concurrence, stocker les sorties et mesurer le coût par image conservée.
Préparer la machine
Choisissez une image récente avec pilotes NVIDIA et PyTorch compatibles. Vérifiez le GPU avant d’installer l’application :
~~~bash nvidia-smi python -c "import torch; print(torch.cuda.is_available())" ~~~
Créez ensuite l’environnement :
~~~bash python -m venv .venv source .venv/bin/activate pip install -U torch diffusers transformers accelerate safetensors fastapi 'uvicorn[standard]' pillow ~~~
Acceptez si nécessaire les conditions du dépôt de modèle et configurez un jeton Hugging Face côté serveur.
Charger le pipeline une seule fois
~~~python import torch from diffusers import FluxPipeline
pipe = FluxPipeline.from_pretrained( "black-forest-labs/FLUX.1-schnell", torch_dtype=torch.bfloat16, ) pipe.enable_model_cpu_offload()
def generate(prompt: str, seed: int = 0): generator = torch.Generator("cpu").manual_seed(seed) return pipe( prompt, guidance_scale=0.0, num_inference_steps=4, max_sequence_length=256, generator=generator, ).images[0] ~~~
L’offload réduit la mémoire GPU mais peut augmenter la latence. Sur une carte suffisamment grande, placez le pipeline directement sur CUDA et mesurez le gain. Les options exactes dépendent de la version de Diffusers : verrouillez vos dépendances après validation.
Ajouter une API
~~~python from pathlib import Path from uuid import uuid4
from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field
app = FastAPI() OUTPUT_DIR = Path("outputs") OUTPUT_DIR.mkdir(exist_ok=True)
class Request(BaseModel): prompt: str = Field(min_length=3, max_length=1500) seed: int = Field(default=0, ge=0)
@app.post("/images") def create_image(request: Request): try: image = generate(request.prompt, request.seed) name = f"{uuid4()}.webp" image.save(OUTPUT_DIR / name, format="WEBP", quality=90) return {"id": name, "seed": request.seed} except torch.cuda.OutOfMemoryError as error: torch.cuda.empty_cache() raise HTTPException(503, "GPU temporairement indisponible") from error ~~~
Lancez avec un seul worker : "uvicorn app:app --host 0.0.0.0 --port 8000 --workers 1". Plusieurs workers chargeraient plusieurs copies du modèle.
Contrôler la concurrence
Placez les requêtes dans une file. Un worker GPU consomme une tâche à la fois ou un petit lot mesuré. Retournez immédiatement un identifiant, puis exposez un endpoint de statut. Cette architecture absorbe les pics sans provoquer d’erreurs mémoire.
Conteneur et stockage
Préchargez les dépendances dans l’image, mais montez le cache des poids sur un volume durable pour accélérer les redémarrages. Envoyez les images finales vers un stockage objet ; le disque local d’une instance GPU est souvent éphémère.
Mesurer avant d’optimiser
Suivez temps de démarrage à froid, images par minute, pic VRAM, durée de file, taux d’échec et coût par image validée. La mise en lot améliore parfois le débit mais augmente la latence et la mémoire. Comparez avec un fournisseur serverless en intégrant le temps d’inactivité.
Sécurité
Authentifiez l’API, limitez longueur et fréquence des prompts, analysez les sorties selon votre politique, et n’exposez jamais directement le port du serveur d’inférence à Internet. Vérifiez aussi la licence du modèle et celle de chaque dépendance pour votre usage.
FAQ
Pourquoi le premier appel est-il très lent ?
Les poids sont téléchargés puis chargés. Conservez un cache durable et effectuez une génération de chauffe avant de déclarer l’instance prête.
Faut-il utiliser plusieurs réplicas ?
Oui lorsque la file ou la latence dépasse votre objectif. Scalez sur la profondeur de file et prévoyez le délai de démarrage du GPU.