Diffusers documentation

تدريب نموذج انتشار

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v0.42.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

تدريب نموذج انتشار

يُعد توليد الصور غير الشرطي تطبيقًا شائعًا لنماذج الانتشار، حيث يولّد صورًا تشبه تلك الموجودة في مجموعة البيانات المستخدمة للتدريب. وعادةً ما تُحصد أفضل النتائج من خلال ضبط نموذج مُدرَّب مسبقًا على مجموعة بيانات محددة. يمكنك العثور على العديد من هذه نقاط التفتيش على Hub، ولكن إذا لم تجد ما يناسبك، يمكنك دائمًا تدريب نموذجك الخاص!

ستعلّمك هذه الشرحية كيفية تدريب UNet2DModel من الصفر على جزء من مجموعة بيانات Smithsonian Butterflies لتوليد فراشاتك الخاصة 🦋.

💡 يستند هذا الشرح التدريبي إلى دفتر الملاحظات التدريب باستخدام 🧨 Diffusers. لمزيد من التفاصيل والسياق حول نماذج الانتشار، مثل كيفية عملها، راجع دفتر الملاحظات!

قبل أن تبدأ، تأكد من تثبيت 🤗 Datasets لتحميل مجموعات بيانات الصور ومعالجتها مسبقًا، و🤗 Accelerate لتبسيط التدريب على أي عدد من وحدات معالجة الرسوميات. سيقوم الأمر التالي أيضًا بتثبيت TensorBoard لتصور مقاييس التدريب (يمكنك أيضًا استخدام Weights & Biases لتتبع تدريبك).

# uncomment to install the necessary libraries in Colab
#!pip install diffusers[training]

ننصحك بمشاركة نموذجك مع المجتمع، ولتحقيق ذلك، ستحتاج إلى تسجيل الدخول إلى حسابك على Hugging Face (أنشئ حسابًا هنا إذا لم يكن لديك حساب بالفعل!). يمكنك تسجيل الدخول من دفتر ملاحظات (notebook) وإدخال رمزك (token) عند الطلب. تأكد من أن رمزك يحمل دور الكتابة (write role).

>>> from huggingface_hub import notebook_login

>>> notebook_login()

أو سجّل الدخول من الطرفية:

hf auth login

نظرًا لأن نقاط التفتيش للنموذج كبيرة الحجم إلى حد كبير، قم بتثبيت Git-LFS لإدارة إصدارات هذه الملفات الكبيرة:

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

إعدادات التدريب

للتسهيل، أنشئ فئة TrainingConfig تحتوي على معاملات التدريب الفائقة (يمكنك تعديلها بحرية):

>>> from dataclasses import dataclass

>>> @dataclass
... class TrainingConfig:
...     image_size = 128  # the generated image resolution
...     train_batch_size = 16
...     eval_batch_size = 16  # how many images to sample during evaluation
...     num_epochs = 50
...     gradient_accumulation_steps = 1
...     learning_rate = 1e-4
...     lr_warmup_steps = 500
...     save_image_epochs = 10
...     save_model_epochs = 30
...     mixed_precision = "fp16"  # `no` for float32, `fp16` for automatic mixed precision
...     output_dir = "ddpm-butterflies-128"  # the model name locally and on the HF Hub

...     push_to_hub = True  # whether to upload the saved model to the HF Hub
...     hub_model_id = "<your-username>/<my-awesome-model>"  # the name of the repository to create on the HF Hub
...     hub_private_repo = None
...     overwrite_output_dir = True  # overwrite the old model when re-running the notebook
...     seed = 0


>>> config = TrainingConfig()

تحميل مجموعة البيانات

يمكنك بسهولة تحميل مجموعة بيانات Smithsonian Butterflies باستخدام مكتبة 🤗 Datasets:

>>> from datasets import load_dataset

>>> config.dataset_name = "huggan/smithsonian_butterflies_subset"
>>> dataset = load_dataset(config.dataset_name, split="train")

💡 يمكنك العثور على مجموعات بيانات إضافية من فعالية مجتمع HugGan أو يمكنك استخدام مجموعة بياناتك الخاصة من خلال إنشاء ImageFolder محلي. اضبط config.dataset_name على معرّف المستودع الخاص بمجموعة البيانات إذا كانت من فعالية مجتمع HugGan، أو imagefolder إذا كنت تستخدم صورك الخاصة.

