Files
sum-parts-test/STATUS.md
T
nbrightandClaude Opus 5 f39b093106 Record results so far and fix the memory blowup in the palette decode
Adds STATUS.md as the handoff document: benchmark numbers, the bare-earth
metrics that actually matter for this project, the Korean-data domain gap that
retraining will not fix, and what to do on the 24 GB machine.

The memory problem was in how a prediction's colours were turned back into
class indices. Every consumer built an (N, 13, 3) float64 temporary:

    d = ((rgb[:, None, :] - COLOR_MAP[None, :, :]) ** 2).sum(axis=2)

That is ~250 MB of intermediates per 800k-point tile, several live at once, and
a full 4.7M-point block pushes it into gigabytes. main.py writes exact palette
entries, so an exact hash lookup resolves nearly every point with no large
temporary; only leftovers fall back to a chunked distance search. Peak RSS on a
470k-point tile drops to 61 MB. Extracted to sumparts_palette.py and shared by
coarse_eval.py and split_by_class.py.

Also from this round:

- patch_cm_mutation.sh: ConfusionMatrix.update() rewrote the caller's pred
  tensor in place, folding every ignore_index point into class num_classes-1.
  test() saves its visualization from that same tensor afterwards, so an
  unlabelled tile came out 100% wall and the model looked degenerate when it
  was not.
- patch_class_mask.sh: SUMPARTS_MASK_CLASSES drops known-absent classes from
  the argmax. Measured on Seosan and it does not help - the runner-up for
  "water" is "wall", not "terrain" - but the experiment is worth keeping.
- split_by_class.py now writes .ply alongside .obj. A vertex-only OBJ has zero
  faces and most viewers render nothing, which is why the first export looked
  broken.
- verify_outputs.sh reads exported files back with a parser, so "here are your
  files" can be checked rather than asserted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 09:07:31 +09:00

8.8 KiB

현재 상태 — 2026-08-21

3060 머신에서 여기까지 왔다. 이 문서는 3090으로 옮겨서 이어가기 위한 인수인계다.

목표: 드론 사진측량 메시에서 구조물을 제거한 지형(bare earth) 추출. 부수적으로 건물·수목·차량 분리.


한 줄 요약

SUM Parts 벤치마크는 재현했다. 지면 precision 95.24%로 목표 용도에 충분하다. 문제는 벤치마크가 아니라 한국 데이터에서의 도메인 갭이다.


1. 완료된 것

항목 결과
WSL2 환경 (CUDA 11.8 + torch 2.0.1 + 확장 5종)
SUM Parts 데이터 (train 24 / val 8 / test 8)
PointNet 100 epoch mIoU 17.19 (논문 15.1 — 재현 확인)
PointVector 100 epoch mIoU 51.83 / OA 80.41
서산 OBJ → PLY 변환 타일 1장
서산 추론 13클래스 예측
클래스별 분리 출력 D:\AI_Test\sum-part\

2. SUM 벤치마크 성적 (PointVector, voxel_max 24000)

4클래스 통합

클래스 IoU precision recall
ground 60.69% 95.24% 62.59%
vegetation 90.70% 95.11% 95.14%
building 86.10% 88.01% 97.54%
vehicle 51.05% 78.81% 59.17%
mIoU 72.14% / OA 89.72%

bare earth (지면 vs 나머지) ← 실제 목표 지표

지면 정확히 유지     523,983
비지면 섞여 들어옴    26,217   ← 지형을 오염시킴
지면 놓침           268,755   ← 구멍, 보간 가능

precision  95.24%
recall     66.10%
IoU        63.98%
F1         78.04%

오염원 분해

출처 비중
facade_surface 49.90% ← 절반이 건물 외벽
high_vegetation 17.42%
water 12.04%
car 7.83%
wall (옹벽) 7.54%
roof_surface 5.05%

