Published on

Stable Diffusion VRAM OOM - xFormers·Tiling·LoRA 최적화

Authors

Stable Diffusion을 돌리다 보면 가장 흔하게 마주치는 에러가 CUDA out of memory 입니다. 특히 고해상도(예: 1024x1024 이상), 배치 증가, ControlNet 동시 사용, 여러 LoRA 병행, 업스케일러까지 붙이면 VRAM이 급격히 소모됩니다.

이 글에서는 “VRAM OOM을 실제로 줄이는” 데 초점을 맞춰, xFormers(메모리 효율 어텐션), Tiling(타일 기반 처리), LoRA 로딩/적용 최적화를 중심으로 정리합니다. WebUI(AUTOMATIC1111) 기준 예시를 주로 들지만, 핵심 원리는 ComfyUI나 Diffusers 파이프라인에도 동일하게 적용됩니다.

또한 OOM은 Stable Diffusion만의 문제가 아니라, 딥러닝 추론 전반에서 반복되는 패턴입니다. 로컬 LLM에서의 OOM 대응(4bit, 오프로딩)도 큰 틀에서 유사하니 필요하면 함께 참고하세요: Transformers 로컬 LLM OOM 해결 - 4bit+오프로딩

1) OOM이 터지는 지점을 먼저 분해하기

OOM은 대개 아래 중 하나에서 발생합니다.

  • 어텐션 메모리 폭발: 해상도와 토큰 수가 늘면 어텐션은 메모리를 많이 씁니다. 특히 고해상도에서 치명적입니다.
  • VAE 디코딩/인코딩: vae 단계에서 순간 피크가 튈 수 있습니다.
  • 배치/스텝/샘플러 설정: batch size, batch count, steps는 단순히 시간만 늘리는 게 아니라 중간 텐서 생존 시간에 영향을 줍니다.
  • ControlNet, IP-Adapter, 여러 LoRA: 모델 그래프가 커지고, 추가 연산/특징맵이 생겨 VRAM이 증가합니다.
  • 업스케일/Hi-Res fix: 2패스 렌더링은 사실상 “두 번 생성”에 가까워 피크가 커집니다.

따라서 “무조건 옵션을 켜라”가 아니라, 피크 VRAM을 낮추는 옵션전체 VRAM 상주량을 줄이는 옵션을 나눠서 접근해야 합니다.

2) xFormers: 가장 먼저 켜볼 만한 메모리 절감 장치

2.1 xFormers가 줄이는 것

xFormers는 메모리 효율적인 어텐션 구현을 제공해, 특히 고해상도에서 어텐션 관련 VRAM 피크를 크게 낮춰줍니다. 체감상 768x768 이상에서 효과가 더 커집니다.

2.2 AUTOMATIC1111(WebUI)에서 설정

  • SettingsOptimization 또는 실행 옵션에서 --xformers
  • 최신 환경에서는 torch 버전, CUDA, 드라이버 조합에 따라 설치가 꼬일 수 있으니 “설치 성공 여부”를 로그로 확인하세요.

실행 예시(Windows 배치 파일 또는 리눅스 실행 스크립트에서):

python launch.py --xformers

만약 xFormers가 불안정하거나 특정 GPU에서 크래시가 발생하면, 대체로 아래 전략으로 우회합니다.

  • --xformers 대신 SDP(Scaled Dot Product Attention) 계열 최적화 사용
  • 또는 --opt-sdp-attention 같은 플래그(배포판/버전에 따라 명칭 다름)

핵심은 “어텐션 구현을 메모리 효율 모드로 바꾸는 것”입니다.

2.3 주의점

  • xFormers는 VRAM을 줄여주지만, 일부 조합에서 속도 저하 또는 결과 재현성 차이가 날 수 있습니다.
  • 동일한 시드라도 어텐션 구현 차이로 미세한 픽셀 차이가 생길 수 있습니다. 재현성이 중요한 파이프라인이면 고정 옵션을 문서화하세요.

3) Tiling: 고해상도에서 OOM을 “쪼개서” 피하기

Tiling은 한 번에 큰 텐서를 처리하지 않고, 이미지를 타일로 나눠 순차 처리하는 방식입니다. 대표적으로 다음에서 사용됩니다.

  • VAE 타일링: 디코딩/인코딩을 타일로 수행
  • 업스케일 타일링: ESRGAN/4x 업스케일러 등을 타일로 처리
  • ControlNet/후처리 타일링: 확장 기능에서 제공하는 경우가 있음

