LLM et IA Générative
Modèles de Diffusion
Stable Diffusion, DALL-E et la génération d'images par IA
Les modèles de diffusion sont la technologie derrière les générateurs d'images comme Stable Diffusion, DALL-E et Midjourney. Ils apprennent à créer des images en inversant un processus de dégradation par le bruit.
Principe de fonctionnement
L'intuition
PROCESSUS DE DIFFUSION :
1. DIFFUSION FORWARD (Entraînement)
Image nette → + bruit → + bruit → ... → Bruit pur
[Photo chat] → [Peu bruité] → [Très bruité] → [Bruit pur]
↓ ↓ ↓ ↓
t = 0 t = 1 ... t = T
2. DIFFUSION INVERSE (Génération)
Bruit pur → - bruit → - bruit → ... → Image nette
[Bruit pur] → [Forme vague] → [Plus net] → [Image finale]
↓ ↓ ↓ ↓
t = T ... t = 1 t = 0
↑ ↑
Aléatoire Guidé par promptLe modèle apprend à prédire le bruit ajouté à une image, puis utilise cette capacité en sens inverse pour "débruiter" du bruit aléatoire et générer des images.
Mathématiques simplifiées
DIFFUSION FORWARD :
q(x_t | x_{t-1}) = N(x_t; √(1-β_t) * x_{t-1}, β_t * I)
Où :
- x_t = image au pas t
- β_t = variance du bruit au pas t (schedule)
- N = distribution gaussienne
DIFFUSION INVERSE (ce que le modèle apprend) :
p_θ(x_{t-1} | x_t) = N(x_{t-1}; μ_θ(x_t, t), Σ_θ(x_t, t))
Le modèle prédit μ_θ (la moyenne) pour reconstruire l'image.Architecture des modèles
U-Net avec attention
ARCHITECTURE STABLE DIFFUSION :
┌─────────────────────────────────────────────────────────────┐
│ U-Net │
├─────────────────────────────────────────────────────────────┤
│ │
│ Encodeur Bottleneck Décodeur │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Conv │ │ Attn │ │ Conv │ │
│ │ + Attn │ ───────→ │ Block │ ───────→ │ + Attn │ │
│ └────────┘ ↓ └────────┘ ↑ └────────┘ │
│ │ Skip Skip │ │
│ ↓ Connections Connections ↓ │
│ ┌────────┐ ┌────────┐ │
│ │ Down │ ─────────────────────────────→ │ Up │ │
│ │ Sample │ │ Sample │ │
│ └────────┘ └────────┘ │
│ │
│ + Conditioning (texte, timestep) │
│ │
└─────────────────────────────────────────────────────────────┘Latent Diffusion (Stable Diffusion)
LATENT DIFFUSION :
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Image │ │ Espace │ │ U-Net │
│ 512x512 │ ──→ │ Latent │ ──→ │ Diffusion │
│ (Pixels) │ │ 64x64 │ │ (Latent) │
└─────────────┘ └─────────────┘ └─────────────┘
↑ ↑ ↑
Encodeur 8x moins Beaucoup
VAE de calcul plus rapide
Avantages :
- 64x moins de pixels à traiter (64² vs 512²)
- Qualité préservée grâce au VAE
- Entraînement et inférence beaucoup plus rapidesModèles populaires
Comparatif
| Modèle | Type | Accès | Forces |
|---|---|---|---|
| DALL-E 3 | Propriétaire | API OpenAI | Fidélité au prompt, qualité |
| Midjourney | Propriétaire | Discord | Esthétique, style artistique |
| Stable Diffusion | Open source | Local/API | Flexibilité, fine-tuning |
| SDXL | Open source | Local/API | Haute résolution, qualité |
| Flux | Open source | Local/API | Vitesse, efficacité |
| Imagen | Propriétaire | Google Cloud | Photoréalisme |
DALL-E 3 (OpenAI)
from openai import OpenAI
client = OpenAI()
def generate_image(prompt: str, size: str = "1024x1024") -> str:
"""Génère une image avec DALL-E 3."""
response = client.images.generate(
model="dall-e-3",
prompt=prompt,
size=size, # 1024x1024, 1024x1792, 1792x1024
quality="hd", # standard ou hd
n=1
)
return response.data[0].url
# Exemple
url = generate_image(
"Un chat astronaute flottant dans l'espace, "
"style peinture à l'huile impressionniste, "
"lumière dorée du soleil en arrière-plan"
)
# Avec variation
def create_variation(image_path: str) -> str:
"""Crée une variation d'une image existante."""
response = client.images.create_variation(
image=open(image_path, "rb"),
n=1,
size="1024x1024"
)
return response.data[0].urlStable Diffusion (Local)
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
import torch
# Charger le modèle
pipe = StableDiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1",
torch_dtype=torch.float16
)
pipe = pipe.to("cuda")
# Optimiser le scheduler
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
def generate_image(
prompt: str,
negative_prompt: str = "",
steps: int = 25,
guidance_scale: float = 7.5,
seed: int = None
) -> "Image":
"""Génère une image avec Stable Diffusion."""
generator = None
if seed is not None:
generator = torch.Generator("cuda").manual_seed(seed)
image = pipe(
prompt=prompt,
negative_prompt=negative_prompt,
num_inference_steps=steps,
guidance_scale=guidance_scale,
generator=generator
).images[0]
return image
# Exemple
image = generate_image(
prompt="A serene Japanese garden with cherry blossoms, "
"koi pond, traditional bridge, golden hour lighting, "
"highly detailed, 8k",
negative_prompt="blurry, low quality, distorted, ugly",
steps=30,
guidance_scale=7.5,
seed=42
)
image.save("garden.png")SDXL (Stable Diffusion XL)
from diffusers import StableDiffusionXLPipeline, StableDiffusionXLImg2ImgPipeline
import torch
# Pipeline principal
base = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
variant="fp16"
)
base = base.to("cuda")
# Pipeline de raffinement (optionnel)
refiner = StableDiffusionXLImg2ImgPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-refiner-1.0",
torch_dtype=torch.float16,
variant="fp16"
)
refiner = refiner.to("cuda")
def generate_hd(prompt: str, use_refiner: bool = True) -> "Image":
"""Génère une image HD avec SDXL + Refiner."""
# Génération de base
image = base(
prompt=prompt,
num_inference_steps=40,
denoising_end=0.8 if use_refiner else 1.0,
output_type="latent" if use_refiner else "pil"
).images[0]
if use_refiner:
# Raffinement
image = refiner(
prompt=prompt,
image=image,
num_inference_steps=40,
denoising_start=0.8
).images[0]
return imageTechniques avancées
Image-to-Image (img2img)
from diffusers import StableDiffusionImg2ImgPipeline
from PIL import Image
pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1",
torch_dtype=torch.float16
)
pipe = pipe.to("cuda")
def transform_image(
image_path: str,
prompt: str,
strength: float = 0.75 # 0 = identique, 1 = complètement nouveau
) -> "Image":
"""Transforme une image existante."""
init_image = Image.open(image_path).convert("RGB")
init_image = init_image.resize((768, 768))
result = pipe(
prompt=prompt,
image=init_image,
strength=strength,
guidance_scale=7.5
).images[0]
return result
# Transformer un croquis en image réaliste
result = transform_image(
"sketch.png",
"photorealistic landscape, mountains, lake, sunset",
strength=0.8
)Inpainting (retouche)
from diffusers import StableDiffusionInpaintPipeline
from PIL import Image
pipe = StableDiffusionInpaintPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-inpainting",
torch_dtype=torch.float16
)
pipe = pipe.to("cuda")
def inpaint(
image_path: str,
mask_path: str,
prompt: str
) -> "Image":
"""Remplace une zone masquée par du contenu généré."""
image = Image.open(image_path).convert("RGB")
mask = Image.open(mask_path).convert("L") # Blanc = zone à modifier
result = pipe(
prompt=prompt,
image=image,
mask_image=mask,
num_inference_steps=50
).images[0]
return result
# Remplacer le ciel
result = inpaint(
"photo.jpg",
"sky_mask.png", # Masque blanc sur le ciel
"dramatic sunset sky, orange and purple clouds"
)ControlNet (contrôle précis)
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel
from controlnet_aux import CannyDetector
from PIL import Image
import torch
# Charger ControlNet pour les contours (Canny)
controlnet = ControlNetModel.from_pretrained(
"lllyasviel/sd-controlnet-canny",
torch_dtype=torch.float16
)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1",
controlnet=controlnet,
torch_dtype=torch.float16
)
pipe = pipe.to("cuda")
canny_detector = CannyDetector()
def generate_from_edges(image_path: str, prompt: str) -> "Image":
"""Génère une image suivant les contours d'une image source."""
image = Image.open(image_path)
# Extraire les contours
canny_image = canny_detector(image)
result = pipe(
prompt=prompt,
image=canny_image,
num_inference_steps=30
).images[0]
return result
# Autres ControlNets disponibles :
# - Pose (squelette humain)
# - Depth (carte de profondeur)
# - Segmentation (zones sémantiques)
# - Scribble (croquis)
# - Normal map (éclairage 3D)LoRA (adaptation légère)
from diffusers import StableDiffusionPipeline
import torch
pipe = StableDiffusionPipeline.from_pretrained(
"stabilityai/stable-diffusion-2-1",
torch_dtype=torch.float16
)
pipe = pipe.to("cuda")
# Charger un LoRA personnalisé (style, personnage, etc.)
pipe.load_lora_weights(
"path/to/lora",
weight_name="my_style.safetensors"
)
# Ajuster le poids du LoRA
pipe.fuse_lora(lora_scale=0.8)
# Générer avec le style du LoRA
image = pipe(
"portrait of a woman, my_style", # Trigger word du LoRA
num_inference_steps=30
).images[0]
# Décharger le LoRA
pipe.unfuse_lora()Prompt engineering pour images
Structure d'un bon prompt
ANATOMIE D'UN PROMPT EFFICACE :
1. SUJET PRINCIPAL
"A majestic lion"
2. DÉTAILS ET ATTRIBUTS
"with a golden mane, piercing amber eyes"
3. ENVIRONNEMENT/CONTEXTE
"standing on a rocky cliff, African savanna"
4. ÉCLAIRAGE
"dramatic sunset lighting, golden hour"
5. STYLE/MÉDIUM
"oil painting style, impressionist"
6. QUALITÉ
"highly detailed, 8k resolution, masterpiece"
PROMPT COMPLET :
"A majestic lion with a golden mane, piercing amber eyes,
standing on a rocky cliff overlooking African savanna,
dramatic sunset lighting, golden hour,
oil painting style, impressionist brushstrokes,
highly detailed, 8k resolution, masterpiece"Negative prompts
# Negative prompts courants pour améliorer la qualité
QUALITY_NEGATIVE = """
blurry, low quality, low resolution, pixelated,
jpeg artifacts, compression artifacts, noise,
out of focus, poorly drawn, bad anatomy,
deformed, disfigured, mutated, ugly,
watermark, signature, text, logo
"""
# Pour les portraits
PORTRAIT_NEGATIVE = """
extra fingers, missing fingers, extra limbs,
missing limbs, fused fingers, too many fingers,
long neck, bad proportions, cropped,
worst quality, low quality, normal quality
"""
# Pour les paysages
LANDSCAPE_NEGATIVE = """
humans, people, animals, buildings,
modern elements, cars, roads,
overexposed, underexposed, flat lighting
"""Modificateurs de style
STYLE_MODIFIERS = {
"photoréaliste": "photorealistic, photography, DSLR, 85mm lens, f/1.8",
"anime": "anime style, studio ghibli, vibrant colors, cel shading",
"peinture_huile": "oil painting, canvas texture, visible brushstrokes",
"aquarelle": "watercolor painting, soft edges, flowing colors",
"pixel_art": "pixel art, 16-bit, retro game style",
"3d_render": "3D render, octane render, ray tracing, volumetric lighting",
"cyberpunk": "cyberpunk style, neon lights, rain, futuristic city",
"fantasy": "fantasy art, epic, magical, dramatic lighting",
}
def build_prompt(subject: str, style: str, extras: str = "") -> str:
"""Construit un prompt optimisé."""
style_mod = STYLE_MODIFIERS.get(style, style)
return f"{subject}, {style_mod}, {extras}, highly detailed, masterpiece"Génération de vidéo
Stable Video Diffusion
from diffusers import StableVideoDiffusionPipeline
from PIL import Image
import torch
pipe = StableVideoDiffusionPipeline.from_pretrained(
"stabilityai/stable-video-diffusion-img2vid-xt",
torch_dtype=torch.float16,
variant="fp16"
)
pipe = pipe.to("cuda")
def image_to_video(image_path: str, num_frames: int = 25) -> list:
"""Anime une image en vidéo courte."""
image = Image.open(image_path).resize((1024, 576))
frames = pipe(
image,
num_frames=num_frames,
decode_chunk_size=8,
num_inference_steps=25
).frames[0]
return frames
# Sauvegarder en GIF ou MP4
from diffusers.utils import export_to_video
export_to_video(frames, "output.mp4", fps=7)Bonnes pratiques
Optimisation des ressources
import torch
from diffusers import StableDiffusionPipeline
# 1. Utiliser float16
pipe = StableDiffusionPipeline.from_pretrained(
"model", torch_dtype=torch.float16
)
# 2. Activer l'attention mémoire-efficiente
pipe.enable_attention_slicing() # Réduit la VRAM
pipe.enable_vae_slicing() # Pour grandes images
# 3. Utiliser xformers (si disponible)
pipe.enable_xformers_memory_efficient_attention()
# 4. Compilation (PyTorch 2.0+)
pipe.unet = torch.compile(pipe.unet, mode="reduce-overhead")
# 5. Offload CPU pour économiser VRAM
pipe.enable_model_cpu_offload()
# 6. Réduire les steps avec un bon scheduler
from diffusers import DPMSolverMultistepScheduler
pipe.scheduler = DPMSolverMultistepScheduler.from_config(
pipe.scheduler.config
)
# DPM++ permet d'obtenir de bons résultats en 20-25 stepsBatch generation
def generate_batch(prompts: list[str], batch_size: int = 4) -> list:
"""Génère plusieurs images efficacement."""
all_images = []
for i in range(0, len(prompts), batch_size):
batch_prompts = prompts[i:i+batch_size]
images = pipe(
batch_prompts,
num_inference_steps=25,
guidance_scale=7.5
).images
all_images.extend(images)
return all_imagesRésumé
MODÈLES DE DIFFUSION :
PRINCIPE
├── Forward : Image → Bruit (entraînement)
├── Inverse : Bruit → Image (génération)
└── Le modèle apprend à prédire le bruit
MODÈLES
├── DALL-E 3 : Meilleure fidélité au prompt
├── Midjourney : Style artistique
├── Stable Diffusion : Open source, flexible
└── SDXL : Haute résolution
TECHNIQUES AVANCÉES
├── img2img : Transformer des images
├── Inpainting : Retouche locale
├── ControlNet : Contrôle précis (pose, contours)
└── LoRA : Adaptation de style légère
PROMPT ENGINEERING
├── Sujet + Détails + Contexte
├── Éclairage + Style + Qualité
├── Negative prompts pour éviter défauts
└── Modificateurs de style spécifiques
OPTIMISATIONS
├── float16 + attention slicing
├── xformers / torch.compile
├── Scheduler efficace (DPM++)
└── CPU offload si VRAM limitéeLes modèles de diffusion évoluent rapidement. Stable Diffusion 3 et Flux représentent les dernières avancées avec une meilleure compréhension des prompts et une qualité accrue.