Stable Diffusion是一個文本到圖像的潛在擴散模型,由CompVis、Stability AI和LAION的研究人員和工程師創(chuàng)建。它使用來自LAION-5B數(shù)據(jù)庫子集的512x512圖像進行訓(xùn)練。使用這個模型,可以生成包括人臉在內(nèi)的任何圖像,因為有開源的預(yù)訓(xùn)練模型,所以我們也可以在自己的機器上運行它,如下圖所示。

如果你足夠聰明和有創(chuàng)造力,你可以創(chuàng)造一系列的圖像,然后形成一個視頻。例如,Xander Steenbrugge使用它和上圖所示的輸入提示創(chuàng)建了令人驚嘆的《穿越時間》。
以下是他用來創(chuàng)作這幅創(chuàng)造性藝術(shù)作品的靈感和文本:

本文首先介紹什么是Stable Diffusion,并討論它的主要組成部分。然后我們將使用模型以三種不同的方式創(chuàng)建圖像,這三種方式從更簡單到復(fù)雜。
Stable Diffusion
Stable Diffusion是一種機器學(xué)習(xí)模型,它經(jīng)過訓(xùn)練可以逐步對隨機高斯噪聲進行去噪以獲得感興趣的樣本,例如生成圖像。
擴散模型有一個主要的缺點就是去噪過程的時間和內(nèi)存消耗都非常昂貴。這會使進程變慢,并消耗大量內(nèi)存。主要原因是它們在像素空間中運行,特別是在生成高分辨率圖像時。
Latent diffusion通過在較低維度的潛空間上應(yīng)用擴散過程而不是使用實際的像素空間來減少內(nèi)存和計算成本。所以Stable Diffusion引入了Latent diffusion的方式來解決這一問題計算代價昂貴的問題。
1、Latent diffusion的主要組成部分
Latent diffusion有三個主要組成部分:
自動編碼器(VAE)
自動編碼器(VAE)由兩個主要部分組成:編碼器和解碼器。編碼器將把圖像轉(zhuǎn)換成低維的潛在表示形式,該表示形式將作為下一個組件U_Net的輸入。解碼器將做相反的事情,它將把潛在的表示轉(zhuǎn)換回圖像。
在Latent diffusion訓(xùn)練過程中,利用編碼器獲得正向擴散過程中輸入圖像的潛表示(latent)。而在推理過程中,VAE解碼器將把潛信號轉(zhuǎn)換回圖像。
U-Net

U-Net也包括編碼器和解碼器兩部分,兩者都由ResNet塊組成。編碼器將圖像表示壓縮為低分辨率圖像,解碼器將低分辨率解碼回高分辨率圖像。
為了防止U-Net在下采樣時丟失重要信息,通常在編碼器的下采樣的ResNet和解碼器的上采樣ResNet之間添加了捷徑的連接。
在Stable Diffusion的U-Net中添加了交叉注意層對文本嵌入的輸出進行調(diào)節(jié)。交叉注意層被添加到U-Net的編碼器和解碼器ResNet塊之間。
Text-Encoder

文本編碼器將把輸入文字提示轉(zhuǎn)換為U-Net可以理解的嵌入空間,這是一個簡單的基于transformer的編碼器,它將標記序列映射到潛在文本嵌入序列。從這里可以看到使用良好的文字提示以獲得更好的預(yù)期輸出。
為什么Latent Diffusion快速有效
Latent Diffusion之所以快速有效,是因為它的U-Net是在低維空間上工作的。與像素空間擴散相比,這降低了內(nèi)存和計算復(fù)雜度。例如,一個(3,512,512)的圖像在潛在空間中會變成(4,64,64),內(nèi)存將會減少64倍。
Stable Diffusion的推理過程

首先,模型將潛在空間的隨機種子和文本提示同時作為輸入。然后使用潛在空間的種子生成大小為64×64的隨機潛在圖像表示,通過CLIP的文本編碼器將輸入的文本提示轉(zhuǎn)換為大小為77×768的文本嵌入。
然后,使用U-Net 在以文本嵌入為條件的同時迭代地對隨機潛在圖像表示進行去噪。U-Net 的輸出是噪聲的殘差,用于通過scheduler 程序算法計算去噪的潛在圖像表示。scheduler 算法根據(jù)先前的噪聲表示和預(yù)測的噪聲殘差計算預(yù)測的去噪圖像表示。
許多不同的scheduler 算法可以用于這個計算,每一個都有它的優(yōu)點和缺點。對于Stable Diffusion,建議使用以下其中之一:
PNDM scheduler (默認)
DDIM scheduler
K-LMS scheduler
去噪過程重復(fù)約50次,這樣可以逐步檢索更好的潛在圖像表示。一旦完成,潛在圖像表示就會由變分自編碼器的解碼器部分進行解碼。
使用 Hugging Face的API
Hugging Face提供了一個非常簡單的API來使用我們的模型生成圖像。在下圖中可以看到我使用了“astronaut riding a horse”作為輸入得到輸出圖像:

他提供的模型還包含了一些可用的高級選項來改變生成的圖像的質(zhì)量,如下圖所示:

這里的四個選項說明如下:
images:該選項控制的生成圖像數(shù)量最多為4個。
Steps:此選項選擇想要的擴散過程的步驟數(shù)。步驟越多,生成的圖像質(zhì)量越好。如果想要高質(zhì)量,可以選擇可用的最大步驟數(shù),即50。如果你想要更快的結(jié)果,那么考慮減少步驟的數(shù)量。
Guidance Scale:Guidance Scale是生成的圖像與輸入提示的緊密程度與輸入的多樣性之間的權(quán)衡。它的典型值在7.5左右。增加的比例越多,圖像的質(zhì)量就會越高,但是你得到的輸出就會越少。
Seed:隨機種子夠控制生成的樣本的多樣性。
使用Diffuser 包
第二種使用的方法是使用Hugging Face的Diffusers庫,它包含了目前可用的大部分穩(wěn)定擴散模型,我們可以直接在谷歌的Colab上運行它。
第一步是打開谷歌collab,檢查是否連接到GPU,可以在資源按鈕中查看,如下圖所示:

另一個選擇是從運行時菜單中選擇更改運行時類型,然后檢查硬件加速器被選擇為GPU:

我們確保使用GPU運行時后,使用下面的代碼,查看我們得到的GPU:
!nvidia-smi

非常不幸我們只分配到了一個T4,如果你能分配到一塊P100,那么你的推理速度會變得更快
下面我們安裝一些需要的包:diffusers ,scipy, ftfy和transformer:
!pip install diffusers==0.4.0
!pip install transformers scipy ftfy
!pip install "ipywidgets>=7,<8"
這里需要的額外操作是必須同意模型協(xié)議,還要通過勾選復(fù)選框來接受模型許可?!癏ugging Face”上注冊,并獲得訪問令牌等等。
另外對于谷歌collab,它已經(jīng)禁用了外部小部件,所以需要啟用它。運行以下代碼這樣才能夠使用“notebook_login”
from google.colab import output
output.enable_custom_widget_manager()
現(xiàn)在就可以從的賬戶中獲得的訪問令牌登錄Hugging Face了:
from huggingface_hub import notebook_login
notebook_login()
從diffusers庫加載StableDiffusionPipeline。
StableDiffusionPipeline是一個端到端推理管道,可用于從文本生成圖像。
我們將加載預(yù)訓(xùn)練模型權(quán)重。模型id將是CompVis/ stable-diffusion-v1-4,我們也將使用一個特定類型的修訂版torch_dtype函數(shù)。設(shè)置revision = “fp16”從半精度分支加載權(quán)重,并設(shè)置torch_dtype = " torch。torch_dtype = “torch.float16”告訴模型使用fp16的權(quán)重。
像這樣設(shè)置可以減少內(nèi)存,并且運行的更快。
import torch
from diffusers import StableDiffusionPipeline
# make sure you're logged in with `huggingface-cli login`
pipe = StableDiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4", revision="fp16", torch_dtype=torch.float16)
下面設(shè)置GPU:
pipe = pipe.to("cuda")
現(xiàn)在就可以生成圖片了。我們將編寫一個提示文本并將其交給管道并打印輸出。這里的輸入提示是“an astronaut riding a horse”,讓看看輸出:
prompt = "a photograph of an astronaut riding a horse"
image = pipe(prompt).images[0] # image here is in [PIL format](https://pillow.readthedocs.io/en/stable/)
# Now to display an image you can do either save it such as:
image.save(f"astronaut_rides_horse.png")

每次運行上面的代碼,都會得到不同的圖像。為了每次都得到相同的結(jié)果,你可以向傳遞一個隨機種子,如下面的代碼所示:
import torch
generator = torch.Generator("cuda").manual_seed(1024)
image = pipe(prompt, generator=generator).images[0]
image
還可以使用num_inference_steps參數(shù)更改步驟的數(shù)量。一般來說,推理步驟越多,生成的圖像質(zhì)量越高,但生成結(jié)果需要更多的時間。如果你想要更快的結(jié)果,你可以使用更少的步驟。
下面的單元格使用與前面相同的種子,但步驟更少。注意一些細節(jié),如馬頭或頭盔,比前一張圖定義得更模糊:
import torch
generator = torch.Generator("cuda").manual_seed(1024)
image = pipe(prompt, num_inference_steps=15, generator=generator).images[0]
image

