🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Stable Diffusion 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 9. · 22 Views

Stable Diffusion 완벽 가이드

AI 이미지 생성의 핵심 원리인 Stable Diffusion을 처음부터 끝까지 이해합니다. 노이즈에서 이미지를 만들어내는 마법 같은 과정을 초급자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어냅니다.


목차

  1. Diffusion Model 원리
  2. 노이즈 추가와 제거 과정
  3. UNet in Diffusion
  4. CLIP 텍스트 인코더
  5. Stable Diffusion 구조
  6. 프롬프트 엔지니어링

1. Diffusion Model 원리

김개발 씨는 최근 AI 이미지 생성에 관심을 갖게 되었습니다. "어떻게 텍스트만으로 이렇게 멋진 그림을 그릴 수 있을까요?" 선배 박시니어 씨가 웃으며 답했습니다.

"그건 바로 Diffusion Model의 마법이죠."

Diffusion Model은 한마디로 노이즈에서 의미 있는 이미지를 만들어내는 생성 모델입니다. 마치 안개 속에서 점점 선명해지는 풍경처럼, 무작위 노이즈를 조금씩 제거하며 원하는 이미지를 생성합니다.

이것을 제대로 이해하면 AI가 어떻게 창작을 하는지 근본 원리를 알 수 있습니다.

다음 코드를 살펴봅시다.

import torch
import numpy as np

# Diffusion 과정 시뮬레이션
def forward_diffusion(x0, timesteps=1000):
    # 깨끗한 이미지에 점진적으로 노이즈 추가
    noise = torch.randn_like(x0)

    # timestep에 따라 노이즈 강도 조절
    alpha = 1 - torch.linspace(0, 1, timesteps)

    # 각 단계별로 노이즈 섞기
    noisy_image = torch.sqrt(alpha[t]) * x0 + torch.sqrt(1 - alpha[t]) * noise

    return noisy_image, noise

김개발 씨는 회사에서 AI 이미지 생성 프로젝트를 맡게 되었습니다. 처음에는 막연했습니다.

"컴퓨터가 어떻게 그림을 그릴 수 있지?" 하지만 박시니어 씨의 설명을 듣고 나니 모든 게 명확해졌습니다. Diffusion Model이란 정확히 무엇일까요?

쉽게 비유하자면, Diffusion Model은 마치 흐릿한 사진을 점점 선명하게 복원하는 과정과 같습니다. 처음에는 완전히 뿌옇게 보이던 사진이, 시간이 지나면서 윤곽이 드러나고, 결국 완벽한 이미지가 됩니다.

AI도 똑같은 방식으로 무작위 노이즈에서 시작해 점진적으로 의미 있는 이미지를 만들어냅니다. Diffusion Model이 등장하기 전에는 어땠을까요?

초기 생성 모델들은 GAN이나 VAE 같은 방식을 사용했습니다. 하지만 이들은 학습이 불안정하고, 생성되는 이미지의 품질이 들쭉날쭉했습니다.

특히 GAN은 모드 붕괴라는 문제로 항상 비슷한 이미지만 생성하는 경우가 많았습니다. 더 큰 문제는 고해상도 이미지를 생성하기 어렵다는 점이었습니다.

바로 이런 문제를 해결하기 위해 Diffusion Model이 등장했습니다. Diffusion Model을 사용하면 안정적인 학습이 가능해집니다.

또한 고품질의 다양한 이미지를 생성할 수 있습니다. 무엇보다 수학적으로 명확한 이론에 기반하고 있어 예측 가능하다는 큰 이점이 있습니다.

Diffusion Model의 핵심 아이디어는 두 가지입니다. 첫째, Forward Process입니다.

깨끗한 이미지에 점진적으로 노이즈를 추가하는 과정입니다. 1000단계에 걸쳐 조금씩 노이즈를 섞으면, 결국 완전한 무작위 노이즈가 됩니다.

이 과정은 정해진 수식에 따라 진행되므로 예측 가능합니다. 둘째, Reverse Process입니다.

노이즈에서 다시 깨끗한 이미지로 복원하는 과정입니다. 이것이 바로 AI가 학습해야 하는 부분입니다.

신경망은 각 단계에서 "어떤 노이즈를 제거해야 하는가"를 배웁니다. 위의 코드를 살펴보겠습니다.

forward_diffusion 함수는 깨끗한 이미지 x0에 노이즈를 추가하는 과정을 구현합니다. alpha 값은 각 timestep에서 원본 이미지를 얼마나 유지할지를 결정합니다.

시간이 지날수록 alpha는 작아지고, 노이즈 비중이 커집니다. 최종적으로는 완전한 노이즈가 됩니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 패션 디자인 회사에서 신제품 컨셉 아트를 만든다고 가정해봅시다.

디자이너가 "빨간색 드레스, 우아한 스타일"이라고 텍스트를 입력하면, Diffusion Model이 수백 가지 디자인 시안을 자동으로 생성합니다. 이를 통해 디자이너는 아이디어를 빠르게 구체화할 수 있습니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 timestep을 너무 적게 설정하는 것입니다.

50단계로 줄이면 생성 속도는 빨라지지만, 이미지 품질이 크게 떨어집니다. 따라서 속도와 품질의 균형을 잘 맞춰야 합니다.

보통 50-100단계가 실용적인 선택입니다. 또 다른 중요한 점은 노이즈 스케줄입니다.

alpha 값을 어떻게 설정하느냐에 따라 생성 품질이 크게 달라집니다. 선형(linear), 코사인(cosine) 등 다양한 스케줄이 있으며, 각각 장단점이 있습니다.

다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 감탄했습니다.

