Introduction Ă  đŸ€— Diffusers

Dans ce notebook, vous allez entraĂźner votre premier modĂšle de diffusion pour gĂ©nĂ©rer des images de mignons papillons 🩋. En cours de route, vous apprendrez les composants de base de la bibliothĂšque đŸ€— Diffusers, qui fournira une bonne assise pour les applications plus avancĂ©es que nous couvrirons plus tard dans le cours.

DĂ©butons par une vue d’ensemble de ce qu’on va faire dans ce notebook. Nous allons :

  • Voir un puissant pipeline de modĂšles de diffusion personnalisĂ© en action (avec des informations sur la façon de crĂ©er votre propre version).
  • CrĂ©er votre propre mini-pipeline en :
    • RĂ©capitulant les idĂ©es principales derriĂšre les modĂšles de diffusion
    • Chargement de donnĂ©es Ă  partir du Hub pour l’entraĂźnement
    • Explorer comment ajouter du bruit Ă  ces donnĂ©es Ă  l’aide d’un planificateur
    • CrĂ©er et entraĂźner le modĂšle UNet
    • Rassembler les piĂšces du puzzle pour en faire un pipeline fonctionnel
  • Éditer et exĂ©cuter un script pour initialiser des sĂ©ries d’entraĂźnement plus longues, qui gĂšrera
    • EntraĂźnement multi-GPU via đŸ€— Accelerate
    • Journalisation de l’expĂ©rience pour suivre les statistiques critiques
    • TĂ©lĂ©chargement du modĂšle final sur le Hub d’Hugging Face

Installation des bibliothĂšques

ExĂ©cutez la cellule suivante pour installer la bibliothĂšque đŸ€— Diffusers ainsi que quelques autres prĂ©requis :

%pip install -qq -U diffusers datasets transformers accelerate ftfy pyarrow==9.0.0

Ensuite, rendez-vous sur https://huggingface.co/settings/tokens et crĂ©ez un tokens d’accĂšs avec autorisation d’écriture si vous n’en avez pas dĂ©jĂ  un :

Bref aperçu du contenu du cours.

Vous pouvez vous connecter avec ce token en utilisant la ligne de commande (huggingface-cli login) ou en exécutant la cellule suivante :

from huggingface_hub import notebook_login

notebook_login()

Vous devez ensuite installer Git-LFS pour télécharger les checkpoints de votre modÚle :

%%capture
!sudo apt -qq install git-lfs
!git config --global credential.helper store

Enfin, importons les bibliothÚques que nous utiliserons et définissons quelques fonctions de confort que nous utiliserons plus tard dans le notebook :

import numpy as np
import torch
import torch.nn.functional as F
from matplotlib import pyplot as plt
from PIL import Image


def show_images(x):
    """Étant donnĂ© un lot d'images x, faire une grille et convertir en PIL"""
    x = x * 0.5 + 0.5  # On va de (-1, 1) et revenons (0, 1)
    grid = torchvision.utils.make_grid(x)
    grid_im = grid.detach().cpu().permute(1, 2, 0).clip(0, 1) * 255
    grid_im = Image.fromarray(np.array(grid_im).astype(np.uint8))
    return grid_im


def make_grid(images, size=64):
    """Étant donnĂ© une liste d'images PIL, les empiler en une ligne pour faciliter la visualisation."""
    output_im = Image.new("RGB", (size * len(images), size))
    for i, im in enumerate(images):
        output_im.paste(im.resize((size, size)), (i * size, 0))
    return output_im


# Les utilisateurs de Mac peuvent avoir besoin de device = 'mps' (non testé)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

OK, nous sommes prĂȘts !

Exemple gĂ©nĂ©rique d’infĂ©rence avec Dreambooth, un avant-goĂ»t de ce qui nous attend

Si vous avez un tant soit peu consultĂ© les mĂ©dias sociaux au cours des derniers mois, vous avez certainement entendu parler de Stable Diffusion. Il s’agit d’un puissant modĂšle de diffusion latent conditionnĂ© par le texte (ne vous inquiĂ©tez pas, nous allons apprendre ce que cela signifie). Mais il a un dĂ©faut : il ne sait pas Ă  quoi vous ou moi ressemblons, Ă  moins que nous soyons suffisamment cĂ©lĂšbres pour que nos images soient rĂ©pandues sur internet.

