HuggingFace Diffusers 0.12 : ノートブック : イントロダクション

HuggingFace Diffusers 0.12 : ノートブック : イントロダクション (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 02/20/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 : ノートブック : イントロダクション

Description : 拡散モデルのための Hugging Face の新しいライブラリの紹介。

拡散モデルは人工合成において非常に効果的であることを示し、画像については GAN を打ち負かしてさえいます。そのため、機械学習コミュニティで勢いが増し、テキストのプロンプトを与えられたときに写真のようにリアルな画像を生成する DALL-E 2Imagen のようなシステムに対して重要な役割を果たしています。

拡散モデルの最も多く実を結んだ成功はコンピュータビジョン・コミュニティに在りますが、一方でこれらのモデルは他の分野でも卓越した結果を獲得しています、例えば :

けれども、DALL-E 2 と Imagen のような拡散モデルの最近の研究の殆どは残念ながら広範囲な機械学習コミュニティにアクセス可能ではなく、通常は閉じられたドアの向こう側にあります。

ここに以下の目標を持つ diffusers ライブラリがあります :

  1. 独立したレポジトリから最近の拡散モデルを (コミュニティ により/のために構築された) 単一の 長期間保守される プロジェクトに集め、

  2. DALLE と Imagen のような大きなインパクトのある機械学習システムを一般にアクセス可能な方法で再現し、そして

  3. 独自のモデルを訓練したり、推論のために他のレポジトリからチェックポイントを再利用することを可能にする、使いやすい API を作成します。

 
このノートブックは diffusers の最も重要な特徴を案内します。

拡散モデルがどのように機能するかの最小限の理解を読者が持っていることを仮定しています。幾つかの理論や用語を思い出すために、以下のブログ投稿を読む/目を通すことを勧めます :

 
あるいは論文 :

 

要約

この投稿は diffusers のコア API を紹介するために作成され、3 つのコンポーネントに分かれています :

  1. パイプライン : ユーザフレンドリーな流儀でポピュラーな訓練済み拡散モデルからサンプルを素早く生成するために設計された高位クラス。

  2. モデル : 新しい拡散モデルを訓練するためのポピュラーなアーキテクチャ, e.g. UNet

  3. スケジューラ : 推論の間にノイズから画像を生成し、訓練のためにノイズのある画像を生成するための様々なテクニック。

Note : このノートブックは 推論 だけにフォーカスしています。If you want to get a more hands-on guide on training diffusion models, please have a look at the Training with Diffusers notebook.

 

diffusers のインストール

!pip install diffusers==0.11.1

 

概要

diffusers ライブラリの目的の一つは、拡散モデルを広範囲の深層学習の実践者にアクセス可能にすることです。これを考慮して、使いやすい直感的に理解可能な、そして 貢献しやすい ライブラリの構築を目指しました。

簡単に説明すれば、拡散モデルは、画像のような、関心のあるサンプルを得るためにランダムなガウスノイズをステップ毎に除去するために訓練される機械学習システムです。

基礎的なモデルは、しばしばニューラルネットワークですが、各ステップで画像を僅かにノイズ除去する方法を予測するために訓練されます。一定のステップ数の後、サンプルが得られます。

このプロセスは以下の図で図示されます :

モデル として参照される、ニューラルネットワークのアーキテクチャは一般的には この論文 で提案され Pixel++ 論文で改良された UNet アーキテクチャに従っています。

 
総ては理解できなくても心配しないでください。アーキテクチャのハイライトの幾つかは :

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

拡散過程は望まれる出力のサイズのランダムノイズを取り、それをモデルに何回か通すことから構成されます。この過程は与えられたステップ数後に終了し、出力画像はモデルの訓練データ分布に従ってサンプルを表現するはずです、例えば蝶の画像です。

訓練の間、蝶の画像のような、与えられた分布の多くのサンプルを見せます。訓練後、モデルはランダムノイズを処理して類似の蝶の画像を生成することができるようになります。

細部には入り過ぎないようにしますが、モデルは通常は、僅かに少ないノイズを持つ画像を直接には予測せずに、”noise residual” (ノイズ残差) を予測するように訓練されます、これは (“DDPM” と呼ばれる拡散モデルに対しては) 少ないノイズの画像と入力画像の差分であるか、あるいは同様に、(“Score VE” と呼ばれる拡散モデルのように) 2 つの時間ステップの間の勾配です。

ノイズ除去プロセスを行なうには、特定のノイズスケジューリング・アルゴリズムが必要で、推論には幾つの拡散ステップが必要であるか、そしてモデルの出力からノイズの少ない画像を計算する方法を定義するためにモデルを “wrap” します。ここは diffusers ライブラリの様々な スケジューラ が作用し始めるところです。

最後に、パイプラインモデルスケジューラ を一緒にグループ化して、エンドユーザが完全なノイズ除去ループプロセスを実行することを簡単にします。私たちはパイプラインから始めて、モデルとスケジューラを詳しく見る前に (パイプラインの) 実装の詳細に入ります。

 

コア API

パイプライン

パイプラインのインポートから始めましょう。Google と U.C.Berkeley のコラボレーションで構築された、google/ddpm-celebahq-256 モデルを使用します。それは ノイズ除去拡散確率モデル (DDPM) アルゴリズム に従ったモデルで、セレブ画像のデータセットで訓練されています。

DDPMPipeline をインポートすることができます、これは数行のコードで推論の実行を可能にします :

from diffusers import DDPMPipeline

from_pretrained() メソッドは、コミュニティにより共有される 60,000 以上のモデルのレポジトリ、Hugging Face ハブ からモデルとその configuration をダウンロードすることを可能にします。

image_pipe = DDPMPipeline.from_pretrained("google/ddpm-celebahq-256")
image_pipe.to("cuda")
DDPMPipeline {
  "_class_name": "DDPMPipeline",
  "_diffusers_version": "0.3.0",
  "scheduler": [
    "diffusers",
    "DDPMScheduler"
  ],
  "unet": [
    "diffusers",
    "UNet2DModel"
  ]
}

画像を生成するには、単純にパイプラインを実行するだけでどのような入力を与えることさえ必要ありません、それはランダムな初期ノイズサンプルを生成してから拡散過程を繰り返します。

このパイプラインは出力として関心のある生成画像を含む辞書を返します。これは Google Colab で通常は 2-3 分かかります :

images = image_pipe().images

Let’s take a look 🙂

images[0]

Looks pretty good!

次に、内部で何が起きたのかもう少しより良く理解してみましょう。パイプラインが何からできているか見ましょう :

image_pipe
DDPMPipeline {
  "_class_name": "DDPMPipeline",
  "_diffusers_version": "0.3.0",
  "scheduler": [
    "diffusers",
    "DDPMScheduler"
  ],
  "unet": [
    "diffusers",
    "UNet2DModel"
  ]
}

パイプラインの内部にスケジューラと UNet モデルを見ることができます。それらをより詳しく見て、内部的にこのパイプラインが何をしたのかを調べましょう。

 

モデル

モデルクラスのインスタンスはニューラルネットワークで、これは入力としてノイズのあるサンプルと時間ステップを取り、よりノイズの少ない出力サンプルを予測します。モデル API を理解するために事前訓練済みモデルをロードしてそれで遊んでみましょう!

DDPM 論文 とともにリリースされたタイプ UNet2DModel の単純な条件なし画像生成モデルをロードし、例として教会画像 : google/ddpm-church-256 上で訓練された別のチェックポイントを見ます。

パイプラインクラスについて見たのと同様に、(transformers ライブラリで遊んだことがあれば馴染みがあるかもしれない) from_pretrained() メソッドを使用して 1 行でモデル configuration と重みをロードできます :

from diffusers import UNet2DModel

repo_id = "google/ddpm-church-256"
model = UNet2DModel.from_pretrained(repo_id)

from_pretrained() メソッドはモデル重みをローカルにキャッシュしますので、上のセルを 2 回目に実行する場合、それは遥かに高速に動作します。モデルは純粋な PyTorch torch.nn.Module クラスで、これはモデルをプリントアウトして見ることができます。

model
UNet2DModel(
  (conv_in): Conv2d(3, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
  (time_proj): Timesteps()
  (time_embedding): TimestepEmbedding(
    (linear_1): Linear(in_features=128, out_features=512, bias=True)
    (act): SiLU()
    (linear_2): Linear(in_features=512, out_features=512, bias=True)
  )
  (down_blocks): ModuleList(
    (0): DownBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)
          (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)
          (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
      (downsamplers): ModuleList(
        (0): Downsample2D(
          (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2))
        )
      )
    )
    (1): DownBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)
          (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)
          (conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
      (downsamplers): ModuleList(
        (0): Downsample2D(
          (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(2, 2))
        )
      )
    )
    (2): DownBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 128, eps=1e-06, affine=True)
          (conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(128, 256, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
      (downsamplers): ModuleList(
        (0): Downsample2D(
          (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2))
        )
      )
    )
    (3): DownBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
      (downsamplers): ModuleList(
        (0): Downsample2D(
          (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(2, 2))
        )
      )
    )
    (4): AttnDownBlock2D(
      (attentions): ModuleList(
        (0): AttentionBlock(
          (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
          (query): Linear(in_features=512, out_features=512, bias=True)
          (key): Linear(in_features=512, out_features=512, bias=True)
          (value): Linear(in_features=512, out_features=512, bias=True)
          (proj_attn): Linear(in_features=512, out_features=512, bias=True)
        )
        (1): AttentionBlock(
          (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
          (query): Linear(in_features=512, out_features=512, bias=True)
          (key): Linear(in_features=512, out_features=512, bias=True)
          (value): Linear(in_features=512, out_features=512, bias=True)
          (proj_attn): Linear(in_features=512, out_features=512, bias=True)
        )
      )
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 512, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
      (downsamplers): ModuleList(
        (0): Downsample2D(
          (conv): Conv2d(512, 512, kernel_size=(3, 3), stride=(2, 2))
        )
      )
    )
    (5): DownBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
        )
      )
    )
  )
  (up_blocks): ModuleList(
    (0): UpBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 1024, eps=1e-06, affine=True)
          (conv1): Conv2d(1024, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 1024, eps=1e-06, affine=True)
          (conv1): Conv2d(1024, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 1024, eps=1e-06, affine=True)
          (conv1): Conv2d(1024, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1))
        )
      )
      (upsamplers): ModuleList(
        (0): Upsample2D(
          (conv): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        )
      )
    )
    (1): AttnUpBlock2D(
      (attentions): ModuleList(
        (0): AttentionBlock(
          (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
          (query): Linear(in_features=512, out_features=512, bias=True)
          (key): Linear(in_features=512, out_features=512, bias=True)
          (value): Linear(in_features=512, out_features=512, bias=True)
          (proj_attn): Linear(in_features=512, out_features=512, bias=True)
        )
        (1): AttentionBlock(
          (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
          (query): Linear(in_features=512, out_features=512, bias=True)
          (key): Linear(in_features=512, out_features=512, bias=True)
          (value): Linear(in_features=512, out_features=512, bias=True)
          (proj_attn): Linear(in_features=512, out_features=512, bias=True)
        )
        (2): AttentionBlock(
          (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
          (query): Linear(in_features=512, out_features=512, bias=True)
          (key): Linear(in_features=512, out_features=512, bias=True)
          (value): Linear(in_features=512, out_features=512, bias=True)
          (proj_attn): Linear(in_features=512, out_features=512, bias=True)
        )
      )
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 1024, eps=1e-06, affine=True)
          (conv1): Conv2d(1024, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 1024, eps=1e-06, affine=True)
          (conv1): Conv2d(1024, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(1024, 512, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 768, eps=1e-06, affine=True)
          (conv1): Conv2d(768, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
          (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(768, 512, kernel_size=(1, 1), stride=(1, 1))
        )
      )
      (upsamplers): ModuleList(
        (0): Upsample2D(
          (conv): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        )
      )
    )
    (2): UpBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 768, eps=1e-06, affine=True)
          (conv1): Conv2d(768, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(768, 256, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
        )
      )
      (upsamplers): ModuleList(
        (0): Upsample2D(
          (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        )
      )
    )
    (3): UpBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
          (conv1): Conv2d(512, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(512, 256, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 384, eps=1e-06, affine=True)
          (conv1): Conv2d(384, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=256, bias=True)
          (norm2): GroupNorm(32, 256, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(384, 256, kernel_size=(1, 1), stride=(1, 1))
        )
      )
      (upsamplers): ModuleList(
        (0): Upsample2D(
          (conv): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        )
      )
    )
    (4): UpBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 384, eps=1e-06, affine=True)
          (conv1): Conv2d(384, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(384, 128, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
        )
      )
      (upsamplers): ModuleList(
        (0): Upsample2D(
          (conv): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        )
      )
    )
    (5): UpBlock2D(
      (resnets): ModuleList(
        (0): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
        )
        (1): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
        )
        (2): ResnetBlock2D(
          (norm1): GroupNorm(32, 256, eps=1e-06, affine=True)
          (conv1): Conv2d(256, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (time_emb_proj): Linear(in_features=512, out_features=128, bias=True)
          (norm2): GroupNorm(32, 128, eps=1e-06, affine=True)
          (dropout): Dropout(p=0.0, inplace=False)
          (conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
          (nonlinearity): SiLU()
          (conv_shortcut): Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))
        )
      )
    )
  )
  (mid_block): UNetMidBlock2D(
    (attentions): ModuleList(
      (0): AttentionBlock(
        (group_norm): GroupNorm(32, 512, eps=1e-06, affine=True)
        (query): Linear(in_features=512, out_features=512, bias=True)
        (key): Linear(in_features=512, out_features=512, bias=True)
        (value): Linear(in_features=512, out_features=512, bias=True)
        (proj_attn): Linear(in_features=512, out_features=512, bias=True)
      )
    )
    (resnets): ModuleList(
      (0): ResnetBlock2D(
        (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
        (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
        (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
        (dropout): Dropout(p=0.0, inplace=False)
        (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (nonlinearity): SiLU()
      )
      (1): ResnetBlock2D(
        (norm1): GroupNorm(32, 512, eps=1e-06, affine=True)
        (conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (time_emb_proj): Linear(in_features=512, out_features=512, bias=True)
        (norm2): GroupNorm(32, 512, eps=1e-06, affine=True)
        (dropout): Dropout(p=0.0, inplace=False)
        (conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
        (nonlinearity): SiLU()
      )
    )
  )
  (conv_norm_out): GroupNorm(32, 128, eps=1e-06, affine=True)
  (conv_act): SiLU()
  (conv_out): Conv2d(128, 3, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)

 
次にモデルの configuration を見てみましょう。configuration は config 属性でアクセス可能で、モデルアーキテクチャを定義するために必要なパラメータの総てを (そしてそれらのみを) 表示します。

model.config
FrozenDict([('sample_size', 256),
            ('in_channels', 3),
            ('out_channels', 3),
            ('center_input_sample', False),
            ('time_embedding_type', 'positional'),
            ('freq_shift', 1),
            ('flip_sin_to_cos', False),
            ('down_block_types',
             ['DownBlock2D',
              'DownBlock2D',
              'DownBlock2D',
              'DownBlock2D',
              'AttnDownBlock2D',
              'DownBlock2D']),
            ('up_block_types',
             ['UpBlock2D',
              'AttnUpBlock2D',
              'UpBlock2D',
              'UpBlock2D',
              'UpBlock2D',
              'UpBlock2D']),
            ('block_out_channels', [128, 128, 256, 256, 512, 512]),
            ('layers_per_block', 2),
            ('mid_block_scale_factor', 1),
            ('downsample_padding', 0),
            ('act_fn', 'silu'),
            ('attention_head_dim', None),
            ('norm_num_groups', 32),
            ('norm_eps', 1e-06),
            ('_class_name', 'UNet2DModel'),
            ('_diffusers_version', '0.3.0'),
            ('_name_or_path', 'google/ddpm-church-256')])

ご覧のように、モデル config は frozen 辞書です。これは、configuration はインスタンス化時にモデルアーキテクチャを定義するためだけに使用され、推論時に変更できる属性のためには使用されないことを強制しています。

幾つかの重要な config パラメータは :

  • sample_size : 入力サンプルの高さと幅の次元を定義します。
  • in_channels : 入力サンプルの入力チャネルの数を定義します。
  • down_block_types と up_block_types : ダウン- とアップサンプリング・ブロックのタイプを定義します、これらはこのノートブックの最初の図で見られた、UNet アーキテクチャを作成するために使用されます。
  • block_out_channels : ダウンサンプリング・ブロックの出力チャネル数を定義します、これはまたアップサンプリング・ブロックの入力チャネル数に対しても逆順で使用されます。
  • layers_per_block : 各 UNet ブロックに幾つの ResNet ブロックがあるかを定義します。

UNet config がどのようなものかを知ることで、ランダム重みを持つ正確に同じモデルアーキテクチャをインスタンス化することを素早く試すことができます。それを行なうため、config を unpacked dict として UNet2DModel クラスに渡しましょう。

model_random = UNet2DModel(**model.config)

Cool, 上記は前のものと同じ config によりランダムに初期化されたモデルを作成しました。

今しがた作成したモデルをセーブしたい場合には、save_pretrained() メソッドを使用できます、これはモデル重みとモデル config を提供されたフォルダにセーブします。

model_random.save_pretrained("my_model")

どのようなファイルが my_model にセーブされたかを見てみましょう。

!ls my_model
config.json  diffusion_pytorch_model.bin

diffusion_pytorch_model.bin はバイナリ PyTorch ファイルでモデル重みをストアし、config.json はモデルの configuration をストアします。

モデルを再利用したい場合、単純に from_pretrained() メソッドを再度利用できます、それはローカルのチェックポイントと Hub 上にあるものをロードするからです。

model_random = UNet2DModel.from_pretrained("my_model")

実際に訓練済みのモデルに戻り、次にモデルを推論のためにどのように利用できるかを見ましょう。最初に、画像の shape (batch_size $\times$ in_channels $\times$ sample_size $\times$ sample_size) のランダムなガウシアン・サンプルが必要です。私たちはバッチ軸を持ちます、モデルは複数のランダムノイズを受け取ることができるからです、チャネル軸は、それぞれが (RGB のような) 複数のチャネルから構成されるためです、そして最後に sample_size は高さと幅に対応します。

import torch

torch.manual_seed(0)

noisy_sample = torch.randn(
    1, model.config.in_channels, model.config.sample_size, model.config.sample_size
)
noisy_sample.shape
torch.Size([1, 3, 256, 256])

Time to do the inference!

時間ステップと一緒にノイズのあるサンプルをモデルに渡すことができます。時間ステップはモデルに入力画像が「どのくらいノイズが多いか」(過程の最初にはノイズがより多く、終わりには少ない) を知らせるために重要で、モデルはそれが拡散過程の最初か最後に近いのかを知ります。

イントロダクションで説明したように、モデルは僅かにノイズが少ない画像か、僅かにノイズが少ない画像と入力画像の差分、あるいは何か他のものを予測します。モデルが何の上で訓練されたかを知るために モデルカード を注意深く読むことは重要です。この場合、モデルはノイズ残差 (僅かにノイズが少ない画像と入力画像の差) を予測します。

with torch.no_grad():
    noisy_residual = model(sample=noisy_sample, timestep=2).sample

予測された noisy_residual は入力と正確に同じ shape を持ち、それを僅かに少ないノイズのある画像を計算するために使用します。出力 shape が一致していることを確認しましょう :

noisy_residual.shape
torch.Size([1, 3, 256, 256])

Great.

さて要約すれば、UNet2DModel (PyTorch modules) のような モデル は、僅かに少ないノイズのある画像や残差を予測するために訓練される、パラメータ化されたニューラルネットワークです。それらは .config により定義され、ハブからロードしてローカルにセーブ・ロードできます。次のステップはこの モデル を、実際に画像を生成できるように正しい スケジューラ と組み合わせる方法を学習することです。

 

スケジューラ

スケジューラ は Python クラスにラップされたアルゴリズムです。それらは訓練の間にモデルにノイズを追加するために使用されるノイズ・スケジュールを定義し、モデル出力が与えられたときに僅かに少ないノイズを持つサンプル (ここでは noisy_residual) を計算するアルゴリズムも定義しています。このノートブックは推論のために scheduler クラスを使用する方法だけにフォーカスします。訓練のためにスケジューラを使用する方法を見るにはこのノートブックを調べてください (訳注: リンク等はなし)。

モデルが訓練可能な重みを持つ一方で、スケジューラは通常は (訓練可能な重みを持たないという意味で) パラメータフリーで、僅かにノイズの少ないサンプルを計算するアルゴリズムを単純に定義していることをここで強調しておくことは重要です。そのためスケジューラは torch.nn.Module を継承しませんが、それらはモデルのように configuration からインスタンス化されます。

ハブからスケジューラの config をダウンロードするため、configuration をロードしてスケジューラをインスタンス化するために from_config() メソッドを利用できます。

DDPMScheduler を使用しましょう、DDPM 論文 で提案されたノイズ除去アルゴリズムです。

from diffusers import DDPMScheduler

scheduler = DDPMScheduler.from_config(repo_id)

Let’s also take a look at the config here.

scheduler.config
FrozenDict([('num_train_timesteps', 1000),
            ('beta_start', 0.0001),
            ('beta_end', 0.02),
            ('beta_schedule', 'linear'),
            ('trained_betas', None),
            ('variance_type', 'fixed_small'),
            ('clip_sample', True),
            ('_class_name', 'DDPMScheduler'),
            ('_diffusers_version', '0.3.0')])

異なるスケジューラは通常は異なるパラメータで定義されます。パラメータが何に使われるのかより良く正確に理解するため、読者は src/diffusers/schedulers/scheduling_ddpm.py ファイルのような、
src/diffusers/schedulers/ 下のそれぞれのスケジューラファイルを直接調べることが勧められます。ここに最も重要なものがあります :

  • num_train_timesteps はノイズ除去過程の長さを定義します、例えば、データサンプルにランダムなガウスノイズを処理する幾つの時間ステップが必要とされるか。
  • beta_schedule はノイズスケジュールのタイプを定義します、これは推論と訓練のために使用されます。
  • beta_start と beta_end はスケジュールの最小ノイズ値と最大ノイズ値を定義します。

モデルのように、スケジューラは save_config() と from_config() でセーブしてロードできます。

scheduler.save_config("my_scheduler")
new_scheduler = DDPMScheduler.from_config("my_scheduler")

総てのスケジューラは一つまたは複数の step() メソッドを提供し、これは僅かにノイズが少ない画像を計算するために使用できます。step() メソッドはスケジューラによって変化するかもしれませんが、通常は少なくともモデル出力、時間ステップと現在の noisy_sample は想定しています。

step() メソッドは幾分「単に動作する」ブラックボックスな関数であることに注意してください。前のノイズのあるサンプルがスケジューラの元の論文で定義されているように正確に計算される方法をより良く理解したい場合には、実際のコードを見るべきです、例えば DDPM については ここをクリック してください、これは元の論文へのコメントや参照を含みます。

前のセクションからのモデル出力を使用して試してみましょう。

less_noisy_sample = scheduler.step(
    model_output=noisy_residual, timestep=2, sample=noisy_sample
).prev_sample
less_noisy_sample.shape
torch.Size([1, 3, 256, 256])

計算されたサンプルはモデル入力と正確に同じ shape を持つことがわかるでしょう、つまり次のステップでそれをモデルに再び渡す準備ができています。

次にそれら総てを一つにまとめて実際にノイズ除去ループを定義しましょう。このループは、ノイズ除去ループでより良い可視化のための方法とともに、(段々と少なくなる) ノイズのあるサンプルを表示します。display 関数を定義しましょう、これはノイズ除去された画像を後処理する面倒を見て、PIL.Image に変換してそれを表示します。

import PIL.Image
import numpy as np

def display_sample(sample, i):
    image_processed = sample.cpu().permute(0, 2, 3, 1)
    image_processed = (image_processed + 1.0) * 127.5
    image_processed = image_processed.numpy().astype(np.uint8)

    image_pil = PIL.Image.fromarray(image_processed[0])
    display(f"Image at step {i}")
    display(image_pil)

ループを定義する前に、ノイズ除去過程を少し高速化するために入力とモデルを GPU に移動しましょう。

model.to("cuda")
noisy_sample = noisy_sample.to("cuda")

最後にノイズ除去ループを定義するときです!DDPM に対してはそれは寧ろ簡単です。

  1. モデルで少ないノイズのサンプルの残差を予測する。
  2. スケジューラでノイズの少ないサンプルを計算する。

更に、50th ステップ毎にこれは進捗を表示します。

ノイズ除去過程の間にイテレートする時間ステップのシークエンスを定義するテンソルである、scheduler.timesteps に渡りループすることに、ここで注意することは重要です。通常、ノイズ除去過程は時間ステップの降順に進みますので、時間ステップの総数 (ここでは 1000) から 0 に進みます。

GPU に依存してこれは 1 分間はかかるかもしれません – ノイズだけから構築されている教会を見ることができる間に、ここまでに学習したことのすべてを振り返るには十分な時間です ⛪。

import tqdm

sample = noisy_sample

for i, t in enumerate(tqdm.tqdm(scheduler.timesteps)):
  # 1. predict noise residual
  with torch.no_grad():
      residual = model(sample, t).sample

  # 2. compute less noisy image and set x_t -> x_t-1
  sample = scheduler.step(residual, t, sample).prev_sample

  # 3. optionally look at image
  if (i + 1) % 50 == 0:
      display_sample(sample, i + 1)
  5%|▍         | 47/1000 [00:02<00:43, 21.69it/s]
Image at step 50

10%|▉         | 98/1000 [00:04<00:41, 21.85it/s]
Image at step 100

15%|█▍        | 149/1000 [00:06<00:37, 22.68it/s]
Image at step 150

 20%|█▉        | 197/1000 [00:08<00:36, 22.17it/s]
Image at step 200

 25%|██▍       | 248/1000 [00:11<00:34, 22.00it/s]
Image at step 250

 30%|██▉       | 299/1000 [00:13<00:31, 21.99it/s]
Image at step 300

 35%|███▍      | 347/1000 [00:15<00:30, 21.54it/s]
Image at step 350

 40%|███▉      | 398/1000 [00:18<00:27, 21.81it/s]
Image at step 400

 45%|████▍     | 449/1000 [00:20<00:25, 22.04it/s]
Image at step 450

 50%|████▉     | 497/1000 [00:22<00:22, 22.34it/s]
Image at step 500

 55%|█████▍    | 548/1000 [00:25<00:20, 22.08it/s]
Image at step 550

60%|█████▉    | 599/1000 [00:27<00:18, 21.62it/s]
Image at step 600

 65%|██████▍   | 647/1000 [00:29<00:16, 21.80it/s]
Image at step 650

 70%|██████▉   | 698/1000 [00:32<00:13, 21.99it/s]
Image at step 700

 75%|███████▍  | 749/1000 [00:34<00:11, 22.22it/s]
Image at step 750

 80%|███████▉  | 797/1000 [00:36<00:09, 22.44it/s]
Image at step 800

 85%|████████▍ | 848/1000 [00:39<00:06, 21.77it/s]
Image at step 850

 90%|████████▉ | 899/1000 [00:41<00:04, 21.76it/s]
Image at step 900

 95%|█████████▍| 947/1000 [00:43<00:02, 21.41it/s]
Image at step 950

100%|█████████▉| 998/1000 [00:46<00:00, 21.45it/s]
Image at step 1000

100%|██████████| 1000/1000 [00:46<00:00, 21.57it/s]

 
ある程度意味がある形状を見るにはかなり時間ががかることがわかります - 約 800 ステップ後です。

画像の品質は実際にかなり良い一方で - 画像生成を高速化したいと思うかもしれません。

それを行なうには、DDPM スケジューラを DDIM スケジューラに置き換えることを試すことができます、これは非常に高速化された生成時間で高い生成品質を維持します。

スケジューラの交換 : 拡散モデルライブラリのエキサイティングな展望の一つは、様々なスケジューリング・プロトコルが様々なモデルで動作できることですが、総てのソリューションに適合する一つのサイズのものがあるわけではありません!この場合には、DDIM が DDPM の代替として動作しましたが、これは普遍的ではありません (そして興味深い研究課題を表します)。

DDPM と DDIM スケジューラは多かれ少なかれ同じ configuration を共有しますので、DDPM スケジューラから DDIM スケジューラをロードできます。

from diffusers import DDIMScheduler

scheduler = DDIMScheduler.from_config(repo_id)
{'timestep_values', 'set_alpha_to_one'} was not found in config. Values will be initialized to default values.

DDIM スケジューラは set_timesteps メソッドを通して推論時に幾つのノイズ除去ステップを実行するべきかをユーザが定義することを可能にします。DDPM スケジューラはデフォルトで 1000 ノイズ除去ステップを実行します。この数を DDIM に対して単に 50 推論ステップにまで大幅に減らしましょう。

scheduler.set_timesteps(num_inference_steps=50)

そして前のように同じループを実行できます - 今は遥かに速い DDIM スケジューラを利用しているだけです。

import tqdm

sample = noisy_sample

for i, t in enumerate(tqdm.tqdm(scheduler.timesteps)):
  # 1. predict noise residual
  with torch.no_grad():
      residual = model(sample, t).sample

  # 2. compute previous image and set x_t -> x_t-1
  sample = scheduler.step(residual, t, sample).prev_sample

  # 3. optionally look at image
  if (i + 1) % 10 == 0:
      display_sample(sample, i + 1)
18%|█▊        | 9/50 [00:00<00:01, 24.32it/s]
Image at step 10

 36%|███▌      | 18/50 [00:00<00:01, 22.67it/s]
Image at step 20

 54%|█████▍    | 27/50 [00:01<00:01, 22.21it/s]
Image at step 30

78%|███████▊  | 39/50 [00:01<00:00, 22.21it/s]
Image at step 40

96%|█████████▌| 48/50 [00:02<00:00, 22.26it/s]
Image at step 50

100%|██████████| 50/50 [00:02<00:00, 21.60it/s]

画像生成が実際に遥かに速いことがわかるでしょう - 僅か 2 秒です - しかしまたスピードの代わりに画像品質を犠牲にしていることもわかります。

Cool, 今では貴方はスケジューラの良い最初の理解を得たはずです。覚えておくべき重要なことは :

  1. スケジューラはパラメータフリーです (訓練可能な重みがありません)

  2. スケジューラは推論の間に僅かにノイズが少ないサンプルを計算するアルゴリズムを定義しています。

diffusers に既に追加された多くのスケジューラがあり、diffusers は今後更に多く追加していきます。どのモデルのチェックポイントがどのスケジューラとともに利用できるかを理解するためにモデルカードを読むことは重要です。総ての利用可能なスケジューラは ここ で見つけらます。

モデルスケジューラ についての章を終わらせるにあたり、モデルとスケジューラを互いにできるだけ独立であるように、非常に手間ひまかけて維持しようとしていることにも注意してください。これはスケジューラが入力としてモデルを決して受け取るべきではないことを意味しています。その逆もです。モデルは訓練済み重みを使用してノイズ残差や僅かにノイズが少ない画像を予測する一方で、スケジューラはモデル出力が与えられたときに前のサンプルを計算します。

 

以上