"노이즈를 추가하고 제거하는 과정이 이렇게 체계적이었군요!" Diffusion Model을 제대로 이해하면 최신 AI 이미지 생성 기술의 토대를 이해할 수 있습니다. 여러분도 오늘 배운 내용을 바탕으로 직접 간단한 Diffusion Model을 구현해 보세요.

실전 팁

💡 - timestep은 보통 1000으로 시작하되, 실제 생성 시에는 50-100 단계로 샘플링합니다

  • 노이즈 스케줄은 cosine 스케줄이 linear보다 일반적으로 더 좋은 결과를 냅니다
  • GPU 메모리가 부족하면 이미지 크기를 줄이거나 배치 크기를 조절하세요

2. 노이즈 추가와 제거 과정

김개발 씨가 코드를 작성하다가 궁금해졌습니다. "노이즈를 추가하는 건 간단한데, 어떻게 다시 원래대로 돌릴 수 있을까요?" 박시니어 씨가 칠판에 그림을 그리며 설명하기 시작했습니다.

노이즈 추가와 제거 과정은 Diffusion Model의 양대 축입니다. Forward process에서는 수학적 공식으로 노이즈를 추가하고, Reverse process에서는 신경망이 학습을 통해 노이즈를 예측하고 제거합니다.

이 과정을 이해하면 AI가 어떻게 창조적인 이미지를 만드는지 깊이 있게 파악할 수 있습니다.

다음 코드를 살펴봅시다.

import torch
import torch.nn as nn

class DenoisingNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        # 노이즈 예측 네트워크
        self.model = nn.Sequential(
            nn.Conv2d(3, 64, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 3, 3, padding=1)
        )

    def forward(self, noisy_image, timestep):
        # 주어진 timestep에서 노이즈 예측
        predicted_noise = self.model(noisy_image)
        return predicted_noise

# 노이즈 제거 단계
def denoise_step(model, noisy_image, timestep, alpha):
    # 모델이 노이즈 예측
    noise_pred = model(noisy_image, timestep)

    # 예측된 노이즈를 제거하여 더 깨끗한 이미지 복원
    denoised = (noisy_image - torch.sqrt(1 - alpha) * noise_pred) / torch.sqrt(alpha)

    return denoised

김개발 씨는 이제 Forward process는 이해했지만, Reverse process가 여전히 미스터리였습니다. "노이즈를 추가하는 건 공식대로 하면 되는데, 거꾸로 가는 건 어떻게 하죠?" 박시니어 씨가 친절하게 설명했습니다.

노이즈 추가와 제거 과정을 이해하려면 두 가지를 구분해야 합니다. 첫 번째는 Forward Diffusion Process입니다.

이것은 마치 잉크를 물에 떨어뜨리는 것과 같습니다. 처음에는 잉크가 선명하지만, 시간이 지나면 물과 섞여 점점 흐려집니다.

이 과정은 물리 법칙에 따라 자동으로 일어나며, 수학적으로 정확히 모델링할 수 있습니다. 두 번째는 Reverse Diffusion Process입니다.

이것은 흐려진 잉크 물을 다시 원래의 선명한 잉크로 되돌리는 과정입니다. 현실에서는 불가능하지만, AI는 학습을 통해 이것을 해냅니다.

Forward process는 정말 간단합니다. 수학적으로 q(x_t | x_{t-1}) 로 표현되며, 이전 단계의 이미지에 가우시안 노이즈를 조금씩 추가하는 것입니다.

중요한 것은 재매개변수화 트릭입니다. 이를 통해 중간 단계를 건너뛰고 바로 t단계의 노이즈 이미지를 계산할 수 있습니다.

1000단계를 하나씩 계산하지 않아도 됩니다. Reverse process는 어떻게 작동할까요?

핵심은 신경망이 각 단계에서 추가된 노이즈를 예측하는 것입니다. 완벽하게 예측하면 정확히 노이즈를 제거할 수 있습니다.

하지만 완벽한 예측은 불가능하므로, 조금씩 오차가 누적됩니다. 그래도 1000단계에 걸쳐 천천히 진행하면 놀랍도록 좋은 결과를 얻을 수 있습니다.

위의 코드를 자세히 살펴보겠습니다. DenoisingNetwork는 간단한 CNN 구조입니다.

실제로는 훨씬 복잡한 UNet 구조를 사용하지만, 원리는 같습니다. 노이즈가 섞인 이미지를 입력받아, 어떤 노이즈가 추가되었는지 예측합니다.

denoise_step 함수는 한 단계의 노이즈 제거를 수행합니다. 모델이 예측한 노이즈를 현재 이미지에서 빼내면, 조금 더 깨끗한 이미지가 됩니다.

이것을 1000번 반복하면 완전한 이미지가 생성됩니다. 여기서 중요한 수학적 트릭이 있습니다.

단순히 노이즈를 빼는 것이 아니라, alpha 값으로 스케일을 조정합니다. 이것은 변분 추론에서 나온 공식입니다.

이를 통해 각 단계에서 정확한 양만큼 노이즈를 제거할 수 있습니다. 실제 프로젝트에서는 어떻게 활용할까요?

영화 제작사에서 컨셉 아트를 만드는 상황을 생각해봅시다. 감독이 "외계 행성의 일몰 풍경"이라고 요청하면, Diffusion Model이 무작위 노이즈에서 시작해 점진적으로 이미지를 구체화합니다.

처음 100단계는 대략적인 구도가 잡히고, 중간 단계에서 색상과 형태가 명확해지며, 마지막 단계에서 세밀한 디테일이 추가됩니다. 하지만 주의할 점이 있습니다.