건물 외벽 하단 — 지면과 만나는 경계에서 새는 것으로 보인다. 후처리로 상당 부분 잡을 수 있다: 지면 예측 중 수직으로 튀는 포인트 제거.

precision 95.24%는 목표 용도(구조물 제거 지형도)에 이미 쓸 만하다. recall 66.10%로 지면 34%를 놓치지만, 구멍은 보간으로 메운다.


3. ★ 미해결 — 한국 데이터 도메인 갭

서산 타일 1장 추론 결과:

클래스 비율 판정
terrain 24.29% 타당
unclassified 21.70% 모델이 판단 못 함
water 21.11% 물 없다고 확인됨
high_vegetation 7.85% 타당
wall 6.48% ?
roof_surface 6.11% ?
boat 5.07% 배 있을 리 없다
facade_surface 0.10% 지붕 6%인데 벽 0.1%는 모순

신뢰 불가 47.9%.

원인

닫힌 집합 분류기다. 13개 중 반드시 하나를 고르고 "모르겠음"이 없다. 학습 어휘에 없는 것을 만나면 특징 공간에서 가장 가까운 것으로 간다.

헬싱키에서 배운 water = 어둡고 평평하고 균질하고 수평. 서산 포장면이 그 설명에 맞는다. boat는 물 위에 있는 것이라 따라온다. 일관성 있는 착각이다.

비율 매칭은 아니다 — SUM val의 water는 11.17%인데 서산 예측은 21.11%로 2배다.

시도했고 실패한 것: 클래스 마스킹

water·boat 로짓을 -inf로 죽여 2순위로 넘기는 실험.

wall          +63,783   (6.48% → 20.05%)   ← 절반 이상이 여기로
unclassified  +33,867
terrain       +13,656   ← 11%만 회복

실패. 2순위가 terrain이 아니라 wall이었다. 모델이 그 영역을 수직 구조물로 읽고 있다는 뜻이고, 도메인 갭이 얕지 않다는 증거다. 마스킹은 되돌렸다 (SUMPARTS_MASK_CLASSES 환경변수로 언제든 다시 켤 수 있다).


4. 발견한 업스트림 버그 4건 (전부 패치됨, 멱등, 원본 보존)

# 증상 원인 패치
A bincount ... non-negative 블라인드 test셋 라벨이 전부 -1 patch_unlabeled_test.sh
B UnboundLocalError: 'epoch' mode=val이 학습 루프 변수 참조 patch_val_mode.sh
C np.long / collections.Iterable numpy 1.24 / python 3.10에서 제거·이동 patch_numpy_aliases.sh
D 예측이 전부 한 클래스로 나옴 ConfusionMatrix.update()입력을 제자리 변조 patch_cm_mutation.sh

D가 가장 위험했다. ignore_index에 해당하는 포인트의 예측을 num_classes-1(=wall)로 덮어쓰는데, main.py가 그 다음에 시각화를 저장한다. 라벨이 없는 우리 타일은 전 포인트가 ignore_index100% wall로 저장됐다. 모델이 퇴화한 것처럼 보였지만 멀쩡했다.


5. 메모리 문제 (3090에서 겪은 것)

팔레트 색상 → 클래스 복원에서 (N, 13, 3) 배열을 통째로 만들고 있었다.

d = ((rgb[:, None, :] - COLOR_MAP[None, :, :]) ** 2).sum(axis=2)

80만 포인트면 임시 배열 하나가 250 MB이고 여러 개가 동시에 존재한다. 전체 블록(470만 포인트)이면 GB 단위로 뛴다.

수정 완료sumparts_palette.py로 분리하고 해시 조회 + 청크 폴백으로 바꿨다.

개선 후 peak RSS: 61 MB   (470k 포인트 처리)

coarse_eval.py, split_by_class.py가 이 모듈을 쓴다.


6. 실측 성능표 (RTX 3060 12GB)

