28
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事は有名な画像生成AIであるStable Diffusionの使い方を説明する入門です。

画像生成AIに興味を持ったけどどうやって始めるからわからない人にとっておすすめです。簡単についていけるようにまず簡単な最低限のコードから例を見せてからどんどん説明を追加していきます。

はじめに

きっかけ

私はずっと前から生成AIに興味を持って2018年の時にDCGANに挑戦したこともあって、ここで記事も書いたのです。

DCGANを使ったら絵が描けない私でもある程度新しい絵が作れるようになって嬉しいことです。

しかしあの時のDCGANでは満足できそうな画像が作れなくて限界を感じてしまいました。実用レベルはまだまだでした。

当時はあれで精一杯だったのですが、技術の発達は本当に速いものです。あれから時間が経って2022年になって漸くStable Diffusionが現れました。

これを使ったら本物のイラストや写真みたいな画像が簡単に作れるようになります。本当に素晴らしいです!

以下は私がStable Diffusionを使って生成してみた猫耳少女の画像です。可愛いですね。

nekomimi.jpg

これを見て前の2018年の記事のDCGANと比べたらどれくらい技術が発達したか実感できるでしょう。

因みにこの子達の生成はどれもほぼ既定値のまま調整なしでただ「nekomimi」と書くだけです。もっとちゃんと調整したらもっと素敵な画像が作れますよ。

Stable Diffusionの特徴

現在画像生成AIは既に沢山出てきています。Stable Diffusionとほぼ同じ時期に公開されたMidjourneyDALL-EBing Image Creatorなど画像生成AIも有名ですが、それらは主にウェブアプリとして使うのです。

それに対しStable Diffusionは自分のパソコンにダウンロードして好き放題無料で使えるというのは特徴です。

ただし生成の速度はパソコンに大きく依存するので、低スペックでGPUのないパソコンでは遅いです。それでもただ遅いだけで生成できないわけではないので、古すぎないパソコンを持っていたら基本的に誰でも使えると考えてもいいです。GPUがあれば勿論それは一番ですが、必須ではありません。

そして何よりStable Diffusionのいいところは、Pythonで書いた実装のコードも公開されているから、中身を読んでどんな仕組みなのか勉強してもっと理解を深めることもできます。

とはいっても、Stable Diffusionの仕組みは複雑なもので、使いたいだけなら別に詳しく理解しなければならないわけではないのです。

私もこの記事で使い方を説明したいだけで、仕組みを詳しく説明するつもりがありません。ただし調整項目ではある程度用語が使われているので、これがどういう意味なのか少し理解しておいた方が使いやすいから最低限の説明をしておきます。

この記事の最後の部分に参考資料のリンクがあるので、深く理解したい方はこれを参考にしてください。

公式の実装コードをダウンロードして使うこともできますが、もっと簡単でよく使われるのはdiffusersというモジュールで実装するという方法です。

diffusersとは

Stable Diffusionをインストールしたいと言っても実は方法が色々あります。一般の人はAUTOMATIC1111 Stable Diffusion web UIComfyUIを使うことが多いです。それらはGUIという形で使えるので、プログラミング知識がない人でも簡単に扱えます。

でも我々プログラマーにとって一番飲み込みやすいのはやはりPythonコードを通じて使う方法でしょう。

そもそもStable Diffusionの中身PythonだからPythonを書いて使うのは一番のはずです。寧ろいきなりGUIの画面が現れていっぱい項目が出てきてどう選択したらいいかわからなくて戸惑ってしまうのです。

diffusersではまず既定値のままで最低限のコードで始めることができます。まだすぐによくわからない調整の選択肢を気にしなくてもよくて、もっと詳しく調整したいならその時どんどんもっと勉強していけばいいです。

diffusersから始めてちゃんとStable Diffusionの基本を把握できたらAUTOMATIC1111 Stable Diffusion web UIでもComfyUIも簡単に使えるようになるでしょう。

準備

環境

Stable Diffusionを使うのにGPUがあれば一番速いですが、もしなければCPUでも使えます。速度は全然違いますが使えないわけではないのです。OSはWindows、Mac、Linux何でも使えます。ただしメモリーが8GBだけだときついと言われています。

使うGPUは基本的にCUDAですが、Macの場合はMPS(Metal Perfomance Shaders)というGPUが付いているので、これをStable Diffusionに使うことができます。速度はCUDAほど高速ではないが、CPUより速いです。

又、自分のパソコンのスペックに自信がない方はGoogle Colabを使うという方法があります。ただ私も使ったことがないし、Google Colabの使い方の説明の記事は既に沢山あるので、これについて割愛します。

私が使っている環境はメモリー24GBのMacBook Air M3で、MPSで実行しています。

インストール

diffusersは他のPythonモジュールと同じく、pipで簡単にインストールしてすぐ使うことができます。ただしその他に必要なモジュールも沢山あって、自動的にインストールされないものもあるので同時にインストールします。

まずはこのリンクを参考にしてpytorchをインストールします。
https://pytorch.org/get-started/locally/

次はdiffusersとtransformersとpeftのインストールです。

pip install diffusers transformers peft

その他にもやりたいこと次第他のモジュールが必要となることもありますが、どれもpipでインストールできるから、もしモジュールがないというエラーが出てきたらその時にインストールすればいいです。

diffusersの中身はpytorchtransformersが使われていますが、たとえ使ったことがなくても大丈夫です。

ただ、GPUで実行したい場合まずはpytorch (torch)でGPUが使えるかどうかチェックしておきましょう。

CUDAの場合

print(torch.cuda.is_available())

MACのMPSのの場合

print(torch.backends.mps.is_available())

インストールしてもし問題なければこれでもう使えるはずです。