초보자들이 자주 하는 실수는 노이즈 예측 네트워크의 출력을 잘못 해석하는 것입니다. 네트워크는 노이즈를 예측하는 것이지, 깨끗한 이미지를 직접 예측하는 것이 아닙니다.

이 차이가 중요합니다. 노이즈를 예측하는 것이 학습이 더 안정적이고 결과도 좋습니다.

또 다른 중요한 개념은 분산 스케줄입니다. 각 단계에서 추가할 노이즈의 양을 조절하는 것입니다.

너무 많으면 정보가 빨리 손실되고, 너무 적으면 충분히 노이즈가 섞이지 않습니다. 적절한 밸런스가 핵심입니다.

다시 김개발 씨의 이야기로 돌아가봅시다. "아하!

그러니까 forward는 정해진 공식이고, reverse는 AI가 배우는 거군요!" 김개발 씨가 무릎을 쳤습니다. 박시니어 씨가 웃으며 고개를 끄덕였습니다.

"맞아요. Forward는 데이터를 만드는 과정이고, reverse가 진짜 생성 과정이죠." 노이즈 추가와 제거 과정을 제대로 이해하면 Diffusion Model의 핵심을 파악한 것입니다.

이제 더 복잡한 구조로 나아갈 준비가 되었습니다.

실전 팁

💡 - 노이즈 예측 방식이 이미지 직접 예측보다 학습이 안정적입니다

  • timestep 정보를 네트워크에 꼭 입력해야 각 단계에 맞는 노이즈를 예측할 수 있습니다
  • 실제 구현에서는 DDPM, DDIM 등 다양한 샘플링 방법을 선택할 수 있습니다

3. UNet in Diffusion

김개발 씨가 네트워크 구조를 보다가 놀랐습니다. "이 구조는 어디서 본 것 같은데요?" 박시니어 씨가 미소 지으며 답했습니다.

"맞아요. 의료 영상 분할에서 유명한 UNet이죠.

Diffusion에서도 핵심 역할을 합니다."

UNet은 Diffusion Model의 노이즈 예측 네트워크로 사용되는 대표적인 구조입니다. 인코더-디코더 구조에 스킵 연결을 더해, 고해상도 정보를 보존하면서도 전역적인 맥락을 이해할 수 있습니다.

Stable Diffusion의 핵심 엔진이라 할 수 있습니다.

다음 코드를 살펴봅시다.

import torch.nn as nn

class UNetBlock(nn.Module):
    def __init__(self, in_ch, out_ch):
        super().__init__()
        # 다운샘플링 블록
        self.conv1 = nn.Conv2d(in_ch, out_ch, 3, padding=1)
        self.conv2 = nn.Conv2d(out_ch, out_ch, 3, padding=1)
        self.pool = nn.MaxPool2d(2)
        self.relu = nn.ReLU()

    def forward(self, x):
        # 특징 추출
        x1 = self.relu(self.conv1(x))
        x2 = self.relu(self.conv2(x1))
        # 스킵 연결을 위해 저장
        skip = x2
        # 다운샘플링
        x_down = self.pool(x2)
        return x_down, skip

class UNet(nn.Module):
    def __init__(self):
        super().__init__()
        # 인코더: 점진적으로 공간 해상도 감소
        self.enc1 = UNetBlock(3, 64)
        self.enc2 = UNetBlock(64, 128)
        # 디코더: 해상도 복원하며 스킵 연결 활용
        self.dec1 = nn.ConvTranspose2d(128, 64, 2, stride=2)
        self.dec2 = nn.Conv2d(128, 3, 3, padding=1)

김개발 씨는 Diffusion Model에서 가장 중요한 부분이 노이즈 예측 네트워크라는 것을 알게 되었습니다. "그런데 왜 하필 UNet을 사용하나요?" 박시니어 씨가 칠판에 구조를 그리기 시작했습니다.

UNet이란 정확히 무엇일까요? 쉽게 비유하자면, UNet은 마치 현미경과 망원경을 동시에 사용하는 것과 같습니다.

현미경으로 세밀한 디테일을 보면서도, 망원경으로 전체적인 구도를 파악합니다. 이 두 가지 정보를 결합하면 완벽한 그림을 그릴 수 있습니다.

UNet은 원래 2015년에 의료 영상 분할을 위해 개발되었습니다. 의사들이 CT 스캔에서 종양 영역을 정확히 찾아내야 하는 상황을 생각해봅시다.

단순한 CNN은 세밀한 경계선을 잘 잡아내지 못했습니다. 이미지가 깊은 레이어를 거치면서 해상도가 줄어들어 정보가 손실되기 때문입니다.

바로 이런 문제를 해결하기 위해 UNet의 독특한 구조가 탄생했습니다. UNet은 U자 모양의 구조를 가지고 있습니다.

왼쪽 아래로 내려가는 인코더와 오른쪽 위로 올라가는 디코더, 그리고 이들을 연결하는 스킵 연결로 구성됩니다. 인코더 부분을 먼저 살펴봅시다.

인코더는 이미지를 점진적으로 축소하면서 추상적인 특징을 추출합니다. 64x64 이미지가 32x32, 16x16, 8x8로 줄어들며, 채널 수는 64, 128, 256으로 증가합니다.

공간 정보는 줄어들지만, 의미론적 정보는 풍부해집니다. "이 부분은 하늘이고, 저 부분은 산이다" 같은 전역적인 이해가 가능해집니다.

디코더는 반대 과정을 수행합니다. 압축된 특징을 다시 원래 해상도로 복원합니다.