모델 voxel_max peak VRAM s/iter 100 epoch 논문 mIoU
pointnet 64000 6.01 G 0.291 2.9h 15.1%
pointnet++msg 64000 4.16 G 0.675 6.8h 33.1%
pointvector-xl 24000 6.46 G 0.402 4.1h 70.0%
pointvector-xl 64000 16.49 G 13.113 12GB 불가
pointnext-xl 32000 8.03 G 0.635 6.4h 65.3%
pointnext-xl 64000 15.47 G 46.980 12GB 불가

WSL2에서 VRAM 초과는 OOM을 내지 않는다. 호스트 RAM으로 흘려서 조용히 완주한다 — 25~100배 느리게. peak VRAM과 **전력(W)**으로 판정할 것. 사용률 100%인데 전력이 낮으면 연산이 아니라 PCIe 전송 대기다.


7. 3090에서 할 일

우선순위 1 — 논문 설정 재학습

voxel_max 64000은 16.5 GB가 필요해 12 GB 카드에서 불가능했다. 24 GB면 된다.

기대 효과:

현재 (24000) 재학습 (64000) 기대
SUM terrain IoU 60.69% ~85%
지면 precision 95.24% ~96%
지면 recall 66.10% ~85%
서산 물 오분류 21% 그대로

주의: voxel_max는 중립적 손잡이가 아니다. 64000으로 학습하면 추론도 64000 청크로 해야 성능이 나온다 → 서산 추론도 3090에서 돌려야 한다.

우선순위 2 — 서산 예측 육안 확인

D:\AI_Test\sum-part\unseen\*.ply에 42.82%가 들어 있다. 이게 뭔지 모르면 나머지가 전부 추측이다.

우선순위 3 — 도메인 갭 대응

재학습으로 안 고쳐진다. 선택지:

  1. SAM 3.1 자동 라벨링 + 파인튜닝 — 드론 원본 8,120장 + 카메라 포즈(PPK) 보유. SAM 3의 텍스트 프롬프트로 명명된 마스크 → 포즈로 3D 투영 → 면 투표. 사람 몫은 전수 라벨링이 아니라 1~2타일 검증이다.
  2. 후처리 — 지면 예측 중 수직 이상치 제거로 건물 외벽 오염(49.9%) 감소.

8. 산출물 위치

D:\AI_Test\sum-part\
├── ground\    114,159 pts  (24.29%)
├── building\   93,076 pts  (19.80%)
├── tree\       36,876 pts  ( 7.85%)
├── vehicle\    24,631 pts  ( 5.24%)
└── unseen\    201,258 pts  (42.82%)   ← unclassified + water

각 폴더에 .ply.obj가 있다. .ply를 열어라 — OBJ는 면이 없어서 대부분의 뷰어가 아무것도 안 보여준다. 색상은 클래스 색이 아니라 드론 사진 텍스처라, 그 클래스로 분류된 게 실제로 뭔지 보인다.

D:\MYCLAUDE_PROJECT\sum-parts-test\output\
├── seosan_pred_viewer.ply    클래스 색
└── seosan_rgb_viewer.ply     사진 색

9. 제약

  • test 세트는 블라인드다. 라벨이 전부 -1이라 로컬 채점 불가. 논문 수치와 직접 대조하려면 예측을 저자(gaoweixiaocuhk@gmail.com)에게 보내야 한다.
  • 어노테이션 도구는 비공개다. 저자가 유료 서비스로 판매 중.
  • 학습 가중치도 비공개다. 직접 학습이 유일한 경로.
  • 라이선스: 데이터 CC BY-NC 4.0, 코드 GPL-3.0. 상업 이용은 저자 허락 필요.

10. 문서

문서 용도
SETUP.md 1단계 — 환경 구축 (GPU 불필요, ~80분)
TRAIN.md 2단계 — 학습 (GPU 필요)
docs/pipeline.html 전체 6단계 공정 정의
docs/SUM-Parts-검토노트.md 트러블슈팅 상세, 데이터 스키마 실측