HuggingFace Diffusers 0.12 : ノートブック : Diffusers による訓練

HuggingFace Diffusers 0.12 : ノートブック : Diffusers による訓練 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 02/21/2023 (v0.12.1)

* 本ページは、HuggingFace Diffusers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス

クラスキャット は人工知能・テレワークに関する各種サービスを提供しています。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

  • 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
  • sales-info@classcat.com  ;  Web: www.classcat.com  ;   ClassCatJP

 

 

HuggingFace Diffusers 0.12 : ノートブック : Diffusers による訓練

ここ数ヶ月で、拡散モデルが最先端の生成モデルとして王座についたことは明らかになりました。ここでは、単純な拡散モデルを訓練するために Hugging Face のまったく新しい Diffusers ライブラリを使用します。

 

依存関係のインストール

このノートブックは、画像データセットをロードして前処理するために 🤗 Datasets ライブラリを、任意の数の GPU 上での訓練を単純化するために (自動勾配累積と tensorboard ロギングのような機能を持つ) 🤗 Accelerate ライブラリを活用します。ここでそれらをインストールしましょう :

%%capture
!pip install diffusers[training]==0.11.1

貴方のモデルをコミュニティと共有できるようにするには、まだ幾つかの従うべきステップがあります!

最初に Hugging Face website (まだなら ここ でサインアップ!) からの認証トークンをストアしてから次のセルを実行して 書き込み トークンを入力する必要があります :

from huggingface_hub import notebook_login

notebook_login()
Login successful
Your token has been saved to /root/.huggingface/token
Authenticated through git-credential store but this isn't the helper defined on your machine.
You might have to re-authenticate when pushing to the Hugging Face Hub. Run the following command in your terminal in case you want to set this credential helper as the default

git config --global credential.helper store

それからモデル・チェックポイントをアップロードするために Git-LFS をインストールする必要があります。

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

 

Config

便利のために、総ての訓練ハイパーパラメータをグループ化する configuration を定義しています。これは 訓練スクリプト のために使用される引数に類似しています。ここでは num_epochs, learning_rate, lr_warmup_steps のようなハイパーパラメータに対する合理的なデフォルトを選択しますが、貴方自身のデータセットで訓練する場合にはそれらを自由に調整してください。例えば、num_epochs はより良い視覚的品質のために 100 に増やすことができます。

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 namy locally and on the HF Hub

    push_to_hub = True  # whether to upload the saved model to the HF Hub
    hub_private_repo = False  
    overwrite_output_dir = True  # overwrite the old model when re-running the notebook
    seed = 0

config = TrainingConfig()

 

データセットのロード

画像データセットをダウンロードするために 🤗 Datasets ライブラリを使用します。

この場合、Butterflies データセット はリモートでホストされますが、下のコメントで示されるようにローカルの ImageFolder にロードすることができます。

from datasets import load_dataset

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

# Feel free to try other datasets from https://hf.co/huggan/ too! 
# Here's is a dataset of flower photos:
# config.dataset_name = "huggan/flowers-102-categories"
# dataset = load_dataset(config.dataset_name, split="train")

# Or just load images from a local folder!
# config.dataset_name = "imagefolder"
# dataset = load_dataset(config.dataset_name, data_dir="path/to/folder")
Downloading:   0%|          | 0.00/1.65k [00:00<?, ?B/s]
Using custom data configuration huggan--smithsonian_butterflies_subset-dca27cc6049931aa
Downloading and preparing dataset None/None (download: 226.45 MiB, generated: 226.74 MiB, post-processed: Unknown size, total: 453.19 MiB) to /root/.cache/huggingface/datasets/huggan___parquet/huggan--smithsonian_butterflies_subset-dca27cc6049931aa/0.0.0/7328ef7ee03eaf3f86ae40594d46a1cec86161704e02dd19f232d81eee72ade8...
Downloading data files:   0%|          | 0/1 [00:00<?, ?it/s]
Downloading data:   0%|          | 0.00/237M [00:00<?, ?B/s]
Extracting data files:   0%|          | 0/1 [00:00<?, ?it/s]
0 tables [00:00, ? tables/s]
Dataset parquet downloaded and prepared to /root/.cache/huggingface/datasets/huggan___parquet/huggan--smithsonian_butterflies_subset-dca27cc6049931aa/0.0.0/7328ef7ee03eaf3f86ae40594d46a1cec86161704e02dd19f232d81eee72ade8. Subsequent calls will reuse this data.

