🐱 La diffusion : des photos de chats qui n'ont jamais existé

Chapitre nouveau du projet (run-007) : après les séquences (texte, dessins au trait), la deuxième grande famille de la génération — les images. Un mini-modèle de diffusion (le principe de Stable Diffusion), entraîné de zéro sur ~15 700 vraies photos de visages de chats en 64×64. Coût : 2 h 10 de GPU Kaggle gratuit, 0 €.

Le principe en 30 secondes

Pendant l'entraînement, on noie de vraies photos sous du bruit et le modèle (un U-Net de 5,1 M de paramètres) apprend une seule chose : deviner exactement quel bruit a été ajouté. Pour séparer le bruit du chat, il est obligé d'apprendre à quoi ressemble un chat.

Pour générer, on lui donne du bruit 100 % aléatoire en prétendant que c'est une photo très abîmée, et on lui fait faire 1 000 petits pas de nettoyage. Comme il pousse toujours l'image vers ce qu'il connaît, un visage de chat émerge du néant — un chat qui n'a jamais existé.

Le film du débruitage

6 chats générés, photographiés à 9 stades du nettoyage : à gauche le bruit pur de départ, à droite l'image finale. C'est exactement ce que fait Stable Diffusion en grand.

du bruit pur au chat, étape par étape
Étapes 1000 → 800 → 600 → 400 → 300 → 200 → 100 → 50 → 0. Longtemps « rien », puis le visage se décide dans les 300 derniers pas.

Regarder le modèle apprendre

Trois planches de 64 générations, prises pendant l'entraînement. Même point de départ pédagogique que le robot qui dessine : au début, n'importe quoi — à la fin, des chats.

Époque 10

planche époque 10 : taches
Des nappes de couleur — le modèle sait à peine que « les pixels voisins se ressemblent ».

Époque 40

planche époque 40 : visages émergents
Des visages émergent, encore fantomatiques, avec des cases ratées.

Époque 120 — final

planche finale : 64 chats générés
64 chats générés : yeux, moustaches, pelages variés. Aucun n'existe.

Le raccourci DDIM : 50 pas au lieu de 1 000

À chaque pas, le modèle peut déjà deviner le chat final caché sous le bruit. Le raccourci DDIM exploite ça : on prend sa devinette, on la re-bruite soi-même 20 crans plus bas (salir une image ne demande aucune intelligence), et on ne consulte le modèle qu'à 50 étages au lieu de 1 000. C'est le réglage réel de production de Stable Diffusion. La preuve par l'image : 36 chats frais générés en 124 s sur un simple CPU Kaggle (~3,4 s par chat), qualité intacte.

36 chats générés en DDIM 50 pas
kernel CPU (0 quota GPU) · DDIM 50 pas · 36/36 reconnaissables · code : training/kaggle/run-007-generation/

