svjack's picture
Upload 1392 files
43b7e92 verified
|
raw
history blame
9.35 kB

μž¬ν˜„ κ°€λŠ₯ν•œ νŒŒμ΄ν”„λΌμΈ μƒμ„±ν•˜κΈ°

[[open-in-colab]]

μž¬ν˜„μ„±μ€ ν…ŒμŠ€νŠΈ, κ²°κ³Ό μž¬ν˜„, 그리고 이미지 퀄리티 λ†’μ΄κΈ°μ—μ„œ μ€‘μš”ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ diffusion λͺ¨λΈμ˜ λ¬΄μž‘μœ„μ„±μ€ 맀번 λͺ¨λΈμ΄ λŒμ•„κ°ˆ λ•Œλ§ˆλ‹€ νŒŒμ΄ν”„λΌμΈμ΄ λ‹€λ₯Έ 이미지λ₯Ό 생성할 수 μžˆλ„λ‘ ν•˜λŠ” 이유둜 ν•„μš”ν•©λ‹ˆλ‹€. ν”Œλž«νΌ 간에 μ •ν™•ν•˜κ²Œ λ™μΌν•œ κ²°κ³Όλ₯Ό 얻을 μˆ˜λŠ” μ—†μ§€λ§Œ, νŠΉμ • ν—ˆμš© λ²”μœ„ λ‚΄μ—μ„œ 릴리슀 및 ν”Œλž«νΌ 간에 κ²°κ³Όλ₯Ό μž¬ν˜„ν•  μˆ˜λŠ” μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸΌμ—λ„ diffusion νŒŒμ΄ν”„λΌμΈκ³Ό μ²΄ν¬ν¬μΈνŠΈμ— 따라 ν—ˆμš© μ˜€μ°¨κ°€ λ‹¬λΌμ§‘λ‹ˆλ‹€.

diffusion λͺ¨λΈμ—μ„œ λ¬΄μž‘μœ„μ„±μ˜ μ›μ²œμ„ μ œμ–΄ν•˜κ±°λ‚˜ 결정둠적 μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜λŠ” 방법을 μ΄ν•΄ν•˜λŠ” 것이 μ€‘μš”ν•œ μ΄μœ μž…λ‹ˆλ‹€.

πŸ’‘ Pytorch의 μž¬ν˜„μ„±μ— λŒ€ν•œ μ„ μ–Έλ₯Ό κΌ­ 읽어보길 μΆ”μ²œν•©λ‹ˆλ‹€:

μ™„μ „ν•˜κ²Œ μž¬ν˜„κ°€λŠ₯ν•œ κ²°κ³ΌλŠ” Pytorch 배포, κ°œλ³„μ μΈ 컀밋, ν˜Ήμ€ λ‹€λ₯Έ ν”Œλž«νΌλ“€μ—μ„œ 보μž₯λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ˜ν•œ, κ²°κ³ΌλŠ” CPU와 GPU 싀행간에 심지어 같은 seedλ₯Ό μ‚¬μš©ν•  λ•Œλ„ μž¬ν˜„ κ°€λŠ₯ν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

λ¬΄μž‘μœ„μ„± μ œμ–΄ν•˜κΈ°

μΆ”λ‘ μ—μ„œ, νŒŒμ΄ν”„λΌμΈμ€ λ…Έμ΄μ¦ˆλ₯Ό 쀄이기 μœ„ν•΄ κ°€μš°μ‹œμ•ˆ λ…Έμ΄μ¦ˆλ₯Ό μƒμ„±ν•˜κ±°λ‚˜ μŠ€μΌ€μ€„λ§ 단계에 λ…Έμ΄μ¦ˆλ₯Ό λ”ν•˜λŠ” λ“±μ˜ 랜덀 μƒ˜ν”Œλ§ 싀행에 크게 μ˜μ‘΄ν•©λ‹ˆλ‹€,

DDIMPipelineμ—μ„œ 두 μΆ”λ‘  단계 μ΄ν›„μ˜ ν…μ„œ 값을 μ‚΄νŽ΄λ³΄μ„Έμš”:

from diffusers import DDIMPipeline
import numpy as np

model_id = "google/ddpm-cifar10-32"