Transposed Convolution이나 Upsampling을 사용해 해상도를 두 배씩 키웁니다. 하지만 단순히 확대만 하면 디테일이 뭉개집니다.

여기서 UNet의 핵심인 스킵 연결이 등장합니다. 스킵 연결은 인코더의 고해상도 특징맵을 디코더로 직접 전달합니다.

마치 다리를 놓는 것과 같습니다. 이를 통해 디코더는 전역적인 맥락과 세밀한 디테일을 동시에 활용할 수 있습니다.

이것이 UNet의 가장 큰 혁신입니다. 위의 코드를 분석해봅시다.

UNetBlock은 두 번의 컨볼루션과 맥스풀링으로 구성됩니다. 중요한 것은 풀링 전의 특징맵을 skip으로 저장한다는 점입니다.

이것이 나중에 디코더로 전달됩니다. 디코더에서는 ConvTranspose2d로 해상도를 복원합니다.

그리고 인코더에서 저장한 skip과 concatenate하여 128채널로 만듭니다. 이렇게 합쳐진 특징은 풍부한 정보를 담고 있습니다.

Diffusion Model에서 UNet이 특히 중요한 이유가 있습니다. 노이즈 예측은 매우 어려운 작업입니다.

전체 이미지의 맥락을 이해하면서도, 픽셀 단위의 정확한 노이즈 값을 예측해야 합니다. UNet의 구조가 이것에 완벽히 맞아떨어집니다.

전역적인 구조는 인코더가, 세밀한 디테일은 스킵 연결이 담당합니다. 실제 Stable Diffusion에서는 더욱 발전된 UNet을 사용합니다.

Self-Attention 레이어를 추가해 먼 거리의 픽셀 간 관계도 모델링합니다. Time Embedding을 넣어 현재 timestep 정보를 네트워크에 알려줍니다.

Cross-Attention으로 텍스트 프롬프트 정보를 주입합니다. 이 모든 것이 UNet 구조 위에 구축됩니다.

실무에서 UNet을 사용할 때 주의할 점이 있습니다. 메모리 사용량이 매우 큽니다.

특히 스킵 연결 때문에 중간 특징맵을 모두 저장해야 합니다. 512x512 이미지를 처리하려면 수십 GB의 GPU 메모리가 필요할 수 있습니다.

따라서 Gradient Checkpointing 같은 최적화 기법을 활용해야 합니다. 또 다른 중요한 포인트는 정규화입니다.

Diffusion UNet에서는 주로 Group Normalization을 사용합니다. Batch Normalization보다 작은 배치 크기에서도 안정적이기 때문입니다.

다시 김개발 씨의 이야기로 돌아가봅시다. "아, 그래서 UNet이 Diffusion의 심장이라고 하는군요!" 김개발 씨가 감탄했습니다.

박시니어 씨가 고개를 끄덕였습니다. "맞아요.

좋은 UNet 없이는 좋은 Diffusion Model도 없습니다." UNet의 구조를 이해하면 왜 Diffusion Model이 고품질 이미지를 생성할 수 있는지 알 수 있습니다. 여러분도 직접 UNet을 구현하며 각 부분의 역할을 체험해보세요.

실전 팁

💡 - 스킵 연결은 concatenation 외에도 addition 방식이 있으며, 각각 장단점이 있습니다

  • Self-Attention은 계산량이 많으므로 보통 낮은 해상도 레이어에만 적용합니다
  • Group Normalization의 그룹 수는 보통 32로 설정합니다

4. CLIP 텍스트 인코더

김개발 씨가 프롬프트를 입력하며 신기해했습니다. "어떻게 텍스트로 이미지를 조종할 수 있을까요?" 박시니어 씨가 설명했습니다.

"그건 CLIP 덕분입니다. 텍스트와 이미지를 같은 공간에 매핑하는 마법이죠."

CLIP 텍스트 인코더는 사용자의 텍스트 프롬프트를 벡터로 변환하여 Diffusion Model에 전달하는 핵심 구성요소입니다. OpenAI가 개발한 CLIP 모델은 텍스트와 이미지를 동일한 임베딩 공간에 표현하여, AI가 "빨간 사과"라는 텍스트를 실제 사과 이미지와 연결할 수 있게 합니다.

다음 코드를 살펴봅시다.

from transformers import CLIPTokenizer, CLIPTextModel
import torch

# CLIP 텍스트 인코더 로드
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14")
text_encoder = CLIPTextModel.from_pretrained("openai/clip-vit-large-patch14")

def encode_prompt(prompt):
    # 텍스트를 토큰으로 변환
    text_inputs = tokenizer(
        prompt,
        padding="max_length",
        max_length=77,  # CLIP의 최대 토큰 길이
        truncation=True,
        return_tensors="pt"
    )

    # 텍스트 임베딩 생성
    with torch.no_grad():
        text_embeddings = text_encoder(text_inputs.input_ids)[0]

    # 768차원 벡터로 각 토큰 표현
    return text_embeddings

# 사용 예시
prompt = "a beautiful sunset over mountains, oil painting style"
embedding = encode_prompt(prompt)
print(f"Embedding shape: {embedding.shape}")  # [1, 77, 768]

김개발 씨는 텍스트만으로 이미지를 생성하는 과정이 매우 궁금했습니다. "컴퓨터가 '빨간 사과'라는 단어를 어떻게 이해하죠?" 박시니어 씨가 CLIP의 원리를 설명하기 시작했습니다.

CLIP이란 정확히 무엇일까요? 쉽게 비유하자면, CLIP은 마치 동시 통역사와 같습니다.

