Published on

ComfyUI에서 SDXL LoRA 병합 색감깨짐 해결법

Authors

ComfyUI에서 SDXL LoRA를 merge(병합)한 뒤, 원본 체크포인트로 생성할 때는 멀쩡하던 색감이 갑자기 탁해지거나(회색/갈색 캐스트), 채도가 과하게 튀거나, 피부톤이 녹색/자주색으로 쏠리는 현상이 종종 발생합니다. 이 글은 SDXL 특유의 이중 텍스트 인코더 구조 + VAE 의존성 + 정밀도(fp16/bf16) + 병합 방식 때문에 생기는 대표적인 색감 깨짐을, ComfyUI 기준으로 재현 가능한 점검 순서로 해결하는 방법을 정리합니다.

아래 내용은 “LoRA를 적용해서 쓰는 건 문제 없는데, 체크포인트에 병합하면 색이 망가진다” 케이스를 중심으로 다룹니다.

색감깨짐이 생기는 대표 원인 7가지

1) SDXL에서 VAE가 바뀌거나 누락됨

SDXL은 VAE 영향이 특히 큽니다. 병합 자체가 VAE를 바꾸는 건 아니지만,

  • 병합 후 저장한 체크포인트를 로드할 때 VAE가 자동으로 다른 걸 잡히거나
  • 워크플로에서 VAE Decode 노드가 별도 VAE를 참조하거나
  • Load Checkpoint가 내장 VAE를 쓰지 않고 외부 VAE로 강제되는

상태가 되면 색감이 크게 흔들립니다.

증상

  • 전체적으로 회색/갈색으로 탁해짐
  • 피부가 누렇게 뜨거나, 그림자 색이 이상해짐

해결

  • 병합 전후 모두 동일한 VAE로 디코딩하는지 고정합니다.
  • SDXL 계열이라면 sdxl_vae.safetensors(또는 모델 제작자가 권장한 VAE)로 통일합니다.

2) fp16 저장(또는 잘못된 캐스팅)으로 인한 누적 오차

LoRA 병합은 결국 가중치에 델타를 더하는 연산입니다. 이때

  • 체크포인트가 fp16이고
  • LoRA가 fp16으로 로드되고
  • 병합 결과를 fp16으로 저장

같은 흐름이면, 일부 레이어에서 정밀도 손실이 누적되어 색/톤이 미묘하게 무너질 수 있습니다. 특히 SDXL은 파라미터 규모가 크고, UNet뿐 아니라 텍스트 인코더 관련 LoRA까지 얽히면 체감이 커집니다.

해결

  • 가능하면 병합 연산은 fp32에 가깝게 수행하고, 저장도 fp16 강제 변환을 피합니다.
  • ComfyUI에서 병합 노드가 제공하는 dtype 또는 save precision 옵션이 있다면 fp32 또는 bf16을 우선 고려합니다.

3) SDXL LoRA가 UNet만이 아니라 Text Encoder까지 건드림

SDXL은 텍스트 인코더가 2개(CLIP-L, OpenCLIP-G)이고, LoRA가 어느 쪽을 타깃으로 학습됐는지에 따라 결과가 달라집니다.

  • 어떤 LoRA는 UNet만 학습
  • 어떤 LoRA는 text_encoder까지 포함
  • 어떤 LoRA는 두 인코더 중 한쪽만 포함

이 상태에서 병합 시 텍스트 인코더 델타가 예상과 다르게 적용되면 프롬프트 해석이 변하고, 그 결과 색감이 바뀌는 것처럼 보일 수 있습니다.

해결

  • LoRA 메타정보(학습 대상 모듈)를 확인합니다.
  • 가능하면 UNet 전용 LoRATE 포함 LoRA를 구분해 병합 전략을 다르게 가져갑니다.

4) 알파(alpha)와 강도(strength)를 혼동한 병합

LoRA 적용 시의 강도(예: strength=0.8)와 병합 시의 알파(예: alpha=1.0)는 도구마다 의미가 다를 수 있습니다. 병합 노드에서 알파를 과하게 주면, 색/톤/콘트라스트가 한 번에 무너집니다.

해결

  • 병합은 보수적으로 시작합니다. 예: 0.30.50.7 순으로 확인
  • LoRA가 스타일 계열이면 색감 변형이 크므로 알파를 더 낮게 잡습니다.

5) 병합 대상이 SDXL Base인지 Refiner인지 혼동

SDXL은 Base와 Refiner가 분리되어 쓰이는 경우가 많습니다. 스타일 LoRA를 Refiner에 병합하거나, Refiner용 LoRA를 Base에 병합하면 결과가 어색해지고 색감이 깨진 것처럼 보일 수 있습니다.

