見出し画像

Stable Diffusionのモデルを量子化して画像生成する

Stable DiffusionをするにはGPUが必要だ。GPUを搭載したパソコンというのはBTOパソコンのサイトを見ても10万円以上するものが多く簡単には手が出せない価格のため、一般的に普及しているとは思えない

もし、CPUとメモリで処理することが出来るならばと、夢のような話を考えるわけだが、実際に出来たことを確認したので記事にする

なお、制限もあり画像生成のレスポンスの速度などは期待しないことをオススメする。ここでは、GPUを使わずに生成できるという点だけに着目すること。また、GPUを安価でしたい場合はGoogle ColaboratoryなどクラウドでGPUを使わせてくれるサービスを利用することをオススメする


前提

実行したPCの環境はこちら

  • Windows 11 Pro

  • Ryzen 5 4500 3.6GHz

  • システムRAM 32GB

  • NVIDIA Geforce GTX 1630(※1)4GB

  • 50GBほどの空き容量

 今回、GPUは使用していないのだが、明らかにStable Diffusionをローカルで動かすには不安が残るスペックである。実際、Google Colabで実行しているGPUメモリの使用率と比べると明らかに足りない(今、確認してきたところ8GBは使用していた)

ちなみに、こちらのPCは自作で組み立てていることもあり、GPUはあるが、10万円以下で組み立てることに成功している(当時の値段)。もし、気になるときは下記の記事も参考にするとよい。OSとメモリとSSDを増設して前提のスペックに達して10万円以下だ

対応方法

今回、Stable Diffusion.cppを使うことで解決することが出来た

自分の記事でも取り上げているllama.cppを活用して、Stable Diffusionのモデルを量子化している

Stable Diffusion.cppのGithubに量子化と画像生成のやり方は書いてあるのでそちらを参照をすること。自分の記事では実践したことをレポートしていく

実践

実際にしたことは以下の通りである

  • Stable Diffusionのモデルのダウンロード

  • Stable Diffusion.cppのセットアップ

  • Stable Diffusion.cppを使ったモデルの量子化

  • Stable Diffusion.cppを使って量子化したモデルによる画像生成

    • オプションの指定、時間の計測、LCM LORAの適用など

モデルのダウンロード

Stable Diffusionのモデルは、animagine-XLとCounterfeit-V3.0を使用した。SDXLとSD1.5の量子化を行った

モデルの量子化は".safetensors"だけでもいけたモデルもあったので、コマンドラインからではなくマウスでクリックして直接、ダウンロードしてもいいかもしれない(プロジェクトフォルダ毎、ダウンロードすると容量を食うため)

Stable Diffusion.cppのセットアップ

Stable Diffusion.cppのセットアップが完了したら"./stable-diffusion.cpp\build\bin\Release"のディレクトリに移動する

モデルの量子化

それぞれ、Q8で量子化した

animagine-XLをQ8に量子化する

.\sd.exe -M convert -m .\animagine-xl-3.1\animagine-xl-3.1.safetensors -o .\animagine-xl-3.1\animagine-xl-3.1.q8_0.gguf -v --type q8_0

Counterfeit-V3をQ8に量子化する

.\sd.exe -M convert -m .\Counterfeit-V3.0\Counterfeit-V3.0_fix_fp16.safetensors -o .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf -v --type q8_0

他にもfp16とQ4も量子化したが使用していない。animagine-XLをfp16に量子化したとき一時的とはいえ、メモリの使用量(総量)が20GBに上昇したときに恐怖を覚えたから(笑)Q4については後述する

量子化したモデルによる画像生成

本題だ。画像生成をしていこう

AnimagineXLで画像生成する

.\sd.exe -m .\sd.exe -m .\animagine-xl-3.1\animagine-xl-3.1.q8_0.gguf  -p "masterpiece, best quality,1 girl, peace fingers" -n "(worst quality, low quality:1.4)," --sampling-method euler -H 832 -W 1216
生成結果

明示的な設定値は以下の通りである。明示的に指定していない場合の他のパラメータのデフォルト値はgithubを見ること(例えば、デフォルトのステップ数は20だ)

  • 幅:1216、高さ:832(※2)

  • サンプリングメソッド:  Euler_a