Dreambooth nous permet de crĂ©er notre propre variante de modĂšle avec une connaissance supplĂ©mentaire d’un visage, d’un objet ou d’un style spĂ©cifique. Le Corridor Crew a rĂ©alisĂ© une excellente vidĂ©o (en anglais) en utilisant cette technique pour raconter des histoires avec des personnages cohĂ©rents, ce qui est un excellent exemple de ce que cette technique peut faire :

from IPython.display import YouTubeVideo

YouTubeVideo("W4Mcuh38wyM")

Voici un exemple d’une sortie d’un modĂšle entraĂźnĂ© sur 5 photos du jouet Monsieur Patate.

Tout d’abord, nous chargeons le pipeline. Ceci tĂ©lĂ©charge les poids du modĂšle depuis le Hub. Étant donnĂ© que plusieurs gigaoctets de donnĂ©es sont tĂ©lĂ©chargĂ©s pour une dĂ©monstration d’une ligne, vous pouvez sauter cette cellule et simplement admirer la sortie de l’exemple !

from diffusers import StableDiffusionPipeline

# Consultez https://huggingface.co/sd-dreambooth-library pour découvrir de nombreux modÚles provenant de la communauté
model_id = "sd-dreambooth-library/mr-potato-head"

# Chargement du pipeline
pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16).to(
    device
)

Une fois le chargement du pipeline terminé, nous pouvons générer des images avec :

prompt = "an abstract oil painting of sks mr potato head by picasso"
image = pipe(prompt, num_inference_steps=50, guidance_scale=7.5).images[0]
image
Bref aperçu du contenu du cours.

✏ À votre tour ! Essayez vous-mĂȘme avec des prompts diffĂ©rents. Le token sks reprĂ©sente un identifiant unique pour le nouveau concept : que se passe-t-il si vous l’omettez ? Vous pouvez aussi expĂ©rimenter en changeant le nombre de pas d’échantillonnage (jusqu’oĂč pouvez-vous descendre ?) et le paramĂštre guidance_scale, qui dĂ©termine jusqu’à quel point le modĂšle va essayer de correspondre au prompt.

Il se passe beaucoup de choses dans ce pipeline ! À la fin du cours, vous saurez comment tout cela fonctionne. Pour l’instant, voyons comment nous pouvons entraĂźner un modĂšle de diffusion Ă  partir de zĂ©ro.

MVP (Minimum Viable Pipeline)

Exemple d’infĂ©rence sur les papillons

L’API de base de đŸ€— Diffusers est divisĂ©e en trois composants principaux :

  • Pipelines : classes de haut niveau conçues pour gĂ©nĂ©rer rapidement des Ă©chantillons Ă  partir de modĂšles de diffusion populaires entraĂźnĂ©s de maniĂšre conviviale.
  • Models : architectures populaires pour entraĂźner de nouveaux modĂšles de diffusion, par exemple UNet.
  • Schedulers : diverses techniques pour gĂ©nĂ©rer des images Ă  partir du bruit pendant l’infĂ©rence ainsi que pour gĂ©nĂ©rer des images bruitĂ©es pour l’entraĂźnement.

Les pipelines sont parfaits pour les utilisateurs finaux, mais si vous ĂȘtes ici pour ce cours, nous supposons que vous voulez savoir ce qui se passe sous le capot ! Dans le reste de ce notebook, nous allons donc construire notre propre pipeline capable de gĂ©nĂ©rer de petites images de papillons. Voici le rĂ©sultat final en action :

from diffusers import DDPMPipeline

# Chargement du pipeline de papillons
butterfly_pipeline = DDPMPipeline.from_pretrained(
    "johnowhitaker/ddpm-butterflies-32px"
).to(device)

# Création de 8 images
images = butterfly_pipeline(batch_size=8).images

# Visualisation du résultat
make_grid(images)
Bref aperçu du contenu du cours.

Ce n’est peut-ĂȘtre pas aussi impressionnant que l’exemple de DreamBooth, mais nous entraĂźnons notre modĂšle Ă  partir de zĂ©ro avec ~0,0001% des donnĂ©es utilisĂ©es pour entraĂźner Stable Diffusion. En parlant d’entraĂźnement, rappelez-vous que l’entraĂźnement d’un modĂšle de diffusion ressemble Ă  ceci :

  • Chargement de quelques images Ă  partir des donnĂ©es entraĂźnĂ©es.
  • Ajout de bruit, en diffĂ©rentes quantitĂ©s.
  • Introduction des versions bruitĂ©es des donnĂ©es d’entrĂ©e dans le modĂšle.
  • Évaluation de la capacitĂ© du modĂšle Ă  dĂ©bruiter ces donnĂ©es d’entrĂ©e
  • Utilisation de ces informations pour mettre Ă  jour les poids du modĂšle, et rĂ©pĂ©tition.

Nous allons explorer ces Ă©tapes une par une dans les prochaines parties jusqu’à ce que nous ayons une boucle d’entraĂźnement complĂšte, puis nous verrons comment Ă©chantillonner Ă  partir du modĂšle entraĂźnĂ© et comment regrouper le tout dans un pipeline pour faciliter le partage. Commençons par les donnĂ©es.

TĂ©lĂ©charger le jeu de donnĂ©es d’entraĂźnement

Pour cet exemple, nous utilisons un jeu de donnĂ©es d’images provenant du Hub d’Hugging Face. Plus prĂ©cisĂ©ment, cette collection de 1000 images de papillons. Il s’agit d’un trĂšs petit jeu de donnĂ©es, c’est pourquoi nous avons aussi inclus des lignes en commentaires pour quelques options plus importantes. Si vous prĂ©fĂ©rez utiliser votre propre collection d’images, vous pouvez Ă©galement utiliser l’exemple de code commentĂ© pour charger des images Ă  partir d’un dossier.

import torchvision
from datasets import load_dataset
from torchvision import transforms

dataset = load_dataset("huggan/smithsonian_butterflies_subset", split="train")

# Ou charger des images Ă  partir d'un dossier local
# dataset = load_dataset("imagefolder", data_dir="path/to/folder")

# Nous entraßnerons sur des images carrées de 32 pixels, mais vous pouvez aussi essayer des tailles plus grandes
image_size = 32
# Vous pouvez réduire la taille de votre batch si vous manquez de mémoire GPU
batch_size = 64

# Définition les augmentations de données
preprocess = transforms.Compose(
    [
        transforms.Resize((image_size, image_size)),  # Redimensionner
        transforms.RandomHorizontalFlip(),  # Retournement aléatoire
        transforms.ToTensor(),  # Convertir en tenseur (0, 1)
        transforms.Normalize([0.5], [0.5]),  # Passage en (-1, 1)
    ]
)


def transform(examples):
    images = [preprocess(image.convert("RGB")) for image in examples["image"]]
    return {"images": images}


dataset.set_transform(transform)

# Créer un chargeur de données à partir du jeu de données pour servir les images transformées en batchs
train_dataloader = torch.utils.data.DataLoader(
    dataset, batch_size=batch_size, shuffle=True
)

Nous pouvons saisir un batch d’images et en visualiser quelques-unes comme suit :

xb = next(iter(train_dataloader))["images"].to(device)[:8]
print("X shape:", xb.shape)
show_images(xb).resize((8 * 64, 64), resample=Image.NEAREST)
Bref aperçu du contenu du cours.

Nous nous en tenons Ă  un petit jeu de donnĂ©es avec des images de 32 pixels pour que les temps d’entraĂźnement restent raisonnables dans ce notebook.

Définir le planificateur

Notre plan d’entraĂźnement consiste Ă  prendre ces images d’entrĂ©e et Ă  leur ajouter du bruit, puis Ă  transmettre les images bruitĂ©es au modĂšle. Lors de l’infĂ©rence, nous utiliserons les prĂ©dictions du modĂšle pour supprimer le bruit de maniĂšre itĂ©rative. Dans đŸ€— Diffusers, ces deux processus sont gĂ©rĂ©s par le scheduler (planificateur).

Le planificateur de bruit dĂ©termine la quantitĂ© de bruit ajoutĂ©e Ă  diffĂ©rents moments. Voici comment nous pourrions crĂ©er un planificateur en utilisant les paramĂštres par dĂ©faut pour l’entraĂźnement et l’échantillonnage “DDPM” (d’aprĂšs l’article d’aprĂšs l’article Denoising Diffusion Probabalistic Models) :

from diffusers import DDPMScheduler

noise_scheduler = DDPMScheduler(num_train_timesteps=1000)

Le papier DDPM dĂ©crit un processus de corruption qui ajoute une petite quantitĂ© de bruit Ă  chaque pas de temps. Étant donnĂ© $x_{t-1}$ pour un certain pas de temps, nous pouvons obtenir la version suivante (lĂ©gĂšrement plus bruyante) $x_t$ avec :

