Stable Diffusion with Python 파이썬으로 배우는 확산 모델
Chapter 08 목차
제2·3부 — 11·13장

해상도를 키우고, 형태를 다스리다

img2img 디퓨전으로 흐릿한 이미지를 초해상도로 끌어올리고, ControlNet의 학습 가능한 복사본과 zero convolution으로 포즈·윤곽을 정밀하게 제어한다.

Super-Resolution img2img ControlNet Zero Convolution Canny

Section 01업스케일 용어

Stable Diffusion이 만든 초기 창작물은 아직 최고 품질이 아닐 수 있다. 이미지를 향상하고 해상도를 키우기 전에, 세 가지 관련 용어를 구분해야 한다.

Table 11.1 — 해상도 향상 용어
용어방식
이미지 보간기존 픽셀로 새 픽셀 값을 근사 — 최근접 이웃, 이중선형, 쌍삼차, Lanczos
이미지 업스케일해상도를 키우는 모든 기술을 포괄하는 넓은 용어
이미지 초해상도딥러닝으로 고주파 패턴을 학습해, 품질 손실·아티팩트를 최소화하며 향상

초해상도 솔루션은 크게 세 부류다 — ESRGAN 같은 GAN 기반, SwinIR 같은 Swin Transformer 기반, 그리고 Stable Diffusion 기반. 이 장은 SD 업스케일러에 집중한다. SD는 프롬프트로 초해상도 과정을 지시하며 추가 디테일을 채울 수 있기 때문이다.

Pitfall — Diffusers 기본 업스케일러의 한계 Diffusers의 Latent Upscaler와 Upscale 파이프라인을 곧장 쓰고 싶겠지만, 둘 다 현재 최적이 아니다 — 특정 사전 학습 모델에 크게 의존하고, VRAM을 많이 먹으며, 느리다. 책에서는 img2img 기반ControlNet Tile 기반 두 가지 대안을 제시한다.

Section 02img2img 초해상도

Stable Diffusion은 텍스트만이 아니라 이미지를 시작점으로 쓸 수 있다. 노이즈 제거 강도(denoising strength)를 0.3 같은 낮은 값으로 두면, 초기 이미지의 특징과 스타일이 최종 결과에 유지된다. 이 속성을 이용해 SD를 업스케일러로 쓴다.

img2img 초해상도 — 256 → 512 → 1024 STEP 01 / 5
space 재생 · → 다음 · R 리셋
256×256 시작 이미지 생성
text2img_pipe = StableDiffusionPipeline.from_pretrained(
    "stablediffusionapi/deliberate-v2",
    torch_dtype = torch.float16).to("cuda:0")

raw_image = text2img_pipe(
    prompt = "a realistic photo of beautiful woman face",
    negative_prompt = "NSFW, bad anatomy",
    height = 256, width = 256,
    generator = torch.Generator("cuda").manual_seed(3)
).images[0]
Key — 다단계 초해상도 한 번의 img2img로 업스케일하는 1단계 초해상도를 먼저 익히고, 이를 반복하는 다단계 초해상도로 확장한다 — 256→512, 512→1024처럼. img2img 솔루션은 모든 SD(v1.5·SDXL) 모델과 호환되고, LoRA·긴 프롬프트·Textual Inversion 지원도 그대로 유지한다.

Section 03ControlNet이란

ControlNet은 추가 조건으로 디퓨전 모델을 제어하는 신경망 플러그인이다. 2023년 Lvmin Zhang과 Maneesh Agrawala의 논문 "Adding Conditional Control to Text-to-Image Diffusion Models"에서 소개됐다.

"제어"라는 면에서 Textual Inversion·LoRA·img2img를 떠올릴 수 있지만, ControlNet을 다르게 만드는 것은 UNet 디퓨전 과정에서 직접 작동한다는 점이다.