diffusersがアップデートするたびに新しい機能も増えてきて変化し続けているので、バージョンが違うと使う関数が変わる可能性があります。

だから年のために使っているモジュールのバージョンもここに書いておきます。基本的にその時点の最新バージョンを使っています。

python 3.12.2
diffusers 0.29.1
pytorch 2.3.1
torchvision 0.18.1
transformers 4.41.2
peft 0.11.1
numpy 1.26.4
PIL 10.3.0

チェックポイントモデル

Stable Diffusionを使う時に普段はStable Diffusion自体の他に色んなファイルが必要です。その中で不可欠なのは「チェックポイントモデル」というものです。これはStable Diffusionの「脳」の部分みたいな存在です。

わかりやすく例えるなら、Stable Diffusionは「スタジオ」で、チェックポイントモデルはその中に働いている「人材」です。スタジオがあるからいい作品ができるが、どんな作品が作られるかは人材次第ですね。

だから単に「Stable Diffusionを使っている」と言ってもどんな画像が作れるのか結局はチェックポイントモデルによって大きく違います。違うチェックポイントモデルは違う学習データによって訓練したものなので、生成できる画像は元となったデータ次第違いまする

なお、Stable Diffusionの話をする時にチェックポイントモデルのことを単に「モデル」と呼ぶことが多いのでここでもそう呼びますが、そもそも「モデル」は色んな意味があって紛らわしいことでもあるということは気を付けておいた方がいいです。

チェックポイントモデルは個別としてウェブサイトで公開されてダウンロードして使うのは一般的です。

diffusersを通じて使う場合は手動でダウンロードする必要なく自動的にダウンロードするモデルもあってこれをすぐ使うことができて便利です。

ここでは自動ダウンロードできるモデルを使う方法も、自分でダウンロードしておいたモデルを使う方法も説明します。

簡単に始める

自動ダウンロードのモデル

まず一番簡単ですぐ始められるように自動でダウンロードできるモデルから説明します。

例としてここではまずアニメ調画像の生成によく使われているgsdf/Counterfeit-V2.5というモデルを紹介します。

では早速このコードを実行して生成を始めましょう。

from diffusers import DiffusionPipeline

model_id = 'gsdf/Counterfeit-V2.5'
pipe = DiffusionPipeline.from_pretrained(model_id)
prompt = 'nekomimi'
img = pipe(prompt).images[0]
img.save('nekomimi.jpg')

実行したらまずモデルをダウンロードすることになります。最初は時間がかかりますが、一旦ダウンロードが終わったら保存されて次回使いたい時にすぐ使えます。

そしてダウンロードが終わったら生成が始まります。かかる時間はパソコンのスペックによって違いますが、私の環境では2-3分です。デフォルトではCPUで実行することになっているから遅いです。

生成が終わったらこのような画像が保存されます。ただしランダムなので毎回結果は違います。サイズは既定値の512×512ピクセル。

nekomimi-counterfeit.jpg

このようにたった短いコードを書くだけで可愛い猫耳少女が出てきます。

コードの説明

次はコードを説明します。

まずはDiffusionPipelineクラスのfrom_pretrainedメソッドを使って生成パイプラインオブジェクトを作るのです。ここで指定するmodel_idhttps://huggingface.co/ サイトに公開されている使いたいチェックポイントモデルのIDです。

使えるモデルIDはこのページから参考です。
https://huggingface.co/models?other=stable-diffusion

ただしこのページに乗っている全てのモデルが自動ダウンロードできるわけではありません。サインインしてトークンを取得しておかないとダウンロードできないモデルもあります。

次はパイプラインにプロンプトを入れて生成を始めます。

「プロンプト」(prompt)というのはいわゆる生成したいものの記述です。ここでは猫耳が欲しいので「nekomimi」と書きます。

プロンプト基本的に英語で書くのですが、nekomimiなどは海外でも通じる単語なのでそのまま使えます。日本語の漢字のままプロンプトに使うこともできますが、効果はあまりよくないのであまりおすすめできません。

日本語でプロンプトを書く方法は記事の最後で紹介しますが、この記事の大半はまず単に「nekomimi」というプロンプトで使っていきます。

出力した画像はリストとして出てきますが、ここで1枚しかないので.images[0]を付けて取得できます。

生成できた画像はPILのオブジェクトで、saveメソッドを使って保存できます。これで生成完了です。

モデル保存パス

なお、ダウンロードしたファイルの保存場所はデフォルトではこのようになっています。(バージョンによって違う可能性があります)

windows C:\Users\{ユーザー名}\.cache\huggingface\hub
mac /Users/{ユーザー名}/.cache/huggingface/hub
linux /home/{ユーザー名}/.cache/huggingface/hub

モデルファイルが基本的に数GB大きいので、ハードディスクの余裕がない方はもう使わないモデルを削除しましょう。

ダウンロードしたモデルを使う

自動ダウンロードできるモデルを使ったら便利ですが、残念ながらこのようなモデルは少ないのです。殆どのモデルはhttps://civitai.com というウェブサイトで公開しています。Stable Diffusionを使う人はこのサイトからモデルをダウンロードするのは一番一般的です。

大部分のモデルはすぐダウンロードボタンをクリックしてダウンロードできますが、一部のモデルはサイトにサインインする必要があります。

私が今気になっているのはこの「AnimeKawa」というモデルです。今回はこれを例として使います。

ダウンロードボタンをクリックしたらanimekawa_v10.safetensorsというファイルが保存されます。どこに保存してもいいです。私はこれをstadifmodelというフォルダを使ってそこに置いています。ではこのファイルを読み込んで画像生成させてみます。

from diffusers import StableDiffusionPipeline

model_file = 'stadifmodel/animekawa_v10.safetensors'
pipe = StableDiffusionPipeline.from_single_file(model_file)
prompt = 'nekomimi'
img = pipe(prompt).images[0]
img.save('nekomimi.jpg')