\[\begin{aligned} q(\mathbf{x}_t \vert \mathbf{x}_{t-1}) &= \mathcal{N}(\mathbf{x}_t; \sqrt{1 - \beta_t} \mathbf{x}_{t-1}, \beta_t\mathbf{I}) \\ q(\mathbf{x}_{1:T} \vert \mathbf{x}_0) &= \prod^T_{t=1} q(\mathbf{x}_t \vert \mathbf{x}_{t-1}) \end{aligned}\]

Nous prenons $x_{t-1}$, l’échelonnons de $\sqrt{1 - \beta_t}$ et ajoutons du bruit Ă©chelonnĂ© par $\beta_t$. Ce $\beta$ est dĂ©fini pour chaque $t$ selon un certain planificateur et dĂ©termine la quantitĂ© de bruit ajoutĂ©e par pas de temps. Maintenant, nous ne voulons pas nĂ©cessairement faire cette opĂ©ration 500 fois pour obtenir $x_{500}$, nous avons donc une autre formule pour obtenir $x_t$ pour n’importe quel t Ă©tant donnĂ© $x_0$ :

\[\begin{aligned} q(\mathbf{x}_t \vert \mathbf{x}_0) &= \mathcal{N}(\mathbf{x}_t; \sqrt{\bar{\alpha}_t} \mathbf{x}_0, {(1 - \bar{\alpha}_t)} \mathbf{I}) \end{aligned}\]

oĂč :

\[\bar{\alpha}_t = \prod_{i=1}^{T} \alpha_i,\quad \alpha_i = 1 - \beta_i\]

La notation mathĂ©matique fait toujours peur ! Heureusement, le planificateur s’en charge pour nous. Nous pouvons tracer $\sqrt{\bar{\alpha}_t}$ (appelĂ© sqrt_alpha_prod) et $\sqrt{(1 - \bar{\alpha}_t)}$ (appelĂ© sqrt_one_minus_alpha_prod) pour voir comment l’entrĂ©e ($x$) et le bruit sont mis Ă  l’échelle et mĂ©langĂ©s Ă  travers diffĂ©rents pas de temps :

plt.plot(noise_scheduler.alphas_cumprod.cpu() ** 0.5, label=r"${\sqrt{\bar{\alpha}_t}}$")
plt.plot((1 - noise_scheduler.alphas_cumprod.cpu()) ** 0.5, label=r"$\sqrt{(1 - \bar{\alpha}_t)}$")
plt.legend(fontsize="x-large");
Bref aperçu du contenu du cours.

✏ À votre tour ! Vous pouvez explorer comment ce graphique change avec diffĂ©rents paramĂštres pour beta_start, beta_end et beta_schedule en remplaçant l’une des options commentĂ©es ci-dessous :

## Exemple avec beaucoup de bruit ajouté :
# noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_start=0.001, beta_end=0.004)

## Le planificateur cosinus pouvant s'avérer meilleur pour les images de petite taille :
# noise_scheduler = DDPMScheduler(num_train_timesteps=1000, beta_schedule='squaredcos_cap_v2')

Quel que soit le planificateur que vous avez choisi, nous pouvons maintenant l’utiliser pour ajouter du bruit en diffĂ©rentes quantitĂ©s en utilisant la fonction noise_scheduler.add_noise comme suit :

timesteps = torch.linspace(0, 999, 8).long().to(device)
noise = torch.randn_like(xb)
noisy_xb = noise_scheduler.add_noise(xb, noise, timesteps)
print("Noisy X shape", noisy_xb.shape)
show_images(noisy_xb).resize((8 * 64, 64), resample=Image.NEAREST)
Bref aperçu du contenu du cours.

LĂ  encore, Ă©tudiez l’effet de l’utilisation de diffĂ©rents planificateurs et paramĂštres de bruit. Cette vidĂ©o (en anglais) explique en dĂ©tail certains des calculs ci-dessus et constitue une excellente introduction Ă  certains de ces concepts.

Définir le modÚle

Nous en arrivons maintenant Ă  l’élĂ©ment central : le modĂšle lui-mĂȘme.

La plupart des modùles de diffusion utilisent des architectures qui sont des variantes d’un U-net et c’est ce que nous utiliserons ici.

Bref aperçu du contenu du cours.

En bref :

  • l’image en entrĂ©e du modĂšle passe par plusieurs blocs de couches ResNet, chacun divisant la taille de l’image par 2
  • puis elle passe Ă  travers le mĂȘme nombre de blocs qui la surĂ©chantillonnent.
  • il y a des skip connections qui relient les caractĂ©ristiques sur le chemin du sous-Ă©chantillonnage aux couches correspondantes dans le chemin du surĂ©chantillonnage.

