勾配降下党青年局

万国のグラーディエントよ、降下せよ!

ネガティブプロンプトの理論とPerp-Neg

 ネガティブプロンプトに関する面白そうな論文を見つけたので、ちょっと読んでみますが、その前にネガティブプロンプトの理論的な背景について自分なりの解釈でまとめてみます。
arxiv.org
いきなりですが、拡散モデルはスコアベースモデルと解釈できて、ノイズを予測するモデルは推定値がスコアと比例します。
 \nabla_x \log p(x) = -\frac{1}{\sigma_t}\hat{\epsilon}(x,t)
このスコアというのは、今回は理解する必要はないです(私もそんな理解してないし)。確率分布で一番難しい分配関数(足して1になるための積分)が計算に必要ないので導出しやすいという性質を持ち、拡散モデルの背景の一つになっています。

分類器誘導(Classifier guidance)

拡散モデルの生成に条件を付けるため、分類器誘導と呼ばれる手法では、以下のような計算により与える条件に近づくようにスコアを誘導します。
 \nabla_x \log p_{\gamma}(x|c) = \gamma\nabla_x \log p(c|x) + \nabla_x \log p(x)
第一項のスコアは何らかの分類器を用いて計算します。たとえばおっぱいが小さい子を生成したい場合は、おっぱい分類器を用いて、c="おっぱいが小さい"に近づくようなxの方向を求めてそっちに誘導します。ここで条件をどれだけ重視するかを\gammaという値で決めていて、大きくすれば条件に忠実になるが生成画像の多様性が低くなります。
この分類器誘導という手法は拡散モデルの他に別の分類器を作らなければいけないこと、さらにノイズに頑強なモデルである必要があることから、使いにくいです。そこで拡散モデルそのものに分類器としての機能を持たせるのが分類器無し誘導です。

分類器無し誘導(Classifier free guidance)

 
\begin{eqnarray}
\nabla_x \log p_{\gamma}(x|c) &=& \gamma\nabla_x \log p(c|x) + \nabla_x \log p(x) \\
&=& \gamma\nabla_x \log \frac{p(x|c)p(c)}{p(x)} + \nabla_x \log p(x) (\text{ベイズの定理})\\
&=& \gamma(\nabla_x \log p(x|c) - \nabla_x \log p(x))  + \nabla_x \log p(x) \\
&&(x\text{の微分なので}p(c)\text{に関する項は消える})
\end{eqnarray}

これがCFGの式です。\gammaがCFG scaleとかguidance scaleと呼ばれるものです。これを使うためには、条件付きの推定と、無条件の推定の両方を学習する必要があります。txt2imgモデルでは、学習中に10%くらいの確率でテキストを空文にするだけでいいらしいです。

ネガティブプロンプトのCFG

では複数条件になるとどうなるでしょうか?とりあえず二条件にしてみます。

 
\begin{eqnarray}
\nabla_x \log p_{\gamma}(x|c_1,c_2) &=& \gamma\nabla_x \log p(c_1,c_2|x) + \nabla_x \log p(x) \\
&=& \gamma\nabla_x \log p(c_1|x)p(c_2|x) + \nabla_x \log p(x) (\text{二つの条件が独立なら・・・})\\
&=& \gamma\nabla_x \log \frac{p(x|c_1)p(c_1)}{p(x)}\frac{p(x|c_2)p(c_2)}{p(x)} + \nabla_x \log p(x) (\text{ベイズの定理})\\
&=& \gamma(\nabla_x \log p(x|c_1) - \nabla_x \log p(x))  \\ && +\gamma(\nabla_x \log p(x|c_2) - \nabla_x \log p(x)) \\ && + \nabla_x \log p(x) 
\end{eqnarray}

3つ4つと増やしても項がどんどん増えるだけで式の形は同じです。webuiのAND構文を使えばこれを使った生成ができます。\gammaについて、二条件で別々のものを使うことも考えられます(この辺の理論的裏付けは分からない)。たとえば1つ目の条件を目標の条件c_{\mathrm{pos}}、2つ目の条件の係数に\gammaではなく-\gammaを使えば、望ましくない要素(c_{\mathrm{neg}})を排除するような式が出てきます。
 
\gamma(\nabla_x \log p(x|c_{\mathrm{pos}}) - \nabla_x \log p(x))  - \gamma(\nabla_x \log p(x|c_{\mathrm{neg}}) - \nabla_x \log p(x))  + \nabla_x \log p(x) \\
=\gamma(\nabla_x \log p(x|c_{\mathrm{pos}}) - \nabla_x \log p(x|c_{\mathrm{neg}})) + \nabla_x \log p(x)

これがほとんどネガティブプロンプトになるわけですが、このままだと二つの条件と無条件の3つのノイズ推定を行う必要があります。そこで上の式を以下に置き換えます。
 \gamma(\nabla_x \log p(x|c_{\mathrm{pos}}) - \nabla_x \log p(x|c_{\mathrm{neg}})) + \nabla_x \log p(x|c_{\mathrm{neg}})