データセットは幾つかの特別な特徴 (カラム) を含みますが、私たちが関心あるのは image です :

dataset
Dataset({
    features: ['image_url', 'image_alt', 'id', 'name', 'scientific_name', 'gender', 'taxonomy', 'region', 'locality', 'date', 'usnm_no', 'guid', 'edan_url', 'source', 'stage', 'image', 'image_hash', 'sim_score'],
    num_rows: 1000
})

Image 特徴は画像を PIL でロードしますので、簡単に幾つかのサンプルを見ることができます :

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]),
    ]
)

🤗 Datasets は訓練の間にその場で画像変換を適用するために便利な set_transform() メソッドを提供しています :

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

dataset.set_transform(transform)
Parameter 'transform'= of the transform datasets.arrow_dataset.Dataset.set_format couldn't be hashed properly, a random hash was used instead. Make sure your transforms and parameters are serializable with pickle or dill for the dataset fingerprinting and caching to work. If you reuse this transform, the caching mechanism will consider it to be different from the previous calls and recompute everything. This warning is only showed once. Subsequent hashing failures won't be showed.

Let’s see what they look like now

fig, axs = plt.subplots(1, 4, figsize=(16, 4))
for i, image in enumerate(dataset[:4]["images"]):
    axs[i].imshow(image.permute(1, 2, 0).numpy() / 2 + 0.5)
    axs[i].set_axis_off()
fig.show()

総ての画像が同じサイズを持ちテンソルに変換されたので、訓練のために使用するデータローダを作成できます。

import torch

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

 

拡散モデルの定義

ここで拡散モデルをセットアップします。拡散モデルはニューラルネットワークで、ノイズのある入力からノイズの僅かに少ない画像を予測するために訓練されます。推論では、それらはランダムノイズを画像を生成するために反復的に変換するために使用できます :


DDPM 論文 ( https://arxiv.org/abs/2006.11239 ) からの図

数学については馴染みがなくてもあまり心配しないでください、覚えておくべき重要なパートは、私たちのモデルは矢印 $p_{\theta}(x_{t-1}|x_{t})$ に対応していることです (これは「僅かに少ないノイズのある画像を予測する」を言う洒落た方法です)。

興味深いパートは、ノイズを画像に追加することは実際に簡単なので、訓練は以下のように半教師ありな方法で発生し得ることです :

  1. 訓練セットから画像を取得する。
  2. それにランダムノイズを $t$ 回適用します (これは上の図の $x_{t-1}$ と $x_{t}$ を与えます)。
  3. このノイズのある画像を $t$ の値とともにモデルに与えます。
  4. モデルの出力とノイズの与えられた画像 $x_{t-1}$ から損失を計算します。

それから勾配降下を適用してこの過程を複数回繰り返すことができます。

殆どの拡散モデルは U-net の変形であるアーキテクチャを使用し、それがここで使用するものです。

簡単に言えば :

  • モデルは入力画像を ResNet 層の幾つかのブロックを通します、これは画像サイズを半分にします。
  • そしてそれを再度アップサンプリングする同じ数のブロックを通します。
  • ダウンサンプリング・パスの特徴を、アップサンプリング・パスの対応する層にリンクするスキップ接続があります。

このモデルの主要な機能は、入力と同じサイズの画像を予測することで、それは正確にここで必要なものです。

Diffusers は便利な UNet2DModel クラスを提供しています、これは PyTorch で望まれるアーキテクチャを作成します。

望まれる画像サイズに対して U-net を作成しましょう。down_block_types はダウンサンプリング・ブロック (上の図で緑色) に対応し、up_block_types はアップサンプリング・ブロック (図で赤色) であることに注意してください :

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 channes 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])

そして出力が正確に同じ shape のテンソルであることを確認しましょう :

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

Great!

モデルは (ノイズのある) 画像 に加えて (訓練概要で前に見たように) 現在の時間ステップ も取ることに注意してください。その時間ステップの情報は正弦関数 (= sinusoidal) の位置埋め込みを使用してモデルに対して変換されます、これは Transformer モデルがしばしば行なうものに類似しています。

モデルを持ったので、ノイズを画像に追加するためのオブジェクトだけが必要です。これは diffusers ライブラリの スケジューラ により成されます。

 

ノイズスケジューラの定義