Table 13.1 — 제어 방법 비교
방법작동 단계사용 시나리오
Textual Embedding텍스트 인코더새 스타일·개념·얼굴 추가
LoRAUNet·CLIP 가중치 병합스타일·개념 집합 추가
Image-to-Image초기 latent 제공이미지 수정·스타일 추가
ControlNetUNet과 함께 노이즈 제거에 참여형태·포즈·콘텐츠 세부 제어
Intuition — 다른 모든 모델과 함께 작동한다 ControlNet 모델은 저자가 제공한 하나의 기본 모델에만 묶이지 않고, 같은 아키텍처의 모든 오픈 소스 checkpoint와 함께 작동한다. SD v1.5 ControlNet은 모든 SD v1.5 모델과, SDXL ControlNet은 모든 SDXL 모델과 호환된다. 만든 팀이 학습 코드까지 오픈 소스로 공개했으니, 직접 ControlNet을 훈련할 수도 있다.

Section 04ControlNet 사용법

전형적인 흐름은 이렇다 — SD로 이미지를 생성하고, 객체의 Canny 윤곽을 추출한 뒤, ControlNet의 도움으로 그 윤곽을 따라 새 이미지를 만든다. Canny는 1986년 John F. Canny가 개발한 엣지 감지 알고리즘이다.

Canny 윤곽 추출과 ControlNet 파이프라인
from controlnet_aux import CannyDetector
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline

canny = CannyDetector()
image_canny = canny(image, 30, 100)   # 하한·상한 임계값

canny_controlnet = ControlNetModel.from_pretrained(
    'takuma104/control_v11',
    subfolder='control_v11p_sd15_canny',
    torch_dtype=torch.float16)

cn_pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "stablediffusionapi/deliberate-v2",
    torch_dtype = torch.float16,
    controlnet = canny_controlnet).to("cuda")
Key — 여러 ControlNet 결합 하나의 파이프라인에 여러 ControlNet을 넣을 수 있다 — 가령 Canny와 Normal BAE를 함께. controlnet_conditioning_scale=[0.5, 0.5]처럼 각 모델에 스케일을 준다. 스케일 합이 2를 넘지 않도록 한다. 너무 높으면 원하지 않는 이미지가 나온다. SD v1.5용 ControlNet은 canny·depth·openpose·tile 등 14종, SDXL용도 별도로 제공된다.

Section 05zero convolution

ControlNet은 신경망 블록에 추가 조건을 주입해 작동한다. 학습 단계에서 대상 UNet 블록의 학습 가능한 복사본(trainable copy)을 ControlNet 블록으로 가져온다. 가우시안으로 초기화하는 일반 신경망과 달리, ControlNet은 SD 기본 모델의 사전 학습 가중치를 활용한다. 기본 모델 매개변수 대부분은 동결되고, 추가된 ControlNet 구성 요소만 처음부터 학습된다.

zero convolution — 0에서 시작해 천천히 학습 STEP 01 / 5
space 재생 · → 다음 · R 리셋
Why zero convolution still learns
y = w·x + b
∂y/∂w = x · ∂y/∂x = w · ∂y/∂b = 1
w = 0, x ≠ 0 일 때 — ∂y/∂w ≠ 0, ∂y/∂b ≠ 0 이지만 ∂y/∂x = 0 · 따라서 한 번의 경사 하강이 w를 0이 아니게 만들고, 그 순간부터 ∂y/∂x ≠ 0 — zero convolution은 점차 일반 convolution이 된다
Intuition — 천재적인 설계 zero convolution은 가중치·바이어스가 0으로 초기화된 1D convolution이다. 덕분에 단 한 번의 학습 없이도 ControlNet의 주입값이 이미지 생성에 영향을 주지 않는다 — 사이드 네트워크가 어느 단계에서도 생성을 망치지 않는다. "가중치가 0이면 학습이 안 되지 않나?"라는 의문은, 위 미분식이 풀어준다. SD UNet은 encoder 블록과 middle 블록에서만 ControlNet과 연결된다. 간단하고 효과적이다.