AI絵画の安定拡散研究(7) 安定拡散の動作原理を理解するための記事


皆さんこんにちは、雨でも晴れでも私は元気です。


この記事は次のような人に適しています。

  • AI 描画の基本原理を理解したい友達。

  • 安定拡散AI描画に興味のある友人。


この号の内容:

  • 安定拡散でできること

  • 普及モデルとは

  • 普及モデル実現原理

  • 安定拡散 潜在拡散モデル

  • 安定拡散テキストが画像生成に与える影響

  • 安定拡散クロスアテンション技術

  • 安定拡散ノイズスケジュール技術

  • 安定拡散ヴィンセントグラフ最下層動作のデモ


1. 安定拡散でできることは何ですか?


Stable Diffusion 統合パッケージのインストール、ControlNet プラグインの導入、sd モデルのインストールと使用、および Vincent ダイアグラム機能の導入に関する以前の記事を読んだ友人は、安定拡散が何を行うのかを明確に理解する必要があります。


新しい友達について、さらに詳しく知りたい場合は、次のサイトにアクセスしてください。

AI絵画安定拡散研究 (1) sd統合パッケージ v4.2版 インストール手順
AI絵画安定拡散研究 (2) sdモデル ControlNet1.1 導入とインストール
AI絵画安定拡散研究 (3) sdモデルの種類紹介と詳細なインストールと使い方
AI絵画安定拡散研究 (4) sd文勝図機能の詳細説明 (前編)
AI絵画安定拡散研究 (5) sd文勝図機能詳細説明 (後編)
AI絵画安定拡散研究 (6) sdプロンプトワードプラグイン


ここでもっと率直に言います。SD はテキストから画像へのモデルであり、指定されたテキスト プロンプト (テキスト プロンプト) を通じて、テキストに一致する画像を生成できます。


2. 普及モデルとは


安定拡散は潜在的な拡散モデル (拡散モデル) であるということをよく耳にします。

それでは、まず拡散モデルとは何なのかを理解してみましょう。

なぜ拡散モデルと呼ばれるのでしょうか? なぜなら、その数式は物理的な拡散現象に非常によく似ているからです。


1. 順拡散

次のようにモデルをトレーニングするとします。

ここに画像の説明を挿入


上図に示すように、これは前方拡散のプロセスであり、トレーニング画像に徐々にノイズが追加され、最終的には完全にランダムなノイズ画像になり、最終的にはノイズ画像に対応する初期画像を識別できなくなります。


このプロセスは、透明な水の入ったグラスに滴るインクのようなもので、ゆっくりと広がり、最終的には透明な水の中に均一に分布します。最初に水の入ったカップの中心から滴下したのか、それとも水のカップの中心から滴下したのかを判断することは不可能です。エッジ。これがディフュージョンの名前の由来です。


2. 逆拡散


逆拡散の考え方は、ノイズマップを入力して逆拡散(Reverse Diffusion)し、ランダムなノイズマップから鮮明な画像を生成する逆の処理を上記の処理で得させます。


逆拡散の観点からは、特定の画像にどの程度の「ノイズ」が追加されているかを知る必要があります。


答えを知る方法は、追加されたノイズを予測するためにニューラル ネットワークをトレーニングすることです。これは SD ではノイズ予測子 (Noise Predicator) と呼ばれ、その本質は U-Net モデルです。


トレーニングのプロセスは次のとおりです。

(1)、トレーニング画像(猫の写真など)を選択します

(2)、ランダムノイズマップを生成

(3)、この画像に複数回のノイズを追加し続けます。

(4) ノイズ予測器をトレーニングし、どの程度のノイズが追加されるかを予測し、ニューラル ネットワークを通じて重みをトレーニングし、正解を示します。


ここに画像の説明を挿入


逆拡散トレーニングの焦点は、下図のノイズ予測子 (Noise Predicator) です。これは、毎回減算する必要があるノイズを取得するようにトレーニングでき、毎回どれだけのノイズを減算する必要があるかを予測します。鮮明な画像を復元するという目的を達成します。


3. 普及モデルの実現原理


普及モデルの成功はどこからともなく突然現れたものではなく、突然人々の視界に現れました。実は2015年にも誰かが同様のアイデアを提案しており、2020年にはついに拡散モデルの生成技術を提案しました。


拡散モデルの導出式は以下のとおりです。

ここに画像の説明を挿入


ここに画像の説明を挿入


ここに画像の説明を挿入


より詳細な原則:

参考:普及モデルの詳細な原​​理 + コード


これまでの説明で、拡散モデルとは何かは理解できたと思いますが、これは安定拡散の仕組みではありません。