使用したい拡散アルゴリズムに依存して、画像にノイズが与えられる方法は僅かに異なります。それが 🤗 Diffusers が様々なスケジューラクラスを含む理由です、各々はアルゴリズム固有の拡散ステップを定義しています。ここでは DDPMScheduler を使用していきます、これは Denoising Diffusion Probabilistic Models で提案された、訓練ノイズ除去と訓練アルゴリズムに対応しています。

from diffusers import DDPMScheduler

noise_scheduler = DDPMScheduler(num_train_timesteps=1000)

このノイズスケジューラがどのように動作するかを見ましょう : それは訓練セットからの画像のバッチ (ここでは前からの一つの画像 sample_image のバッチを再利用します)、同じ shape のランダムノイズのバッチ、そして各画像に対する時間ステップ (これは各画像にノイズを適用したい回数に対応します) を受け取ります :

import torch
from PIL import Image

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])

そして DDPM アルゴリズムでは、モデルの訓練目的は noise_scheduler.add_noise で使用したノイズを予測できるようにすることですので、このステップでの損失は :

import torch.nn.functional as F

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

 

訓練のセットアップ

モデルを訓練できるのに必要な総てのものがあります!標準的な AdamW optimizer を使用しましょう :

optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)

そしてコサイン学習率スケジュールです :

from diffusers.optimization import get_cosine_schedule_with_warmup

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 を使用します、これは end-to-end な推論を実行する簡単な方法です。サンプル画像のバッチを生成してそれをグリッドとしてディスクにセーブするためにこのパイプラインを使用します。

from diffusers import DDPMPipeline

import math

def make_grid(images, rows, cols):
    w, h = images[0].size
    grid = Image.new('RGB', size=(cols*w, rows*h))
    for i, image in enumerate(images):
        grid.paste(image, box=(i%cols*w, i//cols*h))
    return grid

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.manual_seed(config.seed),
    ).images

    # Make a grid out of the images
    image_grid = make_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")

これを最後に、総てをまとめて訓練関数を書くことができます。これは前のセクションで見た訓練ステップをループにラップするだけで、簡単な TensorBoard ロギング、勾配累積、混合精度訓練とマルチ GPU or TPU 訓練のために Accelerate を使用します。

from accelerate import Accelerator
from huggingface_hub import HfFolder, Repository, whoami

from tqdm.auto import tqdm
from pathlib import Path
import os

def get_full_repo_name(model_id: str, organization: str = None, token: str = None):
    if token is None:
        token = HfFolder.get_token()
    if organization is None:
        username = whoami(token)["name"]
        return f"{username}/{model_id}"
    else:
        return f"{organization}/{model_id}"

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",
        logging_dir=os.path.join(config.output_dir, "logs")
    )
    if accelerator.is_main_process:
        if config.push_to_hub:
            repo_name = get_full_repo_name(Path(config.output_dir).name)
            repo = Repository(config.output_dir, clone_from=repo_name)
        elif config.output_dir is not None:
            os.makedirs(config.output_dir, exist_ok=True)
        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).to(clean_images.device)
            bs = clean_images.shape[0]

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

            # 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)

                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:
                    repo.push_to_hub(commit_message=f"Epoch {epoch}", blocking=True)
                else:
                    pipeline.save_pretrained(config.output_dir) 

 

訓練しましょう!

Accelerate の notebook_launcher 関数を使用してノートブックから (マルチ GPU 訓練を含む) 訓練を起動しましょう :

from accelerate import notebook_launcher
args = (config, model, noise_scheduler, optimizer, train_dataloader, lr_scheduler)

