Stable Diffusion OpenVINOでIntel CPUでもAIに画像を生成してもらう

最近画像生成AIが流行りです．@shi3zさんの Memeplex.app で遊んだりしています．Memeplexはプロンプトのプリセットがあるし日本語を入力すると英語に翻訳してくれるので英語の出来ない自分でもとっつきやすいです．
ローカル環境でも試したいけどStable Diffusionはローカルで動かすにはGPUが必要．しかし手元にはないので出来ないな，クラウドを使ってみる?とか思っていたのですが，Intel CPU内蔵GPUで画像生成をしてくれる「stable_diffusion.openvino」を知ったので少し試してみました．

Stable Diffusion with Diffusers (NVIDIA製GPUが必要)

Google Colab で はじめる Stable Diffusion v1.4｜npaka｜note (クラウドの一つ)

bes-dev/stable_diffusion.openvino (iGPUで動作する今回試したもの)

Stable Diffusion OpenVINO 動作要件確認

Stable Diffusion OpenVINOの要件は以下のようになっています．OpenVINOに対応したCPUだとOKのようです．

例 1. bes-dev/stable_diffusion.openvino

Requirements
Linux, Windows, MacOS
Python 3.8.+
CPU compatible with OpenVINO.

OpenVINOの要件は以下のページを見るとCore i 6世代目〜12世代目のようです．

例 2. System Requirements for the Intel® Distribution of OpenVINO Toolkit

CPU
Processors
6th to 12th generation Intel Core processors
1st to 3rd generation of Intel® Xeon® Scalable processors
Intel Atom® processor with Intel® Streaming SIMD Extensions 4.2 (Intel® SSE4.2)

手元の端末のCPUは以下のような感じで3世代目と6世代目．後者はぎりぎり対応していそうです．

$ lscpu | grep ^Model\ name:
Model name: Intel(R) Core(TM) i5-3320M CPU @ 2.60GHz
$ lscpu | grep ^Model\ name:
Model name: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz

Stable Diffusion OpenVINO 導入

端末の内蔵diskは狭く空き容量が少ないので外付けHDDのvirtualenvに導入することにします．

$ mkdir /media/matoken/export/sd (1)
$ cd /media/matoken/export/sd
$ virtualenv . (2)
$ source bin/activate (3)
$ git clone https://github.com/bes-dev/stable_diffusion.openvino (4)
$ cd stable_diffusion.openvino
$ pip install -r requirements.txt (5)

HDDに作業ディレクトリ作成

virtualenv で環境作成

virtualenv環境反映

stable_diffusion.openvinoをclone

Install

初回実行時にダウンロードされるmodelが4GBほどあるのですが，10回ほど試してもタイムアウトで中断してしまうのでVPSでダウンロードしてコピーしました．規定値のホームディレクトリ以下に導入しようとしてdisk fullになったのでhddにシンボリックリンクを貼ってHDDに導入しました．オリジナルの場所は ~/.cache/huggingface 以下です．

$ ls -Ss1 ~/.cache/huggingface/hub/models--bes-dev--stable-diffusion-v1-4-openvino/blobs/
合計 4169032
3357512 7655112cde3c5084c2709b2b89994834b24efc457f2c1b302bb3d7a056bfdb7a
480740 b3f34c33d8c272f61c6780251a503911c7e7a448cdc10f8fb7d9bc59c3462951
193328 456fd6d12ffab507af1ad6772b7b1922b35502272cd5be3708c8292252cd4c46
133460 8732df1da0a3026da5ee6070df8899efbccf0d844821368214af8d95abeae467
2952 a6514903a2875c318399e451c1f9ddaf4a3f7ba4
460 dfb9780b4527132dfe9a19ccc7ce5c0d885cb2ae
328 c0f568310ea3a649aea01772031087d324272780
252 87adf22fe4160e26a799270b2f53c8c21c9d6cd0

プロンプトからの画像生成

ここまで来たら画像生成が出来ます．テキストの文字列でどんな画像を生成するか伝えます．この文字列をプロンプトといい基本的に英語，英語圏で通じる言葉で指示します．今回は「kawaii neko」としてみました．

$ python ./demo.py --prompt "kawaii neko"
Terminated

Terminatedと言われて処理が中断しました．どうもメモリが足りなそうです．実メモリは8GBです．disk swapでもう8GB追加して再度実行してみます．

メモリ不足なのでディスクスワップの追加

$ dd if=/dev/zero of=./swap.img bs=1M count=8096 (1)
$ sudo chown 0.0 ./swap.img
$ sudo chmod 600 ./swap.img
$ sudo mkswap ./swap.img (2)
$ sudo swapon `pwd`/swap.img (3)
$ python ./demo.py --prompt "kawaii neko" (4)
32it [13:14, 24.84s/it]