한국어를 영어로 번역하는 것처럼, 텍스트 언어를 이미지 언어로 번역합니다. 더 정확히는 둘 다 같은 중간 언어(임베딩 공간)로 번역하여, 서로 비교할 수 있게 만듭니다.

CLIP이 등장하기 전에는 어땠을까요? 기존의 이미지 생성 모델들은 클래스 레이블만 사용할 수 있었습니다.

"고양이", "개", "자동차" 같은 단순한 카테고리만 가능했습니다. "석양 아래 산 위의 고독한 늑대"처럼 복잡한 설명은 불가능했습니다.

창의성이 크게 제한되었습니다. 2021년, OpenAI가 CLIP을 공개하면서 모든 것이 바뀌었습니다.

CLIP은 Contrastive Language-Image Pre-training의 약자입니다. 4억 개의 이미지-텍스트 쌍으로 학습되었습니다.

학습 방식이 독특합니다. 같이 있던 이미지와 텍스트는 가까워지게, 다른 이미지와 텍스트는 멀어지게 학습합니다.

이를 대조 학습이라고 합니다. CLIP의 구조는 두 부분으로 나뉩니다.

첫째, 텍스트 인코더입니다. 트랜스포머 구조를 사용합니다.

"a cat sitting on a chair"라는 문장을 77개의 토큰으로 나누고, 각 토큰을 768차원 벡터로 변환합니다. 마지막에는 전체 문장의 의미를 담은 하나의 벡터가 나옵니다.

둘째, 이미지 인코더입니다. Vision Transformer나 ResNet을 사용합니다.

이미지를 패치로 나누어 처리하며, 최종적으로 텍스트 인코더와 같은 차원의 벡터를 출력합니다. 핵심은 이 두 벡터가 같은 공간에 있다는 것입니다.

"빨간 사과" 텍스트 벡터와 실제 빨간 사과 이미지 벡터가 가까이 위치합니다. 코사인 유사도로 측정하면 0.8 이상 나옵니다.

반면 "빨간 사과"와 "파란 자동차" 이미지는 멀리 떨어져 있습니다. 위의 코드를 자세히 살펴봅시다.

CLIPTokenizer는 텍스트를 숫자 ID로 변환합니다. 최대 77개 토큰까지 가능하며, 짧으면 패딩하고 길면 잘라냅니다.

"a", "beautiful", "sunset" 같은 단어들이 각각 숫자로 바뀝니다. CLIPTextModel은 이 토큰 ID를 받아 임베딩을 생성합니다.

출력 shape은 [배치, 77, 768]입니다. 77개 각 토큰마다 768차원 벡터가 있습니다.

이것이 바로 UNet의 Cross-Attention에 입력됩니다. Stable Diffusion에서 CLIP이 작동하는 과정을 따라가봅시다.

사용자가 "a cute puppy playing in the garden"을 입력하면, CLIP이 이것을 임베딩으로 변환합니다. 이 임베딩은 UNet의 각 레이어에 Cross-Attention을 통해 주입됩니다.

UNet은 노이즈를 제거하면서 이 텍스트 정보를 계속 참고합니다. "강아지를 그려야 하고, 정원 배경을 넣어야 하고, 귀여운 느낌을 줘야 한다"는 정보를 매 단계마다 확인합니다.

실제 프로젝트에서 CLIP을 활용하는 팁이 있습니다. 프롬프트 엔지니어링이 매우 중요합니다.

CLIP은 특정 키워드에 민감합니다. "highly detailed", "4k", "artstation" 같은 단어를 추가하면 품질이 크게 향상됩니다.

이는 학습 데이터에 이런 태그가 달린 고품질 이미지가 많았기 때문입니다. 또 다른 중요한 개념은 네거티브 프롬프트입니다.

"blurry", "low quality", "watermark" 같은 원하지 않는 요소를 명시할 수 있습니다. 이것도 CLIP으로 인코딩되어, UNet이 이런 특징을 피하도록 유도합니다.

이를 Classifier-Free Guidance라고 합니다. 하지만 CLIP의 한계도 있습니다.

공간적 관계를 잘 이해하지 못합니다. "왼쪽에 빨간 공, 오른쪽에 파란 공"이라고 해도 위치가 뒤바뀔 수 있습니다.

또한 숫자를 정확히 세지 못합니다. "세 마리의 고양이"라고 해도 두 마리나 네 마리가 나올 수 있습니다.

최근에는 이를 개선한 모델들이 나오고 있습니다. CLIP-L, OpenCLIP, CLIPA 등 더 큰 데이터셋으로 학습한 버전들이 있습니다.

Stable Diffusion XL은 두 개의 CLIP 인코더를 동시에 사용해 성능을 높였습니다. 다시 김개발 씨의 이야기로 돌아가봅시다.

"와, CLIP이 단순히 텍스트를 벡터로 바꾸는 게 아니라, 의미를 이해하는 거였네요!" 김개발 씨가 눈을 반짝였습니다. 박시니어 씨가 미소 지으며 답했습니다.

"맞아요. CLIP 없이는 텍스트 투 이미지가 불가능합니다." CLIP 텍스트 인코더를 이해하면 프롬프트를 더 효과적으로 작성할 수 있습니다.

여러분도 다양한 프롬프트를 실험하며 CLIP의 특성을 파악해보세요.

실전 팁

💡 - 프롬프트 앞부분에 중요한 키워드를 배치하면 더 잘 반영됩니다

  • 쉼표로 구분하면 여러 개념을 명확히 전달할 수 있습니다
  • CLIP은 영어로 학습되었으므로 영어 프롬프트가 가장 효과적입니다

5. Stable Diffusion 구조