(※2)幅と高さを逆に設定してしまった、-Hオプションと-Wオプションの指定に注意すること。感覚的にはWidthとHeightの並びで慣れていたので、よく確認しなかったのがよくなかった

Stable Diffusion.cppがGPUを使用してないように見える。プロセスを監視すればよかったか・・・

Counterfeit-V3で画像生成する

.\sd.exe -m .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf -p "masterpiece, best quality,1 girl, black hair, thumbs-up, smile," -n "(worst quality, low quality:1.4)," --sampling-method euler_a -H 512 -W 512 --steps 16
生成結果
  • 幅:512、高さ:512

  • サンプリングメソッド: Euler_a

  • ステップ数:16

課題

さて、CPUとシステムメモリでGPUを使わずに画像生成できることは分かったが課題がある。それは、レスポンスの速度だ。Google Colabやローカルで画像生成をしたことがある人は、1枚、何分で生成しているか思い出してみよう。自分はComfyUIをGoogle Colaboratoryで使用しているが、1枚、1分は掛からなかったのではないかと記憶している

以下の結果を見てみよう

 |=====>                                            | 2/20 - 108.84s/it

animagine-xlによる画像生成のLOG(一部抜粋)

1stepあたり108s掛かっているので30分は画像生成に時間が掛かることが分かる。他にもプロセスはあるので実際にはもっと掛かるが、1枚画像を生成するのに30分というのは流石に実用するのは難しいだろう。効率が悪すぎる。量子化することによりGPUを使わずに画像を生成できることに注目するべきだとは思うけど、残念ながら、llama.cppのときの様なLLMのシングルGPUによる応答時間より、レスポンス速度が早かった感動までは得られなかった(llama.cppは応答のレスポンスが全文返すのに1分未満、短い文章ならば20秒から40秒程度で返していたからだ。ColabのT4 GPUだとシステムメモリがt足りないせいなのかクラッシュしたり、実行できても応答に数分は掛かるなんてこともざらにあった。ローカルPCという限られたリソースの中でシングルGPUよりも高速の処理を行えるという現象が逆転してしまってメリットを感じにくくなってしまった)

以降、レスポンスの速度が上げられないか試していった
Counterfeitを見てみよう。先程はSDXLのモデルだったのでSD1.5のモデルの分、速度が向上しないか期待した

他にも

  • step数を減らして生成時間を短縮する。Eulerならば20ステップより減らしても画像の生成クオリティが許容できるのでは?

  • モデルをQ4に変換したらレスポンスが早くならないか

結論としては「生成する画像のサイズ」で時間が大きく変わった。
512*512のサイズで16stepを踏んだところ、1ステップ20秒という結果が出た。約、5分程度だ。30分が5分になるのであれば許容できる範囲に近付くが、1分/枚を目指したい

|==================================================| 16/16 - 20.57s/it
[INFO ] stable-diffusion.cpp:1381 - sampling completed, taking 330.49s
[INFO ] stable-diffusion.cpp:1389 - generating 1 latent images completed, taking 330.53s
[INFO ] stable-diffusion.cpp:1392 - decoding 1 latents
[INFO ] stable-diffusion.cpp:1402 - latent 1 decoded, taking 42.27s
[INFO ] stable-diffusion.cpp:1406 - decode_first_stage completed, taking 42.27s
[INFO ] stable-diffusion.cpp:1490 - txt2img completed in 372.96s
save result image to 'output.png'

Counterfeit-V3.0による画像生成のLOG(一部抜粋)

試しに"W:512 * H:256"、"W: 256* H: 512"のサイズでも試した

生成結果
生成結果

レスポンスの速度を確認しよう
1ステップが7秒であった。つまり、約2分だ

今回の画像のサイズは"512*512"のサイズから比べれば、1/2となっているので、おおよそ1ステップの処理時間と比例していると考えてもよいのではないか

[INFO ] stable-diffusion.cpp:1338 - generating image: 1/1 - seed 42
|==================================================| 16/16 - 7.95s/it

Counterfeit-V3.0による画像生成のLOG(一部抜粋)Width: 256. Height: 512 

ということは、Width: 256, Height: 256で1分で生成できる計算にはなる。実際に試してみたところ、おおよそ1分で完了した