nekomimi-animekawa01.jpg

又可愛い猫耳が出てきますね。モデルが違うから画風も違います。私はこの方が好きです。

今回使ったのはStableDiffusionPipelineクラスのfrom_single_fileメソッドです。

因みに.safetensorsは拡張子です。こんなに馬鹿長い拡張子は私も初めて見たのです。現在Stable Diffusionのモデルは殆どこの拡張子で保存されています。2022年頃は.ckptが多かったが、安全性の問題を考慮して.safetensorsという拡張子が作られて今は一般的になっています。昔使っていた.ckptもまだ使われていますが、安全性の問題があるのでできれば.safetensorsを使った方がいいと言われています。

XLモデルの場合

Stable Diffusionモデルの中で「Stable Diffusion XL」(SDXL)というタイプがあります。これは2023に公開した大きいモデルです。基本的な使い方は本来のStable Diffusionモデルと同じですが、違うところもあるので使う時にXLかどうかを意識する必要がある場合があります。

詳しくはこの記事を参考に。
https://aitechworld.info/stable-diffusion-sdxl/

そしてdiffusersで.safetensorsを読み込みたい場合、XLモデルならStableDiffusionPipelineではなく、その代わりにStableDiffusionXLPipelineを使う必要があります。間違ったらエラーになります。

普段XLモデルは名前に「XL」が付いているから区別できます。

例えば今回この「AnimeXL-xuebiMIX」というモデルを紹介したいと思います。

このモデルを使いたいならこのように読み込みます。

from diffusers import StableDiffusionXLPipeline

model_file = 'stadifmodel/animexlXuebimix_v60LCM.safetensors'
pipe = StableDiffusionXLPipeline.from_single_file(model_file)

ただし、このモデルは調整なしでそのまま使ってもあまりいい絵ができないので、まず色んな調整方法を説明した後このモデルを使う例を挙げます。

又、XLモデルは本来のモデルよりファイルも大きいし、生成する時間や消耗するメモリーも大きくて、GPUを使っていない人にとって更にきついです。使いたいなら自分のパソコンでは無理かどうか注意する必要があります。

なお、DiffusionPipelineはXLモデルでも問題ないので使い分ける必要がないが、from_single_fileがないので、ダウンロードした.safetensorsファイルを使いたい場合はやはりStableDiffusionPipelineStableDiffusionXLPipelineで使い分ける必要があります。

今後共通の使える方法が出てくるかもしれませんが、現時点ではこういう使い分けをしています。

高速化しよう

GPUを使う(.to(device))

特に指定していない限りデフォルトではdiffusersがCPUで実行することになります。そのままでは遅いです。

GPUで実行したい場合はパイプラインを作る関数に.to()を付ければいいです。例えば私はmacのMPSを使っているのでこう書きます。

from diffusers import StableDiffusionPipeline

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(model_file).to(device)
prompt = 'nekomimi'
img = pipe(prompt).images[0]
img.save('nekomimi.jpg')

nekomimi-animekawa02.jpg

かかる時間はCPUを使った時より3倍くらい速くなりました。もしCUDAを使ったら更に何倍も高速になるはずです。

CPUを使う場合は.to('cpu')を書く必要がないのですが、後でGPUの装置に書き換える時を考慮して互換性のためにこうかけばいいかもしれません。

import torch

if(torch.cuda.is_available()):
    device = 'cuda'
elif(torch.backends.mps.is_available()):
    device = 'mps'
else:
    device = 'cpu'

つまりCUDAが使えればCUDAを使うのが一番ですが、なければ次はMPSで、これもなければ仕方なくCPUを使うしかないですね。

私はMPSを使っているのでこれからの例はdevice = 'mps'だけ書きますが、他の装置を使う人はそれに従って書き直してください。

半精度にして更に高速化且つメモリー節約(torch_dtype)

GPUを使うだけで随分速くなりましたが、それだけでなく更に高速化できる魔法があります。それはパイプラインを作る時にtorch_dtype=torch.float16を入れることです。

from diffusers import StableDiffusionPipeline
import torch

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = 'nekomimi'
img = pipe(prompt).images[0]
img.save('nekomimi.jpg')

nekomimi-animekawa03.jpg

torch_dtype計算するデータ型を指定です。普段float32(単精度)が使われますが、float16(半精度)に指定することで精度が半分になります。精度が下がっても問題なく綺麗な画像ができますが、消耗するメモリーは半分になるのは大きなメリットになります。かかる時間もある程度下がります。

私の場合は本来で50秒で、float16にすると40秒くらいになるので、5/4倍くらいの速さです。

CPUを使う場合は半精度が使えないので、この方法はGPU使う時限定です。CPUを使ったまま半精度にしたら動かなくなるので注意です。

ステップ数を指定する(num_inference_steps)

もう一つ確実に速度を上げる方法があります。それはノイズ除去の処理のステップ数を減らすことです。この方法はCPUでもGPUでも使えます。

これを理解するために少しだけStable Diffusionの仕組みについて触れます。

Stable Diffusionが綺麗な画像を作るためにまずはノイズばかりのランダムな画像から始めてU-netというニューラルネットワークに入力してノイズ除去を行うのです。

そのノイズ除去の処理は何ステップも繰り返す必要があります。処理のステップ数が少なすぎるとノイズいっぱいの画像になりますが、ある程度のステップ数になったらもう十分でそれ以上綺麗になるわけではありません。

diffusersの既定値ではステップ数は50です。これはちょっと多すぎます。ステップ数はどれくらいが十分かはモデルやサンプリング方法によって違いますが、殆どは20-30くらいで十分です。