8GB程のディスクイメージの作成

スワップとして初期化

スワップを有効にする

再度画像生成

今度は15分程経って処理が終了し，output.png として画像が出来ました．何度か試すと10〜15分程で1枚出来ます．このときLoadAverageは10くらいまで上がって他の処理はあまりやりたくない感じに．寝ているときなどに処理する感じが良さそうです．

Note

他のCPUでの処理速度は以下にいくつか紹介されています．やはりGPUに比べると大分遅いですね．

bes-dev/stable_diffusion.openvino

Note

何度か動かすと数回に一回 Terminated が出ます．この環境に導入している nohung がkillしているようなので一時的に無効にすると安定して動作するようになりました．(一般的な環境には導入されていない)

$ sudo systemctl stop nohang-desktop.service
$ sudo systemctl stop nohang.service

次回以降起動時

shellを開いて以下のコマンドでStable Diffusionの環境を有効にします．

$ cd /media/matoken/export/sd/stable_diffusion.openvino (1)
$ source ../bin/activate (2)
$ sudo swapon `pwd`/swap.img (3)

Stable Diffusion 作業ディレクトリに移動

virtualenv環境反映

disk swap反映

help

demo.py のhelpを見るとこんな感じ．色々できそうです．

$ python demo.py --help
usage: demo.py [-h] [--model MODEL] [--seed SEED] [--beta-start BETA_START] [--beta-end BETA_END] [--beta-schedule BETA_SCHEDULE]
[--num-inference-steps NUM_INFERENCE_STEPS] [--guidance-scale GUIDANCE_SCALE] [--eta ETA] [--tokenizer TOKENIZER] [--prompt PROMPT]
[--init-image INIT_IMAGE] [--strength STRENGTH] [--mask MASK] [--output OUTPUT]

optional arguments:
-h, --help show this help message and exit
--model MODEL model name
--seed SEED random seed for generating consistent images per prompt
--beta-start BETA_START
LMSDiscreteScheduler::beta_start
--beta-end BETA_END LMSDiscreteScheduler::beta_end
--beta-schedule BETA_SCHEDULE
LMSDiscreteScheduler::beta_schedule
--num-inference-steps NUM_INFERENCE_STEPS
num inference steps
--guidance-scale GUIDANCE_SCALE
guidance scale
--eta ETA eta
--tokenizer TOKENIZER
tokenizer
--prompt PROMPT prompt
--init-image INIT_IMAGE
path to initial image
--strength STRENGTH how strong the initial image should be noised [0.0, 1.0]
--mask MASK mask of the region to inpaint on the initial image
--output OUTPUT output image name

img2img

初期イメージを指定も試してみます． --init-image で適当な画像を指定してみました．ハンモックの画像にプロンプトで猫などを指定してみました．

$ python demo.py --seed ${seed} --prompt "sleep cat, campe site, hammock" --init-image=./hammock.jpg --output "${outfile}"

ハンモックに猫っぽいものが現れました．

元画像

生成された画像

もう少し線画とかシンプルな画像のほうがいいかも?

同じSEEDとプロンプトで画像が固定されるのを確認

出来上がる画像は毎回同じプロンプトでも既定値ではSEEDがランダム生成されるので別の画像が生成されます．SEEDとプロンプトを固定して同じ画像が出力されるか見てみます．

Note

少し古いStable DiffusionだとSEEDを固定しても別の画像が出てくるようです．

$ python demo.py --seed 999999 --prompt "kawaii neko" --output "kawaii_neko-999999-01.png"
32it [09:47, 18.36s/it]
$ python demo.py --seed 999999 --prompt "kawaii neko" --output "kawaii_neko-999999-02.png"
32it [09:41, 18.18s/it]

見た目もチェックサムを比較しても同じ値になりました．

$ sha512sum ./kawaii_neko-999999-0?.png
3cd2fad521495571842d9f37f13113bd6a18195e120fd4cded40f7fcf59eb3807cdd9aa43ae4831dfd7e4b0228077bdcabd2a7a86947507e2863143a43d7cee9 ./kawaii_neko-999999-01.png
3cd2fad521495571842d9f37f13113bd6a18195e120fd4cded40f7fcf59eb3807cdd9aa43ae4831dfd7e4b0228077bdcabd2a7a86947507e2863143a43d7cee9 ./kawaii_neko-999999-02.png