.\sd.exe -m .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf -p "masterpiece, best quality,1 girl, thumbs-up, smile, upper body, black background" -n "worst quality, low quality:1.4," --sampling-method euler_a -H 256 -W 256 --steps 16 --clip-skip 2

[INFO ] stable-diffusion.cpp:169 - loading model from '.\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf'
[INFO ] model.cpp:730 - load .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf using gguf format
WARNING: Behavior may be unexpected when allocating 0 bytes for ggml_calloc!
[INFO ] stable-diffusion.cpp:192 - Stable Diffusion 1.x
[INFO ] stable-diffusion.cpp:198 - Stable Diffusion weight type: q8_0
[INFO ] stable-diffusion.cpp:419 - total params memory size = 1618.48MB (VRAM 0.00MB, RAM 1618.48MB): clip 125.20MB(RAM), unet 1398.81MB(RAM), vae 94.47MB(RAM), controlnet 0.00MB(VRAM), pmid 0.00MB(RAM)
[INFO ] stable-diffusion.cpp:423 - loading model from '.\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf' completed, taking 3.84s
[INFO ] stable-diffusion.cpp:440 - running in eps-prediction mode
[INFO ] stable-diffusion.cpp:556 - Attempting to apply 0 LoRAs
[INFO ] stable-diffusion.cpp:1203 - apply_loras completed, taking 0.00s
[INFO ] stable-diffusion.cpp:1316 - get_learned_condition completed, taking 135 ms
[INFO ] stable-diffusion.cpp:1334 - sampling using Euler A method
[INFO ] stable-diffusion.cpp:1338 - generating image: 1/1 - seed 42
|==================================================| 16/16 - 3.52s/it

[INFO ] stable-diffusion.cpp:1381 - sampling completed, taking 58.33s
[INFO ] stable-diffusion.cpp:1389 - generating 1 latent images completed, taking 58.38s
[INFO ] stable-diffusion.cpp:1392 - decoding 1 latents
[INFO ] stable-diffusion.cpp:1402 - latent 1 decoded, taking 9.62s
[INFO ] stable-diffusion.cpp:1406 - decode_first_stage completed, taking 9.62s
[INFO ] stable-diffusion.cpp:1490 - txt2img completed in 68.14s
save result image to 'output.png'

Counterfeit-V3.0による画像生成のLOG(一部抜粋)Width: 256. Height: 256

しかし、残念ながら生成結果が崩れてしまったので、これではアウトプットとして使うことが出来ない。こちらのPCのスペックでは256*512で2分~3分/枚、生成するのが自分の許容範囲では限度だ

ちなみに、他にも画像生成の際に確認したことを残しておく

  • Q4にすれば早くなるのではと思ったが、1ステップの処理時間は実際はそこまで変わらなかった。むしろ、遅くなったくらいか。何でも値を下げれば早くなるわけではないことが分かった

[INFO ] stable-diffusion.cpp:192 - Stable Diffusion 1.x
[INFO ] stable-diffusion.cpp:198 - Stable Diffusion weight type: q4_0
[INFO ] stable-diffusion.cpp:419 - total params memory size = 1431.11MB (VRAM 0.00MB, RAM 1431.11MB): clip 66.61MB(RAM), unet 1270.04MB(RAM), vae 94.47MB(RAM), controlnet 0.00MB(VRAM), pmid 0.00MB(RAM)
[INFO ] stable-diffusion.cpp:423 - loading model from '.\Counterfeit-V3.0\Counterfeit-V3.0.q4_0.gguf' completed, taking 3.33s
[INFO ] stable-diffusion.cpp:440 - running in eps-prediction mode
[INFO ] stable-diffusion.cpp:556 - Attempting to apply 0 LoRAs
[INFO ] stable-diffusion.cpp:1203 - apply_loras completed, taking 0.00s
[INFO ] stable-diffusion.cpp:1316 - get_learned_condition completed, taking 165 ms
[INFO ] stable-diffusion.cpp:1334 - sampling using Euler A method
[INFO ] stable-diffusion.cpp:1338 - generating image: 1/1 - seed 42
|=========> | 3/16 - 8.53s/it

Counterfeit-V3.0による画像生成のLOG(一部抜粋)Width: 256. Height: 512