その理由は、上記の拡散プロセスは画像空間内で完了するため、モデルのトレーニングであれ画像生成のプロセスであれ、膨大な計算能力のサポートとメモリ要件が必要となるからです。


想像してください: 512 x 512 の画像 (赤、緑、青の 3 つのカラー チャネルが含まれています)、そのスペースは 786432 次元です。つまり、画像に対して非常に多くの値を指定する必要があります。したがって、単一の GPU で実行することは基本的に不可能です。


Stable Diffusion は、コンピューティング能力とメモリ要件を軽減するソリューションです。これにより、Stable Diffusion をコンシューマ グレードの GPU で実行できるようになります。


4. 安定拡散ポテンシャル拡散モデル

安定拡散 潜在拡散モデル(潜在拡散モデル)です。その方法は、高次元の画像空間で作業するのではなく、画像を「潜在空間」(Latent Space) に圧縮することです。潜在空間はイメージ空間の 48 分の 1 であるため、多くの計算量が節約され、実行速度が速くなります。


拡散プロセスは多くのステップに分かれており、各ステップのプロセスは下図に示されており、テキストの説明、隠し変数、ステップ数などの値が UNet に渡されて新しい隠し変数が生成され、これがこのプロセスにはいくつかのモデルが含まれます。

ここに画像の説明を挿入


最後のループでは、変分オートエンコーダー (VAE) を介して潜在特徴が画像にデコードされます。

ここに画像の説明を挿入


このプロセスの核となる考え方は、「画像の圧縮」であり、変分オートエンコーダー (VAE) モデルを通じて画像を極限まで圧縮します。このタイプの圧縮方法を次元削減と呼び、この次元削減レベルの圧縮は失われません。 . 重要なお知らせです。


圧縮後の画像を低次元の潜在(Latent)「画像」と呼び、U-netの入力として、潜在空間(Latent Space)内で段階的にノイズ除去を行った後、完成した低次元の「画像」となります。逆拡散も同様です。VAE デコーダを介して画像を潜在空間からピクセル空間 (Pixel Space) に変換する必要があります。


VAE は、エンコーダとデコーダの 2 つの部分で構成されます。

  • エンコーダは画像を「潜在空間」の低次元空間表現に圧縮します。

  • デコーダは「潜在空間」の表現から画像を復元します。

ここに画像の説明を挿入


次のコードは、VAE モデルの使用を示しています。ここで、load_vae は設定 init_config に従ってモデルを初期化し、事前トレーニング モデルの model.ckpt からパラメーターを読み取ります。事前トレーニング モデルの first_stage_model は以下を参照します。 VAEモデル。


from ldm.models.autoencoder import AutoencoderKL
#VAE模型
def load_vae():
    #初始化模型
    init_config = {
        "embed_dim": 4,
        "monitor": "val/rec_loss",
        "ddconfig":{
          "double_z": True,
          "z_channels": 4,
          "resolution": 256,
          "in_channels": 3,
          "out_ch": 3,
          "ch": 128,
          "ch_mult":[1,2,4,4],
          "num_res_blocks": 2,
          "attn_resolutions": [],
          "dropout": 0.0,
        },
        "lossconfig":{
          "target": "torch.nn.Identity"
        }
    }
    vae = AutoencoderKL(**init_config)
    #加载预训练参数
    pl_sd = torch.load("model.ckpt", map_location="cpu")
    sd = pl_sd["state_dict"]
    model_dict = vae.state_dict()
    for k, v in model_dict.items():
        model_dict[k] = sd["first_stage_model."+k]
    vae.load_state_dict(model_dict, strict=False)

    vae.eval()
    return vae

#测试vae模型
def test_vae():
    vae = load_vae()
    img = load_image("girl_and_horse.png")  #(1,3,512,512)   
    latent = vae.encode(img).sample()       #(1,4,64,64)
    samples = vae.decode(latent)            #(1,3,512,512)
    save_image(samples,"vae.png")

test_vae()

5. 安定拡散テキストが画像生成に与える影響


安定拡散モデルでは、プロンプトはガイダンス ベクトルを通じて U-Net を制御します。具体的には、プロンプトはテキスト埋め込みベクトル (テキスト埋め込み) にエンコードされ、他の入力とともに U-Net に渡されます。

このようにして、プロンプトは U-Net の出力に影響を与えることができ、それによってモデルが生成プロセス中に期待される結果を生成するように、つまり、プロンプトを通じて必要なグラフを生成するようにガイドされます。


安定拡散モデルでは、プロンプトは 75 ワードに制限されています。


