# 현재 상태 — 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_index`라 **100% wall로 저장됐다.** 모델이 퇴화한 것처럼 보였지만 멀쩡했다. --- ## 5. 메모리 문제 (3090에서 겪은 것) 팔레트 색상 → 클래스 복원에서 `(N, 13, 3)` 배열을 통째로 만들고 있었다. ```python 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](SETUP.md) | 1단계 — 환경 구축 (GPU 불필요, ~80분) | | [TRAIN.md](TRAIN.md) | 2단계 — 학습 (GPU 필요) | | [docs/pipeline.html](docs/pipeline.html) | 전체 6단계 공정 정의 | | [docs/SUM-Parts-검토노트.md](docs/SUM-Parts-검토노트.md) | 트러블슈팅 상세, 데이터 스키마 실측 |