🤗 يستخدم Datasets ميزة Image لفك ترميز بيانات الصورة تلقائيًا وتحميلها كـ PIL.Image حيث يمكننا عرضها:

>>> import matplotlib.pyplot as plt

>>> fig, axs = plt.subplots(1, 4, figsize=(16, 4))
>>> for i, image in enumerate(dataset[:4]["image"]):
...     axs[i].imshow(image)
...     axs[i].set_axis_off()
>>> fig.show()

لكن جميع الصور بأحجام مختلفة، لذا ستحتاج إلى معالجتها مسبقًا:

  • Resize يغيّر حجم الصورة إلى الحجم المحدد في config.image_size.
  • RandomHorizontalFlip يعزز مجموعة البيانات عن طريق عكس الصور عشوائياً.
  • Normalize مهم لإعادة مقياس قيم البكسل إلى نطاق [-1, 1]، وهو ما يتوقعه النموذج.
>>> from torchvision import transforms

>>> preprocess = transforms.Compose(
...     [
...         transforms.Resize((config.image_size, config.image_size)),
...         transforms.RandomHorizontalFlip(),
...         transforms.ToTensor(),
...         transforms.Normalize([0.5], [0.5]),
...     ]
... )

استخدم طريقة set_transform في 🤗 Datasets لتطبيق دالة preprocess أثناء التدريب مباشرة:

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


>>> dataset.set_transform(transform)

لا تتردد في عرض الصور مرة أخرى للتأكد من أنها تم تغيير حجمها. الآن أنت مستعد لتغليف مجموعة البيانات في DataLoader للتدريب!

>>> import torch

>>> train_dataloader = torch.utils.data.DataLoader(dataset, batch_size=config.train_batch_size, shuffle=True)

إنشاء نموذج UNet2DModel

يمكن إنشاء النماذج المُدرَّبة مسبقًا في 🧨 Diffusers بسهولة من خلال فئة النموذج الخاصة بها مع المعاملات التي تريدها. على سبيل المثال، لإنشاء UNet2DModel:

>>> from diffusers import UNet2DModel

>>> model = UNet2DModel(
...     sample_size=config.image_size,  # the target image resolution
...     in_channels=3,  # the number of input channels, 3 for RGB images
...     out_channels=3,  # the number of output channels
...     layers_per_block=2,  # how many ResNet layers to use per UNet block
...     block_out_channels=(128, 128, 256, 256, 512, 512),  # the number of output channels for each UNet block
...     down_block_types=(
...         "DownBlock2D",  # a regular ResNet downsampling block
...         "DownBlock2D",
...         "DownBlock2D",
...         "DownBlock2D",
...         "AttnDownBlock2D",  # a ResNet downsampling block with spatial self-attention
...         "DownBlock2D",
...     ),
...     up_block_types=(
...         "UpBlock2D",  # a regular ResNet upsampling block
...         "AttnUpBlock2D",  # a ResNet upsampling block with spatial self-attention
...         "UpBlock2D",
...         "UpBlock2D",
...         "UpBlock2D",
...         "UpBlock2D",
...     ),
... )

غالبًا ما يكون من الجيد التحقق بسرعة من أن شكل الصورة العينة يتطابق مع شكل مخرجات النموذج:

>>> sample_image = dataset[0]["images"].unsqueeze(0)
>>> print("Input shape:", sample_image.shape)
Input shape: torch.Size([1, 3, 128, 128])

>>> print("Output shape:", model(sample_image, timestep=0).sample.shape)
Output shape: torch.Size([1, 3, 128, 128])

رائع! في الخطوة التالية، ستحتاج إلى مُجدوِل (scheduler) لإضافة بعض الضوضاء إلى الصورة.

إنشاء مُجدوِل

يعمل المجدول بشكل مختلف اعتمادًا على ما إذا كنت تستخدم النموذج للتدريب أو للاستدلال. أثناء الاستدلال، يولّد المجدول الصورة من الضوضاء. أما أثناء التدريب، فيأخذ المجدول مخرجات النموذج - أو عينة - من نقطة محددة في عملية الانتشار، ثم يطبّق الضوضاء على الصورة وفقًا لـجدول الضوضاء وقاعدة التحديث.

لنلقِ نظرة على DDPMScheduler ونستخدم طريقة add_noise لإضافة بعض الضوضاء العشوائية إلى sample_image من قبل:

>>> import torch
>>> from PIL import Image
>>> from diffusers import DDPMScheduler