3.1 VAE 타일링의 의미

고해상도에서 OOM이 나는 경우, 실제로는 “UNet”보다 “VAE 디코딩”에서 터지는 경우가 있습니다. 이때 VAE 타일링은 피크를 낮추는 데 매우 효과적입니다.

WebUI에서 흔히 쓰는 접근은 확장 기능 또는 설정으로 VAE 타일 옵션을 켜는 것입니다(버전/포크에 따라 위치가 다릅니다).

3.2 업스케일 타일 크기 튜닝

업스케일러 타일링은 보통 tile size를 조절합니다. 타일이 작을수록 VRAM은 줄지만, 경계(seam) 아티팩트가 생길 수 있고 속도도 느려질 수 있습니다.

일반적인 튜닝 가이드:

  • VRAM이 빡빡하면 tile size128 또는 256부터 시작
  • 여유가 있으면 512까지 올려 seam을 줄임

업스케일러가 타일 간 오버랩을 지원한다면 오버랩을 약간 주는 것이 경계 품질에 유리합니다.

3.3 타일링의 트레이드오프

  • 장점: 피크 VRAM을 강하게 낮춤
  • 단점: 속도 저하, seam 처리 필요, 일부 후처리에서 품질 영향

즉, “최고 품질”보다 “OOM 없이 끝까지 돌리는 것”이 목표일 때 가장 강력한 카드입니다.

4) LoRA 최적화: 많이 붙일수록 VRAM이 늘어나는 이유와 대응

LoRA는 기본 모델 파라미터를 직접 바꾸지 않고 저랭크 어댑터를 덧대는 방식이라 “가볍다”는 인식이 있지만, 실전에서는 다음 이유로 VRAM 압박이 생깁니다.

  • 여러 LoRA를 동시에 적용하면 레이어별 추가 연산이 늘어남
  • 특정 구현에서는 LoRA를 합성(merge)하거나, 캐시/중간 텐서를 더 들고 있을 수 있음
  • 고해상도에서 UNet 피크가 커진 상태에서 LoRA가 추가 비용을 얹음

4.1 LoRA 개수와 강도부터 정리

가장 현실적인 최적화는 “LoRA를 줄이는 것”입니다.

  • LoRA 4개를 약하게 섞는 것보다, 핵심 LoRA 1~2개를 제대로 고르는 편이 VRAM과 품질 모두에서 유리한 경우가 많습니다.
  • 가중치(weight)를 과하게 주면 디테일이 늘어나는 대신 노이즈/아티팩트가 늘고, 재시도 횟수가 증가해 총 자원 소비가 커집니다.

4.2 LoRA를 병합(merge)할지, 런타임 적용할지

환경에 따라 다르지만, “런타임에 여러 LoRA를 계속 갈아끼우는” 워크플로우는 메모리 단편화나 캐시 누적으로 불안정해질 수 있습니다.

  • 자주 쓰는 조합이 고정이라면, 별도의 체크포인트로 병합해 운영하는 전략이 안정적일 수 있습니다.
  • 반대로 실험이 잦다면 병합은 번거로우니, 세션당 LoRA 수를 제한하고 필요할 때만 로딩하는 방식이 낫습니다.

4.3 fp16, bf16과 LoRA

가능하면 half precision을 유지하세요.

  • NVIDIA 계열에서 대부분 fp16이 기본 절감 옵션입니다.
  • 일부 GPU에서는 bf16이 더 안정적일 수 있지만, 소프트웨어 스택에 따라 다릅니다.

LoRA까지 포함해 전체 파이프라인 dtype이 섞이면(예: UNet은 fp16, 일부 모듈은 fp32) 예상보다 VRAM이 늘 수 있습니다.

5) 추가로 효과 큰 “현실적인” OOM 회피 옵션들

xFormers, 타일링, LoRA 최적화만으로도 대부분 해결되지만, 여전히 OOM이면 아래를 순서대로 적용하는 것이 효율적입니다.

5.1 해상도와 배치 전략

  • batch size1로 고정하고, 여러 장이 필요하면 batch count로 돌리는 편이 VRAM에 유리합니다.
  • 1024x1024가 터지면 896x896 또는 832x832로 낮추고, 업스케일을 타일링으로 처리하는 것이 성공률이 높습니다.

