본문으로 이동
메뉴 여닫기
환경 설정 메뉴 여닫기
개인 메뉴 여닫기
로그인하지 않음
지금 편집한다면 당신의 IP 주소가 공개될 수 있습니다.
ControlNet; 컨트롤넷
사전 학습된 텍스트-이미지 확산 모델을 고정해 둔 채, 윤곽선·깊이·자세 같은 공간 조건으로 생성 결과의 구도를 제어하도록 덧붙이는 신경망 구조

ControlNet(컨트롤넷)은 스테이블 디퓨전 같은 대형 텍스트-이미지 확산 모델에 공간 조건 입력을 추가하는 구조다. 원래 모델의 가중치는 잠그고(locked), 인코더 부분의 학습 가능한 사본(trainable copy)을 만들어 조건 이미지를 받게 한 뒤, 0으로 초기화한 1×1 합성곱("제로 합성곱", zero convolution)으로 두 쪽을 잇는다. 2023년 2월 스탠퍼드 대학교의 장뤼민(Lvmin Zhang), 라오안이(Anyi Rao), 머니시 아그라왈라(Maneesh Agrawala)가 발표했고,[1] 이 논문은 ICCV 2023 최우수 논문상(마르 상)을 받았다.[2]

산업 자동화에 쓰는 ODVA의 필드버스 네트워크 ControlNet과는 다른 것이다.[3]

텍스트만으로는 "사람이 왼손을 들고 오른쪽을 보는" 자세나 건물의 정확한 윤곽을 지정하기 어렵다. ControlNet은 에지 지도, 깊이 지도, 사람 골격 같은 그림을 직접 넣어 구도를 고정하고, 내용과 화풍은 프롬프트로 정하게 해 준다. 조건 종류마다 ControlNet을 따로 학습하지만 원 모델의 가중치는 건드리지 않는다.

잠근 모델과 학습 사본

편집 원본 편집

신경망 블록 ℱ(⋅;Θ)가 특성맵 x를 y로 바꾼다고 하자. ControlNet은 Θ를 고정하고, 같은 블록을 복제해 학습 가능한 매개변수 Θc를 만든 뒤 조건 벡터 c를 더해 넣는다. 두 개의 제로 합성곱 𝒵(⋅;Θz1), 𝒵(⋅;Θz2)로 연결하면 출력은 다음과 같다.[1]

yc=ℱ(x;Θ)+𝒵(ℱ(x+𝒵(c;Θz1);Θc);Θz2)

제로 합성곱은 가중치와 편향을 모두 0으로 초기화한 1×1 합성곱이다. 학습 첫 단계에서는 두 𝒵 항이 모두 0이라 yc=y, 곧 원래 모델과 똑같이 동작한다. 학습 사본도 원 모델의 가중치를 그대로 이어받았으므로, 무작위 초기화된 새 층이 내는 잡음이 수십억 장으로 학습한 백본을 망가뜨리지 않는다.

출력이 0인데 학습이 되는 이유는 기울기에 있다. 1×1 합성곱 y=Wx+b에서 W=0이어도 ∂ℒ/∂W는 입력 x에 비례하므로 0이 아니다. 그래서 첫 단계에서 바깥쪽 제로 합성곱 가중치가 먼저 0을 벗어나고, 그다음부터 안쪽 학습 사본과 첫 제로 합성곱으로 기울기가 흐른다. 아래 최소 구현에서 이를 확인할 수 있다.

스테이블 디퓨전에 붙이기

편집 원본 편집

스테이블 디퓨전의 U-Net은 인코더 12블록, 중간 블록 1개, 디코더 12블록으로 되어 있다. ControlNet은 인코더 12블록과 중간 블록의 학습 사본을 만들고, 그 출력 13개를 제로 합성곱을 거쳐 U-Net 디코더의 스킵 연결 12개와 중간 블록에 더한다.[1]

  • 조건 이미지는 512×512지만 U-Net은 64×64 잠재에서 동작하므로, 4×4 커널·보폭 2인 합성곱 4층(채널 16, 32, 64, 128, ReLU)으로 된 작은 인코더가 조건을 64×64 특성 공간으로 바꾼다. 이 인코더는 가우스 초기화하고 전체와 함께 학습한다.
  • 잠근 부분은 기울기를 계산할 필요가 없어 효율적이다. A100 PCIe 40GB 한 장에서 ControlNet을 붙인 학습은 붙이지 않은 스테이블 디퓨전 미세 조정에 비해 GPU 메모리 약 23%, 반복당 시간 약 34%만 더 들었다.[1]
  • 스테이블 디퓨전 1.5와 2.1은 U-Net 구조가 같아 같은 방식으로 붙는다.