次の図は、テキスト プロンプトがどのように処理され、Noise Predictor に入力されるかのプロセスを示しています。


ここに画像の説明を挿入


上の図によれば、このプロセスがわかります。

まず、Tokenizer (トークナイザー) が各入力単語をトークンと呼ばれる数値に変換します。

次に、各トークンは単語埋め込みと呼ばれる 768 次元のベクトルに変換されます。

最後に、単語の埋め込みは Text Transformer によって処理され、Noise Predictor によって使用できます。


1.トークナイザー

人間は単語を読むことができますが、コンピューターは数字しか読めません。そのため、テキスト プロンプトが最初に単語に変換されます。

テキスト プロンプトは、まずCLIP トークナイザーによってセグメント化されます。

CLIP は、Open AI によって開発された深層学習モデルで、あらゆる画像のテキスト説明を生成します。


以下にCLIPの具体例を示します。

ここでは、テキスト「apple」をトークン データに変換し、CLIP テクノロジーによるトレーニング用にニューラル ネットワークに入力できるようにする方法を示します。

これは、Python と OpenAI ライブラリを使用して実現されます。


(1)、依存ライブラリをインストールします

pip install torch openai

(2)、関連ライブラリのインポート

import torch import openai

(3)、CLIPモデルをロードします

model, preprocess = openai.clip.load("ViT-B/32")

(4)、テキストを入力する準備ができました

text_description = "苹果"

(5)、テキストをトークンに変換します

CLIP モデルのメソッドを使用してtokenizeテキストをトークンに変換します。

text_tokens = openai.clip.tokenize(text_description)

ここで、 は、text_tokensshape の PyTorch テンソルです(1, N)。ここで、N はテキスト記述内のトークンの数です。

この例では、「apple」が 3 つのトークンに分割されるため、N=3 になります。


(6)、トークンを表示

print(f"Tokens: {text_tokens}")

出力は次のようになります。

Tokens: tensor([[49406, 3782, 49407]])

ここでは、49406開始記号(文頭)を表し、3782「リンゴ」を表し、49407終了記号(文末)を表している。

上記の手順により、テキスト「apple」をトークンに変換しました。


追伸:

  • Stable Diffusion v1 は CLIP モデルのトークナイザーを使用します

  • Tokenizer はトレーニング中に見た単語のみをセグメント化できます

    例: CLIP モデルに「dream」と「beach」という単語はあるが、「dreambeach」という単語はないとします。

    トークナイザーは、「dreambeach」を「dream」と「beach」の 2 つの単語に分割します。

  • 1 つの単語が 1 つのトークンを表すわけではありませんが、さらに分割することは可能です

  • スペースもトークンの一部です

    たとえば、「dream beach」というフレーズは、「dream」と「[space]beach」という 2 つのトークンを生成します。

    これらのトークンは、「dreambeach」によって生成される「dream」と「beach」(beach の前にスペースなし)のトークンとは異なります。


2. 埋め込み


(1)、なぜ単語の埋め込み(Embedding)が必要なのでしょうか?

いくつかの単語は互いに非常に似ているため、この意味情報を利用したいと考えています。


例えば:

man、gentleman、guy の単語埋め込みは非常に似ているため、相互に置き換えることができます。

モネ、マネ、ドガは皆、印象派のスタイルで描きましたが、方法は異なりました。

これらの名前は非常に似ていますが、単語の埋め込み (Embedding) においては同じではありません。


(2) 埋め込みはどのように機能しますか?


埋め込みにより、入力トークンが連続ベクトル表現に変換され、テキスト内の意味情報をキャプチャできます。この例では、「リンゴ」トークンは、encode_textCLIP モデルのメソッドを通過した後に特徴ベクトルを取得します。


この固有ベクトルは高次元空間内の点であり、通常は固定次元 (CLIP モデルでは次元は 512) です。結果として得られる特徴ベクトルは、モデルの重みとランダム性により、実行ごとにわずかに異なる場合があることに注意してください。出力例は次のとおりです。


print(f"Text features: {
      
      text_features}")

出力は次のようになります。

Text features: tensor([[-0.0123,  0.0345, -0.0678, ...,  0.0219, -0.0456,  0.0789]])

ここに、単語「apple」のベクトル表現を含む形状の PyTorch テンソルがtext_featuresあります。(1, 512)ニューラル ネットワークは、トレーニングおよび予測タスクにこのベクトル表現を使用できます。


Stable diffusion v1 は Open AI のViT-L/14モデルを使用しており、単語の埋め込みは 768 次元のベクトルです。


3. テキストコンバータ