これがAUTOMATIC1111氏が実装したネガティブプロンプトです。これなら二つのノイズ予測で済みます。\gammaが十分大きければほとんど同じ値になるからおっけーってことなんでしょうね。実装的にも上の単一条件での式の無条件部分にネガティブプロンプトを入れるだけなので、めちゃくちゃ楽です。

予測対象ごとの式

  • ノイズ予測モデルの場合

ノイズの予測がスコアと比例することから、すぐに以下の式がでてきます。
 \hat {\epsilon} = \hat {\epsilon} (x|c_{\mathrm{neg}}) + \gamma (\hat {\epsilon} (x|c_{\mathrm{pos}}) - \hat {\epsilon} (x|c_{\mathrm{neg}}))

  • ノイズのない画像を予測するモデルの場合


\begin{eqnarray}
\hat {x} &=& \frac{1}{\alpha_t}(x - \sigma_t \hat{\epsilon}) \\
&=& \frac{1}{\alpha_t}(x - \sigma_t (\hat {\epsilon} (x|c_{\mathrm{neg}}) + \gamma (\hat {\epsilon} (x|c_{\mathrm{pos}}) - \hat {\epsilon} (x|c_{\mathrm{neg}}))))\\
&=& \frac{1}{\alpha_t}(x - \sigma_t \hat {\epsilon} (x|c_{\mathrm{neg}})) + \gamma(\frac{1}{\alpha_t}(x - \sigma_t\hat {\epsilon} (x|c_{\mathrm{pos}})) - \frac{1}{\alpha_t}(x- \sigma_t\hat {\epsilon} (x|c_{\mathrm{neg}}))) \\
&=& \hat {x} (x|c_{\mathrm{neg}}) + \gamma (\hat {x} (x|c_{\mathrm{pos}}) - \hat {x} (x|c_{\mathrm{neg}}))
\end{eqnarray}

  • velocityを予測するモデルの場合、


\begin{eqnarray}
\hat{v}&=&\sigma_t \hat{x} - \alpha_t \hat{\epsilon} \\
&=& \sigma_t(\hat {x} (x|c_{\mathrm{neg}}) + \gamma (\hat {x} (x|c_{\mathrm{pos}}) - \hat {x} (x|c_{\mathrm{neg}}))) - \alpha_t(\hat {\epsilon} (x|c_{\mathrm{neg}}) + \gamma (\hat {\epsilon} (x|c_{\mathrm{pos}}) - \hat {\epsilon} (x|c_{\mathrm{neg}}))) \\
&=& \sigma_t \hat{x} (x|c_{\mathrm{neg}}) - \alpha_t\hat {\epsilon} (x|c_{\mathrm{neg}}) + \gamma(\sigma_t \hat{x}(x|c_{\mathrm{pos}}) - \alpha_t\hat {\epsilon} (x|c_{\mathrm{pos}})-(\sigma_t \hat{x}(x|c_{\mathrm{neg}}) - \alpha_t\hat {\epsilon} (x|c_{\mathrm{neg}})))  \\
&=& \hat {v} (x|c_{\mathrm{neg}}) + \gamma (\hat {v} (x|c_{\mathrm{pos}}) - \hat {v} (x|c_{\mathrm{neg}}))
\end{eqnarray}

となり\epsilon,x,vを除いて同じ式になります。つまり予測対象に関わらずUNetの出力にそのままこの式を適用すればいいだけになります。

Perp-Neg

いよいよ論文の中身に入っていきますがそんなに分かってません。特にこの論文は3D生成のための話らしいですが、あんまり興味ないのでその辺は読んでません。複数条件の式を出すとき、さらっと二つが独立であればとか言いましたが、実際独立なわけありません。このままだとポジティブプロンプトとネガティブプロンプトの間で重なるような情報を打ち消しあってしまいます。そこでPerp-negではポジティブプロンプトに対して垂直なスコアを利用します。図としてまとめてみたら割と何やってるかは分かったので、貼り付けます。

適当に書くと最終的な式は以下のようになります。
 \hat {\epsilon} = \hat {\epsilon} (x|c_{\mathrm{neg}}) + \gamma (\hat {\epsilon} (x|c_{\mathrm{pos}}) - \hat {\epsilon} (x|c_{\mathrm{neg}})^{\perp})
ただし論文には複数のネガティブプロンプトをそれぞれ重み付けしたものを使っています。
ネガティブプロンプトの改良ということで話は面白いんですが、AUTOMATIC1111氏のネガティブプロンプトは、ポジティブとネガティブの二つのノイズ予測のみで計算できるという大きな利点があります。3D生成のような複雑なタスクではなく単純な画像生成なら、多少理論的な厳密性を無視して効率のよい計算式で何枚も生成する方がよさそうなのがつまらないですね。
WebUIの拡張スクリプトを作ってみました。できたばっかりなのでちゃんとできてるか分かりませんけど。
GitHub - laksjdjf/Perp-Neg-stablediffusion-webui