학습 목표는 조건을 하나 더 받는 것 말고는 일반 확산 모델과 같다.

ℒ=𝔼z0,t,ct,cf,ϵ∼𝒩(0,1)[‖ϵ−ϵθ(zt,t,ct,cf)‖22]

ct는 텍스트 프롬프트, cf는 조건 이미지에서 얻은 특성이다. 학습 중 텍스트 프롬프트의 50%를 빈 문자열로 바꿔서, 프롬프트가 없을 때도 조건 이미지만 보고 의미를 알아내게 했다.[1]

논문은 "갑작스러운 수렴"(sudden convergence) 현상을 보고했다. 제로 합성곱 덕분에 학습 내내 모델은 정상적인 이미지를 내지만 조건은 무시하다가, 어느 순간(그림의 예에서는 6,133단계) 갑자기 조건을 따르기 시작한다. 보통 1만 단계가 안 되어 일어난다고 했다.[1] 학습 데이터가 5만 장 미만으로 적어도 100만 장 넘게 많아도 학습이 안정적이었고, 깊이 조건 ControlNet을 RTX 3090 Ti 한 장으로 20만 장, 5일 학습한 결과를 대형 A100 클러스터에서 1,200만 장 이상으로 학습한 스테이블 디퓨전 2 깊이 모델과 비교한 사용자 실험에서 사람들은 둘을 거의 구별하지 못했다(정답률 0.52±0.17).[1]

원 논문과 저자가 공개한 모델이 다룬 조건은 다음과 같다.[1]

조건 만드는 방법 쓰임
캐니 에지(Canny) 캐니 에지 검출기 윤곽선을 그대로 살린 재채색·화풍 변환
M-LSD 직선(허프 직선) 직선 검출기 건물, 실내 구조
HED 소프트 에지 HED 에지 검출 신경망 부드러운 윤곽 유지
낙서(scribble), 사용자 스케치 손그림, 에지 지도 변형 대충 그린 그림을 완성된 이미지로
사람 자세(OpenPose) 관절 키포인트 검출 인물 포즈 지정
분할 지도(ADE20K) 의미론적 분할 영역별 배치 지정
깊이 지도 단안 깊이 추정 모델 공간 구도·원근 유지
법선 지도(normal map) 법선 추정 표면 방향, 입체감
만화 선화 선화 추출 애니메이션풍 채색

여러 ControlNet의 출력을 그냥 더하면 여러 조건을 한꺼번에 걸 수 있다(예: 자세 + 깊이). 논문은 별도 가중치나 보간이 필요 없었다고 했다.[1]

CFG 해상도 가중

편집 원본 편집

분류기 없는 가이던스를 쓸 때 조건 이미지를 조건부·무조건부 예측 양쪽에 넣으면, 프롬프트가 없는 어려운 경우 가이던스가 완전히 사라진다. 반대로 조건부 쪽에만 넣으면 가이던스가 너무 세진다. 논문은 조건부 쪽에만 넣되, ControlNet과 스테이블 디퓨전 사이의 각 연결에 블록 해상도에 따른 가중치 wi=64/hi(h1=8,…,h13=64)를 곱하고 가이던스 세기를 낮추는 "CFG 해상도 가중"을 제안했다.[1]

diffusers의 손잡이

편집 원본 편집

Hugging Face diffusers의 StableDiffusionControlNetPipeline은 다음 인자를 제공한다.[4]

  • controlnet_conditioning_scale(기본 1.0): ControlNet 출력을 U-Net 잔차에 더하기 전에 곱하는 값. 여러 ControlNet이면 목록으로 준다.
  • control_guidance_start, control_guidance_end(기본 0.0, 1.0): 전체 단계 중 ControlNet을 적용할 구간의 비율. 초반에만 걸면 구도만 잡고 세부는 자유롭게 둔다.
  • guess_mode: 프롬프트를 지워도 ControlNet 인코더가 입력 이미지의 내용을 알아보게 하는 모드. 문서는 이때 guidance_scale을 3.0~5.0으로 권장한다.

제로 합성곱 최소 구현

편집 원본 편집

PyTorch 2.5에서 실행해, 초기 상태의 출력이 원 블록과 정확히 같고 첫 역전파에서 바깥쪽 제로 합성곱만 기울기를 받는 것을 확인했다.

import copy, torch, torch.nn as nn

def zero_conv(ch):
    conv = nn.Conv2d(ch, ch, kernel_size=1)
    nn.init.zeros_(conv.weight)
    nn.init.zeros_(conv.bias)
    return conv