另一個參數(shù)是Guidance Scale。這是一種提高對條件信號的依從性的方法,在擴散模型的情況下它是文本和整體樣本質(zhì)量。
簡單地說,無分類信息的引導(dǎo)迫使生成與文本提示更好地匹配。像7或8.5這樣的數(shù)字可以給出很好的結(jié)果。如果使用的數(shù)字非常大圖像可能看起來很好,但會減少多樣性。
如果要為相同的文本提示生成多個圖像,只需重復(fù)多次輸入相同的文本即可。我們可以把文本的列表發(fā)送到模型中,讓我們編寫一個助手函數(shù)來顯示多個圖像。
from PIL import Image
def image_grid(imgs, rows, cols):
assert len(imgs) == rows*cols
w, h = imgs[0].size
grid = Image.new('RGB', size=(cols*w, rows*h))
grid_w, grid_h = grid.size
for i, img in enumerate(imgs):
grid.paste(img, box=(i%cols*w, i//cols*h))
return grid
現(xiàn)在,我們可以生成多個圖像并一起展示了。
num_images = 3
prompt = ["a photograph of an astronaut riding a horse"] * num_images
images = pipe(prompt).images
grid = image_grid(images, rows=1, cols=3)
grid

還可以生成n*m張圖像:
num_cols = 3
num_rows = 4
prompt = ["a photograph of an astronaut riding a horse"] * num_cols
all_images = []
for i in range(num_rows):
images = pipe(prompt).images
all_images.extend(images)
grid = image_grid(all_images, rows=num_rows, cols=num_cols)
grid

生成的圖像默認大小為512*512像素??梢允褂胔eight和width參數(shù)來更改生成圖像的高度和寬度。這里有一些選擇好的圖片大小的技巧:
將height和width參數(shù)都選擇為8的倍數(shù)。高度和寬度設(shè)置為小于512,可能會導(dǎo)致質(zhì)量比較差如果兩個都設(shè)置為512以上可能會出現(xiàn)全局連貫性(Global Coherence),所以如果需要大圖像可以試試選一個值固定的512,而另一個大于512。例如下面的大?。?/p>
prompt = "a photograph of an astronaut riding a horse"
image = pipe(prompt, height=512, width=768).images[0]
image

建立你自己的處理管道
我們也可以通過Diffusers自定義擴散管道與擴散器。這里將演示如何使用不同的scheduler,即Katherine Crowson的K-LMS調(diào)度器。
我們先看一下StableDiffusionPipeline:
import torch
torch_device = "cuda" if torch.cuda.is_available() else "cpu"
預(yù)訓(xùn)練的模型包括建立一個完整的管道所需的所有組件。它們存放在以下文件夾中:
text_encoder:Stable Diffusion使用CLIP,但其他擴散模型可能使用其他編碼器,如BERT。
tokenizer:它必須與text_encoder模型使用的標記器匹配。
scheduler:用于在訓(xùn)練過程中逐步向圖像添加噪聲的scheduler算法。
U-Net:用于生成輸入的潛在表示的模型。
VAE,我們將使用它將潛在的表示解碼為真實的圖像。
可以通過引用組件被保存的文件夾,使用from_pretraining的子文件夾參數(shù)來加載組件。
from transformers import CLIPTextModel, CLIPTokenizer
from diffusers import AutoencoderKL, UNet2DConditionModel, PNDMScheduler
# 1. Load the autoencoder model which will be used to decode the latents into image space.
vae = AutoencoderKL.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="vae")
# 2. Load the tokenizer and text encoder to tokenize and encode the text.
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14")
# 3. The UNet model for generating the latents.
unet = UNet2DConditionModel.from_pretrained("CompVis/stable-diffusion-v1-4", subfolder="unet")
現(xiàn)在,我們不加載預(yù)定義的scheduler,而是加載K-LMS:
from diffusers import LMSDiscreteScheduler
scheduler = LMSDiscreteScheduler(beta_start=0.00085, beta_end=0.012, beta_schedule="scaled_linear", num_train_timesteps=1000)
將模型移動到GPU上。
vae = vae.to(torch_device)
text_encoder = text_encoder.to(torch_device)
unet = unet.to(torch_device)
定義用于生成圖像的參數(shù)。與前面的示例相比,設(shè)置num_inference_steps = 100來獲得更明確的圖像。
prompt = ["a photograph of an astronaut riding a horse"]
height = 512 # default height of Stable Diffusion
width = 512 # default width of Stable Diffusion
num_inference_steps = 100 # Number of denoising steps
guidance_scale = 7.5 # Scale for classifier-free guidance
generator = torch.manual_seed(32) # Seed generator to create the inital latent noise
batch_size = 1
prompt = ["a photograph of an astronaut riding a horse"]
height = 512 # default height of Stable Diffusion
width = 512 # default width of Stable Diffusion
num_inference_steps = 100 # Number of denoising steps
guidance_scale = 7.5 # Scale for classifier-free guidance
generator = torch.manual_seed(32) # Seed generator to create the inital latent noise
batch_size = 1
獲取文本提示的text_embeddings。然后將嵌入用于調(diào)整U-Net模型。
text_input = tokenizer(prompt, padding="max_length", max_length=tokenizer.model_max_length, truncation=True, return_tensors="pt")
with torch.no_grad():
text_embeddings = text_encoder(text_input.input_ids.to(torch_device))[0]
獲得用于無分類器引導(dǎo)的無條件文本嵌入,這只是填充令牌(空文本)的嵌入。它們需要具有與text_embeddings (batch_size和seq_length)相同的形狀。
max_length = text_input.input_ids.shape[-1]
uncond_input = tokenizer(
[""] * batch_size, padding="max_length", max_length=max_length, return_tensors="pt"
)
with torch.no_grad():
uncond_embeddings = text_encoder(uncond_input.input_ids.to(torch_device))[0]
對于無分類的引導(dǎo),需要進行兩次向前傳遞。第一個是條件輸入(text_embeddings),第二個是無條件嵌入(uncond_embeddings)。把兩者連接到一個批處理中,以避免進行兩次向前傳遞:
text_embeddings = torch.cat([uncond_embeddings, text_embeddings])
生成初始隨機噪聲:
latents = torch.randn(
(batch_size, unet.in_channels, height // 8, width // 8),
generator=generator,
)
latents = latents.to(torch_device)
產(chǎn)生的形狀為64 * 64的隨機潛在空間。模型會將這種潛在的表示(純噪聲)轉(zhuǎn)換為512 * 512的圖像。
使用所選的num_inference_steps初始化scheduler。這將計算sigma和去噪過程中使用的確切步長值:
scheduler.set_timesteps(num_inference_steps)
K-LMS需要用它的sigma值乘以潛在空間的值:
latents = latents * scheduler.init_noise_sigma
最后就是去噪的循環(huán):
from tqdm.auto import tqdm
from torch import autocast
for t in tqdm(scheduler.timesteps):
# expand the latents if we are doing classifier-free guidance to avoid doing two forward passes.
latent_model_input = torch.cat([latents] * 2)
latent_model_input = scheduler.scale_model_input(latent_model_input, t)
# predict the noise residual
with torch.no_grad():
noise_pred = unet(latent_model_input, t, encoder_hidden_states=text_embeddings).sample
# perform guidance
noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)
# compute the previous noisy sample x_t -> x_t-1
latents = scheduler.step(noise_pred, t, latents).prev_sample
然后就是使用vae可將產(chǎn)生的潛在空間解碼回圖像:
# scale and decode the image latents with vae
latents = 1 / 0.18215 * latents
with torch.no_grad():
image = vae.decode(latents).sample
最后將圖像轉(zhuǎn)換為PIL,以便我們可以顯示或保存它。
image = (image / 2 + 0.5).clamp(0, 1)
image = image.detach().cpu().permute(0, 2, 3, 1).numpy()
images = (image * 255).round().astype("uint8")
pil_images = [Image.fromarray(image) for image in images]
pil_images[0]
這樣一個完整的Stable Diffusion模型的處理過程就完成了??赐瓯疚南M阋呀?jīng)知道了如何使用Stable Diffusion以及它具體工作的原理,如果你對他的處理流程還有疑問,可以通過自定義處理管道來深入的了解他的工作流程,希望本文對你有所幫助。
如果你對本文感興趣 代碼在這里:
https://avoid.overfit.cn/post/b4dfff8d1d1a4808a296c33b2e8a952b文章來源:http://www.zghlxwxcb.cn/news/detail-785128.html
作者:Youssef Hosni文章來源地址http://www.zghlxwxcb.cn/news/detail-785128.html
到了這里,關(guān)于Stable Diffusion的入門介紹和使用教程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!