Skip to content

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

패키지 관리자

uv

문제 1: NCCL 심볼 누락

증상

ImportError: .venv/.../torch/lib/libtorch_cuda.so:
  undefined symbol: ncclCommWindowDeregister

원인

  • 설치된 torch 버전과 NCCL 패키지 간 ABI 불일치
  • 조사 결과:
    • torch-2.11.0.dist-info 설치됨 (+cu130 bundle)
    • torchvision-0.22.0+cu128, torchaudio-2.7.0+cu128 등 companion은 pt27용
    • nvidia-nccl-cu12-2.26.2nvidia-nccl-cu13-2.28.9 양쪽 설치됨
  • ncclCommWindowDeregister는 NCCL 2.27+에서 도입된 심볼이라 2.26.2에서는 해결 불가

해결

requirements.cu128.txtuv-install-cu128.sh를 검토·강화한 뒤 venv 재구축.

문제 2: 설치 스크립트/의존성 pin 강화

진단

모든 URL과 wheel은 유효했으나, pin이 느슨해 다운그레이드 시 실패 가능:

  • torch==2.7.0 pin은 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 -eset -euo pipefail
  • uv 존재 여부 선검사 추가
  • g++-13 선검사 경고 추가
  • read -p-t 0로 감싸 non-interactive hang 방지
  • uv pip install -ruv pip install --reinstall -r
  • TORCH_CUDA_ARCH_LIST10.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.tomlpeft>=0.19.0이 transitive dep으로 torch 요구
  • uv.locktorch==2.11.0(PyPI, cu130 bundle) 고정
  • 사용자가 uv run --with open3d로 실행할 때 uv가 lock 기반 환경을 복원하여 torch 2.11 재설치
  • uv pip install --reinstall (pip-compat 레이어)과 uv run (lock 레이어)이 서로 다른 시스템

해결

rm -rf .venv uv.lock
./uv-install-cu128.sh

이후 실행은 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

upcast_attention=False, upcast_softmax=False

거의 없음

중간

1

enc/dec_patch_size 축소 (1024→512→256)

작음

중간~큼

2

SphereCrop point_max 축소

작음 (loop 보상)

3

grid_size 증가 (0.02→0.025)

중간 (bead 경계 정밀도)

4

모델 채널·depth 축소

적용 단계 (누적 variant 생성)

configs/weld/에 단계별 variant config을 누적 생성. train.sh에서 주석으로 모든 과거 config을 남겨 빠른 롤백 가능하게.

단계

파일 접미사

조치

결과

0

-noattention_nosoftmax

upcast 끄기

6.84 GiB peak, 여전히 OOM

1

-small

patch_size 1024→512

OOM

2

-small-halfpoint

point_max 102400→51200

QK^T 288 MiB 요청 OOM

3

-smaller-halfpoint

patch_size 512→256

QK^T 100 MiB 요청 OOM

4

-smaller-quarterpoint

point_max 51200→25600

Train 돌아감, epoch 11 backward OOM

5a

-valcrop

val에 SphereCrop 추가

eval OOM은 해결, index out of bounds

5b

-valcrop-noinv

val에서 inverse 경로 제거

Train+Eval 성공, 10 epoch 진행, backward OOM

6

-emptycache

empty_cache=True

역효과: epoch 3 OOM

7

-smaller-eighthpoint-valcrop-noinv

empty_cache 롤백, point_max 25600→12800

100 epoch 완주

8

-nopreceval

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.pyif "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_segmentsempty_cache()가 서로 간섭하여 큰 연속 청크 재할당 실패
  • 가설 B: empty_cache=False 유지 + point_max 반감 (25600→12800)
    • 실제 결과: 100 epoch 완주

교훈

PYTORCH_CUDA_ALLOC_CONF=expandable_segments:Truetorch.cuda.empty_cache()를 섞으면 오히려 효과가 깨질 수 있음. 둘 중 하나만 사용.

문제 7: 학습 종료 hook 실패

증상

100 epoch 학습 후 PreciseEvaluator 단계에서:

AttributeError: 'ConfigDict' object has no attribute 'test'

원인

_base_/default_runtime.pyhooksPreciseEvaluator가 포함되어 있어 학습 종료 후 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에 맞춰 center SphereCrop (기본 12800 points) 사용.
  • infer.sh: 래퍼. PYTORCH_CUDA_ALLOC_CONF/PYTHONPATH 설정, 기본 weight 및 input 자동 선택, WEIGHT env var 지원.

최종 config 체인

configs/weld/semseg-pt-v3m1-0-base-noattention_nosoftmax-smaller-eighthpoint-valcrop-noinv-nopreceval.py

누적 적용 내역:

  1. upcast_attention=False, upcast_softmax=False
  2. enc/dec_patch_size=256
  3. SphereCrop point_max=12800 (train·val 모두)
  4. val에서 Copy/inverse/origin_segment 제거
  5. empty_cache=False
  6. 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, face2 IoU는 항상 0 — 단일 씬 데이터로는 근본적으로 학습 불가
  • 데이터 확보 전까지는 "학습 파이프라인이 돈다"는 sanity check 수준의 의미

교훈

  1. 느슨한 pin은 다운그레이드 막는다: torch==2.7.0 대신 torch==2.7.0+cu128로 local version 명시.
  2. uv 레이어 두 개 섞지 말 것: uv pipuv run/sync의 의존성 소스가 달라 충돌 가능. 한 프로젝트에서 하나만 사용.
  3. OOM 해결은 비용 0인 것부터: upcast flag → patch_size → point_max → grid_size → model capacity 순.
  4. Allocator 튜닝은 교차 조합 조심: expandable_segmentsempty_cache() 동시 사용은 역효과.
  5. val transform의 inverse-mapping은 SphereCrop과 호환 안 됨: 원본 해상도 복원과 crop은 양립 불가.
  6. 누적 variant 파일 전략: 각 단계마다 새 config 파일을 만들고 train.sh에 주석으로 이력을 남기면 롤백이 쉽다. 단, 파일명이 길어지는 단점.

See also

Favorite site