13장 — 가이던스: 조건 쪽으로 더 세게

부정 프롬프트: 빈 조건 자리에 피할 것을 넣는다

분류기 없는 가이던스의 식은 「빈 조건으로 읽은 예측에서 조건으로 읽은 예측 쪽으로」 배율만큼 간다. 빈 조건 자리에 꼭 아무것도 아닌 것을 넣어야 할까? ComfyUI 마디의 negative 입력은 바로 그 자리로 들어간다. 그 자리에 「안개」를 넣으면, 그림은 안개 낀 그림에서 멀어지는 쪽으로도 밀릴까?

역사: 빈 문자열 대신 사용자의 글

부정 프롬프트는 논문보다 사용자 도구에서 먼저 퍼졌다. 2022년 Stable Diffusion이 공개된 뒤 사용자들이 널리 쓴 도구 AUTOMATIC1111 웹 UI의 설명서(위키)는 이 기능을 「조건 없는 자리(unconditional_conditioning)에 빈 문자열 대신 사용자가 정한 글을 넣는 것」이라고 설명하고, 성 사진 프롬프트에 「fog」「grainy」 같은 글을 넣어 보이는 예를 든다. 누가 처음 생각했는지는 확인하지 못했다. 호와 샐리먼스는 논문 끝에서 가이던스가 「조건 없는 우도를 낮추고 조건부 우도를 높인다」고, 곧 조건 없는 쪽을 음의 바늘로 밀어낸다고 해석했다. 빈 조건 자리에 다른 글을 넣으면 밀어내는 대상이 그 글이 된다.

성과 숲, 맑음과 안개

빈 조건과 부정 프롬프트가 달라지려면, 프롬프트가 정하지 않은 성질이 하나 더 있어야 한다. 그래서 그림을 숫자 둘로 줄인다. x₁은 숲(−0.5)인지 성(+0.5)인지, x₂는 안개(−0.5)인지 맑음(+0.5)인지를 나타내고, 맑은 성·안개 낀 성·맑은 숲·안개 낀 숲 네 무리가 같은 비중으로 흩어져 있다(표준편차 0.3). 프롬프트 c⁺는 「성」이다. 「성」은 안개를 말하지 않으므로, 빈 조건으로 w = 3을 걸면 맑은 성과 안개 낀 성이 50.0%씩 나온다. 빈 조건 자리 c⁻에 「안개」를 넣으면 다음과 같다.

