ํจ๊ณผ์ ์ด๊ณ ํจ์จ์ ์ธ Diffusion
[[open-in-colab]]
ํน์ ์คํ์ผ๋ก ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๊ฑฐ๋ ์ํ๋ ๋ด์ฉ์ ํฌํจํ๋๋ก[DiffusionPipeline
]์ ์ค์ ํ๋ ๊ฒ์ ๊น๋ค๋ก์ธ ์ ์์ต๋๋ค. ์ข
์ข
๋ง์กฑ์ค๋ฌ์ด ์ด๋ฏธ์ง๋ฅผ ์ป๊ธฐ๊น์ง [DiffusionPipeline
]์ ์ฌ๋ฌ ๋ฒ ์คํํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ๊ทธ๋ฌ๋ ๋ฌด์์ ์ ๋ฅผ ์ฐฝ์กฐํ๋ ๊ฒ์ ํนํ ์ถ๋ก ์ ๋ฐ๋ณตํด์ ์คํํ๋ ๊ฒฝ์ฐ ๊ณ์ฐ ์ง์ฝ์ ์ธ ํ๋ก์ธ์ค์
๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ์ดํ๋ผ์ธ์์ ๊ณ์ฐ(์๋) ๋ฐ ๋ฉ๋ชจ๋ฆฌ(GPU RAM) ํจ์จ์ฑ์ ๊ทน๋ํํ์ฌ ์ถ๋ก ์ฃผ๊ธฐ ์ฌ์ด์ ์๊ฐ์ ๋จ์ถํ์ฌ ๋ ๋น ๋ฅด๊ฒ ๋ฐ๋ณตํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
์ด ํํ ๋ฆฌ์ผ์์๋ [DiffusionPipeline
]์ ์ฌ์ฉํ์ฌ ๋ ๋น ๋ฅด๊ณ ํจ๊ณผ์ ์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์๋ดํฉ๋๋ค.
runwayml/stable-diffusion-v1-5
๋ชจ๋ธ์ ๋ถ๋ฌ์์ ์์ํฉ๋๋ค:
from diffusers import DiffusionPipeline
model_id = "runwayml/stable-diffusion-v1-5"
pipeline = DiffusionPipeline.from_pretrained(model_id)
์์ ํ๋กฌํํธ๋ "portrait of an old warrior chief" ์ด์ง๋ง, ์์ ๋กญ๊ฒ ์์ ๋ง์ ํ๋กฌํํธ๋ฅผ ์ฌ์ฉํด๋ ๋ฉ๋๋ค:
prompt = "portrait photo of a old warrior chief"
์๋
๐ก GPU์ ์ก์ธ์คํ ์ ์๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ GPU ์ ๊ณต์ ์ฒด์์ ๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค!. Colab
์ถ๋ก ์๋๋ฅผ ๋์ด๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ ์ค ํ๋๋ Pytorch ๋ชจ๋์ ์ฌ์ฉํ ๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก GPU์ ํ์ดํ๋ผ์ธ์ ๋ฐฐ์นํ๋ ๊ฒ์ ๋๋ค:
pipeline = pipeline.to("cuda")
๋์ผํ ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๊ณ ๊ฐ์ ํ ์ ์๋์ง ํ์ธํ๋ ค๋ฉด Generator
๋ฅผ ์ฌ์ฉํ๊ณ ์ฌํ์ฑ์ ๋ํ ์๋๋ฅผ ์ค์ ํ์ธ์:
import torch
generator = torch.Generator("cuda").manual_seed(0)
์ด์ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ ์ ์์ต๋๋ค:
image = pipeline(prompt, generator=generator).images[0]
image
์ด ํ๋ก์ธ์ค๋ T4 GPU์์ ์ฝ 30์ด๊ฐ ์์๋์์ต๋๋ค(ํ ๋น๋ GPU๊ฐ T4๋ณด๋ค ๋์ ๊ฒฝ์ฐ ๋ ๋น ๋ฅผ ์ ์์). ๊ธฐ๋ณธ์ ์ผ๋ก [DiffusionPipeline
]์ 50๊ฐ์ ์ถ๋ก ๋จ๊ณ์ ๋ํด ์ ์ฒด float32
์ ๋ฐ๋๋ก ์ถ๋ก ์ ์คํํฉ๋๋ค. float16
๊ณผ ๊ฐ์ ๋ ๋ฎ์ ์ ๋ฐ๋๋ก ์ ํํ๊ฑฐ๋ ์ถ๋ก ๋จ๊ณ๋ฅผ ๋ ์ ๊ฒ ์คํํ์ฌ ์๋๋ฅผ ๋์ผ ์ ์์ต๋๋ค.
float16
์ผ๋ก ๋ชจ๋ธ์ ๋ก๋ํ๊ณ ์ด๋ฏธ์ง๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค:
import torch
pipeline = DiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)
pipeline = pipeline.to("cuda")
generator = torch.Generator("cuda").manual_seed(0)
image = pipeline(prompt, generator=generator).images[0]
image
์ด๋ฒ์๋ ์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ ๋ฐ ์ฝ 11์ด๋ฐ์ ๊ฑธ๋ฆฌ์ง ์์ ์ด์ ๋ณด๋ค 3๋ฐฐ ๊ฐ๊น์ด ๋นจ๋ผ์ก์ต๋๋ค!
๐ก ํ์ดํ๋ผ์ธ์ ํญ์ float16
์์ ์คํํ ๊ฒ์ ๊ฐ๋ ฅํ ๊ถ์ฅํ๋ฉฐ, ์ง๊ธ๊น์ง ์ถ๋ ฅ ํ์ง์ด ์ ํ๋๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์์์ต๋๋ค.
๋ ๋ค๋ฅธ ์ต์
์ ์ถ๋ก ๋จ๊ณ์ ์๋ฅผ ์ค์ด๋ ๊ฒ์
๋๋ค. ๋ณด๋ค ํจ์จ์ ์ธ ์ค์ผ์ค๋ฌ๋ฅผ ์ ํํ๋ฉด ์ถ๋ ฅ ํ์ง ์ ํ ์์ด ๋จ๊ณ ์๋ฅผ ์ค์ด๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค. ํ์ฌ ๋ชจ๋ธ๊ณผ ํธํ๋๋ ์ค์ผ์ค๋ฌ๋ compatibles
๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ [DiffusionPipeline
]์์ ์ฐพ์ ์ ์์ต๋๋ค:
pipeline.scheduler.compatibles
[
diffusers.schedulers.scheduling_lms_discrete.LMSDiscreteScheduler,
diffusers.schedulers.scheduling_unipc_multistep.UniPCMultistepScheduler,
diffusers.schedulers.scheduling_k_dpm_2_discrete.KDPM2DiscreteScheduler,
diffusers.schedulers.scheduling_deis_multistep.DEISMultistepScheduler,
diffusers.schedulers.scheduling_euler_discrete.EulerDiscreteScheduler,
diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler,
diffusers.schedulers.scheduling_ddpm.DDPMScheduler,
diffusers.schedulers.scheduling_dpmsolver_singlestep.DPMSolverSinglestepScheduler,
diffusers.schedulers.scheduling_k_dpm_2_ancestral_discrete.KDPM2AncestralDiscreteScheduler,
diffusers.schedulers.scheduling_heun_discrete.HeunDiscreteScheduler,
diffusers.schedulers.scheduling_pndm.PNDMScheduler,
diffusers.schedulers.scheduling_euler_ancestral_discrete.EulerAncestralDiscreteScheduler,
diffusers.schedulers.scheduling_ddim.DDIMScheduler,
]
Stable Diffusion ๋ชจ๋ธ์ ์ผ๋ฐ์ ์ผ๋ก ์ฝ 50๊ฐ์ ์ถ๋ก ๋จ๊ณ๊ฐ ํ์ํ [PNDMScheduler
]๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ์ง๋ง, [DPMSolverMultistepScheduler
]์ ๊ฐ์ด ์ฑ๋ฅ์ด ๋ ๋ฐ์ด๋ ์ค์ผ์ค๋ฌ๋ ์ฝ 20๊ฐ ๋๋ 25๊ฐ์ ์ถ๋ก ๋จ๊ณ๋ง ํ์๋ก ํฉ๋๋ค. ์ ์ค์ผ์ค๋ฌ๋ฅผ ๋ก๋ํ๋ ค๋ฉด [ConfigMixin.from_config
] ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค:
from diffusers import DPMSolverMultistepScheduler
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(pipeline.scheduler.config)
num_inference_steps
๋ฅผ 20์ผ๋ก ์ค์ ํฉ๋๋ค:
generator = torch.Generator("cuda").manual_seed(0)
image = pipeline(prompt, generator=generator, num_inference_steps=20).images[0]
image
์ถ๋ก ์๊ฐ์ 4์ด๋ก ๋จ์ถํ ์ ์์์ต๋๋ค! โก๏ธ
๋ฉ๋ชจ๋ฆฌ
ํ์ดํ๋ผ์ธ ์ฑ๋ฅ ํฅ์์ ๋ ๋ค๋ฅธ ํต์ฌ์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ด๋ ๊ฒ์ธ๋ฐ, ์ด๋น ์์ฑ๋๋ ์ด๋ฏธ์ง ์๋ฅผ ์ต๋ํํ๋ ค๊ณ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ ๊ฐ์ ์ ์ผ๋ก ๋ ๋น ๋ฅธ ์๋๋ฅผ ์๋ฏธํฉ๋๋ค. ํ ๋ฒ์ ์์ฑํ ์ ์๋ ์ด๋ฏธ์ง ์๋ฅผ ํ์ธํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ OutOfMemoryError
(OOM)์ด ๋ฐ์ํ ๋๊น์ง ๋ค์ํ ๋ฐฐ์น ํฌ๊ธฐ๋ฅผ ์๋ํด ๋ณด๋ ๊ฒ์
๋๋ค.
ํ๋กฌํํธ ๋ชฉ๋ก๊ณผ Generators
์์ ์ด๋ฏธ์ง ๋ฐฐ์น๋ฅผ ์์ฑํ๋ ํจ์๋ฅผ ๋ง๋ญ๋๋ค. ์ข์ ๊ฒฐ๊ณผ๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ๊ฐ Generator
์ ์๋๋ฅผ ํ ๋นํด์ผ ํฉ๋๋ค.
def get_inputs(batch_size=1):
generator = [torch.Generator("cuda").manual_seed(i) for i in range(batch_size)]
prompts = batch_size * [prompt]
num_inference_steps = 20
return {"prompt": prompts, "generator": generator, "num_inference_steps": num_inference_steps}
๋ํ ๊ฐ ์ด๋ฏธ์ง ๋ฐฐ์น๋ฅผ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ด ํ์ํฉ๋๋ค:
from PIL import Image
def image_grid(imgs, rows=2, cols=2):
w, h = imgs[0].size
grid = Image.new("RGB", size=(cols * w, rows * h))
for i, img in enumerate(imgs):
grid.paste(img, box=(i % cols * w, i // cols * h))
return grid
batch_size=4
๋ถํฐ ์์ํด ์ผ๋ง๋ ๋ง์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋นํ๋์ง ํ์ธํฉ๋๋ค:
images = pipeline(**get_inputs(batch_size=4)).images
image_grid(images)
RAM์ด ๋ ๋ง์ GPU๊ฐ ์๋๋ผ๋ฉด ์์ ์ฝ๋์์ OOM
์ค๋ฅ๊ฐ ๋ฐํ๋์์ ๊ฒ์
๋๋ค! ๋๋ถ๋ถ์ ๋ฉ๋ชจ๋ฆฌ๋ cross-attention ๋ ์ด์ด๊ฐ ์ฐจ์งํฉ๋๋ค. ์ด ์์
์ ๋ฐฐ์น๋ก ์คํํ๋ ๋์ ์์ฐจ์ ์ผ๋ก ์คํํ๋ฉด ์๋นํ ์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ ์ฝํ ์ ์์ต๋๋ค. ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ์ฌ [~DiffusionPipeline.enable_attention_slicing
] ํจ์๋ฅผ ์ฌ์ฉํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค:
pipeline.enable_attention_slicing()
์ด์ batch_size
๋ฅผ 8๋ก ๋๋ ค๋ณด์ธ์!
images = pipeline(**get_inputs(batch_size=8)).images
image_grid(images, rows=2, cols=4)
์ด์ ์๋ 4๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐฐ์น๋ก ์์ฑํ ์๋ ์์์ง๋ง, ์ด์ ๋ ์ด๋ฏธ์ง๋น ์ฝ 3.5์ด ๋ง์ 8๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐฐ์น๋ก ์์ฑํ ์ ์์ต๋๋ค! ์ด๋ ์๋ง๋ ํ์ง ์ ํ ์์ด T4 GPU์์ ๊ฐ์ฅ ๋น ๋ฅธ ์๋์ผ ๊ฒ์ ๋๋ค.
ํ์ง
์ง๋ ๋ ์น์
์์๋ fp16
์ ์ฌ์ฉํ์ฌ ํ์ดํ๋ผ์ธ์ ์๋๋ฅผ ์ต์ ํํ๊ณ , ๋ ์ฑ๋ฅ์ด ์ข์ ์ค์ผ์ค๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ถ๋ก ๋จ๊ณ์ ์๋ฅผ ์ค์ด๊ณ , attention slicing์ ํ์ฑํํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์๋น๋ฅผ ์ค์ด๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ต๋๋ค. ์ด์ ์์ฑ๋ ์ด๋ฏธ์ง์ ํ์ง์ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ง์ค์ ์ผ๋ก ์์๋ณด๊ฒ ์ต๋๋ค.
๋ ๋์ ์ฒดํฌํฌ์ธํธ
๊ฐ์ฅ ํ์คํ ๋จ๊ณ๋ ๋ ๋์ ์ฒดํฌํฌ์ธํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. Stable Diffusion ๋ชจ๋ธ์ ์ข์ ์ถ๋ฐ์ ์ด๋ฉฐ, ๊ณต์ ์ถ์ ์ดํ ๋ช ๊ฐ์ง ๊ฐ์ ๋ ๋ฒ์ ๋ ์ถ์๋์์ต๋๋ค. ํ์ง๋ง ์ต์ ๋ฒ์ ์ ์ฌ์ฉํ๋ค๊ณ ํด์ ์๋์ผ๋ก ๋ ๋์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์๋ ๊ฒ์ ์๋๋๋ค. ์ฌ์ ํ ๋ค์ํ ์ฒดํฌํฌ์ธํธ๋ฅผ ์ง์ ์คํํด๋ณด๊ณ , negative prompts ์ฌ์ฉ ๋ฑ ์ฝ๊ฐ์ ์กฐ์ฌ๋ฅผ ํตํด ์ต์์ ๊ฒฐ๊ณผ๋ฅผ ์ป์ด์ผ ํฉ๋๋ค.
์ด ๋ถ์ผ๊ฐ ์ฑ์ฅํจ์ ๋ฐ๋ผ ํน์ ์คํ์ผ์ ์ฐ์ถํ ์ ์๋๋ก ์ธ๋ฐํ๊ฒ ์กฐ์ ๋ ๊ณ ํ์ง ์ฒดํฌํฌ์ธํธ๊ฐ ์ ์ ๋ ๋ง์์ง๊ณ ์์ต๋๋ค. Hub์ Diffusers Gallery๋ฅผ ๋๋ฌ๋ณด๊ณ ๊ด์ฌ ์๋ ๊ฒ์ ์ฐพ์๋ณด์ธ์!
๋ ๋์ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ ์์
ํ์ฌ ํ์ดํ๋ผ์ธ ๊ตฌ์ฑ ์์๋ฅผ ์ต์ ๋ฒ์ ์ผ๋ก ๊ต์ฒดํด ๋ณผ ์๋ ์์ต๋๋ค. Stability AI์ ์ต์ autodecoder๋ฅผ ํ์ดํ๋ผ์ธ์ ๋ก๋ํ๊ณ ๋ช ๊ฐ์ง ์ด๋ฏธ์ง๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค:
from diffusers import AutoencoderKL
vae = AutoencoderKL.from_pretrained("stabilityai/sd-vae-ft-mse", torch_dtype=torch.float16).to("cuda")
pipeline.vae = vae
images = pipeline(**get_inputs(batch_size=8)).images
image_grid(images, rows=2, cols=4)
๋ ๋์ ํ๋กฌํํธ ์์ง๋์ด๋ง
์ด๋ฏธ์ง๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉํ๋ ํ ์คํธ ํ๋กฌํํธ๋ prompt engineering์ด๋ผ๊ณ ํ ์ ๋๋ก ๋งค์ฐ ์ค์ํฉ๋๋ค. ํ๋กฌํํธ ์์ง๋์ด๋ง ์ ๊ณ ๋ คํด์ผ ํ ๋ช ๊ฐ์ง ์ฌํญ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์์ฑํ๋ ค๋ ์ด๋ฏธ์ง ๋๋ ์ ์ฌํ ์ด๋ฏธ์ง๊ฐ ์ธํฐ๋ท์ ์ด๋ป๊ฒ ์ ์ฅ๋์ด ์๋๊ฐ?
- ๋ด๊ฐ ์ํ๋ ์คํ์ผ๋ก ๋ชจ๋ธ์ ์ ๋ํ๊ธฐ ์ํด ์ด๋ค ์ถ๊ฐ ์ธ๋ถ ์ ๋ณด๋ฅผ ์ ๊ณตํ ์ ์๋๊ฐ?
์ด๋ฅผ ์ผ๋์ ๋๊ณ ์์๊ณผ ๋ ๋์ ํ์ง์ ๋ํ ์ผ์ ํฌํจํ๋๋ก ํ๋กฌํํธ๋ฅผ ๊ฐ์ ํด ๋ด ์๋ค:
prompt += ", tribal panther make up, blue on red, side profile, looking away, serious eyes"
prompt += " 50mm portrait photography, hard rim lighting photography--beta --ar 2:3 --beta --upbeta"
์๋ก์ด ํ๋กฌํํธ๋ก ์ด๋ฏธ์ง ๋ฐฐ์น๋ฅผ ์์ฑํฉ๋๋ค:
images = pipeline(**get_inputs(batch_size=8)).images
image_grid(images, rows=2, cols=4)
๊ฝค ์ธ์์ ์
๋๋ค! 1
์ ์๋๋ฅผ ๊ฐ์ง Generator
์ ํด๋นํ๋ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง์ ํผ์ฌ์ฒด์ ๋์ด์ ๋ํ ํ
์คํธ๋ฅผ ์ถ๊ฐํ์ฌ ์กฐ๊ธ ๋ ์กฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค:
prompts = [
"portrait photo of the oldest warrior chief, tribal panther make up, blue on red, side profile, looking away, serious eyes 50mm portrait photography, hard rim lighting photography--beta --ar 2:3 --beta --upbeta",
"portrait photo of a old warrior chief, tribal panther make up, blue on red, side profile, looking away, serious eyes 50mm portrait photography, hard rim lighting photography--beta --ar 2:3 --beta --upbeta",
"portrait photo of a warrior chief, tribal panther make up, blue on red, side profile, looking away, serious eyes 50mm portrait photography, hard rim lighting photography--beta --ar 2:3 --beta --upbeta",
"portrait photo of a young warrior chief, tribal panther make up, blue on red, side profile, looking away, serious eyes 50mm portrait photography, hard rim lighting photography--beta --ar 2:3 --beta --upbeta",
]
generator = [torch.Generator("cuda").manual_seed(1) for _ in range(len(prompts))]
images = pipeline(prompt=prompts, generator=generator, num_inference_steps=25).images
image_grid(images)
๋ค์ ๋จ๊ณ
์ด ํํ ๋ฆฌ์ผ์์๋ ๊ณ์ฐ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ๋์ด๊ณ ์์ฑ๋ ์ถ๋ ฅ์ ํ์ง์ ๊ฐ์ ํ๊ธฐ ์ํด [DiffusionPipeline
]์ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์ ์ต๋๋ค. ํ์ดํ๋ผ์ธ์ ๋ ๋น ๋ฅด๊ฒ ๋ง๋๋ ๋ฐ ๊ด์ฌ์ด ์๋ค๋ฉด ๋ค์ ๋ฆฌ์์ค๋ฅผ ์ดํด๋ณด์ธ์:
- PyTorch 2.0 ๋ฐ
torch.compile
์ด ์ด๋ป๊ฒ ์ถ๋ก ์๋๋ฅผ 5~300% ํฅ์์ํฌ ์ ์๋์ง ์์๋ณด์ธ์. A100 GPU์์๋ ์ถ๋ก ์๋๊ฐ ์ต๋ 50%๊น์ง ๋นจ๋ผ์ง ์ ์์ต๋๋ค! - PyTorch 2๋ฅผ ์ฌ์ฉํ ์ ์๋ ๊ฒฝ์ฐ, xFormers๋ฅผ ์ค์นํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ์ดํ ์ ๋ฉ์ปค๋์ฆ์ PyTorch 1.13.1๊ณผ ํจ๊ป ์ฌ์ฉํ๋ฉด ์๋๊ฐ ๋นจ๋ผ์ง๊ณ ๋ฉ๋ชจ๋ฆฌ ์๋น๊ฐ ์ค์ด๋ญ๋๋ค.
- ๋ชจ๋ธ ์คํ๋ก๋ฉ๊ณผ ๊ฐ์ ๋ค๋ฅธ ์ต์ ํ ๊ธฐ๋ฒ์ ์ด ๊ฐ์ด๋์์ ๋ค๋ฃจ๊ณ ์์ต๋๋ค.