5.2 Hi-Res fix의 2패스 피크 줄이기

Hi-Res fix는 2단계로 돌기 때문에 피크가 큽니다.

  • 업스케일 배율을 낮추고(예: 2.0 대신 1.5)
  • 2패스 denoise를 과하게 주지 말고
  • 가능하면 업스케일 타일링을 병행하세요.

5.3 메모리 단편화 대응

PyTorch는 반복 실행 중 메모리 단편화로 OOM이 더 쉽게 날 수 있습니다. 세션을 오래 켜두고 많은 조합을 실험할수록 발생 확률이 올라갑니다.

  • 일정 횟수 생성 후 WebUI 재시작
  • 백그라운드에서 VRAM 점유하는 앱(브라우저 GPU 가속, 게임 런처 등) 종료

서버 운영 관점에서는 “원인이 비슷한데 갑자기만 실패하는” 문제가 자주 나오며, 이런 류의 디버깅은 다른 인프라 장애 대응과도 결이 같습니다. 예를 들어 리눅스에서 작업이 간헐적으로 안 도는 원인을 환경 차이로 추적하는 방식은 여기서도 유효합니다: 리눅스 crontab 안 돈다? 로그·환경·쉘 차이

6) Diffusers 기준: xFormers와 메모리 효율 옵션 코드 예시

WebUI가 아닌 Python 코드로 Stable Diffusion을 돌릴 때는 Diffusers 옵션이 명확합니다.

아래 예시는 xFormers와 메모리 절감 옵션을 켜는 전형적인 패턴입니다.

import torch
from diffusers import StableDiffusionPipeline

model_id = "runwayml/stable-diffusion-v1-5"

pipe = StableDiffusionPipeline.from_pretrained(
    model_id,
    torch_dtype=torch.float16,
)
pipe = pipe.to("cuda")

# xFormers 메모리 효율 어텐션
pipe.enable_xformers_memory_efficient_attention()

# VAE 슬라이싱(피크 VRAM 감소에 도움)
pipe.enable_vae_slicing()

# 일부 환경에서 추가 절감 옵션
pipe.enable_attention_slicing("max")

prompt = "a photo of a futuristic city, ultra detailed"
image = pipe(prompt, num_inference_steps=30, guidance_scale=7.5).images[0]
image.save("out.png")

여기서 중요한 점:

  • enable_xformers_memory_efficient_attention()은 어텐션 피크를 줄이는 핵심
  • enable_vae_slicing()은 VAE 단계 피크를 낮추는 데 자주 유효
  • enable_attention_slicing()은 속도와 맞바꿔 메모리를 절약

즉, “OOM이 어디서 나는지”에 따라 조합을 달리해야 합니다.

7) 체크리스트: OOM을 줄이는 적용 순서

실전에서 가장 효율적인 적용 순서는 보통 아래입니다.

  1. --xformers 또는 동급의 메모리 효율 어텐션 활성화
  2. batch size1로, 해상도를 한 단계 낮춤
  3. VAE 관련 피크가 의심되면 VAE 슬라이싱/타일링 적용
  4. 업스케일러/Hi-Res fix는 타일링 기반으로 전환
  5. LoRA 개수/가중치 정리(필요 시 병합)
  6. 그래도 불안정하면 세션 재시작, 백그라운드 GPU 점유 제거

OOM은 “옵션 하나로 끝”이 아니라, **피크를 낮추는 장치(xFormers, 슬라이싱, 타일링)**와 **상주량을 줄이는 습관(배치/해상도/LoRA 관리)**을 같이 가져가야 재발이 줄어듭니다.

8) 마무리: 최적화의 목표는 품질이 아니라 “완주”

Stable Diffusion에서 VRAM OOM을 해결하는 최종 목표는 벤치마크 점수가 아니라, 원하는 해상도와 워크플로우를 실패 없이 끝까지 실행하는 것입니다. xFormers로 어텐션 피크를 줄이고, 타일링으로 큰 연산을 쪼개며, LoRA를 운영 관점에서 정리하면 8GB~12GB급 GPU에서도 작업 성공률이 눈에 띄게 올라갑니다.

추가로, 운영 중에 OOM이 “간헐적”으로 변했다면 메모리 단편화, 세션 누적, 환경 차이를 의심하고 로그와 실행 조건을 고정하는 방식으로 접근하는 것이 좋습니다.