x^0=x^0(c−)+w (x^0(c+)−x^0(c−))⟺p~(x)∝p(x∣c+)wp(x∣c−)w−1\textcolor{#1b9e77}{\hat x_0} = \textcolor{#1b9e77}{\hat x_0}(\textcolor{#0093b8}{c^-}) + \textcolor{#f0694b}{w}\,\big(\textcolor{#1b9e77}{\hat x_0}(\textcolor{#0093b8}{c^+}) - \textcolor{#1b9e77}{\hat x_0}(\textcolor{#0093b8}{c^-})\big) \quad\Longleftrightarrow\quad \tilde{\textcolor{#c2408a}{p}}(\textcolor{#1b9e77}{x}) \propto \frac{\textcolor{#c2408a}{p}(\textcolor{#1b9e77}{x} \mid \textcolor{#0093b8}{c^+})^{\textcolor{#f0694b}{w}}}{\textcolor{#c2408a}{p}(\textcolor{#1b9e77}{x} \mid \textcolor{#0093b8}{c^-})^{\textcolor{#f0694b}{w} - 1}}
c+프롬프트 (여기서는 「성」)c−빈 조건 자리에 넣은 글 (여기서는 「안개」)x^0(c+), x^0(c−)두 글을 각각 넣고 읽은 원래 그림w가이던스 배율p~(x)이 바늘이 잡음 0에서 겨냥하는 분포\begin{array}{ll} \textcolor{#0093b8}{c^+} & \text{프롬프트 (여기서는 「성」)} \\ \textcolor{#0093b8}{c^-} & \text{빈 조건 자리에 넣은 글 (여기서는 「안개」)} \\ \textcolor{#1b9e77}{\hat x_0}(\textcolor{#0093b8}{c^+}),\ \textcolor{#1b9e77}{\hat x_0}(\textcolor{#0093b8}{c^-}) & \text{두 글을 각각 넣고 읽은 원래 그림} \\ \textcolor{#f0694b}{w} & \text{가이던스 배율} \\ \tilde{\textcolor{#c2408a}{p}}(\textcolor{#1b9e77}{x}) & \text{이 바늘이 잡음 0에서 겨냥하는 분포} \end{array}

w = 3에서 맑은 성이 93.0%, 안개 낀 성이 7.0%가 된다. 프롬프트에 「맑은」을 쓰지 않았는데도 안개에서 멀어지는 쪽으로 밀린 것이다. 대신 밀리는 세기도 배율을 따른다. 안개·맑음 축 x₂의 평균이 1.814로 데이터의 맑음(0.5)을 멀리 지나가고, 생성한 점의 90.4%가 데이터가 있는 네모(두 좌표 모두 ±1 안) 밖에 있다. 이처럼 빈 조건 자리에 피하고 싶은 글을 넣어, 그 글로 읽은 예측에서 멀어지게 하는 것을 부정 프롬프트 (빈 조건 대신 넣는 피할 글 / negative prompt)라 한다.

낮·밤 장난감을 둘로 늘린 2차원 장난감. 회색은 데이터, 초록은 프롬프트 「성」, w = 3으로 생성한 점. 왼쪽: 빈 조건 자리에 아무것도 넣지 않으면 맑은 성과 안개 낀 성이 반반이다. 오른쪽: 「안개」를 넣으면 거의 모두 맑은 쪽으로 가지만, 데이터의 맑은 성보다 훨씬 위쪽까지 밀린다.
낮·밤 장난감을 둘로 늘린 2차원 장난감. 회색은 데이터, 초록은 프롬프트 「성」, w = 3으로 생성한 점. 왼쪽: 빈 조건 자리에 아무것도 넣지 않으면 맑은 성과 안개 낀 성이 반반이다. 오른쪽: 「안개」를 넣으면 거의 모두 맑은 쪽으로 가지만, 데이터의 맑은 성보다 훨씬 위쪽까지 밀린다.
직접 움직여 보기빈 조건 자리에 무엇을 넣을까새 창에서 열기 ↗

ML에서: 겹치는 글과 여러 조건

부정 프롬프트는 빈 조건과 같은 신경망 호출 한 번이라 시간이 더 들지 않는다. ComfyUI의 KSampler에서 negative에 연결한 조건이 그대로 cfg_function 의 조건 없는 예측 자리로 간다. 프롬프트와 부정 프롬프트가 겹치는 내용을 담으면 문제가 생긴다. 아르만드푸르(Mohammadreza Armandpour)와 동료들(2023)은 지금의 부정 프롬프트가 「주 프롬프트와 부정 프롬프트가 겹칠 때 원하는 결과를 내지 못한다」고 지적하고, 빈 조건, 프롬프트, 부정 프롬프트를 모두 읽은 뒤 「부정 프롬프트 − 빈 조건」의 차이에서 「프롬프트 − 빈 조건」과 나란한 성분을 빼고, 수직인 성분만 밀어내는 Perp-Neg를 내놓았다(ComfyUI comfy_extras/nodes_perpneg.py). 걸음마다 세 번 읽는 대신, 겹치는 내용 때문에 프롬프트까지 밀어내는 일을 막는다.

여러 조건을 따로 계산해 곱하도록 만든 방법도 있다. 2022년 리우(Nan Liu)와 동료들의 합성 디퓨전(Composable Diffusion)은 「A AND B」를 p(x) × p(A ∣ x) × p(B ∣ x)에 비례하는 분포로 정의하고, 두 조건이 그림을 두고 서로 독립이라고 가정해 이 곱을 계산한다. 바늘로 적으면 조건 없는 바늘에 「A − 빈 조건」과 「B − 빈 조건」 두 차이를 더하는 것이다. 장난감에서 「성」과 「맑음」을 이렇게 합치면 배율 1에서도 맑은 성이 87.5%다.

문제 10. 무거운 코트는 빼고

온라인 쇼핑몰에서 「긴 코트」를 찾는데 「무거운 것」은 피하고 싶다. 코트 셋의 (긴 코트다움, 무거움) 점수가 A (8, 7), B (7, 2), C (5, 1)이다. 쇼핑몰은 「무거움 + 세기 × (긴 코트다움 − 무거움)」이 큰 순서로 늘어놓는다. (가) 세기 3이면 순서는? (나) 세기 1이면? (다) 세기 0이면?

김민준 M01
김민준

(가)는 A가 7 + 3 × 1 = 10, B가 2 + 3 × 5 = 17, C가 1 + 3 × 4 = 13이라 B, C, A예요. (나)도 무거운 걸 빼 주니까 B가 1등이겠죠.

선생님 T01
선생님

세기 1을 식에 넣어 볼까요?

김민준 M04
김민준

무거움 + 1 × (긴 코트다움 − 무거움) = 긴 코트다움이에요. 무거움이 지워져서 A(8), B(7), C(5)… 가장 무거운 A가 1등이에요.

이서연 S07
이서연

(다)는 세기 0이면 무거움 점수만 남으니까 A, B, C. 피하려던 게 1등이 돼.

김민준 M07
김민준

피할 것을 적어 놔도 세기가 1을 넘어야 비로소 피하는 거네요. 세기 1이면 아예 안 듣고요. 조교님한테 「표절만 빼고 봐 주세요」라고 해도 채점 기준표에 그 항목이 없으면 소용없는 거랑 같아요.

문제 11. 밀어내는 거리

1차원 장난감에서 프롬프트의 분포가 N(0.5, 0.3²), 부정 프롬프트의 분포가 N(−0.5, 0.3²)이다. 부정 프롬프트를 쓴 가이던스는 잡음 0에서 p(x ∣ c⁺)w / p(x ∣ c⁻)w − 1에 비례하는 분포를 겨냥한다. (가) w = 2, 3에서 이 분포의 평균과 표준편차는? (나) 부정 프롬프트의 분포가 N(0.4, 0.3²)로 프롬프트와 가깝다면 w = 3에서 평균은? (다) 부정 프롬프트가 프롬프트와 같다면?

김민준 M01
김민준

분모가 있으니까 퍼짐이 이상해질 것 같은데요. 일단 지수를 정리하면 −w(x − 0.5)²/(2 · 0.09) + (w − 1)(x + 0.5)²/(2 · 0.09)예요.

이서연 S08
이서연

x²의 계수를 보면 −w + (w − 1) = −1이라, 분산은 언제나 0.3²이야. 퍼짐은 그대로고 평균만 움직여. 일차항에서 평균이 0.5w + 0.5(w − 1), 곧 0.5 + (w − 1) × 1이야. 1은 두 분포의 평균 거리고.

김민준 M06
김민준

그럼 w = 2면 평균 1.5, w = 3이면 2.5예요. 표준편차는 0.3 그대로고요. 프롬프트 분포의 평균 0.5를 한참 지나갔어요.

선생님 T02
선생님

(나)와 (다)는 두 분포가 가까워지는 경우예요.

이서연 S09
이서연

같은 식이면 평균은 0.5 + (w − 1) × (0.5 − 0.4)라 w = 3에서 0.7이에요. (다)는 거리가 0이니 배율이 얼마든 0.5, 프롬프트 분포 그대로고요. 밀어내는 거리는 「배율 − 1」 × 「두 분포가 떨어진 거리」네요. 피할 것이 멀리 있을수록 세게, 프롬프트와 같으면 전혀 밀지 않아요.

김민준 M08
김민준

「작년 발표처럼만 하지 마」라는 말을 세게 들으면, 작년 발표와 다를수록 좋다고 생각해서 엉뚱한 데까지 가는 거랑 같네요.

문제 12. 2차원 장난감에서 확인하기

위 2차원 장난감(성/숲 × 맑음/안개, 프롬프트 「성」)에서 (가) w = 1에 부정 프롬프트 「안개」를 넣으면 맑은 성과 안개 낀 성의 몫은? (나) w = 8에 부정 프롬프트로 「성」을 넣으면? (다) w = 8에 「안개」를 넣으면 맑은 성의 몫과 x₂의 평균은? (위젯 2의 「문제 12」 단추로 불러올 수 있다.)

김민준 M01
김민준

(가)는 안개를 빼 달라고 했으니까 맑은 성이 많겠죠.

이서연 S01
이서연

코트 문제에서 세기 1이면 피할 게 지워졌잖아. 여기도 w = 1이면 x̂₀(c⁺)만 남을 텐데.

김민준 M04
김민준

돌려 보니 맑은 성 46.7%, 안개 낀 성 46.8%예요. 숲도 3.3%, 3.2%씩 있고요. 빈 조건으로 w = 1을 건 것과 똑같아요. (나)도 프롬프트와 같은 걸 넣었더니 w = 8인데도 똑같이 46.7%예요.

선생님 T02
선생님

(다)는요?

김민준 M06
김민준

맑은 성이 99.9%예요! 그런데 x₂의 평균이 6.682예요. 데이터의 맑음은 0.5였는데요. 점이 전부 네모 밖이에요.

이서연 S08
이서연

앞 문제의 식으로 보면 x₂ 축에서는 「맑음 또는 안개를 반반 가진 성」에서 「안개」 쪽을 밀어내는 거라, 배율 − 1 = 7만큼 계속 밀려. 잡음이 짙을 때 두 예측의 차이가 크게 잡히니까 더 멀리 간 것 같아요.

선생님 T13
선생님

부정 프롬프트는 피할 것을 지워 주는 지우개가 아니라, 같은 배율로 반대쪽에서 미는 손이에요. 배율이 크면 그 손도 세져요.

김민준 M07
김민준

「졸지 마」를 일곱 배로 강조하면 수업 내내 눈을 부릅뜨고만 있는 거네요.