Models & Algorithms•SOTAAZ Lab••EN

8GB GPU로 사진 한 장을 3D 모델로 만들 수 있을까요? 8GB로 제한한 A100에서 Idol은 7,380MiB를 썼습니다

사진 한 장을 3D 물체로 바꾸는 Speridlabs의 Idol을 8GB로 제한한 A100에서 재 봤습니다. 실제 8GB 카드로 재지는 못했습니다. 실제 사진의 최고치는 nvidia-smi 기준 7,380MiB입니다. 8,192MiB로 세고 데스크톱이 512MiB를 쓰면 300MiB가 남고, 8×10⁹바이트로 세면 263MiB가 모자랍니다. 실행 명령과 762MiB를 더 쓰는 단계를 정리했습니다.

8GB GPU로 사진 한 장을 3D 모델로 만들 수 있을까요? 8GB로 제한한 A100에서 Idol은 7,380MiB를 썼습니다

8GB GPU로 사진 한 장을 3D 모델로 만들 수 있을까요? 8GB로 제한한 A100에서 Idol은 7,380MiB를 썼습니다

Idol의 README에는 "7.2 GB of VRAM"을 쓴다고 적혀 있습니다. 그런데 저희 첫 테스트에서는 6,618MiB가 나왔습니다.

문제는 저희가 고른 입력 이미지에 있었습니다. Idol README의 미리보기 이미지를 썼는데, 이 이미지들은 배경이 이미 투명합니다. 그래서 배경 제거 단계가 한 번도 돌지 않았습니다. 일반 사진을 넣으니 nvidia-smi 기준 최고치가 7,380MiB, 즉 7.21GiB가 나왔습니다. 같은 사진의 배경을 미리 지워 다시 넣자 6,616MiB로 내려갔습니다.

그럼 8GB 카드에 들어갈까요? 저희에게는 8GB 카드가 없어서, A100에서 PyTorch가 쓸 수 있는 메모리를 제한해 측정했습니다. Idol은 이미지 인코더(DINOv3)를 기본으로 bf16 정밀도로 돌리는데, 이 기본 설정은 제한 아래에서 모두 끝까지 돌았습니다. 8GB 카드를 8,192MiB로 보면 모니터가 없을 때 812MiB, 데스크톱이 512MiB를 쓴다고 가정하면 300MiB가 남습니다. 앞선 글의 더 엄격한 계산처럼 8×10⁹바이트로 보면 같은 데스크톱 조건에서 7,380MiB는 7,117MiB보다 263MiB 많습니다. fp32 이미지 인코더는 첫 번째 계산으로도 데스크톱 512MiB를 뺀 7,680MiB를 넘습니다.

입력 사진 아래에서 돌아가는 3D 결과: 신발, 등불, 생성한 용. 입력에 없던 면까지 보입니다

입력에 없는 면은 모델이 생성한 것입니다. 실제 물체와 얼마나 비슷한지는 따로 측정하지 않았습니다.

신발과 등불은 Pexels 스톡 사진이고, 용 이미지는 저희가 flux로 생성한 이미지로 테스트했습니다.

준비물

  • 8GB 이상 NVIDIA GPU. 다만 실제 8GB 카드에서 돌려 본 것은 아닙니다. 뒤의 한계 절을 봐 주세요.
  • Python 3.10 이상(저희는 3.11)과 드라이버에 맞는 PyTorch.
  • Hugging Face 계정과 접근 승인 두 건. facebook/dinov3-vith16plus-pretrain-lvd1689m과 black-forest-labs/FLUX.2-dev입니다. FLUX.2-dev에서는 VAE만 내려받습니다.

라이선스도 확인해 두세요. Idol 모델 페이지에는 Apache-2.0, BiRefNet 페이지에는 MIT로 표시되어 있습니다. DINOv3에는 자체 DINOv3 License가 있습니다. FLUX.2-dev 페이지에는 FLUX Non-Commercial License라고 적혀 있고, VAE에 대한 별도 라이선스는 나와 있지 않습니다. 상업적으로 쓰려면 약관을 직접 확인하시기 바랍니다. 접근 승인은 각 저장소 페이지에서 신청하며, 바로 나지 않을 수도 있습니다.