notebook_launcher(train_loop, args, num_processes=1)
Launching training on one GPU.
Cloning https://huggingface.co/anton-l/ddpm-butterflies-128 into local empty directory.
Epoch 0: 100%
63/63 [00:32<00:00, 2.56it/s, loss=0.346, lr=1.26e-5, step=62]
Epoch 1: 100%
63/63 [01:15<00:00, 3.03it/s, loss=0.0947, lr=2.52e-5, step=125]
Epoch 2: 100%
63/63 [00:50<00:00, 3.10it/s, loss=0.11, lr=3.78e-5, step=188]
Epoch 3: 100%
63/63 [00:25<00:00, 3.09it/s, loss=0.0384, lr=5.04e-5, step=251]
Epoch 4: 100%
63/63 [00:25<00:00, 3.00it/s, loss=0.0758, lr=6.3e-5, step=314]
Epoch 5: 100%
63/63 [01:15<00:00, 3.04it/s, loss=0.0215, lr=7.56e-5, step=377]
Epoch 6: 100%
63/63 [00:50<00:00, 3.05it/s, loss=0.0152, lr=8.82e-5, step=440]
Epoch 7: 100%
63/63 [00:25<00:00, 3.07it/s, loss=0.0127, lr=0.0001, step=503]
Epoch 8: 100%
63/63 [00:25<00:00, 3.09it/s, loss=0.0181, lr=9.98e-5, step=566]
Epoch 9: 100%
63/63 [02:14<00:00, 3.04it/s, loss=0.1, lr=9.94e-5, step=629]
100%
1000/1000 [01:23<00:00, 11.97it/s]
Epoch 10: 100%
63/63 [00:25<00:00, 3.07it/s, loss=0.0344, lr=9.87e-5, step=692]
Epoch 11: 100%
63/63 [00:25<00:00, 3.05it/s, loss=0.0113, lr=9.77e-5, step=755]
Epoch 12: 100%
63/63 [01:15<00:00, 3.00it/s, loss=0.049, lr=9.65e-5, step=818]
Epoch 13: 100%
63/63 [00:50<00:00, 2.98it/s, loss=0.00527, lr=9.5e-5, step=881]
Epoch 14: 100%
63/63 [00:25<00:00, 2.99it/s, loss=0.0103, lr=9.32e-5, step=944]
Epoch 15: 100%
63/63 [00:25<00:00, 3.00it/s, loss=0.0251, lr=9.12e-5, step=1007]
Epoch 16: 100%
63/63 [01:15<00:00, 3.06it/s, loss=0.0305, lr=8.9e-5, step=1070]
Epoch 17: 100%
63/63 [00:50<00:00, 3.08it/s, loss=0.0195, lr=8.65e-5, step=1133]
Epoch 18: 100%
63/63 [00:25<00:00, 3.00it/s, loss=0.00184, lr=8.39e-5, step=1196]
Epoch 19: 100%
63/63 [00:24<00:00, 3.04it/s, loss=0.00705, lr=8.1e-5, step=1259]
100%
1000/1000 [01:23<00:00, 11.93it/s]
Epoch 20: 100%
63/63 [00:50<00:00, 3.05it/s, loss=0.0265, lr=7.8e-5, step=1322]
Epoch 21: 100%
63/63 [00:25<00:00, 3.07it/s, loss=0.00787, lr=7.49e-5, step=1385]
Epoch 22: 100%
63/63 [00:25<00:00, 3.02it/s, loss=0.0409, lr=7.16e-5, step=1448]
Epoch 23: 100%
63/63 [01:15<00:00, 3.09it/s, loss=0.004, lr=6.81e-5, step=1511]
Epoch 24: 100%
63/63 [00:50<00:00, 3.05it/s, loss=0.00863, lr=6.46e-5, step=1574]
Epoch 25: 100%
63/63 [00:25<00:00, 3.09it/s, loss=0.0206, lr=6.1e-5, step=1637]
Epoch 26: 100%
63/63 [00:24<00:00, 3.12it/s, loss=0.0261, lr=5.73e-5, step=1700]
Epoch 27: 100%
63/63 [01:14<00:00, 3.08it/s, loss=0.0159, lr=5.36e-5, step=1763]
Epoch 28: 100%
63/63 [00:49<00:00, 3.07it/s, loss=0.0012, lr=4.99e-5, step=1826]
Epoch 29: 100%
63/63 [00:24<00:00, 3.11it/s, loss=0.00604, lr=4.62e-5, step=1889]
100%
1000/1000 [01:21<00:00, 12.19it/s]
Adding files tracked by Git LFS: ['samples/0009.png', 'samples/0019.png', 'samples/0029.png']. This may take a bit of time if the files are large.
Upload file unet/diffusion_pytorch_model.bin: 100%
434M/434M [05:11<00:00, 1.50MB/s]
Upload file samples/0009.png: 100%
507k/507k [05:11<00:00, 1.31kB/s]
Upload file logs/train_example/events.out.tfevents.1658394859.fbf7f1e9c87d.193.0: 100%
234k/234k [05:11<00:00, 656B/s]
Upload file samples/0019.png: 100%
525k/525k [05:11<00:00, 1.61kB/s]
Upload file samples/0029.png: 100%
534k/534k [05:11<00:00, 1.54kB/s]
To https://huggingface.co/anton-l/ddpm-butterflies-128
   fe22ac0..1b22dd2  main -> main