L’une des principales caractĂ©ristiques de ce modĂšle est qu’il prĂ©dit des images de la mĂȘme taille que l’entrĂ©e, ce qui est exactement ce dont nous avons besoin ici.

đŸ€— Diffusers nous fournit une classe UNet2DModel pratique qui crĂ©e l’architecture dĂ©sirĂ©e dans PyTorch.

CrĂ©ons un U-net pour la taille d’image dĂ©sirĂ©e. Notez que les down_block_types correspondent aux blocs de sous-Ă©chantillonnage (en vert sur le diagramme ci-dessus), et que les up_block_types sont les blocs de surĂ©chantillonnage (en rouge sur le diagramme) :

from diffusers import UNet2DModel

# Création d'un modÚle
model = UNet2DModel(
    sample_size=image_size,  # la résolution de l'image cible
    in_channels=3,  # le nombre de canaux d'entrée, 3 pour les images RVB
    out_channels=3,  # le nombre de canaux de sortie
    layers_per_block=2,  # le nombre de couches ResNet Ă  utiliser par bloc UNet
    block_out_channels=(64, 128, 128, 256),  # Plus de canaux -> plus de paramĂštres
    down_block_types=(
        "DownBlock2D",  # un bloc de sous-échantillonnage ResNet standard
        "DownBlock2D",
        "AttnDownBlock2D",  # un bloc de sous-échantillonnage ResNet avec auto-attention spatiale
        "AttnDownBlock2D",
    ),
    up_block_types=(
        "AttnUpBlock2D",
        "AttnUpBlock2D",  # un bloc de suréchantillonnage ResNet avec auto-attention spatiale
        "UpBlock2D",
        "UpBlock2D",  # un bloc de suréchantillonnage ResNet standard
    ),
)
model.to(device)

Lorsque vous traitez des donnĂ©es d’entrĂ©e en haute rĂ©solution, vous pouvez utiliser davantage de blocs descendants et ascendants, et ne conserver les couches d’attention que pour les couches de rĂ©solution les plus basses (infĂ©rieures) afin de rĂ©duire l’utilisation de la mĂ©moire. Nous verrons plus tard comment vous pouvez expĂ©rimenter pour trouver les meilleurs paramĂštres pour votre cas d’utilisation.

Nous pouvons vĂ©rifier que le passage d’un batch de donnĂ©es et de pas de temps alĂ©atoires produit une sortie de mĂȘme forme que les donnĂ©es d’entrĂ©e :

with torch.no_grad():
    model_prediction = model(noisy_xb, timesteps).sample
model_prediction.shape

Dans la section suivante, nous verrons comment entraĂźner ce modĂšle.

CrĂ©er une boucle d’entraĂźnement

Il est temps d’entraĂźner ! Voici une boucle d’optimisation typique dans PyTorch, oĂč nous parcourons les donnĂ©es batch par batch et mettons Ă  jour les paramĂštres de notre modĂšle Ă  chaque Ă©tape Ă  l’aide d’un optimiseur, ici, l’optimiseur AdamW avec un taux d’apprentissage de 0,0004.

Pour chaque batch de données, nous

  • Ă©chantillonnons des pas de temps alĂ©atoires
  • bruitons les donnĂ©es en consĂ©quence
  • transmettons les donnĂ©es bruitĂ©es au modĂšle
  • comparons les prĂ©dictions du modĂšle avec la cible (c’est-Ă -dire le bruit dans ce cas) en utilisant l’erreur quadratique moyenne comme fonction de perte
  • mettons Ă  jour les paramĂštres du modĂšle via loss.backward() et optimizer.step().

Au cours de ce processus, nous enregistrons aussi les pertes au fil du temps pour un tracé ultérieur.

NB : ce code prend prĂšs de 10 minutes Ă  exĂ©cuter. N’hĂ©sitez pas Ă  sauter ces deux cellules et Ă  utiliser le modĂšle prĂ©-entraĂźnĂ© si vous ĂȘtes pressĂ©. Vous pouvez Ă©galement Ă©tudier comment la rĂ©duction du nombre de canaux dans chaque couche via la dĂ©finition du modĂšle ci-dessus peut accĂ©lĂ©rer les choses.

