Notice
Recent Posts
Recent Comments
Link
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
Today
Total
관리 메뉴

빙글빙글 돌아가는 바람개비

[Study] ControlNet으로 이미지 제어하기 본문

Image Generation/Study

[Study] ControlNet으로 이미지 제어하기

바람개비은하 2026. 3. 20. 13:52

ControlNet

Stable Diffusion 으로 이미지를 생성하다 보면 한계에 부딪히는 순간이 있다. 프롬프트로 스타일과 내용은 어느 정도 제어할 수 있지만, "정확히 이 포즈로", "딱 이 구도로", "이 선을 따라서" 같은 요청은 텍스트만으로 전달하기가 매우 어렵다. 아무리 정밀하게 묘사해도 모델이 원하는 대로 정확히 배치해주지 않는다.

2023년 Stanford 연구팀이 발표한 ControlNet 은 이 문제를 정면으로 해결한다. 텍스트 외에 이미지 형태의 조건(포즈, 윤곽선, 깊이맵 등) 을 추가 입력으로 받아, 생성 결과의 구조를 정밀하게 제어할 수 있게 한다. "이 사람 포즈 그대로 다른 캐릭터를 그려달라" 가 실제로 가능해진 것이다.


1. 문제 — 텍스트만으로는 구조를 제어하기 어려움

CFG 로 프롬프트 충실도를 높이고, LoRA 로 스타일을 학습시켜도 여전히 해결되지 않는 것이 있다. 이미지의 구조적 요소다. 인물의 팔이 어디를 향하는지, 건물이 화면 왼쪽에 있는지 오른쪽에 있는지, 얼굴이 어떤 각도를 향하는지 같은 것들은 텍스트로 정확히 지정하기가 근본적으로 어렵다.

  • 공간 정보를 언어로 표현하는 데 한계가 있다. "왼팔을 45도 각도로 들고 오른손은 허리에" 를 프롬프트로 쓴다고 해서 모델이 그대로 따라주지 않는다.
  • 같은 프롬프트도 매번 구도가 달라진다. 시드를 고정해도 포즈나 구도의 세부 배치는 생성마다 달라질 수 있다.
  • 레퍼런스 이미지의 구조를 그대로 가져올 방법이 없다. "이 사진의 포즈를 유지하면서 다른 스타일로" 같은 요청을 텍스트만으로 전달하는 건 불가능하다.

결국 필요한 건 텍스트와 다른 종류의 입력 채널이다. 이미지로 구조를 직접 알려줄 수 있는 방법. ControlNet 은 바로 이 채널을 추가한다.


2. 핵심 아이디어 — U-Net 복사본을 나란히 붙인다

ControlNet 의 구조를 이해하려면 먼저 기존 U-Net 의 모양을 떠올려야 한다. U-Net 은 인코더(압축) 와 디코더(복원) 로 이루어져 있고, 인코더의 각 블록 출력이 잔차 연결로 디코더에 전달된다. 텍스트는 각 블록의 Cross-Attention 레이어를 통해 주입된다.

여기에 포즈나 윤곽선 같은 구조 정보를 추가하려면 어떻게 해야 할까. 가장 단순한 방법은 U-Net 의 입력 채널을 늘리거나 레이어를 추가하는 것이다. 그런데 이렇게 하면 수십억 장으로 사전 학습된 U-Net 의 가중치가 망가진다. 새 입력에 맞게 전체를 다시 학습해야 한다.

ControlNet 의 해법은 다르다. 기존 U-Net 은 완전히 고정(freeze) 하고, 그 인코더 부분만 복사한 별도 네트워크를 옆에 붙인다. 이 복사본이 조건 이미지를 처리하는 역할을 맡는다.

텍스트 → 기존 U-Net (고정)   ↕ 주입 ↕   ControlNet (복사본, 학습) ← 조건 이미지

구체적으로 보면 이렇다. 기존 U-Net 의 인코더는 블록마다 중간 표현을 만들어낸다. ControlNet 복사본도 똑같은 구조로 조건 이미지를 처리하며 블록마다 중간 표현을 만든다. 이 두 표현이 각 레이어에서 더해지면서 구조 정보가 주입된다. 기존 U-Net 은 텍스트를 보고 내용을 결정하고, ControlNet 의 출력이 더해지면서 공간 구조가 보정되는 방식이다.

복사본은 인코더와 병목(bottleneck) 부분만 복사한다. 디코더는 복사하지 않는다. 인코더가 구조 정보를 인코딩하는 역할을 하고, 그 출력을 기존 U-Net 디코더의 각 레이어에 더해주는 것으로 충분하기 때문이다. 디코더까지 복사하면 파라미터가 두 배로 늘어나는 데 비해 실질적인 이점이 없다.


3. Zero Convolution 

ControlNet 복사본의 출력을 기존 U-Net 에 더할 때 한 가지 문제가 생긴다. 학습 초반에 ControlNet 은 아직 조건 이미지를 제대로 처리하는 법을 모른다. 이 상태에서 복사본의 출력이 기존 U-Net 에 더해지면, 잘 학습된 기존 모델의 동작이 망가진다. 처음 몇 스텝만에 수십억 장으로 쌓아온 사전 학습 능력이 훼손될 수 있다.

ControlNet 은 Zero Convolution 으로 이 문제를 해결한다. ControlNet 의 출력이 기존 U-Net 에 더해지기 직전, 가중치와 편향이 모두 0 으로 초기화된 1×1 합성곱 레이어를 하나 끼워 넣는다.