>>> noise_scheduler = DDPMScheduler(num_train_timesteps=1000)
>>> noise = torch.randn(sample_image.shape)
>>> timesteps = torch.LongTensor([50])
>>> noisy_image = noise_scheduler.add_noise(sample_image, noise, timesteps)

>>> Image.fromarray(((noisy_image.permute(0, 2, 3, 1) + 1.0) * 127.5).type(torch.uint8).numpy()[0])

تتمثل غاية التدريب للنموذج في التنبض بالضوضاء المضافة إلى الصورة. يمكن حساب الخسارة في هذه الخطوة من خلال:

>>> import torch.nn.functional as F

>>> noise_pred = model(noisy_image, timesteps).sample
>>> loss = F.mse_loss(noise_pred, noise)

تدريب النموذج

حتى الآن، تمتلك معظم العناصر اللازمة لبدء تدريب النموذج، وكل ما تبقّى هو تجميع كل شيء معًا.

أولاً، ستحتاج إلى مُحسِّن وجدول لمعدل التعلم:

>>> from diffusers.optimization import get_cosine_schedule_with_warmup

>>> optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)
>>> lr_scheduler = get_cosine_schedule_with_warmup(
...     optimizer=optimizer,
...     num_warmup_steps=config.lr_warmup_steps,
...     num_training_steps=(len(train_dataloader) * config.num_epochs),
... )

ثم، ستحتاج إلى طريقة لتقييم النموذج. وللتقييم، يمكنك استخدام DDPMPipeline لتوليد دفعة من الصور العينة وحفظها كشبكة:

>>> from diffusers import DDPMPipeline
>>> from diffusers.utils import make_image_grid
>>> import os

>>> def evaluate(config, epoch, pipeline):
...     # Sample some images from random noise (this is the backward diffusion process).
...     # The default pipeline output type is `List[PIL.Image]`
...     images = pipeline(
...         batch_size=config.eval_batch_size,
...         generator=torch.Generator(device='cpu').manual_seed(config.seed), # Use a separate torch generator to avoid rewinding the random state of the main training loop
...     ).images

...     # Make a grid out of the images
...     image_grid = make_image_grid(images, rows=4, cols=4)

...     # Save the images
...     test_dir = os.path.join(config.output_dir, "samples")
...     os.makedirs(test_dir, exist_ok=True)
...     image_grid.save(f"{test_dir}/{epoch:04d}.png")

الآن يمكنك تجميع جميع هذه المكونات معًا في حلقة تدريب باستخدام 🤗 Accelerate لتسجيل سهل في TensorBoard، وتراكم التدرجات، والتدريب بدقة مختلطة. لرفع النموذج إلى Hub، اكتب دالة للحصول على اسم مستودعك ومعلوماته ثم ادفعه إلى Hub.

💡 قد تبدو حلقة التدريب أدناه مخيفة وطويلة، لكنها ستستحق العناء لاحقًا عندما تبدأ تدريبك بسطر واحد من الكود! إذا لم تستطع الانتظار وتريد البدء في توليد الصور، فلا تتردد في نسخ الكود أدناه وتشغيله. يمكنك دائمًا العودة لاحقًا لفحص حلقة التدريب عن كثب، مثلما تفعل أثناء انتظار انتهاء تدريب نموذجك. 🤗

>>> from accelerate import Accelerator
>>> from huggingface_hub import create_repo, upload_folder
>>> from tqdm.auto import tqdm
>>> from pathlib import Path
>>> import os

>>> def train_loop(config, model, noise_scheduler, optimizer, train_dataloader, lr_scheduler):
...     # Initialize accelerator and tensorboard logging
...     accelerator = Accelerator(
...         mixed_precision=config.mixed_precision,
...         gradient_accumulation_steps=config.gradient_accumulation_steps,
...         log_with="tensorboard",
...         project_dir=os.path.join(config.output_dir, "logs"),
...     )
...     if accelerator.is_main_process:
...         if config.output_dir is not None:
...             os.makedirs(config.output_dir, exist_ok=True)
...         if config.push_to_hub:
...             repo_id = create_repo(
...                 repo_id=config.hub_model_id or Path(config.output_dir).name, exist_ok=True
...             ).repo_id
...         accelerator.init_trackers("train_example")

...     # Prepare everything
...     # There is no specific order to remember, you just need to unpack the
...     # objects in the same order you gave them to the prepare method.
...     model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare(
...         model, optimizer, train_dataloader, lr_scheduler
...     )

