[基礎] "ノイズ" の正体 (書きかけ)

ComfyUI や Stable Diffusion を使う上で重要なことの一つは、Stable Diffusion やその基礎になる拡散モデルにおける "ノイズ" を感覚的に捉えられること、でしょう。 この "ノイズ" はほとんどの場合、『ホワイトノイズっぽいざらざらした目に見えるノイズ』や統計で説明されます。 しかし私は、最初はもっと具体的な存在として見た方が理解しやすいと考えています。

そんなわけでこのページでは、ノイズの理解の前提となる画像の見方、ノイズの正体、拡散プロセスと画像生成の関係について、他のサイトではあまり見ない観点で解説します。 例によって数式は最小限ですが、義務教育レベルの数学を超えた範囲にも少し入ります。 また用語に関しては、大元の diffusion model や画像生成界隈で使われているものではなく、ComfyUI のユーザから見たものを使用します。

まとめ
  • 一つの画像は (例えば 16384 次元空間内の) 一つの点である。
  • ノイズ = ランダムウォークした画像の位置 - 元画像の位置
  • ノイズの大きさ (量) は、バルクで見た統計的な量 (標準偏差) で表す。
  • KSampler の順方向では、点が一回だけ動く。 標準偏差が大きくなる。
  • KSampler の逆方向では、点が steps 回動く。 標準偏差が小さくなっていき、最終的に 0 になる。
  • 順方向、逆方向それぞれで、点がもぞもぞ動くと同時に標準偏差の円が大きくなったり小さくなったりする様子が想像できるようになると楽しい。

目次

画像 = とある点

基本的で重要な考え方があります。 『画像は多次元空間内の点である』ということです。

Stable Diffusion 関連の論文には、直接的にはこのことについて書かれていません。 画像処理では古くから広く使われている考え方で、あまりに基本的すぎるからです。 しかし関連する分野でも、例えば三次元 CG ではほとんど使われていない考え方であったりもします。 そのため、Stable Diffusion で初めてこの領域に興味を持った人 (例えばゲーム業界のテクニカルアーティスト、つまり CG の技術や理論に詳しい人) が、Stable Diffusion を起点に VAE や拡散モデルなどを調べていっても、なんとなく分からない、などということになったりします。

Stable Diffusion が使用している VAE では、RGB 3 チャネルの 8 * 8 ピクセルは、4 チャネルの 1 * 1 latent 画素になります (注)。 512 * 512 の大きさの pixel 画像は、16384 次元 (4 * 64 * 64 次元) の latent 空間の中の 1つの点、ということになります。 2048 * 2048 なら 262144 次元空間内の点です。 数式中ではベクトルとして、太文字で x (画像一般)、xt (タイムステップや時刻 t での画像)、z (latent 画像であることを強調する場合) などと表記されます。

説明の都合上、ここから先は二次元平面で考えます。 二次元でも 262144 次元でも、起きていることの本質は変わりません。

上の図では、いくつかの画像が平面上に描かれています。 その点を目に見える画像として表すとこうなる、というイメージです。 図では数個の画像しか描かれていませんが、実際には描かれていない場所にも、masterpiece だったりノイズ混じりだったり単色ベタだったりと様々な、なんらかの画像があり、無限の平面全体をびっしりと埋め尽くしています。 ただし VAE の設計により、意味のある (= 学習に使われたような) 画像は原点付近に集まるようになっています。

KSampler に入力された元画像も一つの点です。 例えば EmptyLatentImage で作った空の画像は、latent 空間の原点にあります。 KSampler はまず、元画像に対して denoise で指定された量のノイズを加えた後、同量のノイズを少しずつ取り除いていきます。 この過程で、画像を示す点が動いていきます。 詳しくは次節以降で説明します。

順方向プロセス

ここからは、一つの画像にノイズを加える 『順方向プロセス』 について見ていきます。 KSampler が最初に行う処理にあたります。

(上図左) ノイズを加えるということは、画像を示す点の位置が変わるということです。 点の位置が変わると画像の内容も変わります。 ノイズを加えたからと言って、目に見えるノイズが増えるとは限りません。 目に見えるノイズが増える場合もあれば、減る場合もあります。 被写体が変わったり画風が変わったりすることもあります。

(上図中央) ノイズはランダムです。 同じ元画像であっても、ノイズを加えられた先は毎回違います (実際には KSamplerseed を元にしてノイズが決定されます)。 Stable Diffusion で与えるノイズは、その基盤であり様々な物理現象のモデルでもある『拡散モデル』でよく使われている『正規乱数』 (正規分布を持つ乱数) です。

(上図右) ここまでは個別の画像にノイズが与える影響を見てきましたが、拡散モデルの理論では多くの場合、確率・統計的に考えます。 正規分布の広がりを表す量『標準偏差』は、Stable Diffusion ではノイズの大きさを表す量として使われます。

繰り返しますが、ノイズの量である標準偏差は図右で、具体的なノイズは図左です。 この、ノイズ (正規乱数) を加える、という操作を繰り返すのが、『順方向プロセス』です。

繰り返しノイズを加えられた先に行き付く場所は、元の点から遠い所かもしれませんし、近い所かもしれません。 ただ、行き付く場所を確率的に見ると、その分布もやはり正規分布になります (ただし分布は、各回に加えた個々のノイズよりも広い)。 つまり、細かいノイズを加え続けるという操作を、適切な大きさのノイズを一回だけ加えるという操作で置き換えることができるということです。 KSampler が最初に行うのがこれで、schedulerdenoise から計算した標準偏差に相当するノイズを、入力された latent 画像に対し一回で加えます。 また繰り返しますが、ノイズの量である標準偏差は図右で、KSampler が加える具体的なノイズは図左です。

単一試行だとわかりにくいですが、上図中央 (32試行) や右 (分布) を見ると、このプロセスがまさに『拡散』(散逸) の過程であることが理解できると思います。

逆方向プロセス

Stable Diffusion における逆方向プロセスは、『ある量のノイズが加えられたと分かっている画像から、元の画像に辿りつこうとするプロセス』です。 ここで分かっているのは、現在のノイズ混じりの画像と、加えられたノイズの量 (KSamplerschedulerdenoise から計算した標準偏差) です。 元の画像 (KSamplerlatent_image) の位置は分かりません。

元画像の位置は分からないものの、ありそうな位置の確率的な分布は実は分かります。 都合の良いことに、現在の画像を中心とした正規分布になるのです。 そしてその標準偏差は、加えられたノイズの標準偏差と同じになります。

逆方向プロセスで働くのは乱数発生器ではなく、U-Net です。 U-Net は三種の入力 (現在の画像の位置、元画像がありそうな位置の分布の標準偏差 (に相当する時刻)、プロンプト) を元に、ノイズを推定します。 ノイズが分かれば、逆に辿って元画像の位置に戻れます。 とはいえ一回では元画像にうまく戻れません。 U-Net で推定できるのは、加えられたであろうノイズの平均値だからです。 そこで、複数ステップ (KSamplersteps) に分けて、少しずつ戻していきます。 少し戻す = 少し元画像に近づく = 少しノイズを除去する = 少し標準偏差が小さくなる、です。 最終ステップを終えると標準偏差は 0 になります。 でもなぜか、別の画像に辿りついてしまいました (主にプロンプトのせい)。 というのが画像の生成過程で起きていることです。