모델을 저장하고 다시 불러오기만 했을 뿐인데 에러가 났다. 그 원인은 다름 아닌 pickle이었다.
🧩 문제는 환경의 “미묘한 차이”였다
Stable Diffusion 기반 API 서버를 개발하는 과정에서 .pth 파일로 저장한 모델을 불러오는 도중 다음과 같은 오류를 만났다.
ModuleNotFoundError: No module named 'diffusers.models.unets'
흥미로운 점은 같은 서버에서 저장한 모델임에도, Docker 컨테이너에서 실행했을 때 이 오류가 발생했다는 것이다. 알고 보니 로컬 환경과 컨테이너 내부의 라이브러리 버전이 미묘하게 달랐고, 바로 그 차이로 인해 모델을 제대로 불러올 수 없었던 것이다.
왜 이런 일이 발생했을까?
# 저장 시
[UNet2DConditionModel 객체]
└── torch.save() → .pth 파일 (pickle 직렬화)
# 로드 시
torch.load()
└── diffusers.models.unets.UNet2DConditionModel
✖️ 모듈 없으면 → ModuleNotFoundError
PyTorch에서 사용하는 torch.save()는 내부적으로 Python의 pickle을 사용해 객체를 직렬화한다. 이때 객체의 클래스 경로 정보(예: diffusers.models.unets.UNet2DConditionModel)가 문자열로 저장되기 때문에, 모델을 로드할 시점에 해당 경로가 정확히 존재해야만 오류 없이 불러올 수 있다.
즉, Docker 이미지가 생성되는 시점에 설치된 diffusers 패키지 구조가 로컬과 달라졌다면, torch.load()는 해당 클래스를 찾지 못하고 ModuleNotFoundError를 발생시킨다.
해결 방법
방법 1: 저장 당시와 동일한 라이브러리 버전 맞추기
[pip freeze > requirements.txt] → Docker 이미지 생성
│
▼
[torch.load()] → 동일 구조 & 버전이면 정상 작동 ✅
가장 안정적인 방법은 모델을 저장한 환경과 완전히 동일한 버전의 라이브러리를 사용하는 것이다. 특히 다음 3가지 패키지는 반드시 버전을 고정하는 걸 추천한다:
diffusers==0.34.0 transformers==4.54.1 torch==2.2.2
이 버전들을 requirements.txt에 명시해 Docker 이미지를 구성하면, 모델 로딩 시 문제가 생기지 않는다.
💡 팁: 모델 저장 당시의 pip freeze를 기록해두면 나중에 환경 재현이 훨씬 쉬워진다.
방법 2 (참고용): state_dict만 저장
pickle 직렬화 자체를 피하고 싶다면, state_dict() 방식으로 모델의 파라미터만 저장할 수 있다:
# 저장
torch.save(model.state_dict(), "unet_state_dict.pth")
# 로드
model = UNet2DConditionModel(...) # 구조 명시 필요
model.load_state_dict(torch.load("unet_state_dict.pth"))
이 방식은 라이브러리 버전이 달라도 잘 작동하지만, 모델 구조를 직접 다시 정의해야 해서 번거롭다. 라이브러리 구조가 자주 바뀌지 않는 프로젝트라면 고려해볼 만한 대안이다.
정리: Docker 환경에서는 버전 관리가 생명
- torch.save()와 torch.load()는 pickle 기반이라 구조 의존성이 매우 크다.
- Docker 환경을 새로 빌드하거나 서버 환경이 바뀔 때마다 반드시 다음 세 가지 버전을 체크하자:
- diffusers
- transformers
- torch
- ModuleNotFoundError가 떴다면, 가장 먼저 라이브러리 버전 불일치를 의심해야 한다.
후속 작업으로 남긴 것들
이번 경험을 바탕으로 앞으로는 다음을 실천하려 한다:
- 모델 저장 시점의 requirements.txt를 반드시 백업해두기
- 모델을 배포할 컨테이너에는 정확한 라이브러리 버전 명시
- 장기적으로는 state_dict 방식도 병행 검토
이 문제는 작지만 은근히 자주 발생하고, 특히 협업 중에 환경이 미묘하게 달라졌을 때 디버깅 시간을 잡아먹는다. 나처럼 이 문제에 막히는 개발자 분들께 이 글이 작은 힌트가 되길 바란다!
'development > 머신러닝 운영' 카테고리의 다른 글
| 🐳 Stable Diffusion API Docker 패키징: 로컬 모델 조립 → 컨테이너화까지 (5) | 2025.08.09 |
|---|---|
| EFS에 모델 구성요소 저장하고, 컨테이너에서 불러오는 구조로 전환하기 (3) | 2025.08.08 |
| from_pretrained 없이 로컬 구성요소만으로 Stable Diffusion 파이프라인 조립하기 (4) | 2025.08.07 |
| 🤯 [Error 분석]Stable Diffusion 모델을 Git에서 받다가 생긴 시행착오 (4) | 2025.08.06 |
| Stable Diffusion 이미지 생성 API 만들기 (2) | 2025.08.06 |