LCM LORAも試したがうまくいかなかった。確かにステップ数が半分程度で済むので時間は圧縮されるのだが、絵が崩れてしまった。また、LoRAを読み込む時間も加味される

.\sd.exe -m .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf -p "masterpiece, best quality,1 girl, black hair, thumbs-up, smile, <lora:lcm-lora-sdv1-5:1>," -n "(worst quality, low quality:1.4)," --sampling-method lcm -H 512 -W 512 --steps 4 --lora-model-dir .\LoRA\ --cfg-scale 1 --clip-skip 2

[INFO ] stable-diffusion.cpp:169 - loading model from '.\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf'
[INFO ] model.cpp:730 - load .\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf using gguf format
WARNING: Behavior may be unexpected when allocating 0 bytes for ggml_calloc!
[INFO ] stable-diffusion.cpp:192 - Stable Diffusion 1.x
[INFO ] stable-diffusion.cpp:198 - Stable Diffusion weight type: q8_0
[INFO ] stable-diffusion.cpp:419 - total params memory size = 1618.48MB (VRAM 0.00MB, RAM 1618.48MB): clip 125.20MB(RAM), unet 1398.81MB(RAM), vae 94.47MB(RAM), controlnet 0.00MB(VRAM), pmid 0.00MB(RAM)
[INFO ] stable-diffusion.cpp:423 - loading model from '.\Counterfeit-V3.0\Counterfeit-V3.0.q8_0.gguf' completed, taking 1.06s
[INFO ] stable-diffusion.cpp:440 - running in eps-prediction mode
[WARN ] stable-diffusion.cpp:538 - In quantized models when applying LoRA, the images have poor quality.
[INFO ] stable-diffusion.cpp:556 - Attempting to apply 1 LoRAs
[INFO ] model.cpp:733 - load .\LoRA\lcm-lora-sdv1-5.safetensors using safetensors format
[INFO ] lora.hpp:31 - loading LoRA from '.\LoRA\lcm-lora-sdv1-5.safetensors'
[INFO ] stable-diffusion.cpp:533 - lora 'lcm-lora-sdv1-5' applied, taking 15.80s
[INFO ] stable-diffusion.cpp:1203 - apply_loras completed, taking 15.80s
[INFO ] stable-diffusion.cpp:1316 - get_learned_condition completed, taking 82 ms
[INFO ] stable-diffusion.cpp:1334 - sampling using Euler A method
[INFO ] stable-diffusion.cpp:1338 - generating image: 1/1 - seed 42
|==================================================| 4/4 - 10.00s/it
[INFO ] stable-diffusion.cpp:1381 - sampling completed, taking 39.37s
[INFO ] stable-diffusion.cpp:1389 - generating 1 latent images completed, taking 39.42s
[INFO ] stable-diffusion.cpp:1392 - decoding 1 latents
[INFO ] stable-diffusion.cpp:1402 - latent 1 decoded, taking 40.06s
[INFO ] stable-diffusion.cpp:1406 - decode_first_stage completed, taking 40.06s
[INFO ] stable-diffusion.cpp:1490 - txt2img completed in 95.37s
save result image to 'output.png'

Counterfeit-V3.0による画像生成のLOG(一部抜粋)LCM LORA

設定を失敗した可能性はあるので、改善する余地はあるかもしれない

[WARN ] stable-diffusion.cpp:538 - In quantized models when applying LoRA, the images have poor quality.

とはいえ、量子化することが目的なのに、この警告は矛盾している気がしている

まとめ

GPUを搭載していないPCでも、ちょっとしたサイズの画像であれば、モデルをダウンロードしてきて量子化して試せる点はよかった

一方でGPUを使った画像生成ほどのレスポンスの速度は得られないので課題はある。どこまで自分の中で許容できるかで検討するのもよい

はじめに、Stable Diffusionも量子化すれば、CPUでも処理できるのでは?といった思いつきで調べ始めた内容であるけど、正解ではあったようだ

もう少し、速度改善が図れるようになれば、subprocessによる自作関数を作成したり、まだ、したことはないがpythonによるbindingのプロジェクトなど進めてもいいかもしれない

番外:GPUによる画像生成について

Google ColaboratoryでGPUを使って画像生成をしているが、その中で自分が躓いたことなどの対応をまとめているので、よければどうぞ

おわり!