(1) なぜ必要なのでしょうかtext transformer?


embedding通過後は学習用モデルに直接入力できるのに、なぜ安定拡散embeddingではモデルの入力として変換を使用する必要があるのでしょうかtext transformer?


これは、安定拡散モデルが画像生成モデルであり、入力テキストの意味情報を理解してそれに関連する画像を生成する必要があるためです。基本的なテキスト埋め込みを直接使用すると、テキスト内の複雑な意味関係を適切に捕捉できない場合があります。テキスト トランスフォーマーを使用すると、より豊かで表現力豊かなテキスト表現が得られ、生成された画像の品質と入力テキストとの関連性が向上します。


テキスト トランスフォーマーを使用してテキストの意味情報を取得する場合、より多くのコンテキストと抽象的な概念を考慮できます

このコンバータは一般的な条件(コンディショニング)アダプタのようなものです。


(2)、テキストトランスフォーマーの変換例


「Apple」を例として説明してみましょう。

「apple」( (1, 512)shape の PyTorch テンソル) の基本的な埋め込みを取得したとします。

text_features = tensor([[-0.0123,  0.0345, -0.0678, ...,  0.0219, -0.0456,  0.0789]])

次に、このテンソルをテキスト トランスフォーマーにフィードします。

transformed_text_features = text_transformer(text_features)

テキスト トランスフォーマーの処理後、次のような新しいテンソルが得られる場合があります。

print(f"Transformed text features: {
      
      transformed_text_features}")

出力は次のようになります。

Transformed text features: tensor([[ 0.0234, -0.0567,  0.0890, ..., -0.0321,  0.0672, -0.0813]])

この新しいテンソル (形状はまだ(1, 512)) には、文脈上の関係や抽象概念など、より豊富な意味論的な情報が含まれています。

これは、安定拡散モデルが入力テキストをよりよく理解し、それに関連する画像を生成するのに役立ちます。


ご注意ください:

結果として得られる特徴ベクトルは、モデルの重みとランダム性により、実行ごとにわずかに異なる場合があります。

さらに、具体的な変更プロセスは、使用されるテキスト トランスフォーマーの構造とパラメーターによって異なります。


6. 安定拡散クロスアテンション技術


クロスアテンションは、プロンプトの言葉を通じて画像を生成するためのコアテクノロジーです。

テキスト コンバータの出力は、U-Net のノイズ プレディクタによって複数回使用されます。

U-Net は、クロス アテンション メカニズムと呼ばれる方法でこれを使用します。クロス アテンション メカニズムにより、モデルはさまざまな特徴レベルで関連する領域に焦点を当てることができるため、生成される結果の品質が向上します。プロンプトが最適なのはここです。


次のコードは、安定した拡散によって使用されるトランスフォーマー ブロックであり、クロスアテンションを実装します。

class SpatialTransformer(nn.Module):
    """
    Transformer block for image-like data.
    First, project the input (aka embedding)
    and reshape to b, t, d.
    Then apply standard transformer action.
    Finally, reshape to image
    """
    def __init__(self, in_channels, n_heads, d_head,
                 depth=1, dropout=0., context_dim=None):
        super().__init__()
        self.in_channels = in_channels
        inner_dim = n_heads * d_head
        self.norm = Normalize(in_channels)

        self.proj_in = nn.Conv2d(in_channels,
                                 inner_dim,
                                 kernel_size=1,
                                 stride=1,
                                 padding=0)

        self.transformer_blocks = nn.ModuleList(
            [BasicTransformerBlock(inner_dim, n_heads, d_head, dropout=dropout, context_dim=context_dim)
                for d in range(depth)]
        )

        self.proj_out = zero_module(nn.Conv2d(inner_dim,
                                              in_channels,
                                              kernel_size=1,
                                              stride=1,
                                              padding=0))

    def forward(self, x, context=None):
        # note: if no context is given, cross-attention defaults to self-attention
        b, c, h, w = x.shape
        x_in = x
        x = self.norm(x)
        x = self.proj_in(x)
        x = rearrange(x, 'b c h w -> b (h w) c')
        for block in self.transformer_blocks:
            x = block(x, context=context)
        x = rearrange(x, 'b (h w) c -> b c h w', h=h, w=w)
        x = self.proj_out(x)
        return x + x_in

7. 安定拡散ノイズスケジュール技術


1. 騒音スケジュールとは何ですか?

ノイズは U-Net によって複数回処理され、最終的に目的の画像が出力されます。

これらの複数の処理では、それぞれのノイズ リダクションの大きさが異なるため、スケジューラーを使用して各ノイズ リダクションの大きさを制御する必要があります (大きさは一般的に減少します)。このテクニックは と呼ばれますnoise schedule