モデル配布のページで適切なステップ数が書かれることもあります。例えばAnimeKawaの場合は18-28がいいと書いてあります。

ステップ数はパイプラインを呼び出す時にnum_inference_stepsというキーワードを入れることで指定できます。

では23に指定してみます。

from diffusers import StableDiffusionPipeline
import torch

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = 'nekomimi'
img = pipe(prompt,num_inference_steps=23).images[0]
img.save('nekomimi.jpg')

nekomimi-animekawa04.jpg

結果は既定値のステップ数50と比べてどう違うかわかりませんね。だからこれくらいのステップ数で十分です。

これで生成するのにかかる時間は半分くらいになります。

私の環境ではこの画像の生成は20秒です。

画像のサイズを指定する(width、height)

画像のサイズのピクセル数はwidthheightのキーワードで指定できます。ただし8で割り切れる必要があります。

既定値では普通のモデルなら512×512ピクセルで、XLモデルは1024×1024ピクセルです。

サイズが大きくなると当然その分かかる時間と消耗するメモリーが大きくなるので限界があります。

サイズが違うと品質が変わることもあります。基本的に正方形で学習することが多いので、正方形に指定した方はいい画像ができやすいです。

試しに正方形でない画像も作ってみます。

from diffusers import StableDiffusionPipeline
import torch

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = 'nekomimi'
img = pipe(
    prompt,
    width=600,
    height=480,
    num_inference_steps=23
).images[0]
img.save('nekomimi.jpg')

nekomimi-animekawa-600x480.jpg

普通に綺麗な画像もできますが、傾向として確かに512×512の方が綺麗な画像ができる可能性は高いです。

又、小さすぎる画像を作ることも苦手なようなので、小さい画像が欲しい場合でもまずは512×512で生成して後で縮小させた方がいいと思います。

複数生成

同じ条件で複数生成(num_images_per_prompt)

パイプラインを実行する時にnum_images_per_promptというキーワードを入れたら複数画像を引き続き生成することができます。

ただしこの方法ではその分メモリーの消耗が激しくなるので、あまりおすすめできません。forループを使って生成した方がいいです。

使う例としてメモリーの消耗の問題がないように試しに小さな256×256サイズの画像を量産してみます。

from diffusers import StableDiffusionPipeline
import torch
from diffusers.utils import make_image_grid

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = 'nekomimi'
lis_img = pipe(
    prompt,
    width=256,
    height=256,
    num_inference_steps=23,
    num_images_per_prompt=9
).images
make_image_grid(lis_img,3,3).save('nekomimi.jpg')

nekomimi-animekawa-fukusuu.jpg

結果はあまりよくないですね。やはりこのモデルは小さい画像の生成は苦手です。

因みにここではmake_image_grid関数を使っています。これはdiffusersが準備しておいた、指定した行と列で画像をグリッドに並べるための関数です。これを使うと画像が沢山並ぶ画像を簡単に作れて便利です。

複数プロンプトで一緒に生成する

複数画像を生成するもう一つの方法はプロンプトをリストとして入れることです。

例えば「猫耳巫女」と「猫耳忍者」を作りたいならこう書きます。

from diffusers import StableDiffusionPipeline
import torch
from diffusers.utils import make_image_grid

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = ['nekomimi miko','nekomimi ninja']
img1,img2 = pipe(
    prompt,
    num_inference_steps=23
).images

make_image_grid([img1,img2],2,1).save('nekomimi.jpg')

nekomimi-mikoninja.jpg

ちゃんとそれぞれのプロンプトの特徴で出てきますね。

メモリーの開放(empty_cache)

メモリーの消耗の問題を避けるために複数の画像の生成はforループを使った方が一番だと言いましたが、実はそれだけではまだメモリーの問題が完全に解決できるわけではありません。

ループで引き続き実行する場合でも前回の実行でメモリーが使ったままの状態だからです。まずメモリーを開放する必要があります。

そのためにCUDAではtorch.cuda.empty_cache()、MPSではtorch.mps.empty_cache()を使います。

生成の部分を関数にしてループの中で生成関数を呼び出してその直後にこの関数を使うという書き方をおすすめします。

このように書くと512×512ピクセルの画像を何枚生成してもメモリーの心配はありません。

from diffusers import StableDiffusionPipeline
import torch
from diffusers.utils import make_image_grid

def seisei():
    pipe = StableDiffusionPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16
    ).to(device)
    img = pipe(
        prompt,
        num_inference_steps=23
    ).images[0]
    return img

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
prompt = 'nekomimi'
lis_img = []
for i in range(6):
    lis_img.append(seisei())
    if(device=='cuda'):
        torch.cuda.empty_cache()
    elif(device=='mps'):
        torch.mps.empty_cache()

make_image_grid(lis_img,3,2).save('nekomimi.jpg')

nekomimi-ryousan.jpg

可愛い猫耳少女がいっぱい出てきて素敵ですね。

seedで結果を固定させる(generator、manual_seed)

ここまで毎回生成する時結果はランダムで違っていくので、前回と同じような画像が欲しくてももう作ることができません。

結果を固定したい場合はseedを指定してtorch.Generatorオブジェクトを作ってパイプラインを実行する時にgeneratorキーワードに入れるのです。seedの指定は0より大きい何でもいい整数を使います。

seedも他の設定も同じなら全く同じ画像が作れるはずですが、他の設定を変えることで似ているようで少し違う画像が作れて比較することができます。

例えば上述のステップ数(num_inference_steps)です。これがどれくらい結果に影響を与えるかここで比較してみましょう。

from diffusers import StableDiffusionPipeline
import torch
import matplotlib.pyplot as plt