김개발 씨가 전체 파이프라인을 보며 감탄했습니다. "이 모든 게 어떻게 조화롭게 작동하죠?" 박시니어 씨가 Stable Diffusion의 전체 구조를 설명하기 시작했습니다.

"각 퍼즐 조각이 완벽하게 맞아떨어지는 걸 보게 될 겁니다."

Stable Diffusion은 VAE, UNet, CLIP을 결합한 완전한 텍스트 투 이미지 시스템입니다. VAE가 이미지를 압축된 잠재 공간으로 변환하고, UNet이 이 공간에서 노이즈를 제거하며, CLIP이 텍스트 조건을 제공합니다.

이 세 가지가 유기적으로 결합되어 놀라운 이미지 생성 능력을 발휘합니다.

다음 코드를 살펴봅시다.

from diffusers import StableDiffusionPipeline
import torch

# Stable Diffusion 전체 파이프라인 로드
pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    torch_dtype=torch.float16
).to("cuda")

def generate_image(prompt, negative_prompt="", steps=50, guidance_scale=7.5):
    # 1. CLIP으로 프롬프트 인코딩
    # 2. 랜덤 노이즈에서 시작
    # 3. UNet으로 반복적 노이즈 제거 (잠재 공간에서)
    # 4. VAE 디코더로 이미지 복원

    image = pipe(
        prompt=prompt,
        negative_prompt=negative_prompt,
        num_inference_steps=steps,
        guidance_scale=guidance_scale,
        height=512,
        width=512
    ).images[0]

    return image

# 이미지 생성
result = generate_image(
    prompt="a serene Japanese garden with cherry blossoms, photorealistic",
    negative_prompt="blurry, low quality, distorted",
    steps=50,
    guidance_scale=7.5
)

result.save("output.png")

김개발 씨는 이제 개별 구성요소는 이해했지만, 전체 그림이 궁금했습니다. "이것들이 어떻게 하나로 합쳐지나요?" 박시니어 씨가 화이트보드에 큰 그림을 그리기 시작했습니다.

Stable Diffusion의 전체 구조를 이해하려면 세 개의 핵심 모듈을 알아야 합니다. 첫 번째는 VAE입니다.

VAE는 Variational Autoencoder의 약자로, 이미지를 압축하고 복원하는 역할을 합니다. 마치 사진을 ZIP 파일로 압축하는 것과 비슷합니다.

512x512x3 이미지가 64x64x4로 줄어듭니다. 64배나 작아집니다.

이것을 잠재 공간이라고 부릅니다. 두 번째는 UNet입니다.

앞서 배웠듯이 노이즈를 예측하고 제거하는 핵심 네트워크입니다. 중요한 점은 원본 이미지 공간이 아닌 잠재 공간에서 작동한다는 것입니다.

64x64 크기로 작업하므로 계산량이 64배 줄어듭니다. 세 번째는 CLIP 텍스트 인코더입니다.

사용자의 프롬프트를 임베딩으로 변환하여 UNet에 조건으로 제공합니다. 이제 전체 생성 과정을 단계별로 따라가봅시다.

단계 1: 텍스트 인코딩입니다. 사용자가 "a serene Japanese garden"을 입력하면, CLIP이 이것을 [1, 77, 768] 크기의 텍스트 임베딩으로 변환합니다.

이것은 프롬프트의 "의미"를 담은 벡터입니다. 단계 2: 잠재 노이즈 초기화입니다.

64x64x4 크기의 랜덤 가우시안 노이즈를 생성합니다. 이것이 생성의 시작점입니다.

매번 다른 노이즈로 시작하므로 같은 프롬프트로도 다른 이미지가 나옵니다. 단계 3: 반복적 노이즈 제거입니다.

이것이 핵심 과정입니다. 50-100단계에 걸쳐 UNet이 노이즈를 조금씩 제거합니다.

각 단계에서 UNet은 현재 노이즈 이미지, timestep, 텍스트 임베딩을 입력받아 노이즈를 예측합니다. 예측된 노이즈를 제거하면 조금 더 깨끗한 잠재 표현이 됩니다.

단계 4: 이미지 디코딩입니다. 최종 잠재 표현을 VAE 디코더에 입력합니다.

디코더는 64x64x4를 512x512x3 이미지로 복원합니다. 이것이 최종 출력 이미지입니다.

여기서 중요한 개념인 Classifier-Free Guidance를 알아봅시다. 단순히 텍스트 조건만 주면 이미지가 프롬프트를 약하게 따릅니다.

이를 강화하기 위해 두 번의 UNet 예측을 수행합니다. 한 번은 텍스트 조건 있이, 한 번은 없이 예측합니다.

그리고 차이를 증폭시킵니다. 수식으로 표현하면 noise = noise_uncond + guidance_scale * (noise_cond - noise_uncond) 입니다.

guidance_scale이 클수록 프롬프트를 강하게 따르지만, 너무 크면 부자연스러워집니다. 보통 7-10 사이 값을 사용합니다.

위의 코드를 분석해봅시다. StableDiffusionPipeline은 모든 구성요소를 하나로 묶은 편리한 인터페이스입니다.

from_pretrained로 사전 학습된 가중치를 로드합니다. torch.float16을 사용하면 메모리를 절반으로 줄일 수 있습니다.

generate_image 함수는 한 줄로 복잡한 과정을 실행합니다. 내부적으로는 앞서 설명한 4단계가 모두 진행됩니다.

num_inference_steps가 많을수록 품질이 좋지만 느립니다. 50이 일반적인 선택입니다.

실제 프로젝트에서 Stable Diffusion을 활용하는 사례를 봅시다. 광고 회사에서 제품 홍보 이미지를 만든다고 가정해봅시다.