To https://huggingface.co/anton-l/ddpm-butterflies-128
   1b22dd2..f5625e7  main -> main

Epoch 30: 100%
63/63 [00:24<00:00, 3.11it/s, loss=0.0237, lr=4.24e-5, step=1952]
Epoch 31: 100%
63/63 [01:25<00:00, 3.12it/s, loss=0.00703, lr=3.88e-5, step=2015]
Epoch 32: 100%
63/63 [01:01<00:00, 3.09it/s, loss=0.038, lr=3.52e-5, step=2078]
Epoch 33: 100%
63/63 [00:36<00:00, 3.11it/s, loss=0.00341, lr=3.16e-5, step=2141]
Epoch 34: 100%
63/63 [00:24<00:00, 3.07it/s, loss=0.00762, lr=2.82e-5, step=2204]
Epoch 35: 100%
63/63 [01:27<00:00, 3.11it/s, loss=0.018, lr=2.49e-5, step=2267]
Epoch 36: 100%
63/63 [01:02<00:00, 3.08it/s, loss=0.0236, lr=2.18e-5, step=2330]
Epoch 37: 100%
63/63 [00:37<00:00, 3.09it/s, loss=0.0141, lr=1.88e-5, step=2393]
Epoch 38: 100%
63/63 [00:24<00:00, 3.09it/s, loss=0.000975, lr=1.59e-5, step=2456]
Epoch 39: 100%
63/63 [02:24<00:00, 3.08it/s, loss=0.00534, lr=1.33e-5, step=2519]
100%
1000/1000 [01:22<00:00, 12.19it/s]
Epoch 40: 100%
63/63 [00:37<00:00, 3.07it/s, loss=0.0221, lr=1.09e-5, step=2582]
Epoch 41: 100%
63/63 [00:24<00:00, 3.10it/s, loss=0.00594, lr=8.66e-6, step=2645]
Epoch 42: 100%
63/63 [01:26<00:00, 3.05it/s, loss=0.0351, lr=6.68e-6, step=2708]
Epoch 43: 100%
63/63 [01:01<00:00, 3.08it/s, loss=0.00301, lr=4.94e-6, step=2771]
Epoch 44: 100%
63/63 [00:37<00:00, 3.05it/s, loss=0.00676, lr=3.45e-6, step=2834]
Epoch 45: 100%
63/63 [00:24<00:00, 3.09it/s, loss=0.0166, lr=2.21e-6, step=2897]
Epoch 46: 100%
63/63 [01:27<00:00, 3.07it/s, loss=0.0222, lr=1.25e-6, step=2960]
Epoch 47: 100%
63/63 [01:02<00:00, 3.09it/s, loss=0.0131, lr=5.57e-7, step=3023]
Epoch 48: 100%
63/63 [00:37<00:00, 3.00it/s, loss=0.000888, lr=1.39e-7, step=3086]
Epoch 49: 100%
63/63 [00:24<00:00, 3.04it/s, loss=0.00508, lr=0, step=3149]
100%
1000/1000 [01:23<00:00, 11.90it/s]
Adding files tracked by Git LFS: ['samples/0039.png', 'samples/0049.png']. This may take a bit of time if the files are large.
Upload file unet/diffusion_pytorch_model.bin: 100%
434M/434M [05:23<00:00, 819kB/s]
Upload file samples/0039.png: 100%
474k/474k [05:23<00:00, 1.16kB/s]
Upload file samples/0049.png: 100%
498k/498k [05:23<00:00, 1.22kB/s]
Upload file logs/train_example/events.out.tfevents.1658394859.fbf7f1e9c87d.193.0: 100%
390k/390k [05:23<00:00, 990B/s]
To https://huggingface.co/anton-l/ddpm-butterflies-128
   f5625e7..c180b35  main -> main

訓練された拡散モデルにより生成された最終的な画像グリッドを見てみましょう :

import glob

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

Not bad! もちろん改良の余地はありますので、ハイパーパラメータ, モデル定義と画像増強で自由に遊んでください 🤗

モデルを Hugging Face ハブにアップロードすることを選択した場合、そのレポジトリはこのように見えるはずです : https://huggingface.co/anton-l/ddpm-butterflies-128/tree/main

If you want to dive deeper into the code, we also have more advanced training scripts with features like Exponential Moving Average of model weights here:

 

以上