run-008 : commander la couleur (« un chat noir, s'il te plaît »)

Fine-tuning du run-007 : une entrée « couleur » (6 embeddings, initialisés à zéro pour ne rien casser) est greffée sur le U-Net, et les 15 700 photos sont auto-étiquetées par leur couleur moyenne de pixels. 25 époques plus tard, chaque rangée ci-dessous est une couleur commandée : noir, blanc, gris, roux, crème, brun tigré.

6 rangées de chats, une couleur commandée par rangée

Verdict honnête : noir, blanc et roux obéissent nettement ; gris, crème et tigré restent flous. La planche de contrôle de l'étiqueteur explique pourquoi — la « couleur moyenne des pixels » confond un siamois sombre et un chat noir, un tigré clair et un gris. La commande ne peut pas être plus précise que les étiquettes qui l'ont enseignée : la qualité des données plafonne le modèle, une fois de plus.

contrôle de l'étiqueteur : 6 vraies photos par étiquette
répartition auto : gris 13 474 (fourre-tout) · noir 5 586 · roux 4 670 · brun tigré 3 414 · blanc 3 072 · crème 1 278 · 29 min de GPU · checkpoint : run-008-couleurs.pt

Le bras de fer photo vs commande (l'expérience « img2img »)

Une vraie photo (le chat le plus sombre du dataset), brouillée avec le même bruit à 5 niveaux, puis débruitée en mode déterministe avec chacune des 6 couleurs commandées. Dans chaque rangée, l'entrée est strictement identique — seule la commande change. C'est le principe du « img2img » de Stable Diffusion (« réimagine ma photo »), redécouvert en raisonnant sur les entrées du modèle.

même photo brouillée, 6 couleurs commandées, 5 niveaux de bruit

Lecture : à t ≤ 500, la photo gagne — les 6 colonnes sont identiques, la commande est ignorée (tout est déjà décidé). À t ≥ 900, la photo d'origine est oubliée (le chat généré n'est plus le même) et la commande teinte le résultat… mais ne fait que le teinter : roux plus chaud, blanc plus clair, pas de vrais recolorages francs. Deux raisons : les étiquettes bruitées du run-008 (une commande floue reste floue), et il manque l'amplificateur que les vrais outils utilisent — le « guidance » (générer avec et sans la commande, puis exagérer la différence). Le curseur « force » des vrais outils img2img, c'est exactement ce niveau t de bruitage.

DDIM déterministe 40 pas · kernel CPU (0 quota GPU) · 6 min · code : training/kaggle/run-008-img2img/

La paréidolie : donne-lui un chien, il verra un chat

Question de l'utilisateur : « si on lui donne une photo brouillée d'un chien, est-ce qu'il va générer un chat ? » Réponse expérimentale : oui — il ne peut rien faire d'autre. Ce modèle n'a vu que des chats de toute sa vie ; les chats sont sa seule grille de lecture du monde (son prior). Ci-dessous, un chiot noir et blanc brouillé à 5 niveaux : dès t = 500, le modèle le « corrige » en chaton — en recyclant fidèlement les taches noires et blanches, la pose et la lumière du chien. C'est la paréidolie des humains qui voient des visages dans les nuages, version machine.

un chiot noir et blanc réinterprété en chaton par le modèle

Deux autres cobayes : le caniche sur fauteuil rose (le fond rose survit, le chien devient tigré) et le chien brun en cage (réinterprété en chat aux yeux bleus). Leçon, la même que le 006a côté dessin : un modèle EST ses données — il génère depuis ce qu'il connaît, jamais depuis ce qu'on imagine qu'il comprend.

3 chiens × 5 niveaux × 6 couleurs · DDIM déterministe 40 pas · kernel CPU (0 quota GPU) · code : training/kaggle/run-008-chien/

run-009 : la machine à manga

La paréidolie, utilisée exprès : on entraîne le même U-Net sur 63 565 visages d'anime (run-009, 2 h 15 de GPU) — et son monde à lui devient le manga. D'abord la preuve qu'il maîtrise le style : 64 visages générés depuis du bruit pur, aucun n'existe.

64 visages manga générés

Puis la machine promise — « rendre une photo en manga » : de vrais portraits (dataset LFW), brouillés avec le même bruit à 5 niveaux, débruités par le modèle-qui-ne-connaît-que-le-manga. Le niveau t est le curseur de force : à t = 300 la photo résiste, vers t = 600-700 le point d'équilibre — la pose, la coiffure et la lumière de la personne, les yeux et les aplats du manga —, à t = 900 un personnage d'anime qui ne garde que les masses de couleur (regarde les lunettes de soleil de la rangée 2 devenir d'immenses yeux bleus).

vrais portraits manga-fiés à 5 niveaux de force
entraînement : 63 565 visages anime · 60 ép. · loss 0,098 → 0,027 · 2 h 15 GPU · manga-fication : kernel CPU, DDIM 50 pas · checkpoints : run-009-manga.pt · planches d'apprentissage : ép. 5 · 20 · 60 · film du débruitage

run-011 : guidé par les contours (ControlNet en miniature)

Constat de l'utilisateur sur la manga-fication : « ça ne garde pas les traits communs ». Exact — l'identité habite les détails fins, que le bruit détruit en premier. Le remède des pros (le principe de ControlNet) : extraire la carte des contours de la photo (filtre de Sobel) et la montrer au U-Net à chaque pas du débruitage, intacte, en 4e canal d'entrée. Entraînement auto-étiqueté (chaque visage anime apparié à ses propres contours, 20 ép.), greffe à zéro. La loss est divisée par deux (0,026 → 0,013) : le modèle s'appuie massivement sur les lignes.

portraits manga-fiés guidés par leurs contours — 3 tirages par personne

Verdict : l'identité est sauvée — chaque personne reste reconnaissable (les lunettes restent des lunettes, la mâchoire suit), et les 3 tirages varient le style en gardant la structure. Le prix : le rendu est moins « anime pur » qu'avant — les contours d'une photo réelle sont denses et texturés, loin des lignes propres du dessin (preuve par le contrôle : sur des contours d'anime, la régénération est fidèle ET stylée). C'est le curseur éternel : plus de contrainte = plus de ressemblance, moins de liberté de style.

fine-tuning du run-009 · 20 ép. · 49 min GPU · loss 0,026 → 0,013 · checkpoint : run-011-contours.pt

run-010 : la vidéo — en cours de cuisson (échec provisoire instructif)

Premier modèle 3D du projet : U-Net à convolutions 3×3×3 (le temps est une dimension), qui débruite des blocs de 16 images d'un coup — Moving MNIST fabriqué sur place (le dataset en GIF : 2 chiffres qui rebondissent). Après 30 époques (3 h 30 de GPU) : des taches blanches qui dansent sur fond granuleux — pas encore des chiffres. Mais la trajectoire est saine : époque 10 = bruit pur, époque 30 = formes émergentes, loss encore en chute (0,11 → 0,0099). Diagnostic : sous-entraîné, pas cassé — 5 600 pas de gradient contre 29 500 pour les chats. Le levier « durée », encore lui.

U-Net 3D 7,6 M params · 6 000 clips × 16 images · 3 h 30 GPU · à suivre : reprise de l'entraînement

run-012 : la diffusion latente maison (l'architecture de Stable Diffusion, entière, à nous)

Notre mur était la résolution : en 64×64, un trait fin n'existe pas. Diffuser en 128×128 coûte 4× trop cher… sauf si on ajoute la pièce qui manquait à notre pile : le compresseur. Un auto-encodeur (0,5 M params) apprend — en recopiant ses entrées, zéro étiquette — à résumer chaque visage 128×128 en une grille de 32×32 codes, et à le reconstruire. La preuve (originaux en haut, reconstructions en bas — compression 48×) :

auto-encodeur : originaux vs reconstructions

Puis notre U-Net habituel fait sa diffusion dans l'espace compressé — le run de diffusion le moins cher du projet (32×32 !) pour les images les plus grandes. Le modèle rêve en langage compressé, le décodeur traduit :

36 visages manga générés en 128×128

Premiers 128×128 du projet : mèches de cheveux individuelles, reflets dans les yeux — des détails qui n'existaient pas en 64×64. Reste un grain « hachuré » par endroits (la signature des petits modèles latents) : la loss diffusion descendait encore à l'époque 100, il y a de la marge.

AE 10 ép. (loss 0,024 → 0,004) + diffusion 100 ép. (0,216 → 0,074) · 1 h 32 au total · checkpoints : run-012-ae.pt + run-012-latent.pt

run-013 : le duel des architectures — et si on remplaçait la diffusion par notre GPT ?

Sur les mêmes 63 565 visages et le même budget, l'autre grande école (la recette du premier DALL-E) : un VQ-VAE compresse chaque image en une « phrase » de 256 tokens pris dans un dictionnaire visuel de 512 codes appris… puis notre mini-GPT (le même moteur que le dessinateur de chats) apprend à écrire ces phrases, token par token. Générer un visage = écrire 256 tokens, puis regonfler.

36 visages manga générés token par token par le GPT

Verdict du duel : le GPT gagne la cohérence, la diffusion garde le piqué. Les visages du GPT sont remarquablement propres et harmonieux (pas de grain hachuré), mais plus doux/flous — le VQ à 512 codes lisse les détails (voir ses reconstructions). Ceux de la diffusion latente (run-012) sont plus piqués mais plus granuleux. Deux écoles, deux signatures — exactement le débat qui agite la recherche, reproduit sur notre paillasse.

VQ-VAE 12 ép. (loss 3,75 → 0,13) + GPT 5,1 M · 30 ép. (4,10 → 1,91) · 1 h 47 GPU · 256 tokens/image · checkpoint : run-013-vqgpt.pt

Ce qu'on retient

1. Le volume par sujet, encore et toujours : 15 700 photos d'un seul sujet (des visages de chats cadrés pareil) — c'est exactement ce qui manquait à Sketchy (~420 croquis par catégorie). Avec le bon volume, un petit modèle de 5,1 M de paramètres suffit.
2. Deux familles, une même recette : GPT apprend « continue la séquence », la diffusion apprend « nettoie l'image » — dans les deux cas, un modèle qui reconstruit ses données d'entraînement, une loss simple, beaucoup d'exemples.
3. Limite assumée : du 64×64, petit et parfois flou. Midjourney fait pareil en principe, avec des milliers de GPU en plus.

run-007 · U-Net 5,1 M params · DDPM 1000 pas · 120 époques (photos vues 2×/époque) · loss bruit 0,128 → 0,027 · 2 h 10 de P100 Kaggle · 0 € · checkpoint : run-007-diffusion.pt