L’exemple officiel d’entraĂźnement de đŸ€— Diffusers entraĂźne un modĂšle plus grand sur ce jeu de donnĂ©es Ă  une rĂ©solution plus Ă©levĂ©e, et constitue une bonne rĂ©fĂ©rence pour ce Ă  quoi ressemble une boucle d’entraĂźnement moins minimale :

# Définir le planificateur de bruit
noise_scheduler = DDPMScheduler(
    num_train_timesteps=1000, beta_schedule="squaredcos_cap_v2"
)

# Boucle d'entraĂźnement
optimizer = torch.optim.AdamW(model.parameters(), lr=4e-4)

losses = []

for epoch in range(30):
    for step, batch in enumerate(train_dataloader):
        clean_images = batch["images"].to(device)
        # Exemple de bruit Ă  ajouter aux images
        noise = torch.randn(clean_images.shape).to(clean_images.device)
        bs = clean_images.shape[0]

        # Échantillonner un pas de temps alĂ©atoire pour chaque image
        timesteps = torch.randint(
            0, noise_scheduler.num_train_timesteps, (bs,), device=clean_images.device
        ).long()

        # Ajouter du bruit aux images propres en fonction de l'ampleur du bruit à chaque étape
        noisy_images = noise_scheduler.add_noise(clean_images, noise, timesteps)

        # Obtenir la prédiction du modÚle
        noise_pred = model(noisy_images, timesteps, return_dict=False)[0]

        # Calculer la perte
        loss = F.mse_loss(noise_pred, noise)
        loss.backward(loss)
        losses.append(loss.item())

        # Mise Ă  jour des paramĂštres du modĂšle Ă  l'aide de l'optimiseur
        optimizer.step()
        optimizer.zero_grad()

    if (epoch + 1) % 5 == 0:
        loss_last_epoch = sum(losses[-len(train_dataloader) :]) / len(train_dataloader)
        print(f"Epoch:{epoch+1}, loss: {loss_last_epoch}")

En traçant la perte, nous constatons que le modĂšle s’amĂ©liore rapidement dans un premier temps, puis continue Ă  s’amĂ©liorer Ă  un rythme plus lent (ce qui est plus Ă©vident si nous utilisons une Ă©chelle logarithmique, comme indiquĂ© Ă  droite) :

fig, axs = plt.subplots(1, 2, figsize=(12, 4))
axs[0].plot(losses)
axs[1].plot(np.log(losses))
plt.show()
Bref aperçu du contenu du cours.

Au lieu d’exĂ©cuter le code d’entraĂźnement ci-dessus, vous pouvez utiliser le modĂšle du pipeline comme suit :

## Décommenter pour charger le modÚle que j'ai entraßné plus tÎt à la place :
# model = butterfly_pipeline.unet

Générer des images

Comment obtenir des images avec ce modĂšle ?

‱ Option 1 : CrĂ©ation d’un pipeline :

from diffusers import DDPMPipeline

image_pipe = DDPMPipeline(unet=model, scheduler=noise_scheduler)

pipeline_output = image_pipe()
pipeline_output.images[0]
Bref aperçu du contenu du cours.

Nous pouvons enregistrer un pipeline dans un dossier local comme suit :

image_pipe.save_pretrained("my_pipeline")

Inspection du contenu du dossier :

!ls my_pipeline/
model_index.json  scheduler  unet

Les sous-dossiers scheduler et unet contiennent tout ce qui est nĂ©cessaire pour recrĂ©er ces composants. Par exemple, dans le dossier unet vous trouverez les poids du modĂšle (diffusion_pytorch_model.bin) ainsi qu’un fichier de configuration qui spĂ©cifie l’architecture UNet.

!ls my_pipeline/unet/
config.json  diffusion_pytorch_model.bin

Ensemble, ces fichiers contiennent tout ce qui est nĂ©cessaire pour recrĂ©er le pipeline. Vous pouvez les tĂ©lĂ©charger manuellement sur le Hub pour partager le pipeline avec d’autres personnes, ou consulter le code pour le faire via l’API dans la section suivante.

‱ Option 2 : Ă©crire une boucle d’échantillonnage

Si vous inspectez la méthode forward du pipeline, vous pourrez voir ce qui se passe lorsque nous lançons image_pipe() :

# ??image_pipe.forward

Nous commençons par un bruit alĂ©atoire et parcourons les pas de temps de l’ordonnanceur du plus bruyant au moins bruyant, en supprimant une petite quantitĂ© de bruit Ă  chaque Ă©tape sur la base de la prĂ©diction du modĂšle :