디자이너가 "luxury watch on velvet cushion, studio lighting"이라고 입력하면, 수십 가지 변형이 생성됩니다. 마음에 드는 것을 골라 세부 조정합니다.

Image-to-Image 기능으로 러프 스케치를 완성된 그림으로 만들 수도 있습니다. 또 다른 활용은 Inpainting입니다.

이미지의 일부분만 수정할 수 있습니다. 사진에서 배경을 바꾸거나, 객체를 추가하거나, 불필요한 요소를 제거합니다.

마스크로 영역을 지정하면 그 부분만 재생성됩니다. Stable Diffusion의 장점은 무엇일까요?

첫째, 오픈소스입니다. 누구나 무료로 사용할 수 있습니다.

둘째, 효율적입니다. 잠재 공간에서 작동하므로 소비자용 GPU로도 실행 가능합니다.

셋째, 확장 가능합니다. LoRA, ControlNet, IP-Adapter 같은 다양한 확장을 추가할 수 있습니다.

하지만 한계도 있습니다. 텍스트를 정확히 렌더링하지 못합니다.

손가락이나 얼굴이 이상하게 나올 수 있습니다. 복잡한 공간 관계를 이해하지 못합니다.

이런 문제를 해결하기 위해 ControlNet이나 Depth Conditioning 같은 기법이 등장했습니다. 최신 버전인 Stable Diffusion XL은 많은 개선을 가져왔습니다.

더 큰 UNet, 두 개의 CLIP 인코더, 1024x1024 해상도를 지원합니다. 특히 손과 얼굴이 크게 개선되었습니다.

Refiner 모델을 추가로 사용하면 디테일이 더욱 향상됩니다. 다시 김개발 씨의 이야기로 돌아가봅시다.

"이제 전체 퍼즐이 맞춰졌네요! VAE가 압축하고, UNet이 생성하고, CLIP이 안내하는군요!" 김개발 씨가 감탄했습니다.

박시니어 씨가 고개를 끄덕였습니다. "맞아요.

각 부분이 완벽하게 협력하는 아름다운 시스템이죠." Stable Diffusion의 전체 구조를 이해하면 더 효과적으로 활용할 수 있습니다. 파라미터를 조정하고, 확장 기능을 추가하며, 나만의 모델을 파인튜닝할 수 있습니다.

실전 팁

💡 - guidance_scale은 7-10 사이가 적절하며, 너무 높으면 과포화됩니다

  • 배치 생성으로 여러 변형을 한번에 만들어 최적을 고를 수 있습니다
  • Karras 스케줄러가 DPM 스케줄러보다 일반적으로 더 좋은 결과를 냅니다

6. 프롬프트 엔지니어링

김개발 씨가 생성한 이미지를 보며 고민했습니다. "같은 모델인데 왜 어떤 사람은 훨씬 좋은 결과를 낼까요?" 박시니어 씨가 웃으며 답했습니다.

"비결은 프롬프트 엔지니어링입니다. 올바른 질문법을 배워야죠."

프롬프트 엔지니어링은 원하는 이미지를 얻기 위해 텍스트 프롬프트를 최적화하는 기술입니다. 키워드 선택, 순서, 가중치, 네거티브 프롬프트 등을 조합하여 AI의 출력을 정밀하게 제어합니다.

같은 모델로도 프롬프트에 따라 천차만별의 결과가 나옵니다.

다음 코드를 살펴봅시다.

from diffusers import StableDiffusionPipeline
import torch

pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    torch_dtype=torch.float16
).to("cuda")

# 기본 프롬프트 (일반적인 결과)
basic_prompt = "a cat"

# 향상된 프롬프트 (고품질 결과)
advanced_prompt = """
a majestic fluffy cat, piercing blue eyes, sitting on a velvet cushion,
soft natural lighting, shallow depth of field, highly detailed fur texture,
professional photography, 8k uhd, dslr, film grain,
Fujifilm XT3, award winning photo
"""

# 네거티브 프롬프트 (피할 요소)
negative = """
ugly, blurry, low quality, distorted, deformed, bad anatomy,
watermark, text, cropped, worst quality, low resolution
"""

# 생성
image = pipe(
    prompt=advanced_prompt,
    negative_prompt=negative,
    num_inference_steps=50,
    guidance_scale=8.0  # 프롬프트 준수 강도
).images[0]

김개발 씨는 Stable Diffusion을 처음 사용하며 좌절했습니다. "고양이"라고 입력했는데, 뭔가 이상한 그림이 나왔습니다.

반면 선배가 만든 이미지는 사진처럼 완벽했습니다. "대체 무슨 주문을 외운 거죠?" 박시니어 씨가 프롬프트 엔지니어링의 세계를 열어주었습니다.

프롬프트 엔지니어링이란 정확히 무엇일까요? 쉽게 비유하자면, AI와 대화하는 특별한 언어를 배우는 것과 같습니다.

마치 구글 검색을 잘하려면 검색어 조합 기술이 필요하듯, Stable Diffusion도 효과적인 프롬프트 작성법이 있습니다. 같은 의도라도 표현 방식에 따라 결과가 천지차이입니다.

초보자들이 흔히 하는 실수가 있습니다. 너무 짧게 쓰는 것입니다.

"cat"이라고만 하면 AI는 무엇을 그려야 할지 모호합니다. 어떤 품종인지, 어떤 포즈인지, 어떤 배경인지 알 수 없습니다.

결과는 평범하고 일반적인 고양이 그림이 됩니다. 효과적인 프롬프트는 구조화되어 있습니다.