def seisei(n_step):
    pipe = StableDiffusionPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16
    ).to(device)
    # Generatorオブジェクトを作る
    generator = torch.Generator(device).manual_seed(seed)
    img = pipe(
        prompt,
        num_inference_steps=n_step,
        generator=generator # ここに入れる
    ).images[0]
    return img

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
prompt = 'nekomimi'
seed = 22233 # ここでseed固定

plt.figure(figsize=[6,9.5],dpi=100)
for i,n_step in enumerate(range(5,31,5)):
    img = seisei(n_step)
    plt.subplot(3,2,i+1,title="num_inference_steps=%d"%n_step)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout()
plt.savefig('nekomimi.jpg')
plt.close()

nekomimi-step.jpg

やはり20より少ないとあまりいい絵ができないですね。20でも物足りない場合もありますが、25くらいになったらもっと増えてもただ細部が変わるだけで品質が上がるかどうかわかりにくくなります。

プロンプトの大切さの指定(guidance_scale)

パイプラインを実行する時に入れるguidance_scaleというキーワードは「どれくらいプロンプトを重視するか」を決める数値です。既定値は7.5で、0から10で指定できます。小さい数値だとプロンプトはあまり重視されず望んだのと違う画像になります。

違うguidance_scaleの比較をしてみます。

from diffusers import StableDiffusionPipeline
import torch
import matplotlib.pyplot as plt

def seisei(gs):
    pipe = StableDiffusionPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16
    ).to(device)
    generator = torch.Generator(device).manual_seed(seed)
    img = pipe(
        prompt,
        num_inference_steps=23,
        guidance_scale=gs,
        generator=generator
    ).images[0]
    return img

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
prompt = 'nekomimi miko'
seed = 22230

plt.figure(figsize=[6,12.5],dpi=100)
for i in range(1,9):
    img = seisei(i)
    plt.subplot(4,2,i,title="guidance_scale=%.1f"%i)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout()
plt.savefig('nekomimi.jpg')
plt.close()

nekomimi-miko-gs.jpg

普段は既定値の7.5のままで問題ないが、その辺りの数値で調整してどれくらいがいいか試してみてもいいです。

CLIPを飛ばす(clip_skip)

guidance_scaleと似ているclip_skipというキーワードがあります。guidance_scaleと同様にプロンプトの影響を決めるパラメータですが、仕組みは違います。これについて少し触れてみます。

CLIP(Contrastive Language-Image Pretraining)というのは自然言語処理によって使われるモデルの一種です。これはStable Diffusionに構成されている一つの部品として搭載されています。役割は「入力したプロンプトを解析する」ことです。

だから私達の書いたプロンプトの通り画像が作られるかどうかはCLIPの影響が大きいです。

そのCLIPは12層からなされていて、生成する時に基本的にその12層を全部使われますが、実際に全部の層を使う必要がなく、途中でスキップして使いたい時もあります。それはCLIP skipと呼ばれます。

少しスキップしたらいい結果になることもありますが、スキップしすぎると思ったのと違う画像が出てくる可能性が高くなります。

clip_skipは0から11の数値で指定できます。既定値では0、つまりスキップを行わなず全部の層を使うということです。

では違うclip_skipを比較してみましょう。

from diffusers import StableDiffusionPipeline
import torch
import matplotlib.pyplot as plt

def seisei(cs):
    pipe = StableDiffusionPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16
    ).to(device)
    generator = torch.Generator(device).manual_seed(seed)
    img = pipe(
        prompt,
        num_inference_steps=20,
        guidance_scale=7,
        clip_skip=cs,
        generator=generator
    ).images[0]
    return img

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
prompt = 'nekomimi miko'
seed = 22241

plt.figure(figsize=[7,10],dpi=100)
for i in range(12):
    img = seisei(i)
    plt.subplot(4,3,i+1,title="clip_skip=%d"%i)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout()
plt.savefig('nekomimi.jpg')
plt.close()

nekomimi-miko-cs.jpg

clip_skip=9からはもう猫耳ではなくなりますね。

1や2くらいが丁度いい場合が多いと言われていますが、一概に言えません。実際に試して比較してみましょう。

スケジューラー(scheduler)

スケジューラー(scheduler)又はサンプラー(sampler)というのは簡単に言うとStable Diffusionの中でノイズばかりの画像から綺麗な画像を作る処理のアルゴリズムの一部です。

これがかなり複雑なものなので詳しく触れませんが、ここではただ生成された画像に影響を与える要因の一つと認識しても構いません。

又、スケジューラーが違うと適切なステップ数など他のパラメータも変わる場合もあるということも注意しておきましょう。

スケジューラーの種類はものすごく多くてどう選べばいいか迷ってしまうこともありますが、基本的に各モデルでは既定値のスケジューラーも入れられているのでそのまま使ってもいい場合が多くて、普段はスケジューラーのことを意識しなくてもいいです。

パイプラインを使った後scheduler属性調べればどのスケジューラーが既定値で使われるかわかります。例えばAnimeKawaの場合は見てみましょう。

from diffusers import StableDiffusionPipeline

model_file = 'stadifmodel/animekawa_v10.safetensors'
pipe = StableDiffusionPipeline.from_single_file(model_file)
print(pipe.scheduler)
結果
PNDMScheduler {
  "_class_name": "PNDMScheduler",
  "_diffusers_version": "0.29.1",
  "beta_end": 0.012,
  "beta_schedule": "scaled_linear",
  "beta_start": 0.00085,
  "clip_sample": false,
  "num_train_timesteps": 1000,
  "prediction_type": "epsilon",
  "set_alpha_to_one": false,
  "skip_prk_steps": true,
  "steps_offset": 1,
  "timestep_spacing": "leading",
  "trained_betas": null
}

なんかわかりにくそうな値がいっぱい出てきますね。今のところは構わなくても大丈夫です。ここではただ「PNDMScheduler」というスケジューラーのクラスが使われていると理解しておけばいいです。