# λͺ¨λΈκ³Ό μŠ€μΌ€μ€„λŸ¬λ₯Ό 뢈러였기
ddim = DDIMPipeline.from_pretrained(model_id)

# 두 개의 단계에 λŒ€ν•΄μ„œ νŒŒμ΄ν”„λΌμΈμ„ μ‹€ν–‰ν•˜κ³  numpy tensor둜 값을 λ°˜ν™˜ν•˜κΈ°
image = ddim(num_inference_steps=2, output_type="np").images
print(np.abs(image).sum())

μœ„μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ ν•˜λ‚˜μ˜ 값이 λ‚˜μ˜€μ§€λ§Œ, λ‹€μ‹œ μ‹€ν–‰ν•˜λ©΄ λ‹€λ₯Έ 값이 λ‚˜μ˜΅λ‹ˆλ‹€. 무슨 일이 μΌμ–΄λ‚˜κ³  μžˆλŠ” κ±ΈκΉŒμš”?

νŒŒμ΄ν”„λΌμΈμ΄ 싀행될 λ•Œλ§ˆλ‹€, torch.randn은 λ‹¨κ³„μ μœΌλ‘œ λ…Έμ΄μ¦ˆ μ œκ±°λ˜λŠ” κ°€μš°μ‹œμ•ˆ λ…Έμ΄μ¦ˆκ°€ μƒμ„±ν•˜κΈ° μœ„ν•œ λ‹€λ₯Έ 랜덀 seedλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ λ™μΌν•œ 이미지λ₯Ό μ•ˆμ •μ μœΌλ‘œ 생성해야 ν•˜λŠ” κ²½μš°μ—λŠ” CPUμ—μ„œ νŒŒμ΄ν”„λΌμΈμ„ μ‹€ν–‰ν•˜λŠ”μ§€ GPUμ—μ„œ μ‹€ν–‰ν•˜λŠ”μ§€μ— 따라 λ‹¬λΌμ§‘λ‹ˆλ‹€.

CPU

CPUμ—μ„œ μž¬ν˜„ κ°€λŠ₯ν•œ κ²°κ³Όλ₯Ό μƒμ„±ν•˜λ €λ©΄, PyTorch Generator둜 seedλ₯Ό κ³ μ •ν•©λ‹ˆλ‹€:

import torch
from diffusers import DDIMPipeline
import numpy as np

model_id = "google/ddpm-cifar10-32"

# λͺ¨λΈκ³Ό μŠ€μΌ€μ€„λŸ¬ 뢈러였기
ddim = DDIMPipeline.from_pretrained(model_id)

# μž¬ν˜„μ„±μ„ μœ„ν•΄ generator λ§Œλ“€κΈ°
generator = torch.Generator(device="cpu").manual_seed(0)

# 두 개의 단계에 λŒ€ν•΄μ„œ νŒŒμ΄ν”„λΌμΈμ„ μ‹€ν–‰ν•˜κ³  numpy tensor둜 값을 λ°˜ν™˜ν•˜κΈ°
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
print(np.abs(image).sum())

이제 μœ„μ˜ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λ©΄ seedλ₯Ό 가진 Generator 객체가 νŒŒμ΄ν”„λΌμΈμ˜ λͺ¨λ“  랜덀 ν•¨μˆ˜μ— μ „λ‹¬λ˜λ―€λ‘œ 항상 1491.1711 값이 좜λ ₯λ©λ‹ˆλ‹€.

νŠΉμ • ν•˜λ“œμ›¨μ–΄ 및 PyTorch λ²„μ „μ—μ„œ 이 μ½”λ“œ 예제λ₯Ό μ‹€ν–‰ν•˜λ©΄ λ™μΌν•˜μ§€λŠ” μ•Šλ”λΌλ„ μœ μ‚¬ν•œ κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ’‘ μ²˜μŒμ—λŠ” μ‹œλ“œλ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ •μˆ˜κ°’ λŒ€μ‹ μ— Generator 개체λ₯Ό νŒŒμ΄ν”„λΌμΈμ— μ „λ‹¬ν•˜λŠ” 것이 μ•½κ°„ 비직관적일 수 μžˆμ§€λ§Œ, GeneratorλŠ” 순차적으둜 μ—¬λŸ¬ νŒŒμ΄ν”„λΌμΈμ— 전달될 수 μžˆλŠ” \λžœλ€μƒνƒœ\이기 λ•Œλ¬Έμ— PyTorchμ—μ„œ ν™•λ₯ λ‘ μ  λͺ¨λΈμ„ λ‹€λ£° λ•Œ ꢌμž₯λ˜λŠ” μ„€κ³„μž…λ‹ˆλ‹€.