1단계: 설치

bash
git clone https://github.com/speridlabs/idol-sdk
cd idol-sdk
git checkout 4702a7f
python3 -m venv .venv
. .venv/bin/activate
pip install torch==2.11.0 torchvision==0.26.0 --index-url https://download.pytorch.org/whl/cu128
pip install -e . diffusers==0.42.1 transformers==5.22.1
hf auth login

torch를 먼저 설치한 데는 이유가 있습니다. 기본으로 깔리는 2.15는 CUDA 13.2용이라 저희 드라이버(580.178.04, CUDA 13.0)와 맞지 않았습니다. nvidia-smi 첫 줄에 드라이버의 CUDA 버전이 나오니, 그보다 새롭지 않은 torch를 고르면 됩니다. 위 명령은 저희가 잰 SDK 커밋과 주요 버전을 고정합니다. 저희는 uv로 설치했고, 이 pip 명령을 그대로 돌려 보지는 않았습니다.

2단계: 물체 하나 만들기

bash
python -m idol.sample --image photo.jpg --mode real --out outputs

사진이면 --mode real을 붙이고, 렌더링했거나 생성한 이미지면 빼면 됩니다. 기본값이 synthetic입니다. 이미지마다 photo.ply, 모델이 실제로 본 이미지인 photo_input.png, photo_camera.json 세 파일이 나옵니다.

신발 사진으로 이 명령을 그대로 돌렸더니 가중치를 이미 내려받은 상태에서 전체 37.2초가 걸렸고, 17.8MB짜리 .ply가 나왔습니다. README에는 어떤 Gaussian splat 뷰어로도 열 수 있다고 적혀 있습니다. 저희는 gsplat으로 직접 렌더링했는데, Y축을 아래 방향으로 봐야 했습니다. 물체가 거꾸로 보이면 뒤집어 보세요.

돌아가는 동안 메모리를 보려면 터미널을 하나 더 열어 이렇게 실행합니다.

bash
nvidia-smi --query-gpu=memory.used --format=csv -lms 200

0.2초마다 재는 명령입니다. 1초 간격으로 재면 최고치를 놓칠 수 있습니다. 다른 작업이 없던 A100에서 카드 전체 최고치는 7,390MiB였습니다.

메모리를 얼마나 썼을까요

bf16 설정은 먼저 제한 없이 재고, 8GB 카드를 대신하는 두 제한 아래에서 다시 쟀습니다. fp32는 제한 아래에서만 쟀습니다. 제한에는 PyTorch의 set_per_process_memory_fraction을 썼습니다. 모니터 없는 카드는 7,700MiB로 잡았습니다. 모델을 올린 뒤 nvidia-smi 값이 PyTorch reserved보다 492MiB 컸는데, 8,192에서 그만큼을 뺀 값입니다. 데스크톱이 있는 카드는 여기서 512를 더 뺀 7,188MiB입니다. 아래 그림의 7,680 선은 8,192에서 512를 뺀 값입니다.

8,192MiB 카드와 7,680 데스크톱 선에 견준 막대: README 미리보기 이미지 6,618, 배경을 미리 지운 Pexels 사진 6,616, 배경 제거를 실행한 Pexels 사진 7,380(300 남음), fp32 인코더 8,044(선보다 364 초과)

표에는 수치가 세 가지 있습니다. allocated는 PyTorch의 텐서가 실제로 차지한 최대량입니다. reserved는 PyTorch가 드라이버에서 받아 둔 양으로, 다 쓰고 돌려주지 않은 블록까지 포함합니다. nvidia-smi는 드라이버가 프로세스 전체에 대해 보고하는 값이고, 카드 크기와 견줄 때 보는 수치입니다.

입력과 설정PyTorch allocatedPyTorch reservednvidia-smi
README 미리보기 이미지(배경이 이미 투명)5,1176,0966,618
Pexels 사진, 배경을 미리 지움5,1176,0966,616
Pexels 사진, 배경 제거 실행5,1196,8587,380
Pexels 사진, fp32 이미지 인코더, 7,700 제한6,8207,5208,044