ということは恐らく低解像度だと処理時間は短くなると思うので，低解像度でたくさん画像生成して気に入ったものを高解像度で作り直すといったことをすると効率が良さそう．でもまだ解像度指定は出来ないようです．

Suggestion: Add –height and –width for generating images in different pixel sizes · Issue #6 · bes-dev/stable_diffusion.openvino

とりあえず解像度指定できるようになったときのために毎回SEEDを指定して，SEEDとプロンプトをexifに保存しておくことにしました．
SEEDの範囲は手元で試した感じでは2^32で 0〜4294967295 のようです．

seed=$( shuf -i 0-4294967295 -n 1 )
python demo.py --seed ${seed} --prompt "${prompt}" --init-image="${image}" --output "${outfile}"
exiftool -overwrite_original -comment="SEED:${seed}/PROMPT:${prompt}" "${outfile}"

SEEDで探索

MemeplexのDiscordでSEEDが近いと似た画像が生成されるというのを見たので試してみます．(そしてMemeplexにはSEED固定機能が追加された)

適当な画像のプロンプトは固定でSEEDを少しづつずらして画像生成を試してみます．

step 1

step 10

step 100

step 1000

近いと似た傾向に画像になるような感じ?

strengthで探索

--strength オプションは0〜1で既定値は0.5のようです．これを変更してどうなるか見てみます．

まずはプロンプトとSEEDを固定して初期画像無しで—​strengthを0.1刻みで変更してみました．
見た目は変わらない?hashを比較すると別の画像になっています．

次は猫画像を作成して，その画像を初期画像に指定してプロンプトに「dog」を指定，SEEDを固定しました．
段々と猫から犬になり，–strengthが1では顔の向きくらいしか残ってなさそうです．

初期画像に対するプロンプトの強さが変わる感じですね．

CPUを全部使うようにしてみる(速度は変わらない)

しばらく動かしてみていると4コア中2コアしか使っていないように見えます．以下のISSUEを参考に4コア使うようにしてみましたが，他の方と同じように特に速度は変わらなかったので元に戻し( $ git restore stable_diffusion_engine.py )ました．

Only 4 threads seem to be used on an 8 thread machine. · Issue #10 · bes-dev/stable_diffusion.openvino

$ git diff ./stable_diffusion_engine.py
diff --git a/stable_diffusion_engine.py b/stable_diffusion_engine.py
index f73541b..6c92198 100644
--- a/stable_diffusion_engine.py
+++ b/stable_diffusion_engine.py
@@ -27,6 +27,7 @@ class StableDiffusionEngine:
self.scheduler = scheduler
# models
self.core = Core()
+ self.core.set_property("CPU", {"INFERENCE_NUM_THREADS": 4})
# text features
self._text_encoder = self.core.read_model(
hf_hub_download(repo_id=model, filename="text_encoder.xml"),

プロンプト(呪文)の作り方

まずは他の方のプロンプトを真似させてもらうのがお手軽です．Memeplex.appだとギャラリーで気に入ったものをクリックするとプロンプトが表示されるのでそれをコピーしてカスタマイズするといいです．ちなみにMemeplexはプリセットが充実しているので便利です．SEEDや英語翻訳後のプロンプトも表示されるのでべつのStable Diffusionに指定することも出来ます．

Memeplex.app

The Ai Art も便利で，構図やフィルタなどの例がたくさん紹介されています．Cyberpunk styleを多用しがち……．

The Ai Art – AI, Art, and everything in between

Twitterで「#StableDiffusion」などで検索するとたくさんの画像が出てきてプロンプトが記載されていることが多いです．

https://twitter.com/search?q=%23stablediffusion&f=live

プロンプトの不明な画像は以下のsiteでプロンプトを推測してもらうことが出来ます．自分の生成した画像を入れてみましたが自分のプロンプトとはかけ離れたものが出てきました．あくまで推測ですね．

methexis-inc/img2prompt – Run with an API on Replicate

試した環境

$ git log -1
commit 294dd34234f0629f69d5af5b1190841560242969 (HEAD -> master, origin/master, origin/HEAD)
Author: Brian Foley <bpfoley@gmail.com>
Date: Mon Sep 5 09:25:43 2022 -0400

Update requirements.txt (#39)

Add ftfy 6.1.1 to stop OpenAIGPTTokenizer's warning/fallback to BERT BasicTokenizer.
$ dpkg-query -W python3
python3 3.9.2-3
$ lsb_release -dr
Description: Debian GNU/Linux 11 (bullseye)
Release: 11
$ lscpu | grep ^Model\ name:
Model name: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
$ cat /proc/meminfo | grep MemTotal:
MemTotal: 8019112 kB