Mon pipeline d’images avec InvokeAI – et où il échoue à 16 Go de VRAM
Comment j’ai construit avec InvokeAI et Krea 2 Turbo un pipeline local pour les images de couverture du blog : de la recette de prompt à l’API puis au script, plus les limites d’une carte AMD de 16 Go – HIP OOM, décodage VAE par tuiles et un démarrage à froid de dix minutes.

Les images de couverture de ce blog sont créées en local, sur ma propre carte graphique, sans service cloud. Derrière se trouve un petit pipeline composé d’InvokeAI, du modèle Krea 2 Turbo et d’un script qui transforme une phrase de scène en un WebP terminé. Le résultat me plaît. Mais le chemin pour y arriver a consisté en grande partie en limites de mémoire, et c’est la partie la plus instructive.
Le système
- Carte AMD de la classe RX 6800 (gfx1030) avec 16 Go de VRAM
- InvokeAI comme serveur, piloté via son API HTTP
- Krea 2 Turbo en GGUF quantifié (Q4_K_M) : un transformer d’environ 12 milliards de paramètres, plus un encodeur Qwen3-VL comme encodeur de texte
- Les modèles sont sur un disque externe, pas sur la partition système
Le modèle n’est pas conçu pour cette carte, il y est tout juste glissé. Cela marque chaque réglage.
Le pipeline en quatre étapes
- Écrire la scène : un motif concret par article, tiré du sujet, en anglais.
- Ajouter le style : une phrase de style fixe, identique pour toutes les images.
- Générer : le script charge le graphe de workflow depuis un PNG de sortie précédent (InvokeAI l’y stocke comme métadonnées), remplace le prompt et le seed, puis place la tâche dans la file via l’API.
- Enregistrer : le résultat est écrit en WebP dans le dossier du blog, et
imageAi: trueest ajouté au frontmatter pour que l’image soit signalée comme image IA.
L’appel est volontairement petit :
python3 scripts/blog-image.py <post-key> "<scène>" --seed 11L’astuce du graphe tiré du PNG m’évite de reconstruire moi-même le workflow : ce qui a fonctionné une fois dans l’interface sert de modèle.
Comment la recette a évolué
Au début, il y avait des presets de style (Blueprint, Dark-Cyan-HUD). Ils fournissaient des icônes interchangeables qui allaient avec tous les sujets et donc avec aucun. Vint ensuite une phase de machines hard-surface, empruntées à un projet de jeu : c’était beau, mais un style qui ne convenait presque qu’aux sujets techniques.
Enfin, j’ai utilisé le modèle comme il a été entraîné : une scène tirée du sujet, plus une courte phrase de style fixe. Pas de chaîne de presets, pas de listes négatives. Ce que j’ai appris au passage :
- L’essentiel d’abord : motif et forme dans la première phrase, style à la fin.
- Pas de negative prompt : Turbo tourne avec CFG 1, et dans ce cas un negative prompt reste sans effet. Les exclusions ne fonctionnent que si on les décrit positivement.
- Le texte dans l’image est difficile à interdire : même « no text » n’aide pas toujours pour les documents, le modèle griffonne alors de la pseudo-écriture. Mieux vaut décrire les feuilles comme « traits abstraits, pas de lettres ».
- Les matériaux colorés brisent la palette : une grue rouillée sortait aussitôt du cadre, une grue grise non.
- L’ambiguïté vaut deux seeds : je tire deux variantes et choisis sur la planche-contact.
Là où 16 Go ne suffisent pas
Venons-en au vrai sujet. Un modèle de 12 milliards de paramètres plus encodeur plus décodeur d’image sur 16 Go : c’est possible, mais seulement avec des mesures.
1. Le format d’image est strictement limité. À 1024 × 576, tout passe ; à 1280 × 720, on obtient un HIP OOM, la variante AMD de l’erreur de mémoire insuffisante. Les images de couverture font donc 1024 × 576. Pour un blog, cela suffit ; pour des sorties plus grandes, il faudrait agrandir par upscaling.
2. Réserve de mémoire de travail trop faible. Par défaut, InvokeAI laisse environ 3 Go de VRAM libres comme réserve de travail. Avec Krea 2, cela ne suffisait pas, l’exécution s’arrêtait sur un OOM. Seul device_working_mem_gb: 5 a réglé le problème. Une partie de la VRAM n’appartient donc pas au modèle, mais au calcul.
3. Le décodeur veut beaucoup d’un coup. Lors du décodage des latents en image, une exécution SDXL réclamait environ 9 Go d’un seul bloc. La solution est le décodage par tuiles : l’image est décodée par morceaux, force_tiled_decode: true. Cela coûte un peu de temps et évite le pic de consommation.
4. Un benchmark de noyau bloque la carte. Lors du décodage VAE du modèle FLUX.2, la carte restait bloquée dans le réglage de noyaux de MIOpen. La variable d’environnement MIOPEN_FIND_MODE=FAST saute le benchmark et figure depuis dans le script de démarrage.
5. Le démarrage à froid est long. Le transformer et l’encodeur doivent passer du disque à la mémoire. Pour la première image après le démarrage du serveur, cela prend chez moi huit à dix minutes, l’encodeur seul en demande bien sept. Avec max_cache_ram_gb: 18, les deux modèles restent en RAM pour que l’exécution suivante ne recharge pas depuis le disque. Ensuite, une image prend environ une minute et demie à deux minutes.
La configuration qui tient tout cela ensemble est courte :
force_tiled_decode: true # décodeur par tuiles, pas de pic de 9 Go
device_working_mem_gb: 5 # plus de réserve de travail que les 3 Go par défaut
max_cache_ram_gb: 18 # garder les modèles en RAM, pas de démarrage à froid par imageCe que cela signifie pour le pipeline
Le pipeline n’est pas un outil interactif mais un outil par lots. Qui veut une seule image attend dix minutes la première fois. Qui en veut vingt démarre le serveur une fois et laisse la file se vider : mon passage sur toutes les images de couverture du blog n’a duré qu’environ une demi-heure, car le démarrage à froid n’a eu lieu qu’une fois.
Le point sous-jacent est le même que pour les modèles de langage locaux : sur du petit matériel, ce n’est pas la qualité du modèle qui décide, mais la précision avec laquelle on connaît les goulets d’étranglement. Qui sait où va la mémoire peut la répartir. Qui ne le sait pas ne voit qu’un message d’erreur et change de modèle.
Ce qui reste ouvert
- Les résolutions plus élevées ne passent que par l’upscaling, qui n’est pas encore dans le pipeline.
- Le démarrage à froid est un problème de confort. Un service qui tourne en permanence le masquerait, mais coûterait alors de la RAM en permanence.
- Je renseigne encore le frontmatter à la main, le script n’affiche qu’un rappel.