見出し画像

Stable Diffusionを使ったイラスト作成の記録(18) ~ ネガティブプロンプトのエンコーディング ~

前回の記事

シリーズ一覧

Layered Diffusion Pipelineを使うためのリンク集

今回やること

前回の記事では、embeddingテンソル全体に係数を掛けたり、全ての行列要素を同じ値にしたりしましたが、今回はembeddingの中身をさらに細かく分析していきます。

前々回の記事で説明したように、Stable Diffusionのプロンプトのembeddingは、各トークンごとに対応する数値ベクトルの集合になっています。そして、トークンはプロンプトの文字列に対応するものに加えて、開始トークン(<|startoftext|>)、終了トークン(最初の<|endoftext|>)と残りのトークン(残りの<|endoftext|>)があります。

再掲)トークナイザーの出力

今回は、このプロンプトの文字列に対応していない開始トークン、終了トークン、残りのトークンの3つについて、値を書き換えて画像生成を行います。

各トークンのembeddingに0.5を掛ける

開始トークン、終了トークン、残りのトークンの3つのembeddingベクトルにそれぞれ0.5を掛けて値を半分にして画像生成をしてみます。使用するネガティブプロンプトは空文字列("")と"monochrome"の2種類です。

← 空文字列  | monochrome →

赤枠で示した開始トークンを変化させた画像に、明らかに大きな変化が起きました。また、終了トークンを変化させた画像では、ごくわずかな変化しか起きていません。

テキストエンコーダーの仕様上、開始トークンのembeddingは、どんなプロンプトを与えても、プロンプトの内容に関わらず、全く同じ値が生成されます。それに対し、終了トークンや残りのトークンはプロンプトによって異なる値が生成されます。

このことから、開始トークンには訓練画像に含まれる共通的な要素が紐づけられていて、その中には人間が見て画像が乱れのない画像として認識されるのに必要な要素が含まれているため、開始トークンを変化させると画像が大きく乱れてしまい、それがネガティブプロンプトとして効果的なのではないかと考えています。

各トークンのembeddingを0にする

0.5を掛ける(半分にする)代わりに、完全に0にするとどうなるかも、同様に試してみました。

← 空文字列  | monochrome →

開始トークンを0にした画像はさらに大きく変化して、特に空文字列をネガティブプロンプトに使った画像では、人物の形状を維持することができなくなってしまいました。しかし、"monochrome"をネガティブプロンプトに使った画像は、人物の形状を辛うじて維持しています。

この2つの違いは、2つのプロンプトの差異に当たるプロンプトの文字列に対応するトークンの有り無しが影響を与えている可能性があります。

トークンの数を増やしてみる

トークンの有り無しが画像生成に影響するのならば、トークンの個数も影響を与える可能性があります。そこで、"monochrome"を繰り返し並べて変化が起きるかを試しました。

繰り返す回数は、上から1回、10回、30回、50回、70回です。また、embeddingに対する操作は、左から、開始トークンのembeddingの要素を0にする、要素の値を半分にする、全てのトークンのembeddingの要素の値を半分にする、の3通りです。

"monochrome"を1回、10回、30回、50回、70回と繰り返す

トークンを繰り返すことで、全ての操作において画像に変化が見られました。

列同士で比較すると、真ん中の列が左の列と右の列の両方の要素を含む、中間的な画像が生成されていることが確認できます。(帽子の色と形状によく表れています。)

"monochrome"を1回、70回と繰り返す

最初と最後だけ取り出して比較すると、開始トークンのみを変化させた方(左列と中列)が、全トークンを変化させたもの(右列)よりも、トークンの数を増やした時の変化が大きいことが見て取れます。(構図と色数に注目してください。)

これは、右列において、プロンプトに与えた変化が生成画像に与える影響が、embeddingの値が半分になったことで、減少したことを示唆していると考えられます。

なお、左列でトークンを繰り返したことで色数が増えたことは、"monochrome"の意味(モノクロ、白黒)をネガティブプロンプトとして使っていることに対応している可能性があります。ただし、中列と右列との比較では、右列の方がよりカラフルに見えることに留意が必要だと思われます。

開始トークンの係数を変化させてみる

上記では開始トークンのembeddingに0.5と0.0をそれぞれ掛け算しましたが、他の係数を試して比較するとどうなるでしょうか。以下は、上から順に-2.0, -1.0, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.6, 2.0, 2.4, 2.8と変化させて比較した画像です。