GPU

예λ₯Ό λ“€λ©΄, GPU μƒμ—μ„œ 같은 μ½”λ“œ μ˜ˆμ‹œλ₯Ό μ‹€ν–‰ν•˜λ©΄:

import torch
from diffusers import DDIMPipeline
import numpy as np

model_id = "google/ddpm-cifar10-32"

# λͺ¨λΈκ³Ό μŠ€μΌ€μ€„λŸ¬ 뢈러였기
ddim = DDIMPipeline.from_pretrained(model_id)
ddim.to("cuda")

# μž¬ν˜„μ„±μ„ μœ„ν•œ generator λ§Œλ“€κΈ°
generator = torch.Generator(device="cuda").manual_seed(0)

# 두 개의 단계에 λŒ€ν•΄μ„œ νŒŒμ΄ν”„λΌμΈμ„ μ‹€ν–‰ν•˜κ³  numpy tensor둜 값을 λ°˜ν™˜ν•˜κΈ°
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
print(np.abs(image).sum())

GPUκ°€ CPU와 λ‹€λ₯Έ λ‚œμˆ˜ 생성기λ₯Ό μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— λ™μΌν•œ μ‹œλ“œλ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ κ²°κ³Όκ°€ 같지 μ•ŠμŠ΅λ‹ˆλ‹€.

이 문제λ₯Ό ν”Όν•˜κΈ° μœ„ν•΄ 🧨 DiffusersλŠ” CPU에 μž„μ˜μ˜ λ…Έμ΄μ¦ˆλ₯Ό μƒμ„±ν•œ λ‹€μŒ ν•„μš”μ— 따라 ν…μ„œλ₯Ό GPU둜 μ΄λ™μ‹œν‚€λŠ” randn_tensor()κΈ°λŠ₯을 가지고 μžˆμŠ΅λ‹ˆλ‹€. randn_tensor κΈ°λŠ₯은 νŒŒμ΄ν”„λΌμΈ λ‚΄λΆ€ μ–΄λ””μ—μ„œλ‚˜ μ‚¬μš©λ˜λ―€λ‘œ νŒŒμ΄ν”„λΌμΈμ΄ GPUμ—μ„œ μ‹€ν–‰λ˜λ”λΌλ„ 항상 CPU Generatorλ₯Ό 톡과할 수 μžˆμŠ΅λ‹ˆλ‹€.

이제 결과에 훨씬 더 λ‹€κ°€μ™”μŠ΅λ‹ˆλ‹€!

import torch
from diffusers import DDIMPipeline
import numpy as np

model_id = "google/ddpm-cifar10-32"

# λͺ¨λΈκ³Ό μŠ€μΌ€μ€„λŸ¬ 뢈러였기
ddim = DDIMPipeline.from_pretrained(model_id)
ddim.to("cuda")

#μž¬ν˜„μ„±μ„ μœ„ν•œ generator λ§Œλ“€κΈ° (GPU에 μ˜¬λ¦¬μ§€ μ•Šλ„λ‘ μ‘°μ‹¬ν•œλ‹€!)
generator = torch.manual_seed(0)

# 두 개의 단계에 λŒ€ν•΄μ„œ νŒŒμ΄ν”„λΌμΈμ„ μ‹€ν–‰ν•˜κ³  numpy tensor둜 값을 λ°˜ν™˜ν•˜κΈ°
image = ddim(num_inference_steps=2, output_type="np", generator=generator).images
print(np.abs(image).sum())

