👻

Stable Diffusion のプロンプトで意味の足し算・引き算をする

2022/08/27に公開
1

TL;DR

  • Stable Diffusion でプロンプトに重みをつけたり、意味の世界で足したり引いたりするよ
    • 例: ピラミッド - エジプト + 日本 = ?
  • 画風をシームレスに変換できるよ
  • seed 変更だと大きく変わってしまうけど、小さい重みで補正かければ構図を維持したまま絵に微調整を加えられるよ

意味の足し算・引き算

Stable Diffusion では、内部的に以下の 2 ステップの処理を行うことでテキストを画像に変換している

  1. テキストをベクトル(数の組)に変換する
  2. ベクトルを画像に変換する

ところで、以下の動画を見てほしい
https://www.youtube.com/watch?v=sK3HqLwag_w

テキストをベクトルにすることで、意味の世界で足し算や引き算が実現できている。
これを Stable Diffusion に応用したらどうなるだろうか[1]

以下では、プロンプトの計算ができるように機能追加したStable Diffusionを用いる。
機能追加の手順は記事後半を参照のこと。

たとえば、以下のような式が考えられる。

  • ピラミッド + (日本 - エジプト) = 日本風のピラミッド?
エジプトを引く理由

ピラミッドにはエジプト要素が含まれているため、エジプト成分を引き算しないと、エジプトと日本双方に含まれるものを足しすぎになってしまう。

日本 - エジプト を計算することで、"日本とエジプトの差"あるいは"エジプト要素を日本要素に変換するために必要な差分"という意味を取り出すことができる。

そのため、補正プロンプトを考える場合は、差分を前提に組み立てると成功しやすい。
また、実装の項にも記載しているが、今回の実装では差分を取ることを前提としている。

実際に、pyramid というプロンプトと、pyramid + (japan - egypt) というプロンプトで画像生成してみると、以下のように寺が出力された。
prompt: pyramid
prompt: pyramid
prompt: pyramd + (japan - egypt)
prompt: pyramd + (japan - egypt)

ピラミッドからエジプトを引くことで、宗教施設・墓・建造物のような要素だけが残り、そこに日本要素を足したために寺になったと解釈できる。

しかし、寺の画像がほしいだけなら最初から寺(prompt: temple)を指定すればいいだろう。

プロンプトを計算するメリットとして、意味の足し引き度合いを調整できる点がある。
先程の式を以下のように修正して、20%だけ日本風のピラミッドや、50%だけ日本風のピラミッドを出力してみよう。

  • ピラミッド + 0.2 * (日本 - エジプト)
  • ピラミッド + 0.5 * (日本 - エジプト)

prompt: pyramd + 0.2(japan - egypt)
prompt: pyramd + 0.2(japan - egypt)

prompt: pyramd + 0.5(japan - egypt)
prompt: pyramd + 0.5(japan - egypt)

20%の方は、日本の公園にありそうな小さなピラミッドになった。
また、50%の方は、寺になりつつもピラミッドの形がよりはっきり残るようになった。
この重みを細かく調整することで、エジプト要素と日本要素のどちらを強く出すか調整したり、更に式を改変してアメリカ要素をちょい足ししたりできる。

面白い応用はいろいろあると思うが、以下では画像の調整を例にあげる。

画像生成への応用

画風変換

現状の Stable Diffusion で綺麗なアニメ風イラストを出力するのは難しい[2]。そこで、比較的出しやすい綺麗な油絵をベースにできないか考えてみよう。

次の式から始める。
綺麗な油絵 - 油絵 + アニメ塗り = 綺麗なアニメ塗りのイラスト (これを式Aと置く)

調整を行うため、補正度合いを表す重み α を導入する
綺麗な油絵 + α(アニメ塗り - 油絵)

  • α = 0 のときは元の綺麗な油絵
  • α = 1 のときは式 A に一致する。
  • α を 0 ~ 1 まで少しずつ変化させると、絵も少しずつ油絵からアニメ塗りに変わっていく。

実際に、seedを固定した状態で α を変化させていくと、以下のようになった。

※ α = 0 ~ 0.4 まで、0.01 刻みで 40 枚の画像を生成し動画化