今まで意識していなくてもAnimeKawaモデルを使う時にこのスケジューラーが使われていましたが、実はモデル配布ページを見ると別の「DPM++ SDE Karras」というスケジューラーが推奨されているとわかります。これはdiffusersではDPMSolverMultistepSchedulerクラスです。

スケジューラーが変わるとどれくらい結果が変わるか比較してみましょう。

import diffusers
import torch
import matplotlib.pyplot as plt

def seisei(scheduler):
    pipe = diffusers.StableDiffusionPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16,
    ).to(device)
    pipe.scheduler = scheduler.from_config(pipe.scheduler.config)
    generator = torch.Generator(device).manual_seed(seed)
    img = pipe(
        prompt,
        generator=generator,
        num_inference_steps=23
    ).images[0]
    return img

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
prompt = 'nekomimi'
seed = 10026
lis_schdl = [
    'DDIMScheduler',
    'DDPMScheduler',
    'PNDMScheduler',
    'DPMSolverSinglestepScheduler',
    'DPMSolverMultistepScheduler',
    'LMSDiscreteScheduler',
    'EulerDiscreteScheduler',
    'EulerAncestralDiscreteScheduler',
    'HeunDiscreteScheduler',
    'KDPM2AncestralDiscreteScheduler',
]

plt.figure(figsize=[6,15.5],dpi=100)
for i,schdl in enumerate(lis_schdl):
    img = seisei(getattr(diffusers,schdl))
    plt.subplot(5,2,i+1,title=schdl)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout()
plt.savefig('nekomimi.jpg')
plt.close()

nekomimi-animekawa-scheduler.jpg

それぞれ違うような似ているような、どれがいいか言いにくいですね。

既定値のPNDMSchedulerと推奨されているDPMSolverMultistepSchedulerは全然違うようですが、どれもいいようです。

AnimeKawaの場合はスケジューラーの違いはそこまで見えないのですが、モデルによっては大きく違う場合もあるので、ここではスケジューラーを設定する方法を覚えておけばいいでしょう。

VAE(AutoencoderKL)

Stable Diffusionに構成されている一つの部品として、VAE(Variational AutoEncoder、変分オートエンコーダー)が使われます。

VAEはニューラルネットワークモデルの一種で元々これ単体で生成AIとして使われることもありましたが、あまり綺麗な画像が期待できずDCGANよりも劣っています。

そんなVAEですが、Stable Diffusionの中では補助的な存在となっています。

詳しいことは割愛しますが、ここではVAEがスケジューラーと同様に生成結果に影響を与える要素の一つで適切なものを選ぶ必要がある、ということは覚えておけばいいです。

今まで使っていたAnimeKawaモデルは既定値のVAEを使っても問題ないのでVAEのことを意識しなくてもいいのですが、使いたいモデルによって一緒に適用するVAEが指定されていることがあります。その場合その通り設定を変えましょう。

今回は例として上述でも既に言及したAnimeXL-xuebiMIXモデルを使います。このモデルはSDXL-VAE-FP16-FixというVAEと一緒に使ったらいいと指定されています。又、スケジューラーはEulerAncestralDiscreteSchedulerを使うと指定されています。

VAEはチェックポイントモデルと同様に.from_pretrainedでhuggingfaceから自動ダウンロードして使う、又は.from_single_fileで自分でダウンロードした.safetensorファイルを使うことができます。

今回使うmadebyollin/sdxl-vae-fp16-fixはhuggingfaceにあるので.from_pretrainedを使います。

ではVAEを指定して使う例を見てみましょう。

from diffusers import StableDiffusionXLPipeline,EulerAncestralDiscreteScheduler
from diffusers.models import AutoencoderKL # VAEのクラス
import torch

model_file = 'stadifmodel/animexlXuebimix_v60LCM.safetensors'
device = 'mps'
# ここでファイルを読み込んでVAEオブジェクトを作成
vae = AutoencoderKL.from_pretrained(
    'madebyollin/sdxl-vae-fp16-fix',
    torch_dtype=torch.float16
)
pipe = StableDiffusionXLPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16,
    vae=vae # ここにVAEを入れる
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
seed = 23006
generator = torch.Generator(device).manual_seed(seed)
prompt = 'nekomimi'
img = pipe(
    prompt,
    width=512,
    height=512,
    num_inference_steps=23,
    generator=generator
).images[0]
img.save('nekomimi.jpg')

結果はこんな画像です。
nekomimi23006-xuebi.jpg

VAEを指定しない場合はこのような画像になります。

nekomimi23006-xuebi-vaenashi.jpg

大体同じですが、色合いが違ったり変な白い点が入ったりしますね。このように適切なVAEを使うのは重要です。

望ましくない結果を避ける(negative_prompt、load_textual_inversion)

普段欲しいものをプロンプトとして入力しますが、逆に欲しくないものはネガティブプロンプト(negative prompt)として同時に入力することができます。そうすることで望ましくない画像が生成されることを阻止できます。

ただし望ましくない要素をいちいち指定するのは面倒ですね。実際に人々が欲しくない要素は共通点があります。それらを纏めてエンベディングモデル(embedding model)として保存しているのでこれを使ったらいいです。

一番有名なネガティブプロンプトのエンベディングはEasyNegativeです。これは元々上述のCounterfeitモデルの作者がCounterfeitモデルに適用するために作ったものですが、他のモデルにもある程度通用するので広く使われています。

ここに公開されているのでダウンロードして使うことができます。今はV2となっています。
https://huggingface.co/gsdf/Counterfeit-V3.0/blob/main/embedding/EasyNegativeV2.safetensors

拡張子はチェックポイントモデルと同様に.safetensorsですが、サイズはただ数十kbで小さいです。

