Diffusion Model에서 Seed를 고정하는 주된 이유는 재현성을 높이기 위해서다. 이는 이미지 퀄리티 향상에 매우 중요한 요소이다.

파이프라인은 가우시안 노이즈 생성과 스케줄링 단계의 노이즈 추가와 같은 랜덤 샘플링에 크게 의존한다.

 

🧑‍🦲 일반적인 경우

아래와 같이 코드를 실행했을 때, seed를 고정하지 않았기 때문에 매번 다른 결과가 나오게 된다.

from diffusers import DDIMPipeline
import numpy as np

model_id = "google/ddpm-cifar10-32"

ddim = DDIMPipeline.from_pretrained(model_id)

image = ddim(num_inference_steps=2, output_type="np").images
print(np.abs(image).sum())
1435.5261
1383.0615

 

Diffusion 모델이 이미지를 생성하는 방식을 쉽게 설명하면 무작위 노이즈에서 노이즈를 제거하는 방식으로 이미지를 생성하게 된다. 이때 노이지를 제거하기 위해 `torch.randn` 을 통해 가우시안 노이즈를 생성하게 되는데 이때 랜덤 seed를 사용하게 된다. 그러므로 이미지 재현성을 위해 `Generator` 를 사용해 seed를 고정한다. 이때, CPU와 GPU는 다른 난수 생성기를 사용하기 때문에 동일한 시드를 사용하더라도 결과가 같지않다.

 

🖥️ CPU

from diffusers import DDIMPipeline
import numpy as np
import torch

model_id = "google/ddpm-cifar10-32"

ddim = DDIMPipeline.from_pretrained(model_id)

generator = torch.Generator(device="cpu").manual_seed(0)

image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
print(np.abs(image).sum())
1491.1711
1491.1711

 

🤖 GPU

from diffusers import DDIMPipeline
import numpy as np
import torch
import warnings
warnings.filterwarnings('ignore')

model_id = "google/ddpm-cifar10-32"

ddim = DDIMPipeline.from_pretrained(model_id)
ddim.to("cuda")

generator = torch.Generator(device="cuda").manual_seed(0)

image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
print(np.abs(image).sum())
1389.831
1389.831

 

🔍 Deterministic algorithms

이 외에도 Deterministic algorithms을 사용하여 재현 가능한 파이프라인을 구성할 수 있다. 속도는 느릴 수 있지만 재현성이 중요하다면 고려해볼 수 있다.

 

import os

os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":16:8"

torch.backends.cudnn.benchmark = False
torch.use_deterministic_algorithms(True)
import torch
from diffusers import DDIMScheduler, StableDiffusionPipeline
import numpy as np

model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(model_id).to("cuda")
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
g = torch.Generator(device="cuda")

prompt = "A bear is playing a guitar on Times Square"

g.manual_seed(0)
result1 = pipe(prompt=prompt, num_inference_steps=50, generator=g, output_type="latent").images

g.manual_seed(0)
result2 = pipe(prompt=prompt, num_inference_steps=50, generator=g, output_type="latent").images

print("L_inf dist = ", abs(result1 - result2).max())
"L_inf dist =  tensor(0., device='cuda:0')"

 

📄 참고자료

https://huggingface.co/docs/diffusers/v0.29.2/ko/using-diffusers/reproducibility