해결

  • LoRA가 어떤 베이스(예: sdxl_base_1.0, sdxl_refiner_1.0)에서 학습됐는지 확인하고 동일 계열에 병합합니다.

6) 워크플로에서 샘플러/CFG/스케줄이 바뀌어 “병합 부작용”처럼 보임

병합 후 테스트할 때 워크플로를 살짝만 바꿔도(샘플러, 스케줄러, CFG, steps) 색감이 달라집니다. 특히 SDXL은 CFG 변화에 민감한 편입니다.

해결

  • 병합 전후 비교는 반드시 동일한 seed, 동일한 sampler/scheduler, 동일한 steps, 동일한 CFG로 고정합니다.

7) 잘못된 노드 조합: VAE Decode와 후처리(색보정/업스케일) 혼재

ComfyUI에서는 후처리 노드(예: 색보정, 톤매핑, 업스케일)가 워크플로 중간에 끼어들면 “병합이 색을 망쳤다”로 오해하기 쉽습니다.

해결

  • 병합 전후 비교용 워크플로는 가장 단순하게 구성합니다.

가장 안전한 진단 순서(체크리스트)

아래 순서대로 하면 원인을 빠르게 좁힐 수 있습니다.

  1. 병합 전 LoRA 적용 결과병합 후 체크포인트 결과를 같은 워크플로로 비교
  2. VAE Decode가 동일한 VAE를 쓰는지 고정
  3. 병합 대상이 Base인지 Refiner인지 확인
  4. 병합 알파를 낮춰서 재시도
  5. 텍스트 인코더 포함 LoRA인지 확인(포함이면 병합 대신 런타임 적용을 권장하는 경우가 많음)
  6. 저장 정밀도(fp16 강제)를 피하고 재저장

이 과정은 문제를 “모델 자체”와 “워크플로/디코딩”로 분리하는 데 목적이 있습니다.

ComfyUI에서 재현 가능한 비교 워크플로(최소 구성)

병합 전후를 비교할 때는 아래처럼 최소 노드로 고정하세요.

  • Load Checkpoint
  • CLIP Text Encode (Prompt)
  • CLIP Text Encode (Negative)
  • Empty Latent Image
  • KSampler
  • VAE Decode
  • Save Image

프롬프트/네거티브/seed/CFG/steps/sampler/scheduler를 고정합니다.

예시 파라미터(권장 시작점)

  • sampler: dpmpp_2m
  • scheduler: karras
  • steps: 25
  • CFG: 5.0
  • seed: 123456789

이 값이 정답은 아니지만, “비교”에는 안정적입니다.

병합 시 색감깨짐을 줄이는 실전 팁

1) VAE를 워크플로에서 명시적으로 고정

ComfyUI에서 체크포인트가 바뀌면 VAE 선택이 흔들릴 수 있습니다. 비교/운영 모두에서 VAE를 명시적으로 고정하는 게 안전합니다.

  • Load VAE 노드를 사용해 특정 VAE를 로드
  • VAE Decode에 해당 VAE를 직접 연결

이렇게 하면 “병합했더니 색이 바뀜”의 상당수가 해결됩니다.

2) 알파를 낮게, 여러 번 누적 병합하지 않기

여러 LoRA를 한 번에 병합하거나, 병합된 모델에 또 병합을 반복하면 오차와 스타일 편향이 누적됩니다.

  • 한 번에 하나씩 병합하고 결과를 저장
  • 색감이 흔들리면 알파를 낮춰 재시도
  • 스타일 LoRA는 특히 0.3~0.7 영역에서 먼저 탐색

3) Text Encoder 포함 LoRA는 병합보다 런타임 적용이 안전한 경우가 많음

TE까지 건드리는 LoRA는 병합 시 프롬프트 해석 자체가 바뀌어, “색감”뿐 아니라 전체 이미지 성격이 달라질 수 있습니다.

  • 운영 목적이 “항상 같은 스타일”이면 병합이 편하지만
  • 색감 안정성이 최우선이면 런타임 LoRA 로딩이 더 안전합니다.

4) Base/Refiner 파이프라인을 쓰는 경우, 병합 모델을 어디에 넣을지 고정

SDXL을 Base 생성 후 Refiner로 마무리하는 워크플로라면,

  • Base에 병합한 모델을 Base 단계에만 사용
  • Refiner는 별도로 유지

하는 편이 결과가 안정적입니다.

(중요) 병합 자체를 피하는 대안: 런타임 LoRA + 프리셋화

병합은 배포/관리 측면에서 편하지만, 색감/재현성 문제가 생기면 디버깅 비용이 큽니다. 팀 작업이나 반복 생성 파이프라인이라면 “병합 모델” 대신

  • 런타임 LoRA 로딩
  • 워크플로 JSON을 프리셋화
  • LoRA/가중치/프롬프트를 버전 관리

가 더 낫습니다.