모두 기본값인 Gaussian 262,144개에서 잰 최고치이고 단위는 MiB입니다. 앞의 세 행은 제한 없이 잰 값입니다. 미리보기 이미지와 사진은 두 제한에서도 최고치가 같았습니다. bf16의 reserved 최고치가 어느 제한에도 닿지 않았기 때문입니다. 배경을 미리 지운 행은 제한 없이만 쟀습니다.

8GB를 어떻게 세느냐에 따라 답이 달라집니다. 이 글은 이 연재의 직전 글처럼 8,192MiB로 계산합니다. 그 글은 여유분 16MiB를 더 빼서 7,664MiB를 씁니다. 실제 카드의 memory.total은 읽어 보지 못했습니다. 더 앞선 글들은 8×10⁹바이트, 즉 7,629MiB로 세고 데스크톱 512MiB를 빼 7,117MiB를 썼습니다. 이 기준이면 사진의 7,380MiB는 데스크톱 화면이 같은 카드를 함께 쓰면 7,117MiB보다 263MiB 많고, 모니터가 없으면 249MiB가 남습니다. 저희가 건 7,188MiB 제한은 7,117보다 높아서, 이만큼 엄격한 제한은 시험하지 못했습니다. 내 카드의 값은 nvidia-smi --query-gpu=memory.free --format=csv로 확인할 수 있습니다.

7.2는 두 가지로 해석됩니다. 7,380MiB는 7.21GiB이고, PyTorch reserved 6,858MiB는 십진 단위로 7.19GB입니다. 둘 다 반올림하면 7.2입니다. README에 어느 수치인지 나와 있지 않아서 어느 쪽을 뜻하는지는 알 수 없습니다.

bf16에서는 배경을 미리 지운 입력의 최고치가 더 낮았습니다(사진 두 장, 제한 없음). 이미지에 투명 영역이 있으면 배경 제거가 돌지 않아 최고치가 6,616MiB입니다. 데스크톱 512MiB 가정에서 1,064MiB, 더 엄격한 계산으로는 501MiB가 남습니다. 배경 제거 모델의 가중치는 어느 경우든 올라갑니다. 저희는 Idol의 배경 제거 모델을 별도 프로세스로 돌려 배경을 지웠습니다. 이 단계 자체의 메모리는 재지 않았고, 배경을 지운 입력을 fp32로 돌리거나 3D 결과의 품질을 비교하지도 않았습니다.

fp32는 사진에서 7,680 선을 넘습니다. --dinov3_dtype fp32는 이미지 인코더를 fp32(32비트) 그대로 돌립니다. README에는 1.7GB를 더 쓴다고 적혀 있습니다. 사진에서 allocated 최고치는 실제로 1,701MiB 늘었지만, nvidia-smi는 664MiB만 늘었습니다. PyTorch가 이미 받아 둔 메모리를 다시 쓴 것으로 보이지만, 추적해 확인하지는 않았습니다. 7,700 제한에서 8,044MiB이므로 8,192까지 148MiB가 남습니다. README 미리보기 이미지는 첫 요청에서 8,124MiB까지 올라 68MiB만 남았고, 이후 요청은 7,570MiB였습니다. 7,188 제한에서 사진은 7,686MiB로 선보다 6MiB 많았고 이후 7,564MiB였습니다. 미리보기 이미지는 처음 두 요청에서 7,650MiB, 이후 7,488MiB였습니다. 모두 끝까지 돌았습니다. 이 제한은 PyTorch의 메모리만 막을 뿐 CUDA 컨텍스트는 막지 않으므로, 실제 카드에서 실패할지는 이 결과로 알 수 없습니다. 제한 없이 fp32를 돌린 기록은 없고, fp32가 결과를 바꾸는지도 재지 않았습니다. 여유가 이 정도면 저희는 데스크톱 화면을 함께 띄운 8GB 카드에서 fp32를 쓰지 않겠습니다.

7,380MiB는 어디에 쓰일까요

7,380MiB를 나눈 막대: 로드 후 allocated 3,519, 요청 최고 시점에 더해지는 1,600, 그 위로 PyTorch가 잡아 둔 1,739, PyTorch 밖 522

