- Published on
ComfyUI에서 SDXL LoRA 병합 색감깨짐 해결법
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
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 전용 LoRA와 TE 포함 LoRA를 구분해 병합 전략을 다르게 가져갑니다.
4) 알파(alpha)와 강도(strength)를 혼동한 병합
LoRA 적용 시의 강도(예: strength=0.8)와 병합 시의 알파(예: alpha=1.0)는 도구마다 의미가 다를 수 있습니다. 병합 노드에서 알파를 과하게 주면, 색/톤/콘트라스트가 한 번에 무너집니다.
해결
- 병합은 보수적으로 시작합니다. 예:
0.3→0.5→0.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에서는 후처리 노드(예: 색보정, 톤매핑, 업스케일)가 워크플로 중간에 끼어들면 “병합이 색을 망쳤다”로 오해하기 쉽습니다.
해결
- 병합 전후 비교용 워크플로는 가장 단순하게 구성합니다.
가장 안전한 진단 순서(체크리스트)
아래 순서대로 하면 원인을 빠르게 좁힐 수 있습니다.
- 병합 전 LoRA 적용 결과와 병합 후 체크포인트 결과를 같은 워크플로로 비교
VAE Decode가 동일한 VAE를 쓰는지 고정- 병합 대상이 Base인지 Refiner인지 확인
- 병합 알파를 낮춰서 재시도
- 텍스트 인코더 포함 LoRA인지 확인(포함이면 병합 대신 런타임 적용을 권장하는 경우가 많음)
- 저장 정밀도(fp16 강제)를 피하고 재저장
이 과정은 문제를 “모델 자체”와 “워크플로/디코딩”로 분리하는 데 목적이 있습니다.
ComfyUI에서 재현 가능한 비교 워크플로(최소 구성)
병합 전후를 비교할 때는 아래처럼 최소 노드로 고정하세요.
Load CheckpointCLIP Text Encode (Prompt)CLIP Text Encode (Negative)Empty Latent ImageKSamplerVAE DecodeSave 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 고정만으로 해결되는 케이스(가장 흔함)
- 병합 전후 동일 워크플로 준비
Load VAE로 VAE를 명시적으로 로드VAE Decode에 고정 연결- 동일 seed로 비교
이 단계에서 색감이 정상으로 돌아오면, 원인은 병합이 아니라 디코딩(VAE) 불일치였던 겁니다.
레시피 B: 알파 낮추고, TE 포함 LoRA는 병합 제외
- LoRA가 TE까지 포함인지 확인
- TE 포함이면 병합하지 말고 런타임 적용으로 전환
- UNet 전용 LoRA만 병합
- 알파를
0.3~0.6부터 탐색
레시피 C: 저장 정밀도를 바꿔 재병합
- 병합 노드에서
save precision이 있으면 fp16 강제를 피함 - 가능하면 fp32에 가깝게 병합 수행
- 저장 후 다시 로드하여 동일 워크플로로 비교
마무리: “병합 문제”처럼 보이는 것의 절반은 VAE/워크플로 문제
SDXL LoRA 병합 후 색감깨짐은 실제로는 아래 두 축에서 많이 발생합니다.
- 디코딩 축: VAE 불일치, 후처리 노드 혼입, Base/Refiner 단계 혼동
- 가중치 축: 알파 과다, TE 포함 LoRA 병합, fp16 정밀도 손실 누적
가장 빠른 해결은 “비교 워크플로 최소화 + VAE 고정 + 알파 보수적 설정”입니다. 그래도 해결이 안 되면, 해당 LoRA가 TE를 포함하는지와 Base/Refiner 호환성을 확인하고, 병합 대신 런타임 적용으로 운영하는 편이 색감 안정성 면에서 더 나은 선택일 때가 많습니다.
추가로, 병합 전후 워크플로 JSON이나 모델 파일을 버전으로 남겨두면(언제 어떤 설정에서 색이 깨졌는지) 재현과 롤백이 쉬워집니다. 운영 자동화 관점의 체크리스트 습관은 GitHub Actions 캐시가 안 먹을 때 키·경로 9분 점검처럼 “고정 변수/변경 변수를 분리”하는 방식과 통합니다.