🧨ディフューザーを使用した安定した拡散
Stable diffusion using a diffuser.
…🧨 ディフューザーを使用して
Stable Diffusionは、CompVis、Stability AI、およびLAIONの研究者とエンジニアによって作成されたテキストから画像への潜在的な拡散モデルです。これは、LAION-5Bデータベースのサブセットから512×512の画像でトレーニングされています。LAION-5Bは現在存在する最大の、自由にアクセス可能な多様性のあるデータセットです。
この記事では、Stable Diffusionと🧨 ディフューザーのライブラリを使用する方法、モデルの動作の説明、およびディフューザー
を使用して画像生成パイプラインをカスタマイズする方法について説明します。
注意:ディフュージョンモデルの動作原理を基本的に理解することを強くお勧めします。ディフュージョンモデルが完全に新しいものである場合、次のブログ記事のいずれかを読むことをお勧めします:
- 注釈付きディフュージョンモデル
- 🧨 ディフューザーの始め方
それでは、いくつかの画像を生成しましょう 🎨。
Stable Diffusionの実行
ライセンス
モデルを使用する前に、モデルのライセンスを受け入れて重みをダウンロードして使用する必要があります。 注意:ライセンスはもはやUIを介して明示的に受け入れる必要はありません。
このライセンスは、このような強力な機械学習システムの潜在的な有害な影響を緩和するために設計されています。ユーザーには、ライセンスを完全かつ注意深く読むことをお願いします。以下に要約を提供します:
- モデルを意図的に違法または有害な出力やコンテンツの生成や共有に使用することはできません。
- 生成した出力に対する権利は主張しません。使用は自由であり、使用に関してはライセンスで設定された規定に違反してはならず、その使用については責任があります。
- 重みを再配布し、モデルを商業的および/またはサービスとして使用することができます。ただし、その場合、ライセンスで設定された使用制限とCreativeML OpenRAIL-Mのコピーをすべてのユーザーに提供する必要があります。
使用方法
まず、次のコードスニペットを実行するためにdiffusers==0.10.2
をインストールする必要があります:
pip install diffusers==0.10.2 transformers scipy ftfy accelerate
この記事では、モデルバージョンv1-4
を使用しますが、1.5、2、および2.1などの他のバージョンも最小限のコード変更で使用することができます。
Stable Diffusionモデルは、StableDiffusionPipeline
パイプラインを使用して簡単なfrom_pretrained
関数呼び出しでテキストから画像を生成するために必要なすべてを設定します。
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4")
GPUが利用可能な場合、それを使用するようにしましょう!
pipe.to("cuda")
注意:GPUメモリに制限があり、10GB未満のGPU RAMしか利用できない場合は、上記のようにデフォルトのfloat32精度ではなく、float16精度でStableDiffusionPipeline
を読み込むことを確認してください。
これは、fp16
ブランチから重みを読み込み、diffusers
に重みがfloat16精度であることを伝えることによって行うことができます:
import torch
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", revision="fp16", torch_dtype=torch.float16)
パイプラインを実行するには、単にプロンプトを定義してpipe
を呼び出します。
prompt = "宇宙飛行士が馬に乗っている写真"
image = pipe(prompt).images[0]
# 画像を保存することもできます
# image.save(f"astronaut_rides_horse.png")
結果は次のようになります
前のコードは実行するたびに異なる画像を生成します。
もし黒い画像が表示された場合、モデル内に組み込まれたコンテンツフィルタがNSFW(不適切なコンテンツ)を検出した可能性があります。もしそうでない場合は、プロンプトを微調整するか別のシードを使用してみてください。実際に、モデルの予測には特定の結果においてNSFWが検出されたかどうかの情報が含まれています。それらがどのように見えるか確認してみましょう。
result = pipe(prompt)
print(result)
{
'images': [<PIL.Image.Image image mode=RGB size=512x512>],
'nsfw_content_detected': [False]
}
決定論的な出力を得るには、ランダムシードを設定し、ジェネレータをパイプラインに渡すことができます。同じシードを使用してジェネレータを使うたびに、同じ画像の出力が得られます。
import torch
generator = torch.Generator("cuda").manual_seed(1024)
image = pipe(prompt, guidance_scale=7.5, generator=generator).images[0]
# 画像を保存することができます
# image.save(f"astronaut_rides_horse.png")
結果は次のようになります
num_inference_steps
引数を使用することで、推論ステップの数を変更することができます。
一般的に、ステップ数が多いほど結果は良くなりますが、ステップ数が多いほど生成にかかる時間が長くなります。Stable Diffusionは比較的少ないステップ数でもうまく機能するため、デフォルトの推論ステップ数 50
を使用することをおすすめします。より高速な結果を得たい場合は、小さい数を使用することができます。より高品質な結果を得たい場合は、大きな数を使用することができます。
次に、デノイジングステップの数を少なくしてパイプラインを実行してみましょう。
import torch
generator = torch.Generator("cuda").manual_seed(1024)
image = pipe(prompt, guidance_scale=7.5, num_inference_steps=15, generator=generator).images[0]
# 画像を保存することができます
# image.save(f"astronaut_rides_horse.png")
構造は同じですが、宇宙飛行士のスーツや馬の全体的な形に問題があります。これは、デノイジングステップを15回しか使用しなかったことで生成結果の品質が著しく低下したことを示しています。先ほど述べたように、通常は 50
のデノイジングステップが高品質な画像を生成するのに十分です。
num_inference_steps
以外に、これまでのすべての例で guidance_scale
という関数引数を使用してきました。guidance_scale
は、生成をガイドする条件付きシグナル(この場合はテキスト)に対する遵守度と、全体的なサンプルの品質を向上させるための方法です。これは分類器フリーガイダンスとも呼ばれ、単純に言えば、画像の品質や多様性の犠牲を払ってもプロンプトとの一致度を向上させることを意味します。Stable Diffusionでは、通常 7
から 8.5
の値が良い選択肢です。デフォルトでは、パイプラインは guidance_scale
を 7.5 として使用します。
非常に大きな値を使用すると、画像は良く見えるかもしれませんが、多様性が低くなります。このパラメータの技術的な詳細については、この投稿のこのセクションで学ぶことができます。
次に、同じプロンプトの複数の画像を生成する方法を見てみましょう。まず、グリッド形式で画像を見やすく表示するためのimage_grid
関数を作成します。
from PIL import Image
def image_grid(imgs, rows, cols):
assert len(imgs) == rows*cols
w, h = imgs[0].size
grid = Image.new('RGB', size=(cols*w, rows*h))
grid_w, grid_h = grid.size
for i, img in enumerate(imgs):
grid.paste(img, box=(i%cols*w, i//cols*h))
return grid
同じプロンプトに対して複数の画像を生成するには、単純にプロンプトが何度も繰り返されたリストを使用します。文字列ではなく、リストをパイプラインに送ります。
num_images = 3
prompt = ["宇宙飛行士が馬に乗っている写真"] * num_images
images = pipe(prompt).images
grid = image_grid(images, rows=1, cols=3)
# グリッドを保存することができます
# grid.save(f"astronaut_rides_horse.png")
デフォルトでは、安定した拡散は512 × 512
ピクセルの画像を生成します。縦長または横長のアスペクト比で長方形の画像を作成するには、height
とwidth
引数を使用してデフォルトを上書きすることが非常に簡単です。
画像サイズを選ぶ際には、以下の点に注意してください:
height
とwidth
がいずれも8
の倍数であることを確認してください。- 512以下にすると、画質が低下する可能性があります。
- 両方の方向に512を超えると、画像の領域が繰り返され、全体的な一貫性が失われます。
- 正方形でない画像を作成する最良の方法は、一方の次元に
512
を使用し、もう一方の次元にそれより大きな値を使用することです。
例を実行してみましょう:
prompt = "宇宙飛行士が馬に乗っている写真"
image = pipe(prompt, height=512, width=768).images[0]
# 画像を保存するには
# image.save(f"astronaut_rides_horse.png")
安定した拡散の仕組みはどのように動作しますか?
安定した拡散が生成できる高品質な画像を見た後、モデルがどのように機能するかをより良く理解してみましょう。
安定した拡散は、潜在的な拡散モデルと呼ばれる特定のタイプの拡散モデルに基づいています。このモデルは、高解像度の画像合成における潜在的な拡散モデルとして提案されました。
一般的に、拡散モデルは、ランダムなガウスノイズを段階的に除去して、画像などの興味のあるサンプルに到達するために訓練された機械学習システムです。これらのモデルの動作方法の詳細な概要については、このcolabをご覧ください。
拡散モデルは、画像データを生成するための最先端の結果を達成してきました。しかし、拡散モデルの欠点の一つは、逆のノイズ除去プロセスが反復的であるために遅いことです。また、これらのモデルはピクセル空間で動作するため、メモリを大量に消費します。高解像度の画像を生成する場合、ピクセル空間は非常に大きくなります。そのため、これらのモデルの訓練と推論の両方を行うことは困難です。
潜在的な拡散は、実際のピクセル空間を使用する代わりに、低次元の潜在空間に拡散プロセスを適用することにより、メモリと計算の複雑さを削減することができます。これが標準的な拡散モデルと潜在的な拡散モデルの主な違いです: 潜在的な拡散では、モデルは画像の潜在的な(圧縮された)表現を生成するように訓練されます。
潜在的な拡散には、主に3つの主要なコンポーネントがあります。
- オートエンコーダ(VAE)。
- U-Net。
- テキストエンコーダ、例えばCLIPのテキストエンコーダ。
1. オートエンコーダ(VAE)
VAEモデルには、エンコーダとデコーダの2つのパートがあります。エンコーダは画像を低次元の潜在的な表現に変換し、それがU-Netモデルへの入力となります。逆に、デコーダは潜在的な表現を画像に戻します。
潜在的な拡散のトレーニングでは、エンコーダは正の拡散プロセスのために画像の潜在的な表現(潜在変数)を取得するために使用されます。正の拡散プロセスでは、ステップごとにより多くのノイズが適用されます。推論中には、逆の拡散プロセスによって生成されたノイズ除去された潜在変数は、VAEデコーダを使用して画像に変換されます。推論中には、VAEデコーダのみが必要です。
2. U-Net
U-Netは、エンコーダ部分とデコーダ部分からなり、いずれもResNetブロックで構成されています。エンコーダは画像表現をより低解像度の画像表現に圧縮し、デコーダはより低解像度の画像表現を元のより高解像度の画像表現に復元します。復元された画像表現は、ノイズ残差を予測するために使用され、ノイズ除去された画像表現を計算するのに使用することができます。
ダウンサンプリング中に重要な情報が失われないようにするために、エンコーダのダウンサンプリングResNetとデコーダのアップサンプリングResNetの間には通常、ショートカット接続が追加されます。さらに、安定した拡散U-Netは、クロスアテンション層を介してテキスト埋め込みに対して出力を条件付けることができます。クロスアテンション層は、通常、エンコーダ部分とデコーダ部分のResNetブロック間に追加されます。
3. テキストエンコーダー
テキストエンコーダーは、入力プロンプト(例:「馬に乗った宇宙飛行士」)をU-Netが理解できる埋め込み空間に変換する役割を担っています。通常、シーケンスの入力トークンをシーケンスの潜在的なテキスト埋め込みにマッピングするシンプルなトランスフォーマベースのエンコーダーです。
Imagenからのインスピレーションを受けて、Stable Diffusionではトレーニング中にテキストエンコーダーをトレーニングせず、すでにトレーニングされたCLIPのテキストエンコーダー、CLIPTextModelを単純に使用します。
潜在的拡散はなぜ高速かつ効率的なのですか?
潜在的拡散は低次元空間で操作を行うため、ピクセル空間の拡散モデルと比較してメモリと計算要件を大幅に削減します。たとえば、Stable Diffusionで使用されるオートエンコーダーは、縮小率が8です。つまり、形状が(3, 512, 512)
の画像は潜在空間では(3, 64, 64)
になり、メモリは8 × 8 = 64
分の1になります。
これがなぜ、16GBのColab GPUでも512 × 512
の画像を非常に高速に生成できるのです。
推論中のStable Diffusion
これまでの説明をまとめると、モデルが推論中にどのように機能するかについて、論理的なフローを説明します。
安定的な拡散モデルは、潜在シードとテキストプロンプトの両方を入力として受け取ります。潜在シードは、サイズが64 × 64
のランダムな潜在画像表現を生成するために使用されます。一方、テキストプロンプトは、CLIPのテキストエンコーダーによってサイズが77 × 768
のテキスト埋め込みに変換されます。
次に、U-Netはテキスト埋め込みに基づいてランダムな潜在画像表現を反復的にノイズ除去します。U-Netの出力であるノイズ残差は、スケジューラーアルゴリズムを使用してノイズ除去された潜在画像表現を計算するために使用されます。この計算にはさまざまなスケジューラーアルゴリズムを使用できますが、それぞれには利点と欠点があります。Stable Diffusionでは、次のいずれかを使用することをお勧めします。
- PNDMスケジューラー(デフォルトで使用)
- DDIMスケジューラー
- K-LMSスケジューラー
スケジューラーアルゴリズムの詳細については、このノートブックの範囲外ですが、簡単に言えば、前のノイズ表現と予測されたノイズ残差から予測されたノイズ除去画像表現を計算することを覚えておくべきです。詳細は「拡散ベースの生成モデルの設計空間の解明」を参照してください。
ノイズ除去プロセスは約50回繰り返され、段階的により良い潜在画像表現が取得されます。完了したら、潜在画像表現は変分オートエンコーダーのデコーダー部分によって復号化されます。
これで、潜在的および安定的な拡散の簡単な紹介が終わりました。次は、🤗 Hugging Faceのdiffusers
ライブラリを高度に活用する方法を見てみましょう!
独自の推論パイプラインの作成
最後に、diffusers
を使用してカスタムの拡散パイプラインを作成する方法を示します。カスタムの推論パイプラインを作成することは、上記で説明したVAEやスケジューラーなどの特定のコンポーネントを切り替えるために役立つ、diffusers
ライブラリの高度な使用法です。
たとえば、このPRで追加されたKatherine CrowsonのK-LMSスケジューラーを使用したStable Diffusionの使用方法を示します。
事前学習済みモデルには、完全な拡散パイプラインを設定するために必要なすべてのコンポーネントが含まれています。これらは次のフォルダに保存されています:
text_encoder
:Stable DiffusionはCLIPを使用しますが、他の拡散モデルではBERT
などの他のエンコーダーを使用する場合があります。tokenizer
:これはtext_encoder
モデルで使用されるものと一致する必要があります。scheduler
:トレーニング中に画像に徐々にノイズを追加するために使用されるスケジューラーアルゴリズム。unet
:入力の潜在表現を生成するために使用されるモデル。vae
:潜在表現を実際の画像にデコードするために使用するオートエンコーダーモジュール。
コンポーネントをロードするには、from_pretrained
のsubfolder
引数を使用して、保存されたフォルダを参照します。
from transformers import CLIPTextModel, CLIPTokenizer
from diffusers import AutoencoderKL, UNet2DConditionModel, PNDMScheduler
# 1. 画像空間にlatentsをデコードするために使用されるオートエンコーダーモデルをロードします。
vae = AutoencoderKL.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="vae")
# 2. テキストをトークン化してエンコードするためのトークナイザーとテキストエンコーダーをロードします。
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14")
# 3. latentsを生成するためのUNetモデル。
unet = UNet2DConditionModel.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="unet")
ここで、事前定義されたスケジューラーの代わりに、フィッティングパラメータを持つK-LMSスケジューラーをロードします。
from diffusers import LMSDiscreteScheduler
scheduler = LMSDiscreteScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)
次に、モデルをGPUに移動させます。
torch_device = "cuda"
vae.to(torch_device)
text_encoder.to(torch_device)
unet.to(torch_device)
次に、画像を生成するために使用するパラメータを定義します。
guidance_scale
は、Imazen論文の式(2)のガイダンス重みw
に対応しています。guidance_scale == 1
は、クラシファイアフリーガイダンスを行わないことを意味します。ここでは、以前と同様に7.5に設定します。
以前の例とは異なり、num_inference_steps
を100に設定して、より明確な画像を得ます。
prompt = ["宇宙飛行士が馬に乗っている写真"]
height = 512 # Stable Diffusionのデフォルトの高さ
width = 512 # Stable Diffusionのデフォルトの幅
num_inference_steps = 100 # ノイズ除去ステップ数
guidance_scale = 7.5 # クラシファイアフリーガイダンスのスケール
generator = torch.manual_seed(0) # 初期のlatentノイズを作成するためのジェネレータのシード
batch_size = len(prompt)
まず、渡されたプロンプトのtext_embeddings
を取得します。これらの埋め込みは、UNetモデルを条件付けて画像生成をガイドし、入力プロンプトに似たものを生成します。
text_input = tokenizer(prompt, padding="max_length", max_length=tokenizer.model_max_length, truncation=True, return_tensors="pt")
text_embeddings = text_encoder(text_input.input_ids.to(torch_device))[0]
クラシファイアフリーガイダンスのために、条件付きのtext_embeddings
と同じ形状(batch_size
とseq_length
)を持つ、パディングトークン(空のテキスト)の非条件付きテキスト埋め込みも取得します。
max_length = text_input.input_ids.shape[-1]
uncond_input = tokenizer(
[""] * batch_size, padding="max_length", max_length=max_length, return_tensors="pt"
)
uncond_embeddings = text_encoder(uncond_input.input_ids.to(torch_device))[0]
クラシファイアフリーガイダンスのために、条件付きのtext_embeddings
と非条件付きの埋め込み(uncond_embeddings
)の2回のフォワードパスが必要です。実際には、両方を単一のバッチに連結して、2回のフォワードパスを行わないようにすることができます。
text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
次に、初期のランダムノイズを生成します。
latents = torch.randn(
(batch_size, unet.in_channels, height // 8, width // 8),
generator=generator,
)
latents = latents.to(torch_device)
この段階でlatents
を調べると、その形状はtorch.Size([1, 4, 64, 64])
であり、生成したい画像よりもはるかに小さいことがわかります。モデルは、この潜在表現(純粋なノイズ)を後に512 × 512
の画像に変換します。
次に、選択したnum_inference_steps
でスケジューラを初期化します。これにより、ノイズ除去プロセス中に使用されるsigmas
と正確なタイムステップ値が計算されます。
scheduler.set_timesteps(num_inference_steps)
K-LMSスケジューラでは、latents
にsigma
値を乗算する必要があります。ここで行いましょう。
latents = latents * scheduler.init_noise_sigma
ノイズ除去ループを記述する準備が整いました。
from tqdm.auto import tqdm
scheduler.set_timesteps(num_inference_steps)
for t in tqdm(scheduler.timesteps):
# 分類器フリーガイダンスを行う場合、ラテントを展開して2回の順方向パスを行わないようにします。
latent_model_input = torch.cat([latents] * 2)
latent_model_input = scheduler.scale_model_input(latent_model_input, timestep=t)
# ノイズ残差を予測する
with torch.no_grad():
noise_pred = unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample
# ガイダンスを行う
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
# 以前のノイズが混じったサンプル x_t -> x_t-1 を計算する
latents = scheduler.step(noise_pred, t, latents).prev_sample
生成されたlatents
を使ってvae
を使用して画像に変換します。
# 画像ラテントをスケーリングしてデコードする
latents = 1 / 0.18215 * latents
with torch.no_grad():
image = vae.decode(latents).sample
最後に、画像をPIL形式に変換して表示または保存します。
image = (image / 2 + 0.5).clamp(0, 1)
image = image.detach().cpu().permute(0, 2, 3, 1).numpy()
images = (image * 255).round().astype("uint8")
pil_images = [Image.fromarray(image) for image in images]
pil_images[0]
🤗 Hugging Face Diffusersを使用したStable Diffusionの基本的な使用方法から、ライブラリの高度な使用方法まで、現代のディフュージョンシステムのすべての要素を紹介しました。このトピックが気に入った場合やさらに学びたい場合は、次のリソースをおすすめします:
- 私たちのColabノートブック。
- Diffusionシステムの広範な概要を提供するGetting Started with Diffusersノートブック。
- Annotated Diffusion Modelブログ投稿。
- diffusersが役に立つ場合は、GitHubのコードに⭐をつけていただけると嬉しいです!
引用:
@article{patil2022stable,
author = {Patil, Suraj and Cuenca, Pedro and Lambert, Nathan and von Platen, Patrick},
title = {Stable Diffusion with 🧨 Diffusers},
journal = {Hugging Face Blog},
year = {2022},
note = {[https://huggingface.co/blog/rlhf](https://huggingface.co/blog/stable_diffusion)},
}
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles