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 :
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
âïž Ă votre tour ! Essayez vous-mĂȘme avec des prompts diffĂ©rents. Le token
sksreprĂ©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Ăštreguidance_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)
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)
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");
âïž Ă votre tour ! Vous pouvez explorer comment ce graphique change avec diffĂ©rents paramĂštres pour
beta_start,beta_endetbeta_scheduleen 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)
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.
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()etoptimizer.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()
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]
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)
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]
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)
âïž Ă 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é.