# Point de départ aléatoire (8 images aléatoires) :
sample = torch.randn(8, 3, 32, 32).to(device)

for i, t in enumerate(noise_scheduler.timesteps):

    # Obtenir le modÚle de prédiction
    with torch.no_grad():
        residual = model(sample, t).sample

    # Mise à jour de l'échantillon avec le pas
    sample = noise_scheduler.step(residual, t, sample).prev_sample

show_images(sample)
Bref aperçu du contenu du cours.

La fonction noise_scheduler.step() effectue les calculs nĂ©cessaires pour mettre Ă  jour sample de maniĂšre appropriĂ©e. Il existe un certain nombre de mĂ©thodes d’échantillonnage. Dans l’unitĂ© suivante, nous verrons comment nous pouvons Ă©changer un Ă©chantillonneur diffĂ©rent pour accĂ©lĂ©rer la gĂ©nĂ©ration d’images avec des modĂšles existants, et nous parlerons plus en dĂ©tail de la thĂ©orie derriĂšre l’échantillonnage des modĂšles de diffusion.

Pousser votre modĂšle vers le Hub

Dans l’exemple ci-dessus, nous avons enregistrĂ© notre pipeline dans un dossier local. Pour pousser notre modĂšle vers le Hub, nous aurons besoin d’un dĂ©pĂŽt de modĂšles dans lequel nous pourrons pousser nos fichiers. Nous dĂ©terminerons le nom du dĂ©pĂŽt Ă  partir de l’ID du modĂšle que nous voulons donner Ă  notre modĂšle (n’hĂ©sitez pas Ă  remplacer le nom du modĂšle par votre propre choix ; il doit juste contenir votre nom d’utilisateur, ce que fait la fonction get_full_repo_name()) :

from huggingface_hub import get_full_repo_name

model_name = "sd-class-butterflies-32"
hub_model_id = get_full_repo_name(model_name)
hub_model_id

Ensuite, crĂ©er un dĂ©pĂŽt de modĂšle sur le đŸ€— Hub et pousser notre modĂšle :

from huggingface_hub import HfApi, create_repo

create_repo(hub_model_id)
api = HfApi()
api.upload_folder(
    folder_path="my_pipeline/scheduler", path_in_repo="", repo_id=hub_model_id
)
api.upload_folder(folder_path="my_pipeline/unet", path_in_repo="", repo_id=hub_model_id)
api.upload_file(
    path_or_fileobj="my_pipeline/model_index.json",
    path_in_repo="model_index.json",
    repo_id=hub_model_id,
)

La derniĂšre chose Ă  faire est de crĂ©er une belle carte modĂšle afin que notre gĂ©nĂ©rateur de papillons puisse ĂȘtre facilement trouvĂ© sur le đŸ€— Hub (n’hĂ©sitez pas Ă  dĂ©velopper et Ă  modifier la description !) :

from huggingface_hub import ModelCard

content = f"""
---
license: mit
tags:
- pytorch
- diffusers
- unconditional-image-generation
- diffusion-models-class
---

# Model Card for Unit 1 of the [Diffusion Models Class 🧹](https://github.com/huggingface/diffusion-models-class)

This model is a diffusion model for unconditional image generation of cute 🩋.

## Usage


```python
from diffusers import DDPMPipeline

pipeline = DDPMPipeline.from_pretrained('{hub_model_id}')
image = pipeline().images[0]
image
```python
"""

card = ModelCard(content)
card.push_to_hub(hub_model_id)

Maintenant que le modĂšle est sur le Hub, vous pouvez le tĂ©lĂ©charger de n’importe oĂč en utilisant la mĂ©thode from_pretrained() de DDPMPipeline comme suit :

from diffusers import DDPMPipeline

image_pipe = DDPMPipeline.from_pretrained(hub_model_id)
pipeline_output = image_pipe()
pipeline_output.images[0]
Bref aperçu du contenu du cours.

Bien, ça marche !

Passer Ă  l’échelle supĂ©rieure avec đŸ€— Accelerate

Ce notebook a Ă©tĂ© conçu Ă  des fins d’apprentissage, et en tant que tel, nous avons essayĂ© de garder le code aussi minimal et propre que possible. Pour cette raison, nous avons omis certaines choses que vous pourriez souhaiter si vous deviez entraĂźner un modĂšle plus grand sur beaucoup plus de donnĂ©es, comme le support multi-GPU, la trace de la progression et des images d’exemple, la sauvegarde du gradient pour supporter des tailles de batch plus importantes, le tĂ©lĂ©chargement automatique des modĂšles et ainsi de suite. Heureusement, la plupart de ces fonctionnalitĂ©s sont disponibles dans l’exemple de script d’entraĂźnement ici..

Vous pouvez télécharger le fichier comme suit :

!wget https://github.com/huggingface/diffusers/raw/main/examples/unconditional_image_generation/train_unconditional.py

Ouvrez le fichier et vous verrez oĂč le modĂšle est dĂ©fini et quels sont les paramĂštres disponibles. Nous exĂ©cutons le script Ă  l’aide de la commande suivante :

# Donnons un nom Ă  notre nouveau modĂšle pour le Hub
model_name = "sd-class-butterflies-64"
hub_model_id = get_full_repo_name(model_name)
!accelerate launch train_unconditional.py \
  --dataset_name="huggan/smithsonian_butterflies_subset" \
  --resolution=64 \
  --output_dir={model_name} \
  --train_batch_size=32 \
  --num_epochs=50 \
  --gradient_accumulation_steps=1 \
  --learning_rate=1e-4 \
  --lr_warmup_steps=500 \
  --mixed_precision="no"

Comme prĂ©cĂ©demment, poussons le modĂšle vers le Hub et crĂ©ons une belle carte de modĂšle (et n’hĂ©sitez pas Ă  l’éditer comme vous le souhaitez !):

create_repo(hub_model_id)
api = HfApi()
api.upload_folder(
    folder_path=f"{model_name}/scheduler", path_in_repo="", repo_id=hub_model_id
)
api.upload_folder(
    folder_path=f"{model_name}/unet", path_in_repo="", repo_id=hub_model_id
)
api.upload_file(
    path_or_fileobj=f"{model_name}/model_index.json",
    path_in_repo="model_index.json",
    repo_id=hub_model_id,
)

content = f"""
---
license: mit
tags:
- pytorch
- diffusers
- unconditional-image-generation
- diffusion-models-class
---

# Model Card for Unit 1 of the [Diffusion Models Class 🧹](https://github.com/huggingface/diffusion-models-class)

This model is a diffusion model for unconditional image generation of cute 🩋.

## Usage

```python
from diffusers import DDPMPipeline

pipeline = DDPMPipeline.from_pretrained('{hub_model_id}')
image = pipeline().images[0]
image
```python
"""

card = ModelCard(content)
card.push_to_hub(hub_model_id)

Environ 45 minutes plus tard, voici le résultat :

pipeline = DDPMPipeline.from_pretrained(hub_model_id).to(device)
images = pipeline(batch_size=8).images
make_grid(images)
Bref aperçu du contenu du cours.

✏ À votre tour ! Essayez de trouver des paramĂštres d’entraĂźnement/de modĂšle qui donnent de bons rĂ©sultats en un minimum de temps, et partagez vos rĂ©sultats avec la communautĂ©. Fouillez dans le script pour voir si vous pouvez comprendre le code, et demandez des Ă©claircissements sur tout ce qui vous semble confus.

Pistes pour approndonfir

Nous espĂ©rons vous avoir donnĂ© un avant-goĂ»t de ce que vous pouvez faire avec la bibliothĂšque đŸ€— Diffusers ! Voici quelques pistes possibles pour la suite :

  • Essayez d’entraĂźner un modĂšle de diffusion inconditionnel sur un nouveau jeu de donnĂ©es. Points bonus si vous en crĂ©ez un vous-mĂȘme. Vous pouvez trouver d’excellents jeux de donnĂ©es d’images pour cette tĂąche dans l’organisation HugGan sur le Hub. Assurez-vous simplement de les sous-Ă©chantillonner si vous ne voulez pas attendre trĂšs longtemps pour que le modĂšle s’entraĂźne !
  • Essayez DreamBooth pour crĂ©er votre propre pipeline de Stable Diffusion personnalisĂ© en utilisant ce Space ou ce notebook.
  • Modifiez le script d’entraĂźnement pour explorer diffĂ©rents hyperparamĂštres UNet (nombre de couches, canaux, etc.), diffĂ©rents schĂ©mas de bruit, etc.
  • Consultez le notebook ImplĂ©mentation Ă  partir de 0 pour une approche diffĂ©rente des idĂ©es fondamentales que nous avons abordĂ©es dans cette unitĂ©.