左から、空文字列、空文字列で開始トークンのみ単位プロンプトに置き換えたもの、"monochrome"、"monochrome"で開始トークンのみ単位プロンプトに置き換えたものを使用しています。

開始トークンの係数を変化させる

係数を変化させると、対応して画像が変化します。詳しく見ると、赤枠で囲った画像(係数0.6と1.6に対応)のところで、画像に大きな変化が見られます。

終了トークンと残りのトークンの係数を変化させてみる

終了トークンはあまり影響がなかったため、残りのトークンと一緒にして分析します。前節と同様に係数を変化させますが、使用する係数は少し変えて、上から順に、-2.0, -1.6, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 1.0, 1.6, 2.2, 2.8を使用します。

左から、空文字列、空文字列で終了トークンと残りのトークンを単位プロンプトに置き換えたもの、"monochrome"、"monochrome"で終了トークンと残りのトークンを単位プロンプトに置き換えたものを使用しています。

終了トークンと残りのトークンの係数を変化させる

まず目につくのは、単位プロンプトで置き換えた画像(左から2列目と4列目)が、開始トークンの時とは違い、負の係数で画像が崩壊しないところです。なお、正の係数が大きくなった場合に画像が潰れてしまう点は類似しています。

次に、単位プロンプトで負の係数で画像が成立するのと呼応するように、単位プロンプトを使わない画像(左から1列目と3列目)で負の係数の範囲にきれいなイラストが生成されている点です。特に、赤枠で印をつけた -0.4の当たりからマイナス側(上側)がきれいに出力されています。

そのほか、単位プロンプトを使わない画像(左から1列目と3列目)では2.2の当たりに転換点が見られることと、単位プロンプトを使う画像(左から2列目と4列目)では、0.0を境に大きな変化が見られることが特筆されます。

開始トークン以外のトークンの係数を変化させてみる

終了トークンと残りのトークンだけでなく、プロンプトも含めて開始トークン以外の全てを変化させたらどのようになるでしょうか。さらに、開始トークンが異なる場合はどうなるでしょうか。

前節と同様に、係数は上から順に、-2.0, -1.6, -1.2, -1.0, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 1.0, 1.6, 2.2, 2.8を使用します。また、全てでネガティブプロンプトに"monochrome"を使用し、左から1列目は終了トークンと残りのトークンを変化させ、2列目は開始トークン以外の全てを変化させます。3列目は中列の変化に加えて、開始トークンを0.6倍しました。最後に4列目は全てのトークンを変化させています。

開始トークン以外の係数を変化させる

左から1列目と2列目を比較すると、負の係数の範囲ではほとんど同じような画像が生成されていますが、-0.4より大きい(下側)になると画像に差が生まれてきています。

3列目の画像は1列目2列目とは差があるものの、係数の変化に伴う画像の変化の方向性は類似性が見られます。それに対し4列目の画像では、係数が1.0を超えるあたりから、画像の変化の方向性が急に変わっているのが観察されます。

まとめ

開始トークンの変化は画像に大きな変化を与える

開始トークンのembeddingは、どんなプロンプトでも同一のembeddingベクトルが生成されます。ネガティブプロンプトで開始トークンのembeddingを変化させると、生成画像に非常に大きな影響を与えることができます。

注)通常、機械学習において、このような無駄な特徴量を入力に含めることは、学習の効率を低下させると考えられますが、ネガティブプロンプトというテクニックで、逆に無駄な特徴量を活用することができることは、新しい発見ではないかと思います。誰かこの分野に詳しい人がいたら、コメントなどで教えてくださると大変ありがたいです。

開始トークンとそれ以外のトークンは係数の変化に異なる反応をする

開始トークンの係数を変化させる場合は正の値の範囲でのみ興味深い変化が起きたのに対し、それ以外のトークンの係数を変化させる場合は負の値の範囲でも興味深い変化がおきました。

このことから、開始トークンとそれ以外のトークンは、係数の変化に対し異なる反応を返すと考えられるため、それぞれを別々にコントロールすることができるようなライブラリ設計をすることが望ましいと考えられます。

開始トークンのembeddingの種類

開始トークンはどのようなプロンプトでも常に同じembeddingベクトルにエンコードされるため、これまで確認されているembeddingベクトルは、テキストエンコーダーから出力されるものと、単位プロンプトと、その2つに係数を掛けたものしかありません。

そのため、それ以外のベクトルが与えられた時にどのような画像が生成されるかは未知数です。(UNetにおける共有パラメータの構成に依存すると考えられます。)

この記事が気に入ったらサポートをしてみませんか?