ただしEasyNegativeを使ったら必ずしもいい結果になるとは限らないようです。モデルによってEasyNegativeを使ったら逆に悪くなることもあります。AnimeKawaはそのようです。

でもAnimeXL-xuebiMIXに使うといい結果が見られるので、ここではAnimeXL-xuebiMIXを例とします。

エンベディングの使い方はまず.load_textual_inversion関数を使ってエンベディングモデルの.safetensorsを読み込んでこれを表現するネガティブプロンプトをtoken='EasyNegative'と指定しておいて、パイプラインを実行する時にnegative_promptというキーワードに入れるのです。

使う例は以下となります。

from diffusers import StableDiffusionXLPipeline,EulerAncestralDiscreteScheduler
from diffusers.models import AutoencoderKL
import torch

model_file = 'stadifmodel/animexlXuebimix_v60LCM.safetensors'
device = 'mps'
vae = AutoencoderKL.from_pretrained(
    'madebyollin/sdxl-vae-fp16-fix',
    torch_dtype=torch.float16
)
pipe = StableDiffusionXLPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16,
    vae=vae
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
seed = 23006
generator = torch.Generator(device).manual_seed(seed)
# ここでエンベディングをロードする
pipe.load_textual_inversion(
    pretrained_model_name_or_path='stadifmodel/EasyNegativeV2.safetensors',
    token='EasyNegative')
prompt = 'nekomimi'
img = pipe(
    prompt,
    width=512,
    height=512,
    num_inference_steps=23,
    generator=generator,
    negative_prompt='EasyNegative' # ここに追加する
).images[0]
img.save('nekomimi.jpg')

nekomimi23006-xuebi-easynegative.jpg

上の例と同じseedを使っているので結果を比較できます。EasyNegativeを使った方が細部まで描かれて少し高品質になっているという印象です。

LoRA(load_lora_weights)

LoRA(Low-Rank Adaptation)はもう一つStable Diffusionでよく使われる追加の機能です。既存のモデルに適用して特定な作風を生成する特化させることができます。

詳しく言うとはLoRAは少量の画像を使った追加学習によって作られたもので誰も簡単に作って使うものですが、それはもっと高度なことなのでここでは割愛します。ここでは人が作って公開しているLoRAを使う方法だけ説明します。

ここでは例として「国风插画」(国風挿画)というLoRAを紹介します。これは中華風の絵を生成するためのLoRAです。

LoRAファイルも.safetensorsで、これをダウンロードしてload_lora_weightsメソッドで使うのです。

ただしこれだけではまだLoRAの効果が見えません。有効にするためにはトリガーワード(Trigger Word)というものをプロンプトに入れる必要があります。トリガーワードはLoRAの配布ページに載ってあるのでこれを見て使うといいです。例えばこのLoRAのトリガーワードは「guofeng」です。因みにguofengは中国であり漢字で書くと「国風」です。

LoRAはただ追加部品なので作れる画像はまだ主にチェックポイントモデルの方に依存します。基本的にどんなチェックポイントモデルにも使えますが、どれくらい効果があるかはモデル次第です。又、XLモデルと本来のStable Diffusionモデルに使うLoRAも違います。

今回の「国风插画」はXLモデルに使うLoRAなので、AnimeXL-xuebiMIXモデルに使ってみます。

from diffusers import StableDiffusionXLPipeline,EulerAncestralDiscreteScheduler
from diffusers.models import AutoencoderKL
import torch

model_file = 'stadifmodel/animexlXuebimix_v60LCM.safetensors'
device = 'mps'
vae = AutoencoderKL.from_pretrained(
    'madebyollin/sdxl-vae-fp16-fix',
    torch_dtype=torch.float16
)
pipe = StableDiffusionXLPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16,
    vae=vae
).to(device)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
seed = 23006
generator = torch.Generator(device).manual_seed(seed)
pipe.load_textual_inversion(
    pretrained_model_name_or_path='stadifmodel/EasyNegativeV2.safetensors',
    token='EasyNegative')
# ここでLoRAをロードする
pipe.load_lora_weights(".", weight_name='stadifmodel/国风插画SDXL.safetensors')
prompt = 'guofeng, nekomimi' # ここにトリガーワードが含まれる
img = pipe(
    prompt,
    width=512,
    height=512,
    num_inference_steps=23,
    generator=generator,
    negative_prompt='EasyNegative'
).images[0]
img.save('nekomimi.jpg')

nekomimi23006-xuebi-guofeng.jpg

本当に中華風の猫耳ができましたね。今回もseedを上の画像と同じにしているので、LoRAの効果を比較できます。

LoRAの比率の設定(fuse_lora)

fuse_loraメソッドを使って、LoRAの影響の比率を設定することもできます。0だとLoRAを使っていないのと同じで、大きくなると影響が大きくなります。ただし大きすぎると絵が崩壊します。

違うLoRAの比率で描いて比較してみましょう。

from diffusers import StableDiffusionXLPipeline,EulerAncestralDiscreteScheduler
from diffusers.models import AutoencoderKL
import torch
import matplotlib.pyplot as plt

def seisei(ls):
    vae = AutoencoderKL.from_pretrained(vae_id,torch_dtype=torch.float16)
    pipe = StableDiffusionXLPipeline.from_single_file(
        model_file,
        torch_dtype=torch.float16,
        vae=vae
    ).to(device)
    pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
    generator = torch.Generator(device).manual_seed(seed)
    pipe.load_textual_inversion(
        pretrained_model_name_or_path=easynegative_file,
        token='EasyNegative')
    pipe.load_lora_weights(".", weight_name=lora_file)
    pipe.fuse_lora(lora_scale=ls) # ここに追加
    img = pipe(
        prompt,
        width=512,
        height=512,
        num_inference_steps=23,
        generator=generator,
        negative_prompt='EasyNegative'
    ).images[0]
    return img