πŸ’‘ μž¬ν˜„μ„±μ΄ μ€‘μš”ν•œ κ²½μš°μ—λŠ” 항상 CPU generatorλ₯Ό μ „λ‹¬ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. μ„±λŠ₯ 손싀은 λ¬΄μ‹œν•  수 μ—†λŠ” κ²½μš°κ°€ 많으며 νŒŒμ΄ν”„λΌμΈμ΄ GPUμ—μ„œ μ‹€ν–‰λ˜μ—ˆμ„ λ•Œλ³΄λ‹€ 훨씬 더 λΉ„μŠ·ν•œ 값을 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ UnCLIPPipelineκ³Ό 같은 더 λ³΅μž‘ν•œ νŒŒμ΄ν”„λΌμΈμ˜ 경우, 이듀은 μ’…μ’… μ •λ°€ 였차 μ „νŒŒμ— κ·Ήλ„λ‘œ μ·¨μ•½ν•©λ‹ˆλ‹€. λ‹€λ₯Έ GPU ν•˜λ“œμ›¨μ–΄ λ˜λŠ” PyTorch λ²„μ „μ—μ„œ μœ μ‚¬ν•œ κ²°κ³Όλ₯Ό κΈ°λŒ€ν•˜μ§€ λ§ˆμ„Έμš”. 이 경우 μ™„μ „ν•œ μž¬ν˜„μ„±μ„ μœ„ν•΄ μ™„μ „νžˆ λ™μΌν•œ ν•˜λ“œμ›¨μ–΄ 및 PyTorch 버전을 μ‹€ν–‰ν•΄μ•Ό ν•©λ‹ˆλ‹€.

결정둠적 μ•Œκ³ λ¦¬μ¦˜

결정둠적 μ•Œκ³ λ¦¬μ¦˜μ„ μ‚¬μš©ν•˜μ—¬ μž¬ν˜„ κ°€λŠ₯ν•œ νŒŒμ΄ν”„λΌμΈμ„ μƒμ„±ν•˜λ„λ‘ PyTorchλ₯Ό ꡬ성할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 결정둠적 μ•Œκ³ λ¦¬μ¦˜μ€ 비결정둠적 μ•Œκ³ λ¦¬μ¦˜λ³΄λ‹€ 느리고 μ„±λŠ₯이 μ €ν•˜λ  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μž¬ν˜„μ„±μ΄ μ€‘μš”ν•˜λ‹€λ©΄, 이것이 μ΅œμ„ μ˜ λ°©λ²•μž…λ‹ˆλ‹€!

λ‘˜ μ΄μƒμ˜ CUDA μŠ€νŠΈλ¦Όμ—μ„œ μž‘μ—…μ΄ μ‹œμž‘λ  λ•Œ 비결정둠적 λ™μž‘μ΄ λ°œμƒν•©λ‹ˆλ‹€. 이 문제λ₯Ό λ°©μ§€ν•˜λ €λ©΄ ν™˜κ²½ λ³€μˆ˜ CUBLAS_WORKSPACE_CONFIGλ₯Ό :16:8둜 μ„€μ •ν•΄μ„œ λŸ°νƒ€μž„ 쀑에 였직 ν•˜λ‚˜μ˜ 버퍼 크리만 μ‚¬μš©ν•˜λ„λ‘ μ„€μ •ν•©λ‹ˆλ‹€.

PyTorchλŠ” 일반적으둜 κ°€μž₯ λΉ λ₯Έ μ•Œκ³ λ¦¬μ¦˜μ„ μ„ νƒν•˜κΈ° μœ„ν•΄ μ—¬λŸ¬ μ•Œκ³ λ¦¬μ¦˜μ„ λ²€μΉ˜λ§ˆν‚Ήν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ μž¬ν˜„μ„±μ„ μ›ν•˜λŠ” 경우, λ²€μΉ˜λ§ˆν¬κ°€ 맀 μˆœκ°„ λ‹€λ₯Έ μ•Œκ³ λ¦¬μ¦˜μ„ 선택할 수 있기 λ•Œλ¬Έμ— 이 κΈ°λŠ₯을 μ‚¬μš©ν•˜μ§€ μ•Šλ„λ‘ μ„€μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ§ˆμ§€λ§‰μœΌλ‘œ, torch.use_deterministic_algorithms에 Trueλ₯Ό ν†΅κ³Όμ‹œμΌœ 결정둠적 μ•Œκ³ λ¦¬μ¦˜μ΄ ν™œμ„±ν™” λ˜λ„λ‘ ν•©λ‹ˆλ‹€.

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')"