Wiki IA
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 prompt

Le 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 rapides

Modèles populaires

Comparatif

ModèleTypeAccèsForces
DALL-E 3PropriétaireAPI OpenAIFidélité au prompt, qualité
MidjourneyPropriétaireDiscordEsthétique, style artistique
Stable DiffusionOpen sourceLocal/APIFlexibilité, fine-tuning
SDXLOpen sourceLocal/APIHaute résolution, qualité
FluxOpen sourceLocal/APIVitesse, efficacité
ImagenPropriétaireGoogle CloudPhotoré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].url

Stable 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 image

Techniques 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 steps

Batch 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_images

Ré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ée

Les 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.

On this page