model_file = 'stadifmodel/animexlXuebimix_v60LCM.safetensors'
vae_id = 'madebyollin/sdxl-vae-fp16-fix'
easynegative_file = 'stadifmodel/EasyNegativeV2.safetensors'
lora_file = 'stadifmodel/国风插画SDXL.safetensors'
device = 'mps'
prompt = 'guofeng, nekomimi'
seed = 23512

plt.figure(figsize=[6,12.5],dpi=100)
for i in range(8):
    ls = i*0.2
    img = seisei(ls)
    plt.subplot(4,2,i+1,title="lora_scale=%.1f"%ls)
    plt.imshow(img)
    plt.axis('off')
    torch.mps.empty_cache()

plt.tight_layout()
plt.savefig('nekomimi.jpg')
plt.close()

nekomimi-ls.jpg

不要な忠告を抑える

あえて触れていなかったことですが、今まで実行している中で長い忠告の言葉が沢山出てくるはずですね?ただの忠告だから殆どは別に問題はないのですが、ちょっと煩く感じることもありますよね。

忠告を完全に無視するのも良くないのですが、既に読んで承知した内容なら無視してもいいでしょう。知ったことを何度も忠告しても面倒なだけなので忠告を抑える方法をここに書いておきます。

それはただdiffusers.logging.set_verbosity_error()関数を使うだけです。こうすることで本当に重大なエラーしか出力されません。

このように使います。

from diffusers import StableDiffusionPipeline,logging # ここでインポート
import torch

logging.set_verbosity_error() # この行を追加
model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
prompt = 'nekomimi'
img = pipe(
    prompt,
    num_inference_steps=23
).images[0]
img.save('nekomimi.jpg')

そうしたら生成の時に出てくるのはこれだけになります。

Fetching 11 files: 100%|██████████| 11/11 [00:00<00:00, 136098.36it/s]
Loading pipeline components...: 100%|██████████| 6/6 [00:00<00:00, 55.04it/s]
100%|██████████| 23/23 [00:19<00:00, 1.17it/s]

使わなかった時はこんな感じですね。

Fetching 11 files: 100%|██████████| 11/11 [00:00<00:00, 90288.34it/s]
Loading pipeline components...: 0%| | 0/6 [00:00<?, ?it/s]Some weights of the model checkpoint were not used when initializing CLIPTextModel:
['text_model.embeddings.position_ids']
Loading pipeline components...: 100%|██████████| 6/6 [00:00<00:00, 49.72it/s]
You have disabled the safety checker for by passing safety_checker=None. Ensure that you abide to the conditions of the Stable Diffusion license and do not expose unfiltered results in services or applications open to the public. Both the diffusers team and Hugging Face strongly recommend to keep the safety filter enabled in all public facing circumstances, disabling it only for use-cases that involve analyzing network behavior or auditing its results. For more information, please have a look at https://github.com/huggingface/diffusers/pull/254 .
100%|██████████| 23/23 [00:19<00:00, 1.17it/s]

気にしなければそのまま放っておいてもいいのですが、煩わしく感じたらこうした方がいいかもしれません。

日本語でプロンプトを書く

プロンプトは基本的に英語しか使えないのですが、私みたいに英語苦手な人にとってちょっと抵抗感がありますよね。他の言語で使えればいいのですが、それはちょっと工夫が必要です。

一応Japanese Stable Diffusion XLという日本語でプロンプトを書くモデルがありますが、そのようなモデルは少ないです。私が欲しいのはアニメ風画像わ生成するモデルですが、日本語で使えるものはありません。

だとしたらもう人つの方法です。それは自動翻訳を通じて使うのです。Pythonでは沢山自動翻訳できるモジュールがありますが、ここでは一番近い安い「translate」というモデルを使います。

まずpipでインストールします。

pip install translate

では翻訳モジュールを通じて日本語でもっと具体的なプロンプトを書いて画像生成してみましょう。

from diffusers import StableDiffusionPipeline
import torch
from translate import Translator

model_file = 'stadifmodel/animekawa_v10.safetensors'
device = 'mps'
seed = 24064
pipe = StableDiffusionPipeline.from_single_file(
    model_file,
    torch_dtype=torch.float16
).to(device)
generator = torch.Generator(device).manual_seed(seed)
honyaku = Translator('en','ja').translate
prompt = '夜の浜辺でとんこつラーメンを食べる2人の可愛い銀髪猫耳少女'
img = pipe(
    honyaku(prompt),
    num_inference_steps=30,
    generator=generator,
    ).images[0]
img.save('nekomimi.jpg')

nekomimi-ramen.jpg

食べ方などちょっと変かもしれませんが、書いた通り大体合っていますね。「
とんこつラーメン」がどんな食べ物なのかもちゃんと理解してくれていますし。

終わりに

いかがでしょうか?これで思う存分好きな画像が作れるようになってきたでしょうか?

今回はあえてStable Diffusionの仕組みについて触れずに使い方だけ集中したので、まだ説明足りない部分が多いと思いますが、Stable Diffusionを始めたい人にとって参考になれたら幸いです。

今回紹介したのはただStable Diffusionのほんの一部しかなく、まだ色んな機能があります。

例えばimg2imgという画像から画像を生成するという機能や、画像からビデオを作るStable Video Diffusionです。これも後で使ってみた又記事で紹介したいと思います。

続編の記事:

長い記事になりましたが、最後までお読みいただけありがとうございます。

もっと読みたい方は下に載っている資料も参考にしてください。

参考

基本

使う環境や設定について

MPS

guidance_scale (CFG)

スケジューラー

VAE

ネガティブプロンプト

LoRA

日本語プロンプト

その他の使う例

28
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?