Pointcept:Examples:Weld:LVS:SetupAndOOM
Pointcept Weld 학습 환경 구축 및 OOM (Out Of Memory) 해결 과정.
기존, Pointcept:Examples:Weld:LVS (LVS 로 취득한 데이터로 훈련하는 샘플) 페이지에서 이어진다.
개요
Pointcept 프로젝트에서 자체 제작한 WeldDataset(용접 부위 3-class semantic segmentation)을 PTv3 모델로 학습하는 과정에서 발생한 일련의 환경·메모리 문제를 순차적으로 해결한 기록.
최종 결과: 8 GB RTX PRO 2000 Blackwell(sm_120)에서 PTv3 100 epoch 완주. Best mIoU 0.3093 (bead IoU 0.9279).
환경
| 항목 | 값 |
| GPU | NVIDIA RTX PRO 2000 Blackwell (sm_120), 8 GB VRAM |
| Driver | 590.48.01 |
| CUDA Runtime | 13.1 (nvidia-smi) / 12.8 nvcc |
| OS | Linux 6.17.0-1017-oem |
| Python | 3.10 |
| 패키지 관리자 |
문제 1: NCCL 심볼 누락
증상
원인
- 설치된 torch 버전과 NCCL 패키지 간 ABI 불일치
- 조사 결과:
-
torch-2.11.0.dist-info설치됨 (+cu130bundle) -
torchvision-0.22.0+cu128,torchaudio-2.7.0+cu128등 companion은 pt27용 -
nvidia-nccl-cu12-2.26.2와nvidia-nccl-cu13-2.28.9양쪽 설치됨
-
ncclCommWindowDeregister는 NCCL 2.27+에서 도입된 심볼이라 2.26.2에서는 해결 불가 해결
requirements.cu128.txt와 uv-install-cu128.sh를 검토·강화한 뒤 venv 재구축.
문제 2: 설치 스크립트/의존성 pin 강화
진단
모든 URL과 wheel은 유효했으나, pin이 느슨해 다운그레이드 시 실패 가능:
-
torch==2.7.0pin은 PyPI CPU 빌드와도 매치 가능 (로컬 version identifier 없음) - PyG 확장 (
torch-cluster,torch-scatter,torch-sparse,torch-geometric) 버전 unpin -
spconv-cu126버전 unpin -
uv pip install은 다운그레이드를 자동으로 못 함 (--reinstall필요)
수정
requirements.cu128.txt:
torch==2.7.0+cu128
torchvision==0.22.0+cu128
torchaudio==2.7.0+cu128
torch-cluster==1.6.3
torch-scatter==2.1.2
torch-sparse==0.6.18
torch-geometric==2.7.0
spconv-cu126==2.3.8
uv-install-cu128.sh 개선:
-
set -e→set -euo pipefail -
uv존재 여부 선검사 추가 -
g++-13선검사 경고 추가 -
read -p를-t 0로 감싸 non-interactive hang 방지 -
uv pip install -r→uv pip install --reinstall -r -
TORCH_CUDA_ARCH_LIST에10.0(데이터센터 Blackwell) 추가
문제 3: uv.lock의 torch 2.11 강제 pin
증상
스크립트 재실행 후에도 torch가 여전히 2.11.0. ./.venv/bin/python -c "import torch; print(torch.__version__)" → 2.11.0+cu130.
원인
-
pyproject.toml의peft>=0.19.0이 transitive dep으로 torch 요구 -
uv.lock에torch==2.11.0(PyPI, cu130 bundle) 고정 - 사용자가
uv run --with open3d로 실행할 때 uv가 lock 기반 환경을 복원하여 torch 2.11 재설치 -
uv pip install --reinstall(pip-compat 레이어)과uv run(lock 레이어)이 서로 다른 시스템
해결
이후 실행은 uv run 대신 직접 호출:
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
PYTHONPATH=$PWD:$PYTHONPATH \
./.venv/bin/python tools/train.py ...
문제 4: 첫 OOM — attention upcast
증상
File ".../point_transformer_v3m1_base.py", line 203, in forward
attn = attn.float()
torch.OutOfMemoryError: CUDA out of memory.
Tried to allocate 1.56 GiB.
GPU 0 has a total capacity of 7.53 GiB of which 91.12 MiB is free.
분석: 우선순위별 해결 전략
정확도 손실 크기 순: model capacity (채널·depth) >> grid_size (voxel) > patch_size > point_max > upcast flags.
| 순위 | 항목 | 정확도 영향 | 메모리 효과 |
| 0 | | 거의 없음 | 중간 |
| 1 | | 작음 | 중간~큼 |
| 2 | | 작음 (loop 보상) | 큼 |
| 3 | | 중간 (bead 경계 정밀도) | 큼 |
| 4 | 모델 채널·depth 축소 | 큼 | 큼 |
적용 단계 (누적 variant 생성)
configs/weld/에 단계별 variant config을 누적 생성. train.sh에서 주석으로 모든 과거 config을 남겨 빠른 롤백 가능하게.
| 단계 | 파일 접미사 | 조치 | 결과 |
| 0 | | upcast 끄기 | 6.84 GiB peak, 여전히 OOM |
| 1 | | patch_size 1024→512 | OOM |
| 2 | | point_max 102400→51200 | QK^T 288 MiB 요청 OOM |
| 3 | | patch_size 512→256 | QK^T 100 MiB 요청 OOM |
| 4 | | point_max 51200→25600 | Train 돌아감, epoch 11 backward OOM |
| 5a | | val에 SphereCrop 추가 | eval OOM은 해결, index out of bounds |
| 5b | | val에서 inverse 경로 제거 | Train+Eval 성공, 10 epoch 진행, backward OOM |
| 6 | | | 역효과: epoch 3 OOM |
| 7 | | empty_cache 롤백, point_max 25600→12800 | 100 epoch 완주 |
| 8 | | PreciseEvaluator hook 제거 | 학습 종료까지 깔끔 |
문제 5: val OOM (softmax 2.55 GiB)
증상
첫 epoch training은 성공했으나 Evaluation에서 OOM:
File ".../point_transformer_v3m1_base.py", line 204, in forward
attn = self.softmax(attn)
torch.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.55 GiB.
원인
Training transform엔 SphereCrop이 있지만 val transform엔 빠져 있어 씬 전체(약 200k+ points)를 forward. Softmax 텐서가 크게 할당됨.
해결 1 (실패)
val에 SphereCrop(point_max=25600, mode="center") 추가.
결과: IndexKernel.cu:93: index out of bounds. GridSample(return_inverse=True)의 inverse 인덱스가 원래 voxel 집합을 가리키는데 SphereCrop 이후 pred 크기가 줄어 mismatch.
해결 2 (성공)
val transform에서 Copy, return_inverse=True, origin_segment, inverse를 모두 제거.
evaluator.py의 if "inverse" in input_dict 분기를 타지 않아 crop된 영역만으로 평가. 단일 씬 overfit 모니터링 용도라 영향 미미.
문제 6: backward OOM (allocator 단편화)
증상
epoch 11에서 loss.backward() 중 100 MiB 할당 실패. expandable_segments:True가 켜져 있음에도 발생.
가설 검증
- 가설 A:
empty_cache=True로 epoch 간 단편화 해소- 실제 결과: epoch 3에서 OOM — 악화
-
expandable_segments와empty_cache()가 서로 간섭하여 큰 연속 청크 재할당 실패
empty_cache=False 유지 + point_max 반감 (25600→12800)- 실제 결과: 100 epoch 완주
교훈
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True와 torch.cuda.empty_cache()를 섞으면 오히려 효과가 깨질 수 있음. 둘 중 하나만 사용.
문제 7: 학습 종료 hook 실패
증상
100 epoch 학습 후 PreciseEvaluator 단계에서:
원인
_base_/default_runtime.py의 hooks에 PreciseEvaluator가 포함되어 있어 학습 종료 후 SemSegTester를 빌드. weld config엔 data.test 섹션이 없음.
해결
variant config에서 hooks를 override하여 PreciseEvaluator만 제거:
hooks = [
dict(type="CheckpointLoader"),
dict(type="ModelHook"),
dict(type="IterationTimer", warmup_iter=2),
dict(type="InformationWriter"),
dict(type="SemSegEvaluator"),
dict(type="CheckpointSaver", save_freq=None),
# PreciseEvaluator 제거 — weld는 data.test 없음
]
단일 학습 씬에서는 test metric이 의미가 제한적이라 정식 data.test 추가보다 hook 제거가 합리적.
부수 작업: Inference 스크립트
훈련 완료된 모델을 사용해 임의 point cloud에 대한 예측을 생성하는 standalone 스크립트 추가:
-
tools/infer_weld.py: 체크포인트 로드, 단일 forward pass, colored.ply또는 int.npy출력. 8 GB에 맞춰 centerSphereCrop(기본 12800 points) 사용. -
infer.sh: 래퍼.PYTORCH_CUDA_ALLOC_CONF/PYTHONPATH설정, 기본 weight 및 input 자동 선택,WEIGHTenv var 지원.
최종 config 체인
configs/weld/semseg-pt-v3m1-0-base-noattention_nosoftmax-smaller-eighthpoint-valcrop-noinv-nopreceval.py
누적 적용 내역:
-
upcast_attention=False,upcast_softmax=False -
enc/dec_patch_size=256 -
SphereCrop point_max=12800(train·val 모두) - val에서
Copy/inverse/origin_segment제거 -
empty_cache=False -
hooks에서PreciseEvaluator제거
학습 결과
| Epoch | Train loss | Val mIoU | bead IoU |
| 5 | 2.3784 | 0.3092 | 0.9276 |
| 54 (best) | 1.7828 | 0.3093 | 0.9278 |
| 100 | 1.6137 | 0.0100 | 0.0300 |
관찰:
- Best mIoU는 중반(epoch 54)에 정점, 이후 overfit으로 하락
-
face1,face2IoU는 항상 0 — 단일 씬 데이터로는 근본적으로 학습 불가 - 데이터 확보 전까지는 "학습 파이프라인이 돈다"는 sanity check 수준의 의미
교훈
- 느슨한 pin은 다운그레이드 막는다:
torch==2.7.0대신torch==2.7.0+cu128로 local version 명시. - uv 레이어 두 개 섞지 말 것:
uv pip과uv run/sync의 의존성 소스가 달라 충돌 가능. 한 프로젝트에서 하나만 사용. - OOM 해결은 비용 0인 것부터: upcast flag → patch_size → point_max → grid_size → model capacity 순.
- Allocator 튜닝은 교차 조합 조심:
expandable_segments와empty_cache()동시 사용은 역효과. - val transform의 inverse-mapping은 SphereCrop과 호환 안 됨: 원본 해상도 복원과 crop은 양립 불가.
- 누적 variant 파일 전략: 각 단계마다 새 config 파일을 만들고
train.sh에 주석으로 이력을 남기면 롤백이 쉽다. 단, 파일명이 길어지는 단점.
See also
Favorite site
- PyTorch CUDA Memory Management
- spconv (Blackwell 대응: cu126 wheel + PTX JIT)