왜 0 으로 초기화하면 안전할까. 가중치가 0 이면 레이어의 출력도 정확히 0 이 된다. 즉 학습 시작 시점에 ControlNet 이 기존 U-Net 에 미치는 영향은 완전히 0 이다. 기존 U-Net 은 처음에 아무 방해 없이 원래대로 동작한다. 학습이 진행되면서 Zero Convolution 의 가중치가 점진적으로 업데이트되고, ControlNet 의 출력이 조금씩 의미를 가지기 시작하면서 기존 U-Net 에 영향을 주기 시작한다.

이 덕분에 학습 곡선이 매우 안정적이다. 기존 모델의 능력을 파괴하지 않으면서 조건 정보를 점진적으로 학습할 수 있다. 논문에서는 이를 "학습의 시작점을 항등 함수(identity function) 로 설정한다" 고 표현한다. 처음엔 아무것도 하지 않다가 학습을 통해 점점 의미 있는 변환을 배워나가는 셈이다.

Zero Convolution 은 두 군데에 삽입된다. 하나는 조건 이미지가 ControlNet 복사본에 입력되기 전, 다른 하나는 복사본의 출력이 기존 U-Net 에 더해지기 전이다. 두 지점 모두 초반에는 0 을 출력해 기존 U-Net 을 보호하고, 학습이 진행되면서 점차 실질적인 정보를 전달하기 시작한다.


4. 조건 이미지가 U-Net 안에서 어떻게 흐르는지

디노이징이 실제로 진행될 때 정보가 어떻게 흐르는지 단계별로 살펴보면 이렇다.

  1. 조건 이미지 입력 — 포즈 맵이나 윤곽선 맵 같은 조건 이미지가 Zero Convolution 을 통과한 뒤 ControlNet 복사본의 첫 번째 인코더 블록에 입력된다. 동시에 노이즈가 낀 잠재 텐서는 기존 U-Net 인코더에 입력된다.
  2. 인코더 블록마다 정보 융합 — 기존 U-Net 과 ControlNet 복사본은 각 인코더 블록을 병렬로 통과한다. 복사본의 각 블록 출력은 Zero Convolution 을 거쳐 기존 U-Net 의 같은 해상도 블록 출력에 더해진다. 인코더에 블록이 N 개라면 N 번의 주입이 일어난다.
  3. 병목(Bottleneck) 에서도 주입 — 인코더의 가장 작은 표현인 병목 레이어에서도 복사본의 출력이 기존 U-Net 에 더해진다. 여기서 전체적인 구조 맥락이 통합된다.
  4. 디코더는 결합된 정보로 복원 — 기존 U-Net 디코더는 구조 정보가 이미 주입된 인코더 출력을 받아 이미지를 복원한다. 텍스트는 디코더의 Cross-Attention 을 통해 계속 참조되면서 내용을 결정한다.

결국 기존 U-Net 의 관점에서 보면, 텍스트는 Cross-Attention 으로 "무엇을 그릴지" 를 알려주고, ControlNet 은 각 레이어에 더해지는 방식으로 "어떤 구조로 그릴지" 를 알려주는 셈이다. 두 신호가 U-Net 안에서 자연스럽게 합쳐진다.

한 가지 덧붙이면, U-Net 에서 인코더의 각 블록 출력은 잔차 연결로 디코더에 전달된다. 즉 인코더 블록 출력에 구조 정보를 더하면, 그것이 잔차 연결을 타고 디코더 전반에 영향을 미친다. 한 지점에서만 주입해도 U-Net 전체에 구조 정보가 퍼지는 효율적인 방식이다.


5. Condition Map — 다양한 제어 방식

ControlNet 에 입력되는 조건 이미지를 Condition Map 이라고 한다. 원본 이미지에서 특정 구조 정보만 추출해 만든 중간 표현이다. 어떤 정보를 추출하느냐에 따라 다양한 ControlNet 모드가 존재한다.

모드 설명
OpenPose 인물의 관절 위치를 점과 선으로 표현한 스켈레톤 맵. 포즈를 정확히 제어할 때 쓴다. 얼굴 표정과 손 위치까지 지정 가능하다.
Canny Edge 이미지의 윤곽선만 추출한 맵. 사물의 형태와 외곽선을 유지하면서 스타일만 바꿀 때 유용하다.
Depth Map 원근감과 공간 배치를 담은 깊이 맵. 전경과 배경의 위치 관계를 유지하면서 재생성할 때 쓴다.
Normal Map 표면의 방향 정보를 담은 맵. 3D 느낌의 질감과 입체감을 유지하는 데 효과적이다.
Scribble 대략적인 스케치나 낙서를 입력으로 받는다. 정밀한 구도 없이 대략적인 구성만 잡고 싶을 때.
Segmentation 이미지를 영역별로 색으로 구분한 맵. 하늘, 건물, 도로 등 각 영역의 배치를 유지하면서 재생성한다.

Condition Map 은 보통 별도의 추출 모델(Preprocessor) 로 자동 생성한다. OpenPose Preprocessor 에 사람 사진을 넣으면 스켈레톤 맵이 자동으로 만들어지는 식이다. 직접 그려서 넣을 수도 있다.

강도 조절을 위한 Conditioning Scale 이라는 파라미터도 있다. 1.0 이 기본값이며, 높일수록 조건 맵을 더 강하게 따르고 낮출수록 프롬프트의 자유도가 높아진다. CFG Scale 과 함께 조절하면 내용과 구조의 균형을 세밀하게 잡을 수 있다.