図に示すように:


ここに画像の説明を挿入


では、なぜnoise scheduleテクノロジーを使うのでしょうか?


Stable Diffusion の生成モデルでは、U-Net はノイズのある画像から徐々に元の画像を復元するために使用されるコア コンポーネントです。ノイズ除去の振幅が複数の反復中に徐々に減少する理由は、画像の詳細と構造をより細かく復元するためです。


安定拡散のプロセスは逆拡散プロセスとみなすことができ、ノイズの多い画像から開始し、複数のステップを通じて徐々にノイズを除去して元の画像を再構成します。このプロセスでは、U-Net を使用して各ステップでのノイズ低減動作を予測します。


最初の数回の反復では、画像のノイズが多くなるため、このノイズを除去するにはより大きなノイズ除去強度が必要になります。反復回数が増加すると、画像内のノイズが徐々に減少するため、ノイズ低減の大きさもそれに応じて減少するはずです。これの目的は、過剰な平滑化や、すでに復元された画像の詳細への損傷を避けることです。


ノイズ リダクションの大きさを徐々に減らすことで、U-Net はノイズ除去プロセスをより適切に制御できるようになり、画像の詳細を維持しながらノイズを効果的に除去できます。これにより、より鮮明でリアルな画像が生成されます。


noise scheduleこの手法を説明するための Vincent ダイアグラムのコードを次に示します。

def txt2img():
    #unet
    unet = load_unet()
    #调度器
    scheduler = lms_scheduler()
    scheduler.set_timesteps(100)
    #文本编码
    prompts = ["a photograph of an astronaut riding a horse"]
    text_embeddings = prompts_embedding(prompts)
    text_embeddings = text_embeddings.cuda()     #(1, 77, 768)
    uncond_prompts = [""]
    uncond_embeddings = prompts_embedding(uncond_prompts)
    uncond_embeddings = uncond_embeddings.cuda() #(1, 77, 768)
    #初始隐变量
    latents = torch.randn( (1, 4, 64, 64))  #(1, 4, 64, 64)
    latents = latents * scheduler.sigmas[0]    #sigmas[0]=157.40723
    latents = latents.cuda()
    #循环步骤
    for i, t in enumerate(scheduler.timesteps):  #timesteps=[999.  988.90909091 978.81818182 ...100个
        latent_model_input = latents  #(1, 4, 64, 64)  
        sigma = scheduler.sigmas[i]
        latent_model_input = latent_model_input / ((sigma**2 + 1) ** 0.5)
        timestamp = torch.tensor([t]).cuda()

        with torch.no_grad():  
            noise_pred_text = unet(latent_model_input, timestamp, text_embeddings)
            noise_pred_uncond = unet(latent_model_input, timestamp, uncond_embeddings)
            guidance_scale = 7.5 
            noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)

            latents = scheduler.step(noise_pred, i, latents)

    vae = load_vae()
    latents = 1 / 0.18215 * latents
    image = vae.decode(latents.cpu())  #(1, 3, 512, 512)
    save_image(image,"txt2img.png")

txt2img()

八、安定拡散ヴィンセントグラフの基礎となる動作デモ


テキスト生成グラフのシナリオでは、一連のテキスト プロンプトを SD モデルに入力すると、画像を返すことができます。


最初のステップでは、安定拡散は潜在空間にランダム テンソルを生成します。

ランダムシードを設定することで、このテンソルの生成を制御しますこのランダム シードを特定の値に設定すると、同じランダム テンソルが得られます。これは潜在空間における私たちの写真です。しかし、それでもノイズがいっぱいです。

ここに画像の説明を挿入


2 番目のステップであるノイズ予測器 U-Net は、潜在ノイズ マップとテキスト プロンプトを入力として受け取り、ノイズを予測します。

このノイズは潜在空間にも存在します (4 x 64 x 64 テンソル)

ここに画像の説明を挿入


3 番目のステップでは、潜像から潜像ノイズ ポイントを抽出し、新しい潜像を生成します。

ここに画像の説明を挿入


第2ステップと第3ステップを所定のサンプリング回数、例えば20回繰り返す。


4 番目のステップでは、VAE のデコーダが潜像をピクセル空間に変換します。

SDモデルで最終的に得られた写真がこれです。

ここに画像の説明を挿入


参考文献:

1. 安定拡散はどのように機能しますか?

2. 安定拡散

3. 普及モデルの詳細な原​​理 + コード

おすすめ

転載: blog.csdn.net/lizhong2008/article/details/132257722