사진을 넣은 실행의 수치를 네 조각으로 나누면 이렇습니다. 최고치끼리의 차이이지 한순간의 내역은 아니므로, 각 최고치의 시점은 다를 수 있습니다. 아래에서 요청은 이미지 한 장을 물체 하나로 바꾸는 처리를 뜻합니다.

  • 로드 후 allocated 3,519MiB. 모델의 여섯 부분이 요청 내내 GPU에 머뭅니다. 오프로드나 저사양용 옵션은 없습니다. README는 가중치를 3.7-4.4GB로 적고 있습니다.
  • 이미지를 처리하면서 메모리를 가장 많이 쓸 때 더해지는 1,600MiB. 여기까지가 allocated 최고치 5,119MiB입니다.
  • 그 위로 잡아 둔 1,739MiB. PyTorch는 다 쓴 메모리를 돌려주지 않고 잡아 둡니다. 배경 제거가 바꾸는 부분이 여기입니다. reserved는 762MiB 늘지만 allocated는 2MiB만 움직입니다. 7,188MiB보다 더 낮게 제한하면 줄어들 수도 있지만, 그 조건은 재지 않았습니다.
  • PyTorch 밖 522MiB. PyTorch reserved 밖에서 nvidia-smi가 추가로 세는 양입니다. CUDA 컨텍스트가 들어 있지만 더 나눠 보지는 않았고, 다른 GPU에서는 달라질 수 있습니다.

Gaussian을 늘려도 최고치는 그대로입니다

Gaussian은 물체를 이루는 작은 색 덩어리입니다. --num_gaussians로 32,768개에서 1,048,576개까지 32의 배수로 고를 수 있습니다.

신발 근접 화면을 Gaussian 32,768개와 1,048,576개로 나란히 돌린 그림. 많은 쪽이 더 선명해 보입니다. .ply는 2.2MB와 71.3MB, GPU 최고치는 둘 다 7,380MiB입니다

32,768개에서 1,048,576개로 32배 늘려도 신발의 nvidia-smi 최고치는 7,380MiB 그대로였습니다. .ply만 2.2MB에서 71.3MB로 커졌습니다. README 미리보기 이미지에서 allocated 최고치는 54MiB 늘었는데, PyTorch가 이미 잡아 둔 범위 안입니다. 코드를 보면 모델은 크기가 고정된 latent로 작업하고 Gaussian은 마지막 단계에서 배치합니다. 그래서 생성 중 GPU 메모리만 보면 가장 큰 설정에서도 최고치는 거의 늘지 않습니다. 100만 개짜리 파일을 볼 때 드는 메모리는 재지 않았습니다. 근접 화면은 100만 개 쪽이 선명해 보이지만 품질을 채점하지는 않았습니다.

시간은 조금 늘어납니다. 미리보기 이미지 기준 17.2초에서 18.0초가 됐습니다.

A100에서 걸린 시간

모두 A100 기준이라 8GB 카드의 속도를 직접 말해 주지는 않습니다.

  • 17.4초. 모델이 로드되고 첫 요청이 지난 뒤 물체 하나를 만드는 시간입니다(물체 세 개에 걸친 요청 네 번의 중앙값). 물체별로는 신발 17.4초, 등불 17.5초, 용 17.3초이고 각각 요청 한두 번에서 나온 값입니다. README의 A100 수치는 16.3초입니다.
  • 22.7초. 컴파일 캐시가 남은 상태로 새로 띄운 프로세스의 첫 요청입니다. 맨 처음 요청은 미리보기 이미지에서 34.9초였습니다. 차이는 torch.compile 캐시 때문으로 보지만 따로 확인하지는 않았습니다.
  • --no_optimize를 쓰면 미리보기 이미지에서 최고치는 같고 약 1.35배 느렸습니다(23.4초와 17.3초). 최적화를 꺼도 메모리는 줄지 않습니다.

README 표에서 가장 느린 소비자용 카드는 10GB RTX 3080으로 46.2초입니다. 8GB 카드는 표에 없습니다.

저희 예측