class ControlledBlock(nn.Module):
    def __init__(self, block, ch):
        super().__init__()
        self.trainable = copy.deepcopy(block).requires_grad_(True)  # 학습할 사본
        self.locked = block.requires_grad_(False)                    # 원 블록은 고정
        self.z1, self.z2 = zero_conv(ch), zero_conv(ch)
    def forward(self, x, cond):
        return self.locked(x) + self.z2(self.trainable(x + self.z1(cond)))

block = nn.Sequential(nn.Conv2d(8, 8, 3, padding=1), nn.SiLU())
m = ControlledBlock(block, 8)
x, c = torch.randn(1, 8, 16, 16), torch.randn(1, 8, 16, 16)
out = m(x, c)
assert torch.equal(out, block(x))            # 처음에는 원래 모델과 동일
out.sum().backward()
print(m.z2.weight.grad.abs().sum() > 0)      # True: 바깥 제로 합성곱이 먼저 학습됨
print(m.z1.weight.grad.abs().sum())          # 0: z2가 0인 동안은 안쪽으로 기울기가 안 감

diffusers로 캐니 에지 제어

편집 원본 편집
# diffusers 0.3x~0.41 기준. 최신 판은 torch_dtype 대신 dtype 인자도 받는다.
import cv2, numpy as np, torch
from PIL import Image
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline, UniPCMultistepScheduler
from diffusers.utils import load_image

src = np.array(load_image("input.png"))
edges = cv2.Canny(src, 100, 200)                       # 저·고 임계값
control = Image.fromarray(np.stack([edges] * 3, axis=-1))

controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny", torch_dtype=torch.float16)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "stable-diffusion-v1-5/stable-diffusion-v1-5", controlnet=controlnet,
    torch_dtype=torch.float16).to("cuda")
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)

image = pipe(
    "a cozy wooden cabin in a snowy forest, golden hour",
    image=control,
    controlnet_conditioning_scale=0.8,     # 제어 세기
    control_guidance_end=0.8,              # 마지막 20% 단계는 제어 해제
    num_inference_steps=30,
).images[0]

SDXL, 스테이블 디퓨전 3, FLUX.1용 ControlNet 파이프라인도 같은 방식으로 쓴다. diffusers 공식 안내서는 FLUX.1 [dev]에 캐니 ControlNet을 붙이는 예를 기본 예제로 싣고 있다.[5]

다른 제어 방식과 비교

편집 원본 편집
방식 추가 구조 학습 비용 특징
ControlNet 인코더 전체의 학습 사본 + 제로 합성곱 큼(인코더 크기만큼 매개변수) 조건 충실도가 높음, 여러 조건 합성 가능
T2I-Adapter[6] 원 모델을 고정한 채 가벼운 어댑터만 학습 작음 조합성이 좋고 추론 부담이 적음. ControlNet과 같은 시기(2023년 2월) 발표
LoRA 미세 조정 저랭크 가중치 갱신 작음 화풍·인물 같은 "내용"을 가르치는 데 쓰고 공간 구도 제어용은 아님
전체 미세 조정(예: 깊이 조건 SD 2) 입력 채널 추가 후 전체 재학습 매우 큼 대규모 데이터와 연산 필요
인페인팅·img2img 추가 학습 없음 없음 원본 픽셀을 기반으로 하므로 구도를 바꾸며 유지하기는 어려움

diffusers 문서는 새 작업에는 ControlNet을 권하고, T2I-Adapter는 예전 스테이블 디퓨전 계열용 "레거시 어댑터"로 분류해 두었다.[5]

  1. ↑ 1.00 1.01 1.02 1.03 1.04 1.05 1.06 1.07 1.08 1.09 Lvmin Zhang, Anyi Rao, Maneesh Agrawala, 〈Adding Conditional Control to Text-to-Image Diffusion Models〉, ICCV 2023. arXiv:2302.05543
  2. ↑ Computer Vision Foundation, 〈CVF Awards〉, ICCV Best Paper Award (Marr Prize) 2023 수상 목록. thecvf.com
  3. ↑ ODVA, 〈ControlNet〉. odva.org
  4. ↑ Hugging Face, 〈pipeline_controlnet.py〉, diffusers 소스 코드. github.com
  5. ↑ 5.0 5.1 Hugging Face, 〈ControlNet〉 사용 안내서, diffusers 문서. huggingface.co
  6. ↑ Chong Mou 외, 〈T2I-Adapter: Learning Adapters to Dig out More Controllable Ability for Text-to-Image Diffusion Models〉, 2023. arXiv:2302.08453