이건 인프라/자동화 관점의 이야기이기도 해서, 캐시나 재현성 이슈를 다룬 글들과 접근이 비슷합니다. 예를 들어 CI에서 환경이 바뀌면 결과가 달라지는 문제는 원인 추적이 중요합니다. 관련해서는 GitHub Actions 캐시가 안먹을 때 9가지 원인처럼 “고정해야 할 것들을 체크리스트로 관리”하는 방식이 도움이 됩니다.

코드 예제: 병합 전후 색감 차이를 자동으로 점검하는 스크립트

ComfyUI는 워크플로를 API로 호출할 수 있어, 병합 전/후 모델을 같은 입력으로 돌리고 결과를 비교하는 자동 점검을 만들 수 있습니다. 아래는 생성 이미지의 평균 색(간이) 통계를 뽑아 색쏠림을 빠르게 감지하는 Python 예시입니다.

주의: 아래 예시는 ComfyUI API 호출 부분을 생략하고, 결과 이미지 파일이 before.png, after.png로 저장돼 있다는 가정입니다.

from PIL import Image
import numpy as np

def image_stats(path: str):
    img = Image.open(path).convert('RGB')
    arr = np.asarray(img).astype(np.float32) / 255.0
    mean = arr.mean(axis=(0, 1))  # R,G,B 평균
    std = arr.std(axis=(0, 1))
    return mean, std

before_mean, before_std = image_stats('before.png')
after_mean, after_std = image_stats('after.png')

def fmt(v):
    return f"R={v[0]:.4f}, G={v[1]:.4f}, B={v[2]:.4f}"

print('BEFORE mean:', fmt(before_mean), 'std:', fmt(before_std))
print('AFTER  mean:', fmt(after_mean),  'std:', fmt(after_std))
print('DELTA mean :', fmt(after_mean - before_mean))
  • DELTA mean에서 특정 채널만 유독 커지면(예: G만 상승) 녹색 캐스트 가능성이 큽니다.
  • 이 스크립트는 정교한 품질 평가는 아니지만, 병합 설정을 바꿔가며 빠르게 탐색할 때 유용합니다.

로컬에서 이런 실험을 반복하다 보면 VRAM이 부족해 OOM이 나는 경우도 많은데, 메모리 최적화 관점은 Transformers 로컬 LLM OOM 해결 - 4bit+오프로딩처럼 “정밀도와 오프로딩을 조합해 병목을 줄이는” 접근이 참고가 됩니다.

ComfyUI에서 자주 쓰는 해결 레시피 3종

레시피 A: VAE 고정만으로 해결되는 케이스(가장 흔함)

  1. 병합 전후 동일 워크플로 준비
  2. Load VAE로 VAE를 명시적으로 로드
  3. VAE Decode에 고정 연결
  4. 동일 seed로 비교

이 단계에서 색감이 정상으로 돌아오면, 원인은 병합이 아니라 디코딩(VAE) 불일치였던 겁니다.

레시피 B: 알파 낮추고, TE 포함 LoRA는 병합 제외

  1. LoRA가 TE까지 포함인지 확인
  2. TE 포함이면 병합하지 말고 런타임 적용으로 전환
  3. UNet 전용 LoRA만 병합
  4. 알파를 0.3~0.6부터 탐색

레시피 C: 저장 정밀도를 바꿔 재병합

  1. 병합 노드에서 save precision이 있으면 fp16 강제를 피함
  2. 가능하면 fp32에 가깝게 병합 수행
  3. 저장 후 다시 로드하여 동일 워크플로로 비교

마무리: “병합 문제”처럼 보이는 것의 절반은 VAE/워크플로 문제

SDXL LoRA 병합 후 색감깨짐은 실제로는 아래 두 축에서 많이 발생합니다.

  • 디코딩 축: VAE 불일치, 후처리 노드 혼입, Base/Refiner 단계 혼동
  • 가중치 축: 알파 과다, TE 포함 LoRA 병합, fp16 정밀도 손실 누적

가장 빠른 해결은 “비교 워크플로 최소화 + VAE 고정 + 알파 보수적 설정”입니다. 그래도 해결이 안 되면, 해당 LoRA가 TE를 포함하는지와 Base/Refiner 호환성을 확인하고, 병합 대신 런타임 적용으로 운영하는 편이 색감 안정성 면에서 더 나은 선택일 때가 많습니다.

추가로, 병합 전후 워크플로 JSON이나 모델 파일을 버전으로 남겨두면(언제 어떤 설정에서 색이 깨졌는지) 재현과 롤백이 쉬워집니다. 운영 자동화 관점의 체크리스트 습관은 GitHub Actions 캐시가 안 먹을 때 키·경로 9분 점검처럼 “고정 변수/변경 변수를 분리”하는 방식과 통합니다.