動画では省いたが、ピラミッドの例とは異なり α = 0.5 あたりから絵に破綻が見られ、0.8 を超えるとそもそも人物すら消えてしまった。
良い結果を得るにはもう少し計算式に工夫が必要なのだろう。

逆に、アニメ風イラストから油絵に寄せていった例はこちら。

途中で急激に変化する箇所があるが、どちらも全体の構図は維持されたまま画風や顔の描き方が変わる様子が見てとれる。

惜しい画像の調整

Stable Diffusion で人物の画像を生成すると顔の周りがなかなか安定しないため、乱数シードを変えながらガチャをひたすら回す必要がある。
しかし、乱数シードを変えると絵全体が大きく変わってしまう。
前項で示したように、重みを小さくしたベクトルを足せば、元の画像の構図や特徴を残したまま変えたい部分に微妙な変化を加えることができる。

以下は、とあるプロンプトで重み0.5の補正を加えた場合に得られた画像である。


重み0.5で生成

右下が恐ろしいことになっているのはさておき、左側の人物は悪くない。しかし、腕、特に手首周りのつながりに違和感がある。
ここで、重みを0.45 ~ 0.55 の範囲で変えながら再生成したところ、重み0.49で以下のように改善された。

この結果から、惜しい画像が生成された際に、プロンプト再構成や手での描き直し以外に、重みの微調整による修正という手段も取れることがわかる。

もし、崩れた顔を補正する差分、のような便利な概念をプロンプト式で表現できれば、人物画像生成が捗るようになるだろう。

実装

Stable Diffusion の scripts/txt2img.py 内の model.get_learned_conditioning でプロンプトをベクトルに変換している。そのため、複数のプロンプトについて、このメソッドでベクトル化し、重みを加味して足し算すればよい。

感想とか

  • ガチャよりは効率がいいかもしれないが、補正度合いを変えて 100 通りも生成すれば 1 時間かかり、自宅 PC で回すにはなかなかつらい
  • 良い補正式を見つけたら、ぜひコメントやTwitterのリプ等で教えてほしい

おまけ

連続変化画像の出力と動画化

紹介した動画は ffmpeg を使って以下の手順で作成している。

# 連番画像を動画化する。今回は-r 4(毎秒4フレーム)を指定
ffmpeg -r 4 -i %05d.png -vcodec libx264 -pix_fmt yuv420p -r 4 out.mp4

# 毎秒4フレームではカクカクなので、デモ用に60フレームに補完
ffmpeg -i .\out.mp4 -crf 10 -vf "minterpolate=fps=60:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" out60.mp4

# そのままだと最後のフレームが一瞬出て動画が終わってしまうため
# 最後に2秒ほど止める
ffmpeg -i out60.mp4 -vf tpad=stop_mode=clone:stop_duration=2 result.mp4

参考

プロンプトの計算や画像生成AIの仕組みについて、なんとなく理解するにあたってちょうどいい動画をいくつか紹介しておく[3]

word2vec (言葉の足し算・引き算) をクイズ化した動画。プロンプト計算の感覚を掴むのに良い

https://www.youtube.com/watch?v=q_pqMgFWyUk

Stable Diffusion と似た仕組みの DALL-E 2の解説。プロンプトをベクトル化するCLIPはStable Diffusionにも使われている

https://www.youtube.com/watch?v=8Io3MudjOGg

今回紹介する中では一番詳しい動画。単語のベクトル化の仕組みや、なぜ足し算・引き算ができるのかについて説明している

https://www.youtube.com/watch?v=0CXCqxQAKKQ&t=1101s

https://www.youtube.com/watch?v=jlmt4nY0-o0

脚注
  1. 足し算・引き算は近似的に成り立つだけで、厳密に成立しているものではない。後述する実例で効果があるようにみえることから、実用上成り立つ、ぐらいの意味合いで考えてもらえるとありがたい ↩︎

  2. 後日アニメ版モデルがリリース予定とのこと ↩︎

  3. 本当にちゃんと知りたければ論文や英語の解説記事にあたるべきだが、応用するだけならそこまでする必要はないし、必要な人はすでにやっていることなのでここでは紹介しない ↩︎

Discussion