첫 실행 전에 적어 둔 예측입니다. 첫 실행에는 README 미리보기 이미지를 썼습니다.

예측결과
P1allocated 최고치 5.0-7.2GiB, 7.2GB가 allocated나 reserved와 0.3GB 이내로 가까움틀림. 4.997GiB, 미리보기 이미지에서는 어느 쪽도 가깝지 않음
P2nvidia-smi가 reserved보다 0.4-1.2GiB 높음맞음, 0.51GiB
P3100만 개가 32,768개보다 allocated를 최대 1.0GiB 더 씀맞음, 54MiB
P4--no_optimize는 최대 0.5GiB 낮고 1.3-2.2배 느림맞음, 최고치 같고 1.35배
P5.ply가 71.3MB와 2.2MB, 오차 0.1 안맞음
P67,700 제한에서 Gaussian 개수 세 가지는 끝나고 fp32는 메모리 부족절반 틀림. fp32도 끝남
P77,188 제한에서 하나 이상 메모리 부족틀림. 전부 끝남
P8둘째 요청부터 13-20초, 첫 요청은 30초 이상 더 걸림절반 틀림. 17.2초, 첫 요청은 17.7초 더 걸림

실제 사진에서는 P1의 reserved 조건이 7.19GB로 맞습니다. 이것은 결과를 본 뒤에 알게 된 사실입니다.

빠진 단계를 알아챈 뒤 예측을 두 번 더 적었습니다. 둘 다 사후 예측입니다. 첫 번째는 제한 없는 사진 최고치를 이미 본 뒤에 적었습니다. 그래서 bf16이 두 제한에서 끝나고 7,680MiB 아래에 머문다는 예측은 맞았지만 결과가 사실상 정해져 있었습니다. 제대로 된 검증은 fp32뿐이었습니다. 7,188 제한에서 메모리가 부족할 것이라고 적었는데 끝까지 돌았습니다. 두 번째는 제대로 된 검증이었습니다. 배경을 미리 지운 사진이 미리보기 이미지와 30MiB 안으로 돌아올 것이라고 적었고, 실제로 그랬습니다(nvidia-smi 6,616과 6,618, reserved 6,096과 6,096).

한계

  • 실제 8GB 카드가 아닙니다. A100 카드 한 장에서 PyTorch가 8GB 카드만큼만 쓰도록 제한해 쟀습니다. 실제 8GB 카드는 CUDA 컨텍스트와 커널 크기가 다를 수 있고, 보고하는 총량이 8,192MiB보다 조금 적을 수도 있습니다.
  • 300MiB는 넉넉하지 않습니다. 데스크톱 512MiB는 저희 가정입니다. 하드웨어 가속을 쓰는 브라우저나 두 번째 모니터가 있으면 더 쓸 수 있습니다. 다른 프로그램을 닫거나 모니터를 연결하지 않은 카드를 쓰는 편이 안전합니다.
  • 입력이 적습니다. 신발, 등불, 생성한 용과 README 미리보기 이미지 네 장이 전부입니다. 각 이미지는 입력과 Idol이 만든 결과가 한 프레임에 나란히 있습니다. 같은 묶음 안에서는 최고치가 같았지만 다른 입력에서는 달라질 수 있습니다.
  • 품질은 재지 않았습니다. 어떤 설정에서든 결과를 보여 줄 뿐 채점하지 않았습니다.
  • 일부 시간 기록은 뺐습니다. 사진 실행 후반에 다른 작업이 같은 GPU를 함께 썼습니다. 메모리 수치는 프로세스별이라 영향이 없고, 시간은 GPU를 혼자 쓴 실행만 적었습니다.

다음 한 걸음

같은 카드에 GGUF 텍스트 모델을 llama-server로 올린다면 짧게 두 번 띄워 메모리를 가늠하는 방법을 써 보세요. LFM3 모델 네 개(텍스트, 비전, 오디오, 옴니)를 4·6·8GB 카드에 맞춰 본 내용은 LFM3 글에 있습니다.

더 많은 콘텐츠를 받아보세요

SNS에서 새로운 글과 튜토리얼 소식을 가장 먼저 받아보세요

이메일로 받아보기