...     global_step = 0

...     # Now you train the model
...     for epoch in range(config.num_epochs):
...         progress_bar = tqdm(total=len(train_dataloader), disable=not accelerator.is_local_main_process)
...         progress_bar.set_description(f"Epoch {epoch}")

...         for step, batch in enumerate(train_dataloader):
...             clean_images = batch["images"]
...             # Sample noise to add to the images
...             noise = torch.randn(clean_images.shape, device=clean_images.device)
...             bs = clean_images.shape[0]

...             # Sample a random timestep for each image
...             timesteps = torch.randint(
...                 0, noise_scheduler.config.num_train_timesteps, (bs,), device=clean_images.device,
...                 dtype=torch.int64
...             )

...             # Add noise to the clean images according to the noise magnitude at each timestep
...             # (this is the forward diffusion process)
...             noisy_images = noise_scheduler.add_noise(clean_images, noise, timesteps)

...             with accelerator.accumulate(model):
...                 # Predict the noise residual
...                 noise_pred = model(noisy_images, timesteps, return_dict=False)[0]
...                 loss = F.mse_loss(noise_pred, noise)
...                 accelerator.backward(loss)

...                 if accelerator.sync_gradients:
...                     accelerator.clip_grad_norm_(model.parameters(), 1.0)
...                 optimizer.step()
...                 lr_scheduler.step()
...                 optimizer.zero_grad()

...             progress_bar.update(1)
...             logs = {"loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0], "step": global_step}
...             progress_bar.set_postfix(**logs)
...             accelerator.log(logs, step=global_step)
...             global_step += 1

...         # After each epoch you optionally sample some demo images with evaluate() and save the model
...         if accelerator.is_main_process:
...             pipeline = DDPMPipeline(unet=accelerator.unwrap_model(model), scheduler=noise_scheduler)

...             if (epoch + 1) % config.save_image_epochs == 0 or epoch == config.num_epochs - 1:
...                 evaluate(config, epoch, pipeline)

...             if (epoch + 1) % config.save_model_epochs == 0 or epoch == config.num_epochs - 1:
...                 if config.push_to_hub:
...                     upload_folder(
...                         repo_id=repo_id,
...                         folder_path=config.output_dir,
...                         commit_message=f"Epoch {epoch}",
...                         ignore_patterns=["step_*", "epoch_*"],
...                     )
...                 else:
...                     pipeline.save_pretrained(config.output_dir)

يا لها من كمية كبيرة من الأكواد! لكنك أصبحت أخيرًا مستعدًا لإطلاق التدريب باستخدام دالة notebook_launcher في 🤗 Accelerate. مرّر للدالة حلقة التدريب، وجميع حُجج التدريب، وعدد العمليات (يمكنك تغيير هذه القيمة إلى عدد وحدات معالجة الرسومات المتاحة لديك) المستخدمة للتدريب:

>>> from accelerate import notebook_launcher

>>> args = (config, model, noise_scheduler, optimizer, train_dataloader, lr_scheduler)

>>> notebook_launcher(train_loop, args, num_processes=1)

بمجرد اكتمال التدريب، اطّلع على الصور 🦋 النهائية 🦋 التي ولّدها نموذج الانتشار الخاص بك!

>>> import glob

>>> sample_images = sorted(glob.glob(f"{config.output_dir}/samples/*.png"))
>>> Image.open(sample_images[-1])

الخطوات التالية

يُعد توليد الصور غير الشرطي مثالاً على مهمة يمكن تدريبها. يمكنك استكشاف مهام وتقنيات تدريب أخرى من خلال زيارة صفحة 🧨 أمثلة تدريب Diffusers. فيما يلي بعض الأمثلة على ما يمكنك تعلمه:

  • Textual Inversion، وهو خوارزمية تُعلّم النموذج مفهومًا بصريًا محددًا وتدمجه في الصورة المولّدة.
  • DreamBooth، وهي تقنية لتوليد صور مخصصة لموضوع معين بناءً على عدة صور مدخلة لذلك الموضوع.
  • دليل لضبط نموذج Stable Diffusion على مجموعة بياناتك الخاصة.
  • دليل لاستخدام LoRA، وهي تقنية موفرة للذاكرة لتدريب النماذج الكبيرة جدًا بشكل أسرع.
Update on GitHub