주제를 먼저 명시합니다. "a majestic fluffy Persian cat" 처럼 구체적으로 씁니다.

다음은 세부 특징입니다. "piercing blue eyes, sitting on a velvet cushion" 같은 디테일을 추가합니다.

그다음은 스타일과 품질 키워드입니다. 여기가 핵심입니다.

"highly detailed", "professional photography", "8k uhd" 같은 단어는 마법 같은 효과가 있습니다. CLIP이 학습 데이터에서 이런 태그가 달린 고품질 이미지를 많이 봤기 때문입니다.

AI는 이런 키워드를 보면 "아, 고품질을 원하는구나"라고 이해합니다. 조명과 카메라 설정도 중요합니다.

"soft natural lighting", "shallow depth of field", "bokeh effect" 같은 사진 용어를 사용하면 더욱 전문적인 결과가 나옵니다. "golden hour lighting"이라고 하면 따뜻한 저녁 빛이, "studio lighting"이라고 하면 균일한 조명이 적용됩니다.

아티스트나 스타일 참조도 효과적입니다. "in the style of Greg Rutkowski", "trending on ArtStation", "Unreal Engine" 같은 표현은 특정 예술 스타일을 지정합니다.

학습 데이터에 이런 태그가 많았으므로 AI가 잘 이해합니다. 위의 코드를 살펴봅시다.

기본 프롬프트와 향상된 프롬프트의 차이가 극명합니다. 향상된 버전은 여러 층위의 정보를 담고 있습니다.

주제, 디테일, 조명, 품질, 카메라 설정까지 명시합니다. 네거티브 프롬프트도 매우 중요합니다.

원하지 않는 요소를 명시합니다. "blurry", "low quality", "deformed" 같은 단어로 품질 저하를 방지합니다.

"watermark", "text"로 불필요한 텍스트를 제거합니다. 이것은 Classifier-Free Guidance의 일부로, AI가 이런 특징을 피하도록 유도합니다.

프롬프트 순서도 영향을 미칩니다. 앞부분에 있는 키워드가 더 강하게 반영됩니다.

가장 중요한 요소를 맨 앞에 배치하세요. "a cat, masterpiece"보다 "masterpiece, a cat"이 품질 향상에 더 효과적입니다.

실전 프롬프트 엔지니어링 기법을 알아봅시다. 가중치 조절이 가능합니다.

(keyword:1.5) 처럼 괄호와 숫자로 특정 단어의 중요도를 높일 수 있습니다. (beautiful:1.3)은 "beautiful"을 30% 더 강조합니다.

반대로 (dark:0.7)은 어둡게 하는 효과를 줄입니다. 프롬프트 믹싱도 가능합니다.

[keyword1:keyword2:0.5] 형식으로 생성 중간에 프롬프트를 바꿀 수 있습니다. 처음 50%는 keyword1을 따르고, 나머지는 keyword2를 따릅니다.

실제 활용 사례를 봅시다. 게임 컨셉 아트를 만든다면 "epic fantasy castle on a cliff, dramatic storm clouds, lightning, medieval architecture, highly detailed, concept art by Dylan Cole, matte painting, trending on ArtStation, 4k" 같은 프롬프트를 사용합니다.

인물 사진을 만든다면 "portrait of a young woman, natural beauty, soft smile, rim lighting, shallow depth of field, 85mm lens, f/1.4, professional headshot, neutral background" 처럼 구체적인 카메라 설정을 명시합니다. 커뮤니티에서 검증된 마법 키워드들이 있습니다.

품질 향상: "masterpiece", "best quality", "highly detailed", "8k uhd", "professional", "award winning" 사실적 표현: "photorealistic", "hyperrealistic", "dslr", "film grain", "natural lighting" 예술적 표현: "artstation", "concept art", "digital painting", "illustration", "trending" 하지만 주의할 점도 있습니다. 키워드를 너무 많이 넣으면 오히려 역효과입니다.

AI가 혼란스러워하며 일관성이 떨어집니다. 10-15개 핵심 키워드로 간결하게 작성하는 것이 좋습니다.

또한 모순된 키워드는 피해야 합니다. "bright sunny day, dark night" 처럼 서로 상충되는 요소를 넣으면 이상한 결과가 나옵니다.

최신 트렌드는 프롬프트 템플릿입니다. Civitai, PromptHero 같은 사이트에서 검증된 프롬프트 템플릿을 찾을 수 있습니다.

초보자는 이런 템플릿을 참고하며 배우는 것이 효과적입니다. 다시 김개발 씨의 이야기로 돌아가봅시다.

"이제 알겠어요! 단순히 원하는 것을 말하는 게 아니라, AI가 이해하는 방식으로 말해야 하는군요!" 김개발 씨가 눈을 반짝이며 여러 프롬프트를 실험하기 시작했습니다.

박시니어 씨가 미소 지으며 말했습니다. "맞아요.

연습할수록 실력이 늘어요. 계속 실험해보세요." 프롬프트 엔지니어링을 마스터하면 Stable Diffusion을 자유자재로 다룰 수 있습니다.

여러분도 다양한 프롬프트를 시도하며 자신만의 노하우를 쌓아보세요.

실전 팁

💡 - 쉼표로 키워드를 명확히 구분하면 AI가 더 잘 이해합니다

  • Seed 값을 고정하고 프롬프트만 바꿔가며 실험하면 효과를 비교할 수 있습니다
  • 커뮤니티에서 공유되는 프롬프트를 분석하며 패턴을 익히세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Stable Diffusion#Diffusion Model#UNet#CLIP#Prompt Engineering#Diffusion,Stable Diffusion

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.