이미지 생성 코드에서 매번 고르게 되는 값이 하나 있습니다.
image = pipe(prompt, num_inference_steps=20).images[0] # 20? 50? 1000?
scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
20이면 몇 초 만에 나오고 50이면 두 배 반이 걸립니다. 그런데 1,000으로 올려도 50과 눈에 띄게 달라지지 않습니다. 그리고 스케줄러를 DDIM에서 DPM-Solver로 바꾸면 20스텝에서도 50스텝처럼 보입니다.
이 세 관찰은 전부 하나의 수치해석 사실에서 나옵니다. 샘플링은 미분방정식을 푸는 일이고, 스텝 수는 그 방정식을 얼마나 잘게 쪼개 푸는가입니다. 스텝을 줄이면 빨라지는 대신 오차가 생기고, 그 오차가 얼마나 빨리 줄어드는지는 푸는 방법마다 정해진 숫자가 있습니다.
지난 글에서 모델이 배우는 것이 스코어 라는 것을 확인했습니다. 이 글은 그 스코어를 손에 쥔 뒤 그것으로 무엇을 어떻게 푸는지를 봅니다. 실제 파이프라인 사용법은 확산 모델과 Stable Diffusion이 다루고, 여기서는 방정식만 봅니다.
스텝을 무한히 잘게 쪼개면
앞서 세운 폐형 forward 과정의 출발점은 이산 사슬이었습니다.
가 작을 때 이므로 차이를 적어 보면
입니다. 스텝 간격을 , 로 두고 으로 보내면 확률미분방정식(SDE)이 됩니다.
는 브라운 운동의 증분이고, 핵심은 그 계수가 로 줄어든다는 점입니다 — 잡음 항은 가 아니라 그 제곱근으로 작아지므로 스텝을 아무리 잘게 쪼개도 사라지지 않습니다. 보통의 미분방정식과 갈리는 자리가 여기입니다.
거꾸로 가는 방정식도 있습니다. 확률 과정의 시간을 뒤집으면 이런 모양이 된다는 것이 알려져 있습니다.
여기 스코어가 들어 있습니다. 잡음을 푸는 방정식을 적으려면 각 시각의 로그밀도 기울기가 필요하고, 신경망이 학습한 것이 정확히 그것입니다.
잡음을 뺀 쌍둥이
역방향 SDE 말고 하나가 더 있습니다. 잡음 항을 없애고 드리프트를 조금 고치면 모든 시각에서 주변분포가 똑같은 보통의 미분방정식이 나옵니다.
이것을 확률 흐름 ODE(probability flow ODE)라 부릅니다. 잡음 계수가 절반으로 줄어든 것이 드리프트의 스코어 항 계수에 반영된 형태입니다.
두 길의 차이는 경로이지 분포가 아닙니다. SDE는 매 스텝 새 잡음을 넣으므로 같은 출발점에서도 매번 다른 그림이 나오고, ODE는 출발점이 같으면 결과가 완전히 같습니다. 그런데 출발점을 표준정규분포에서 뽑아 모으면 두 방법이 만드는 이미지 분포는 같습니다.
이 성질이 실무에서 셋을 줍니다.
- 재현성. 같은 씨앗이면 같은 그림이 나옵니다.
- 되돌리기. ODE는 시간을 거꾸로 적분해 이미지에서 잠재 벡터로 되돌아갈 수 있습니다. 이미지 편집이 이 위에 섭니다.
- 적은 스텝. 잡음이 없으니 매끄러운 곡선이고, 매끄러운 곡선은 큰 걸음으로도 따라갈 수 있습니다.
세 번째가 이 글의 본론입니다.
한 걸음의 오차와 쌓인 오차
ODE 를 컴퓨터로 푸는 가장 단순한 방법은 오일러 방법입니다 — 지금 자리의 기울기로 직선을 그어 만큼 갑니다.
한 걸음에서 생기는 오차를 테일러 전개로 셉니다. 참 해는
인데 오일러는 앞의 두 항만 씁니다. 그러니 한 걸음의 오차 — 국소 절단 오차(local truncation error)는 , 즉 입니다.
이제 그 걸음을 구간 전체에 걸쳐 번 합니다. 오차가 단순히 더해진다고 보면
가 되어 전역 오차(global error)는 입니다. 국소 차수보다 하나 낮아집니다.
전역 오차가 인 방법을 차 방법이라 하고, 오일러는 1차입니다. 뜻은 이렇습니다 — 스텝을 열 배 늘리면 오차가 10분의 1이 됩니다.
한 걸음 안에서 기울기를 두 번 재면 차수가 오릅니다. 하이운 방법(Heun's method)은 일단 오일러로 가 보고, 도착지의 기울기를 다시 잰 뒤 두 기울기의 평균으로 다시 갑니다.
평균을 쓰면 테일러의 이차항까지 맞아떨어져 국소 오차가 , 전역 오차가 가 됩니다. 대신 한 스텝에 함수를 두 번 부릅니다.
실제로 재 본다
차수는 이론이니 직접 확인합니다. 데이터 분포가 인 1차원 장난감을 씁니다. 이러면 모든 시각의 주변분포가 정규분포이고 스코어가 손으로 나오므로, 확률 흐름 ODE의 정확한 해를 적을 수 있습니다.
를 0.1에서 20까지 선형으로 늘리고 에서 출발하면 정확한 답이 입니다. 이제 오일러와 하이운으로 풀어 오차를 잽니다.
| 스텝 | 오일러 오차 | 하이운 오차 |
|---|---|---|
| 10 | 2.38e−02 | 1.89e−02 |
| 20 | 1.60e−02 | 5.28e−03 |
| 40 | 9.00e−03 | 1.41e−03 |
| 80 | 4.75e−03 | 3.63e−04 |
| 160 | 2.44e−03 | 9.21e−05 |
| 320 | 1.24e−03 | 2.32e−05 |
| 1000 | 3.99e−04 | 2.39e−06 |
이웃한 두 줄로 차수를 역산해 보면
| 구간 | 오일러 차수 | 하이운 차수 |
|---|---|---|
| 40 → 80 | 0.92 | 1.96 |
| 80 → 160 | 0.96 | 1.98 |
| 160 → 320 | 0.98 | 1.99 |
| 320 → 1000 | 0.99 | 2.00 |
이론값 1과 2로 수렴합니다.
처음의 관찰이 여기서 답을 얻습니다. 오일러로 20스텝을 쓰면 오차가 1.6e−02, 1,000스텝이면 4.0e−04입니다. 50배를 더 쓰고 40배 정확해집니다. 20에서 50으로 올릴 때는 오차가 2.5배 줄어 눈에 보이지만, 200에서 1,000으로 올릴 때도 똑같이 2.5배 줄어드는데 그때는 이미 오차가 화면에서 안 보이는 크기입니다. 개선의 비율은 같은데 절대 크기가 작아지므로 어느 지점부터 스텝을 늘려도 그림이 안 변합니다.
DDIM은 어떤 이산화인가
DDIM 샘플러의 갱신식은 이렇게 생겼습니다.
잡음을 새로 뽑는 자리가 없습니다 — 그래서 결정적입니다. 그런데 왜 이 모양인지는 식만 봐서는 안 보입니다.
변수를 바꾸면 보입니다. , 로 두면 확률 흐름 ODE가 이렇게 단순해집니다.
그리고 이 식에 오일러를 적용하면 이고, 원래 변수로 되돌리면 위의 DDIM 갱신식과 글자 그대로 같아집니다. 확인해 봤습니다.
DDIM = 0.733056595090
σ-오일러 = 0.733056595090
차이 = 0.00e+00
20스텝에서 마지막 자리까지 일치합니다. 그러니 DDIM은 새로운 샘플링 기법이 아니라 확률 흐름 ODE를 잘 고른 좌표계에서 오일러로 푼 것입니다. 결정적인 것은 ODE를 풀기 때문이고, 스텝을 줄일 수 있는 것도 잡음 없는 매끄러운 곡선이기 때문입니다.
고차 solver는 무엇을 벌고 무엇을 치르는가
그러면 항상 하이운을 쓰면 될까요. 계산 비용을 맞춰 비교하면 답이 갈립니다.
확산 샘플링의 비용은 스텝 수가 아니라 신경망 호출 수(NFE)입니다. 하이운은 한 스텝에 두 번 부르므로, 같은 비용이면 스텝 수가 절반입니다.
| 호출 수 | 오일러 | 하이운(절반 스텝) | 비 |
|---|---|---|---|
| 20 | 1.60e−02 | 1.89e−02 | 오일러 우세 |
| 40 | 9.00e−03 | 5.28e−03 | 1.7배 |
| 80 | 4.75e−03 | 1.41e−03 | 3.4배 |
| 160 | 2.44e−03 | 3.63e−04 | 6.7배 |
| 320 | 1.24e−03 | 9.21e−05 | 13.4배 |
호출 20번에서는 오일러가 이깁니다. 차수의 이득은 가 충분히 작아진 뒤에야 나타나는데, 스텝이 아주 성기면 아직 그 영역이 아닙니다. 앞의 로그-로그 그래프에서도 왼쪽 끝에서 두 직선이 붙어 있었습니다.
여기서 실무의 두 갈래가 갈립니다.
- 호출 수를 아주 적게(10~20) 쓰려면 차수를 올리는 것으로는 부족합니다. 한 호출을 낭비하지 않는 쪽이 유리하고, 그래서 이전 스텝의 값을 재활용하는 다단계 방법(DPM-Solver++ 같은 것)이나 스코어의 구조를 식에 반영한 지수 적분기가 쓰입니다.
- 호출 수가 40 이상이면 차수의 이득이 곧바로 나옵니다. 같은 비용에 오차가 몇 배 작아집니다.
그리고 차수 이론이 말하지 않는 것도 있습니다. 위 계산은 스코어가 정확할 때의 이산화 오차입니다. 실제로는 신경망이 배운 스코어 자체에 오차가 있고, 그것은 스텝을 아무리 늘려도 줄지 않습니다. 1,000스텝이 50스텝보다 눈에 띄게 낫지 않은 진짜 이유가 여기 있습니다 — 이산화 오차가 모델 오차 아래로 내려간 뒤에는 더 정확히 풀어 봐야 이미 틀린 방정식을 더 정확히 푸는 것이기 때문입니다.
num_inference_steps=20으로 돌아가면 그 숫자가 무엇을 정하는지 말할 수 있습니다. 그것은 화질 손잡이가 아니라 적분의 격자 간격이고, 20이 충분한지는 쓰는 solver의 차수와 그 모델의 스코어가 얼마나 정확한지가 함께 정합니다.
정리
- 이산 forward 사슬을 으로 보내면 라는 SDE가 된다. 잡음 항의 계수가 라 아무리 잘게 쪼개도 사라지지 않는다.
- 시간을 뒤집은 역방향 SDE의 드리프트에 스코어가 들어 있다. 신경망이 배운 것이 그 자리에 그대로 들어간다.
- 잡음 항을 빼고 드리프트를 고치면 확률 흐름 ODE가 되고, 모든 시각에서 주변분포가 SDE와 같다. 다른 것은 경로뿐이다. 재현성·되돌리기·적은 스텝이 여기서 나온다.
- 오일러의 국소 오차는 테일러 이차항이라 , 그것을 번 쌓아 전역 오차가 다. 전역 차수는 국소 차수보다 항상 하나 낮다.
- 하이운은 한 스텝에 기울기를 두 번 재 평균하고, 전역 오차가 다. 대신 신경망 호출이 두 배다.
- 가우시안 장난감의 정확한 해와 비교해 재 보니 차수가 0.99와 2.00으로 수렴했다. 이론값 1과 2 그대로다.
- 오일러로 스텝을 20에서 1,000으로 50배 늘리면 오차가 40배 줄어든다. 개선의 비율은 어디서나 같으므로, 절대 오차가 작아진 뒤에는 스텝을 늘려도 그림이 변하지 않는다.
- DDIM은 확률 흐름 ODE를 좌표에서 오일러로 푼 것과 완전히 같다 — 20스텝에서 마지막 자리까지 일치했다. 결정적인 이유는 ODE를 풀기 때문이다.
- 신경망 호출 수를 맞춰 비교하면 20번에서는 오일러가 이기고 40번부터 하이운이 앞선다. 차수의 이득은 스텝이 충분히 촘촘해진 뒤에 나온다.
- 위 오차는 전부 스코어가 정확할 때의 값이다. 모델 자체의 오차는 스텝으로 줄지 않으므로, 이산화 오차가 그 아래로 내려간 뒤의 추가 스텝은 이미 틀린 방정식을 더 정확히 푸는 데 쓰인다.
읽어주셔서 감사합니다. 😊

