Skip to content

Repository files navigation

DroneAR — Magic Leap 2용 YOLO26 드론 탐지

🌐 English version: README_English.md

DUT-Anti-UAV(+ Maciullo DroneDetectionDataset 병합, 학습 데이터 10×)로 YOLO26 드론(UAV) 탐지 모델 학습 → Magic Leap 2(ML2) 배포

  • 학습 환경: RTX 4090 24GB / Linux / CUDA (학습 전용)
  • 추론 환경: ML2 — AMD "Mero" SoC (Zen2 쿼드코어 x86-64 CPU + RDNA2 iGPU), 16GB, AOSP Android 10 (API 29). NVIDIA 아님 → 디바이스 TensorRT/CUDA 불가. 검증 경로: ONNX → ONNX Runtime(+MLSDK C API), CPU 백엔드 XNNPACK.
  • **모델 결정: On-device: yolo26n(nano), INT8(CPU)/FP16(GPU) export, Cloud computing(RTX 4090): D-FINE l, fp16(예정)/fp32

확장: 데이터 병합(10×) · 960+P2 · 추론 1280 — Ablation 참조.

GPU 경로 탐색: RDNA2 iGPU 추론용 ncnn-Vulkan 경로 검증은 README_ML2_Vulkan.md 참조 (호스트 4090 Vulkan 검증, ML2 on-device 미검증).


가중치 다운로드

weights/는 계열별로 weights/yolo26/(YOLO)·weights/d_fine/(D-FINE)로 분기. <100MB 파일은 repo 포함(clone 편의), D-FINE-L dfine_l_drone_960_mergedataset_120epoch.pth(477MB)만 GitHub 100MB 한도 초과로 Drive 전용.

모델 imgsz 릴리스 파일 크기 위치 / 다운로드
D-FINE-L 960 dfine_l_drone_960_mergedataset_120epoch.pth 477MB ⬇ Google Drive (Drive 전용)
D-FINE-N 640 dfine_n_drone_640_mergedataset_220epoch.pth 58MB repo weights/d_fine/
yolo26l-P2 960 yolo26l_drone_960p2_mergedataset_100epoch.pt 50MB repo weights/yolo26/
yolo26n-P2 960 yolo26n_drone_960p2_mergedataset_100epoch.pt 5.9MB repo weights/yolo26/
yolo26n merged 640 yolo26n_drone_640_mergedataset_{100,300}epoch.pt (+onnx/ncnn) ~5MB repo weights/yolo26/
yolo26{n,s} (old) 640/960 yolo26{n,s}_drone_{640,960}.pt (+onnx) 5~20MB repo weights/yolo26/

D-FINE-L 보조 체크포인트(best_stg1.pth·last.pth·checkpoint00XX.pth)는 학습 볼륨 runs/merged_dfine_l_960/에 보관(배포 제외). 다운로드 후 weights/d_fine/dfine_l_drone_960_mergedataset_120epoch.pth로 배치하면 평가·추론 경로와 일치한다.


성능 지표 (모델 선택 기준)

정확도

전 구성(yolo·D-FINE 총 10종)을 동일 test·동일 평가기·동일 지표로 측정했다. 생성물: reports/master_table.md(scripts/build_master_table.py) · 원자료 reports/unified/*.json.

모델 ref train imgsz DUT AP50 DUT AP50-95 DUT far(<16px) DUT <8px DUT FP/img Maci AP50 Maci AP50-95 Maci far(<16px) Maci <8px Maci FP/img
yolo26n A DUT 640 0.923 0.630 0.925 0.882 0.063 0.523 0.196 0.359 0.091 0.213
yolo26n B DUT 960 0.947 0.682 0.960 0.963 0.071 0.493 0.176 0.353 0.091 0.210
yolo26s — DUT 640 0.946 0.670 0.952 0.897 0.057 0.534 0.206 0.364 0.182 0.171
yolo26s — DUT 960 0.946 0.709 0.972 0.949 0.041 0.486 0.178 0.247 0.000 0.157
yolo26n C merged 640 0.874 0.597 0.820 0.801 0.064 0.826 0.426 0.783 0.455 0.047
yolo26n D merged 640 0.915 0.632 0.876 0.816 0.041 0.791 0.403 0.748 0.273 0.066
yolo26n-P2 E/F merged 960 0.924 0.673 0.933 0.890 0.067 0.831 0.429 0.793 0.636 0.077
yolo26l-P2 H/I merged 960 0.959 0.755 0.960 0.941 0.023 0.842 0.437 0.783 0.727 0.056
D-FINE-N — merged 640 0.950 0.706 0.947 0.941 0.081 0.865 0.423 0.838 0.636 0.192
D-FINE-L — merged 960 0.973 0.778 0.987 0.971 0.056 0.907 0.456 0.798 0.727 0.161

전 모델 동일 조건: held-out test(DUT-test 2200장·Maciullo-test 2625장) · faster-coco-eval · conf 0.25 · IoU-match 0.5 · side@640 size-bin. far = <16px(=<8 + 8-16) recall.

  • D-FINE-L이 전 도메인 최고: DUT AP50 0.973·AP50-95 0.778, Maci AP50 0.907. far(<16px)도 DUT 0.987로 1위.
  • imgsz 960이 640 대비 DUT AP50-95 향상(A→B +5.2pt) — 소형 객체 ~77%라 해상도 효과 큼. 단 추론 비용 ↑(입력 2.25배).
  • DUT-only(A·B·s) 모델은 Maciullo 데이터셋(더 큰 데이터셋)에서 붕괴(AP50 0.49~0.53) = 도메인 병합 없이는 이전 불가. 구성별 기여 분해: Ablation. 추론 예시: Demo.

정면 비교 차트 — 위 표를 시각화. 재생성: python scripts/plot_master_compare.py.

master comparison

  • DUT-only 4모델(상단)은 Maciullo(주황) 붕괴, merged 모델은 두 도메인 균형. D-FINE-L이 AP50·AP50-95·far 전부 최상위. yolo26l-P2·D-FINE-N이 그 뒤.

모델 복잡도:

모델 imgsz Params(M) FLOPs(G) best.pt
yolo26n 640 2.5 5.8 5.4 MB
yolo26n 960 2.5 13.0 5.5 MB
yolo26s 640 9.9 22.5 20.3 MB
yolo26s 960 9.9 50.6 20.4 MB

FLOPs(G): 각 행 imgsz 기준, ultralytics fused, 2×MAC 관례(곱·합 각 1회 = MACs×2), 정밀도 무관. FLOPs ∝ 입력 픽셀 → 960은 640의 약 2.25배. D-FINE 복잡도는 DETR 절 스펙표.

추론 속도 — 전 모델 통일 벤치마크 테스트

CPU: Ryzen 9 7950X (scripts/bench_unified_latency.py → reports/unified_latency.json). 순수 forward(전·후처리·NMS 제외, torch), batch 1, 각 모델 배포 imgsz. GPU=cuda.Event(warmup20/iter100), CPU=wall-clock(warmup3/iter12).

각 셀 = ms · FPS. 정밀도 fp32 통일(전 모델 동일) — fp16은 계열별 배포 스택이 달라(yolo=ONNX/TensorRT half, D-FINE=TensorRT fp16+grid_sample 플러그인) 공정 비교 불가라 제외.¹

모델 imgsz GPU fp32 CPU 1스레드 CPU 8스레드
yolo26n (merged) 640 2.94 · 340 65 · 15.5 30 · 33.3
yolo26s 640 3.12 · 321 196 · 5.1 58 · 17.1
yolo26l-P2 (merged) 960 8.83 · 113 2047 · 0.49 609 · 1.6
D-FINE-N (merged) 640 5.14 · 195 112 · 9.0 42 · 24.1
D-FINE-L (merged) 960 11.96 · 84 1647 · 0.61 480 · 2.1

¹ 참고: torch에서 yolo .half()는 640 미포화로 무이득·960 l-P2만 이득(113→163 FPS), D-FINE는 autocast fp16이 grid_sample fp32 유지+캐스팅 오버헤드로 fp32보다 느림 → 실 fp16 가속은 TensorRT 엔진 필요(예정). yolo 계열 fp16 상세는 아래 GPU 표.

  • GPU: yolo26n 가장 빠름(340 FPS). D-FINE-N이 195 FPS로 GPU 효율 우수(deformable attn에도 yolo26l-P2 113 FPS보다 빠름). D-FINE-L은 정확도 최고지만 84 FPS(@ RTX4090) — 따라서 클라우드 전용.
  • CPU: D-FINE-N(8스레드 24 FPS)이 yolo26s(17 FPS)보다 빠름(N은 640·소형). 반면 960 모델(yolo26l-P2·D-FINE-L)은 CPU ~2 FPS → GPU 유리.
  • ML2에서는 GPU(display 출력에 사용 중)보다 여유로운 CPU가 더 빠른 것으로 측정됨.
  • ⚠️ 이 표는 torch 순수 forward — ONNX Runtime 최적화 배포 수치는 아래 CPU (ORT) 표(yolo, INT8 포함, 별도 측정)와 다르다(ORT가 더 빠름).

정확도-속도 트레이드오프 (재생성 python scripts/plot_ap_latency.py):

ap vs latency

  • 좌상단일수록 우수(높은 AP·낮은 지연). yolo26n = 최속·최경량, D-FINE-L = 최고 AP. D-FINE-N은 CPU 트레이드오프 양호.

추론 속도 — GPU (RTX 4090), yolo 상세

전 모델(D-FINE 포함) 교차 비교는 위 통일 벤치. 아래는 yolo base 가중치 FP32/FP16 상세.

config: imgsz=640, batch=1(single-stream), warmup=30, iters=200, 순수 forward(전·후처리·NMS 제외), torch CUDA(cuda.Event 계측), FPS = 1000/mean. 측정 하드웨어 NVIDIA RTX 4090. 원본 로그: weights/latency_gpu.md.

모델 정밀도 latency mean±std (ms) FPS
yolo26n FP32 2.30 ± 0.10 433
yolo26n FP16 2.48 ± 0.10 403
yolo26s FP32 2.44 ± 0.14 410
yolo26s FP16 2.57 ± 0.08 389
  • batch=1·작은 모델은 RTX 4090을 포화시키지 못해 커널 실행·메모리 대역폭에 묶임(GPU 미포화) → 모델·정밀도 간 지연 차이가 상대적으로 작다.

정밀도별 GPU 적합성 (산출물은 모두 ONNX):

정밀도 성격 GPU
FP32 중립(기준) 표준 동작
FP16 GPU/NPU 친화(반정밀) 이득 ↑ (CPU는 native 커널 없어 이득 X)
INT8 CPU/XNNPACK 지향(QDQ Conv-only) INT8 가속 못 받음 ⚠️: 그러나 ML2 환경에서는 가장 적합할 것으로 예상.

INT8 GPU 가속은 TensorRT 엔진 별도 빌드 필요.

추론 속도 — CPU (i9-13900K, ONNX Runtime) — yolo ORT 배포 상세

⚠️ 이전 환경(i9-13900K) 측정 · ONNX Runtime 백엔드(위 통일 벤치는 Ryzen·torch forward라 별개). INT8·ML2 배포 관점 참고용.

config: ORT CPUExecutionProvider, imgsz=640, batch=1, warmup=30, iters=200, intra_op_num_threads=1·4 (inter_op=1, sequential), FPS = 1000/mean. 측정 하드웨어 Intel i9-13900K. 원본 로그: weights/latency_report.md.

모델 정밀도 크기(MB) threads=1 (ms) threads=4 (ms) threads=1 (FPS) threads=4 (FPS)
yolo26n FP32 9.80 44.0 ± 0.5 13.2 ± 0.2 23 76
yolo26n FP16 4.97 45.5 ± 0.8 13.9 ± 0.2 22 72
yolo26n INT8 3.01 33.7 ± 0.9 15.1 ± 0.4 30 66
yolo26s FP32 38.17 149.6 ± 1.4 41.3 ± 0.9 7 24
yolo26s FP16 19.15 151.7 ± 1.5 42.4 ± 0.6 7 24
yolo26s INT8 10.24 86.6 ± 2.0 34.6 ± 0.7 12 29
  • FP16: ORT CPU에 native fp16 커널 없음 → 속도 이득 없음(크기/이식성 옵션).
  • INT8: 단일 스레드에서 가장 빠름. Conv-only QDQ라 4스레드에선 dequant 오버헤드로 이점 축소.
  • 속도는 imgsz 640 기준. 960은 미측정(입력 2.25배).
  • ML2 온디바이스 실측(2026-07): yolo26n 640 CPU ~15 FPS — i9-13900K 대비 ~1/5 (Zen2). GPU(ncnn-Vulkan)는 미측정.

Export 산출물 (정밀도·크기) — NMS-free head, 출력 [1,300,6]

정밀도 파일 크기 비고
FP32 weights/yolo26/yolo26n_drone_640_fp32.onnx 9.80 MB 기준; opset17, static, simplified
FP16 weights/yolo26/yolo26n_drone_640_fp16.onnx 4.97 MB native half=True; float16 I/O
INT8 weights/yolo26/yolo26n_drone_640_int8.onnx 3.01 MB static PTQ(QDQ), Conv-only, 200장 캘리브

INT8 vs FP32 (동일 val 20장, conf 0.25): yolo26n 탐지 27→27(평균 IoU 0.961, |Δscore| 0.075), yolo26s 27→26(평균 IoU 0.966, |Δscore| 0.103) → 저하 미미.

비교군/해상도 산출물: yolo26s_640 FP32 38.2 / FP16 19.2 / INT8 10.2 MB · imgsz 960(입력 [1,3,960,960]) yolo26n_960 10.0/5.1/3.2 MB · yolo26s_960 38.4/19.3/10.5 MB (weights/yolo26/yolo26{n,s}_drone_960_{fp32,fp16,int8}.onnx).


Ablation

구성별 기여 분해 — yolo26n(A–F) · yolo26l(H·I), seed 0, 파이프라인 동일. 미측정 = "—". AP·far·FP는 전부 마스터표와 동일한 COCO 평가(held-out test·faster-coco-eval·conf 0.25·IoU 0.5) — 위 마스터표와 같은 수치다(더는 평가기 혼재 없음).

far 기준 = 픽셀 크기: GT 한 변 환산 sqrt(w·h)×640 < 16px인 객체의 recall. <8px = far에서 극원거리, 최난이도 꼬리.

# merged 960학습 P2 1280추론 ep DUT AP50 DUT AP50-95 DUT far Maci AP50 Maci AP50-95 Maci far
A (old) 150 0.923 0.630 0.925 0.523 0.196 0.359
B ✓ 150 0.947 0.682 0.960 0.493 0.176 0.353
C ✓ 100 0.874 0.597 0.820 0.826 0.426 0.783
D ✓ 300 0.915 0.632 0.876 0.791 0.403 0.748
E ✓ ✓ ✓ 100 0.924 0.673 0.933 0.831 0.429 0.793
F (=E, 추론만 1280) ✓ ✓ ✓ ✓ 100 0.925 0.672 0.943 0.839 0.425 0.803
H (l-P2) ✓ ✓ ✓ 100 0.959 0.755 0.960 0.842 0.437 0.783
I (=H, 추론만 1280) ✓ ✓ ✓ ✓ 100 0.959 0.752 0.968 0.852 0.434 0.793

training curves

  • 곡선(검증셋 DUT-val 공통): 학습 해상도(960) = 성능 최대폭 향상, 640은 300ep로도 960 미달. merged의 Maciullo 도메인 이득은 이 곡선에 미반영.
  • 병합(A→C): Maciullo AP50 +30pt(0.523→0.826)·DUT far −10.5pt(0.925→0.820) → epochs(C→D)·960+P2(C→E)로 회복. B(DUT-only 960)는 Maciullo 붕괴(0.493) — 해상도 단독으론 도메인 이전 없음.
  • 모델 스케일(E→H, n→l): DUT AP50-95 +8.2pt(0.673→0.755)·far +2.7pt, Maci <8px 0.636→0.727.
  • 추론 해상도 1280(E→F, H→I)은 AP 중립·그러나 far·<8px는 이득(F/I 측정, reports/unified/*_1280_*.json).
  • 상세: reports/ablation_matrix.md.

배포 권장 (근거는 위 표):

경로 모델 이유
클라우드(4090) — 최고 정확도 D-FINE-L@960 전 모델 최고(DUT/Maci AP50 0.973/0.907, DUT far 0.987). ncnn-Vulkan 이식 불가라 클라우드 전용
클라우드 — yolo 계열 최고 H: merged-l-P2-960 + 추론 1280 (=I) yolo 중 최고, DUT far 0.968, 4090 FP16 103FPS
클라우드 경량 대안 E: merged-P2-960 + 추론 1280 (=F) H 대비 −8pt(AP50-95), 4.2ms(236FPS)
온디바이스(ML2, ncnn-Vulkan) D: merged-300ep (640) FP/img 최저

Maciullo 라벨 감사 — AP50 ~0.9 천장 원인

전 구성에서 Maciullo AP50이 정체(COCO 기준 yolo 0.79~0.85·D-FINE-N 0.865·최고 D-FINE-L 0.907) → l-P2 오답 시각화(FN 406·FP 146) 후 육안 판정(2026-07-08). FP의 68%가 conf≥0.5 — 확인 결과 상위 케이스 다수는 GT 박스 품질 문제로, 모델이 맞게 탐지해도 IoU<0.5가 되어 FP+FN 이중 감점 → AP 천장 형성. 색: 초록=GT, 빨강=FP 예측.

FP 상위 — GT 크기 부정확:

fp_000318 · GT 과대 fp_000126 · GT 과대 fp_002025 · GT 과소
fp_000318 fp_000126 fp_002025

FN 상위 — 라벨 오차·특수 케이스 혼재:

fn_000281 · GT 과대 fn_000807 · 드론 일부만 프레임 fn_000808 · 자막이 드론 가림 fn_002018 · 강한 조명(LED)
fn_000281 fn_000807 fn_000808 fn_002018
  • 결론: Maciullo 0.89 천장은 **라벨 품질(박스 크기 오차·누락)+특수 케이스(조명)**의 영향. 모델 추가 개선의 Maciullo AP 기대치는 이 천장 기준으로 해석할 것.
  • 전체 시각화(506장): reports/label_audit_maciullo/ (로컬 생성물, 미커밋).

Demo (추론 예시)

test set 추론 결과 — yolo26n merged-300ep(권장 배포 모델), imgsz 640, conf 0.25. (demo/: DUT-test image0~9 + Maciullo-test ground0~3)

image0 (DUT) image8 (DUT) ground1 (Maciullo)
image0 image8 ground1

재현: python scripts/predict.py --weights weights/yolo26/yolo26n_drone_640_mergedataset_300epoch.pt --imgsz 640 --source /mnt/ssd_0/dataset/dut_yolo/images/test --max 10 --out demo


모델 상세 (I/O)

ONNX를 추론 엔진에 통합할 때 필요한 입출력 방식에 대해서 간략히 설명한다 (imgsz 640 모델 기준; 960 변형은 입력·좌표가 960).

항목 사양
입력 images (1,3,640,640) — float32(FP32·INT8) / float16(FP16)
전처리 letterbox 640 · RGB · /255 · CHW (종횡비 보존 패딩, pad=114)
출력 output0 (1,300,6) = [x1,y1,x2,y2,score,class], 640 letterbox 픽셀 좌표
후처리 NMS 불필요(one-to-one head). score ≥ 0.25 필터 → letterbox 역산(패딩 빼고 scale로 나눔) → 원본 좌표
클래스 0 = drone (단일 클래스, nc=1)

INT8 모델도 입력은 float32다(Q/DQ는 그래프 내부 처리). 권장 conf 0.25는 임의로 설정했다.


리포지토리 구조

scripts/   [데이터] voc2yolo.py  fetch_maciullo.py  merge_datasets.py  analyze_merge.py  dataset_stats.py
           [학습·평가] train.py  train_all.sh  eval.py  eval_compare.py  analyze_fn.py  predict.py
           [DETR 계열] yolo2coco.py  dfine_eval.py  (+configs/dfine/, D-FINE 레포 별도 clone)
           [통합평가] unified_eval.py  build_master_table.py  run_unified_all.sh
           [export·벤치] export.py  parity_ncnn.py  bench_latency.py  bench_gpu.py  sahi_bench.py
configs/   dut_drone.yaml  merged_drone.yaml  eval_test_{dut,maciullo}.yaml
weights/   yolo26/  ← YOLO 계열 (repo 포함)
             yolo26{n,s}_drone_{640,960}.pt (+_{fp32,fp16,int8}.onnx)
             yolo26n_drone_640_mergedataset_{100,300}epoch.pt (+onnx, +_ncnn_model/)
             yolo26{n,l}_drone_960p2_mergedataset_100epoch.pt
           d_fine/  ← D-FINE 계열
             dfine_n_drone_640_mergedataset_220epoch.pth (repo 포함, 58MB)
             dfine_l_drone_960_mergedataset_120epoch.pth (=best_stg2 ep115, 477MB — Drive 전용, repo 미포함 → 아래 표)
           metrics.json  parity·latency 리포트(생성물, 루트 유지)
cpp/       drone_detector.{h,cpp}  test_host.cpp  CMakeLists.txt  mlsdk_glue.md  (ncnn-Vulkan)
docs/      ML2_ONDEVICE_RUNBOOK.md
reports/   ablation_matrix.md(SSOT)  far_drone_p2_960.md  yolo26_family_fps_4090.md
           training_curves.png  + 생성물(fn_size·sahi·dataset_comparison·old_vs_new 등)
demo/      추론 예시 (DUT image0~9 + Maciullo ground0~3)
Dockerfile · docker-compose.yml · requirements.txt

데이터셋

출처 (Sources)

DUT-Anti-UAV

DUT-Anti-UAV는 수동 준비. 아래 PASCAL VOC 구조로 /mnt/ssd_0/dataset/DUT에 배치/압축해제한다. 변환 스크립트는 이 트리를 직접 수정하지 않는다(read-only).

/mnt/ssd_0/dataset/DUT/{train,val,test}/{img,xml}
  img/  *.jpg
  xml/  *.xml   (VOC: <size>, <object><name>, <bndbox> xmin/ymin/xmax/ymax)
Split 이미지 라벨 박스 Negative Skip(불량박스)
train 5200 5200 5243 3 0
val 2600 2600 2620 0 1
test 2200 2200 2245 0 0
합계 10000 10000 10108 3 1
  • 단일 클래스: 원본 UAV(10,109개) → 0: drone(nc=1) 매핑.
  • 객체 없는 train 3장 → 빈 .txt(negative). 불량 박스(w≤0/h≤0) 1개 스킵.

변환 (원본 read-only):

python scripts/voc2yolo.py        # --src /mnt/ssd_0/dataset/DUT  --dst /mnt/ssd_0/dataset/dut_yolo
python scripts/dataset_stats.py   # 박스 크기 히스토그램 + 샘플 박스 시각화 -> dut_yolo/_viz/

박스 크기 분포 — 소형 객체 위주 (imgsz/P2 결정 근거). 정규화 변 sqrt(w·h): 중앙값 0.0226(~14.5px @640), p25 0.0163, p75 0.0451, max 0.84.

크기 구간 (@imgsz 640) 비율
SMALL (변 <32px) 76.6%
MEDIUM (32–96px) 13.1%
LARGE (변 >96px) 10.3%
tiny (<13px, 정규화변 <0.02) 40.6%

→ 드론 대부분 소형. small 및 tiny object recall 향상 수단 고려 필요(p2, imgsz up, larger models, DETRs).

Maciullo DroneDetectionDataset — 병합

학습 데이터 확장용(10×, 근접·중대형 도메인 추가). HF mirror(pathikg/drone-detection-dataset)로 취득 → /mnt/ssd_0/dataset/DroneDetection에 materialize.

  • 규모: train 51,446 / test 2,625 (HF mirror 값 — 공식 test 5,375과 다름). 전부 640×480, COCO xywh, 단일 class(drone).
  • 578개 영상 파생이나 HF mirror에 video_id 없음 → sequence provenance 복원 불가. leakage-safe fallback: Maciullo train 전량 → merged train, val은 DUT 공식 val만, 원본 test 2개(DUT-test·Maciullo-test)는 별도 eval로 보존.
  • 병합 결과 /mnt/ssd_0/dataset/merged_drone: train 56,646(DUT 5,200 + Maciullo 51,446) / val 2,600 / test_dut 2,200 · test_maciullo 2,625.
  • 파이프라인: scripts/fetch_maciullo.py(데이터셋 취득) → scripts/merge_datasets.py(YOLO 형식 통일) → scripts/analyze_merge.py(스케일 및 배경 비교). 데이터셋 분석·비교·split 매핑은 reports/, old vs new 효과는 reports/old_vs_new.md.
  • 효과 요약(COCO, Ablation A→C): Maciullo AP50 0.523→0.826·FP/img −78%(0.21→0.05), DUT 초소형은 소폭 하락, 즉, trade-off.
.venv/bin/python scripts/fetch_maciullo.py      # HF → images + COCO 어노테이션 + AUDIT.md
.venv/bin/python scripts/merge_datasets.py      # merged_drone + configs/merged_drone.yaml
.venv/bin/python scripts/analyze_merge.py       # reports/dataset_comparison.md

환경 구성

방법 A — Docker

Docker Hub 이미지: hanmyeongil/yolo26:v1.

docker compose pull      # Docker Hub에서 이미지 받기 (또는 docker compose build 로 직접 빌드)
docker compose run --rm dronear python scripts/voc2yolo.py
docker compose run --rm dronear python scripts/train.py
docker compose run --rm dronear python scripts/export.py

⚠️ 작업 경로 필수 설정. docker compose는 docker-compose.yml이 있는 repo 루트에서 실행한다. 다른 경로에서 실행하면 compose 파일·상대 볼륨(./scripts, ./weights, ./runs)을 못 찾아 엉뚱한 경로 기준으로 동작한다. 컨테이너 작업 디렉터리는 working_dir=/workspace 고정이며, scripts/·configs/·weights/·runs/가 여기에 마운트된다.

docker run을 직접 쓸 때도 -w /workspace + repo 루트를 /workspace로 마운트해야 한다:

docker run --rm --gpus all \
  -v "$PWD":/workspace -w /workspace \
  -v /mnt/ssd_0/dataset:/mnt/ssd_0/dataset \
  hanmyeongil/yolo26:v1 python scripts/export.py

데이터셋은 호스트 경로 → 동일 컨테이너 경로로 마운트 → configs/dut_drone.yaml이 네이티브/컨테이너 양쪽 동작. 다른 머신은 docker-compose.yml의 데이터셋 볼륨 + config path: 한 줄을 자기 데이터 경로로 변경한다(안 하면 컨테이너가 데이터를 못 찾음).

재현성 검증 완료. 베이스 ultralytics/ultralytics:latest + onnxruntime/onnxslim/ onnxconverter-common, 기본 polars → polars-lts-cpu 교체 → 동작 GPU 이미지(컨테이너 내 CUDA OK). 컨테이너 안에서 scripts/export.py 실행 → 호스트 venv와 동일 산출물(FP32 9.80MB, FP16 4.97MB native-half, INT8 3.01MB), 모두 ORT 로드·출력 [1,300,6] 확인.

방법 B — venv (빠른 개발 루프)

python3 -m venv .venv && . .venv/bin/activate
# torch는 호스트 CUDA 12.8 드라이버에 맞는 cu128 빌드 먼저 (아래 Troubleshooting 참고)
pip install torch==2.11.0+cu128 torchvision==0.26.0+cu128 --index-url https://download.pytorch.org/whl/cu128
pip install -r requirements.txt
python scripts/voc2yolo.py
python scripts/train.py

재현 절차 (전체 명령)

각 단계는 Docker·venv 형태 모두 제공.

단계 Docker venv
VOC→YOLO 변환 docker compose run --rm dronear python scripts/voc2yolo.py python scripts/voc2yolo.py
데이터 통계 ... python scripts/dataset_stats.py python scripts/dataset_stats.py
학습(단일) ... python scripts/train.py --model yolo26n.pt --name yolo26n_drone_640 python scripts/train.py ...
학습(n+s, 150ep) ... bash scripts/train_all.sh bash scripts/train_all.sh
평가(val+test) ... python scripts/eval.py --weights weights/yolo26/yolo26n_drone_640.pt python scripts/eval.py ...
Export ONNX/FP16/INT8 ... python scripts/export.py --weights weights/yolo26/yolo26n_drone_640.pt --stem yolo26n_drone_640 python scripts/export.py ...
속도 벤치 GPU(4090) ... python scripts/bench_gpu.py python scripts/bench_gpu.py
속도 벤치 CPU(ORT) ... python scripts/bench_latency.py --stems yolo26n_drone_640 yolo26s_drone_640 python scripts/bench_latency.py ...
예측 데모 ... python scripts/predict.py --weights weights/yolo26/yolo26n_drone_640.pt python scripts/predict.py ...

학습 설정(ML2 baseline): yolo26n.pt, imgsz=640, epochs=150, patience=40, batch=-1(자동 → 4090에서 ~35), cache=disk, NMS-free head 유지. yolo26s는 정확도 비교군. 5-epoch 학습 테스트 시 수렴 확인(mAP50 0.62→0.81).

Troubleshooting (환경 이슈 — requirements 반영)

증상 원인 해결
cuda.is_available()=False, "driver too old" ultralytics가 torch cu130 끌어옴; 호스트는 CUDA 12.8 torch==2.11.0+cu128(최신 cu128) 설치
Bus error(SIGBUS) — 첫 체크포인트 저장 시 polars 1.42 휠 import SIGBUS; ultralytics가 매 epoch results.csv를 polars로 읽음 polars-lts-cpu 교체
cache=ram SIGBUS DataLoader가 캐시 배열을 /dev/shm 공유 cache=disk(기본) 또는 --cache False
TFLite export 실패 (tf.tile_36 rank 에러) onnx2tf 1.28.8이 YOLO26 NMS-free head Tile 미지원 ONNX 경로 사용; 필요시 onnx2tf 버전/param_replacement.json

개선 로드맵 (소형·원거리)

배포 방침: 온디바이스(ML2) = yolo26n (ncnn-Vulkan GPU 경로·속도), 클라우드 = D-FINE-L (query 기반 원거리 강점, L@960 학습 완료). D-FINE은 ncnn-Vulkan 이식 불가라 ML2 주력은 yolo26n 유지.

  • 완료 ✅: imgsz 960 · P2 head(960+P2 결합) · 추론 1280 · 데이터 병합 10× · yolo26l-P2@960(더 큰 모델, 클라우드용) · D-FINE-N@640(DETR류) · D-FINE-L@960(클라우드 주력) · 전 모델 COCO 지표 통일(마스터표·scripts/unified_eval.py) → Ablation · D-FINE-L 결과
  • 진행 🔄: temporal(detect-then-track) 후순위 검토
  • 보류: hard-negative(NEG_DIR) — Maciullo all-positive라 배경 FP 감소엔 별도 negative 셋 필요

DETR 계열 1차 — D-FINE-N@640 결과

  • 선정: RT-DETR 최소 모델 l(32M) = 온디바이스급 아님 → D-FINE-N(3.8M, RT-DETR 계보 SOTA, ICLR 2025).
  • 학습: merged · 640 · P2 없음 · seed 0 · COCO ckpt tuning · 220ep(스톡) · batch 32(lr 비례 0.0002). yolo26n C/D행과 동일 선상(테스트셋·imgsz·pretrained 동일). 릴리스 = ep191 EMA(weights/d_fine/dfine_n_drone_640_mergedataset_220epoch.pth).
  • 재현: scripts/yolo2coco.py → configs/dfine/ → D-FINE train.py → scripts/dfine_eval.py (결과 reports/dfine_n_eval.json).

연산량·속도 스펙 (640, 실측) — 4090/CPU는 통일 벤치(torch forward, Ryzen) 값:

모델 Params GFLOPs 4090 fp32 Ryzen CPU t8 ML2 CPU
yolo26n 2.5M 5.8 2.94ms (340FPS) 30ms (33FPS) ~15 FPS 실측
D-FINE-N 3.7M 7.1 5.14ms (195FPS) 42ms (24FPS) ~7–9 FPS 추정
  • GFLOPs는 1.2×인데 지연은 4090 1.75× · CPU 1.4× — FLOPs가 아니라 커널 효율(deformable attention·LayerNorm의 CPU/GPU 비친화) 차이로 인한 것으로 추정. (Params/GFLOPs 산출: yolo=ultralytics profile, D-FINE=calflops — 동일 MACs×2 관례.)

정확도 (held-out test): D-FINE-N·L 포함 전 모델 수치는 상단 마스터표로 통일(held-out test·faster-coco-eval·conf 0.25·IoU 0.5). D-FINE-N 요약 — DUT AP50 0.950 / AP50-95 0.706 · far 0.947 · <8px 0.941(FP/img 0.08), Maci 0.865 / 0.423 · far 0.838 · <8px 0.636(FP/img 0.19). yolo26n C/D와 동일 640·pretrained 선상, far·소형에서 우위(아래).

dfine vs yolo26n curves

위 곡선은 epoch별 DUT-val 학습 추세(D-FINE=COCO eval·yolo=ultralytics val, 학습시점 평가기 상이) — 절대 높이 직접 비교 말고 추세만 볼 것. 최종 정확도 비교는 상단 마스터표.

  • far-recall(동일 프로토콜): DUT +7.1pt(vs D 0.876→0.947)·Maciullo +5.5pt(vs C 0.783→0.838) — 640·P2 없이 yolo26n-P2@960(E: 0.933)급 far. query 방식인 DETR 계열의 small object detection 강점 확인.

정성 예시 — D-FINE 검출 / yolo26n 완전 미검출 (극소형):

동일 조건(640·conf 0.25). 박스: 🟢GT · 🔵D-FINE 검출(YOLO 박스 없음 = 미검출). 우상단 = 4× 확대 인셋.

win1 win2 win3
DUT · 19×8px · conf 0.83 · 지붕 배경 DUT · 26×8px · conf 0.76 · 건물 경계선 Maciullo · 7×6px · conf 0.71 · 하늘 배경
  • 3건 모두 side<8px(장거리) · yolo26n-300ep는 IoU<0.1(즉, bbox 없음). 이러한 D-FINE만 탐지해낸 126건: reports/dfine_wins_yolo_misses/(용량상 로컬에만 있음).
  • D-FINE 강점: 배경 clutter(지붕·경계선) 위 극소형 객체 탐지.

구성·입력 특이점 비교:

항목 yolo26n (C/D) yolo26{n,l}-P2 (E~I) D-FINE-N
학습/추론 imgsz 640/640 960/960 또는 1280 640/640 고정
추론 해상도 상향 ✅ 무비용 far 이득 (0.876→0.944@1280) ✅ E→F·H→I (+1pt) ❌ 붕괴 (아래)
검출 스트라이드 8/16/32 4/8/16/32 (P2 head) 16/32뿐 (P2 없음)
소형 객체 수단 해상도·P2 〃 쿼리 300·deformable attn
후처리 NMS-free 〃 NMS-free
전처리 letterbox 〃 정사각 resize(왜곡)
해상도 결합부 없음(순수 conv) 〃 해상도별 anchor·pos-emb 미리 계산

추론 해상도 변경 (D-FINE-N, 640 학습 및 추가 재학습 없음) — 원본: reports/dfine_n_eval{,_960,_1280}.json:

추론 imgsz DUT AP50 / far / <8px DUT FP/img Maci AP50 / far / <8px Maci FP/img
640 (=학습) 0.951 / 0.944 / 0.941 0.08 0.866 / 0.818 / 0.727 0.20
960 0.819 / 0.722 / 0.552 0.96 0.858 / 0.793 / 0.727 0.98
1280 0.598 / 0.369 / 0.162 2.09 0.715 / 0.495 / 0.273 2.59
  • YOLO와 정반대: pos-emb 재생성에도 붕괴 — 쿼리·anchor가 학습 스케일 특화. 붕괴 속도는 도메인별(DUT 초소형은 960부터, Maciullo 중대형은 1280부터) = 학습 픽셀 스케일 이탈량에 비례. "960 학습+1280 추론" 레시피는 DETR 계열 이식 불가 → D-FINE-L@960(학습 완료, 아래)은 추론 960 고정 전제, 1280은 검증만.
  • 역설: 스트라이드 16/32뿐인데 far 0.944 — DETR의 small object detection 강점: Grid cell을 사용하는 CNN 방식과 달리 query 방식을 사용하므로 small object detection에 상대적으로 강함.
  • 트레이드오프: FP/img 높음(Maciullo 0.20 vs 0.05~0.07) — conf 스윕/라벨 노이즈 감안 필요. Maciullo AP50은 0.89 천장(라벨 품질로 인한)과 거의 동급.
  • 배포: CPU ~1.7–2× 느림(i9 threads=4 26.0ms vs 13.2ms) · ncnn-Vulkan 이식 불가(grid_sample) → 온디바이스 주력은 yolo26n 유지 권장, D-FINE은 클라우드로.

ML2 배포 경로 — D-FINE (요약): CPU만, yolo26n에 비해 느림.

ML2 경로 yolo26n D-FINE-N
ORT CPU ✅ ~15 FPS 실측 ✅ ONNX 확인, ~7–9 FPS 추정
ncnn-Vulkan (RDNA2 GPU) ✅ 현 배포 경로 ❌ grid_sample 미지원
NNAPI·TFLite GPU delegate — ❌ attention op 커버리지 없음 → CPU fallback
TensorRT FP16 — 클라우드(4090) 전용

dfine ml2 tradeoff

  • trade-off: far +7.1pt(vs yolo26n-D) ↔ fps ↓(4090 340→195 FPS · CPU t8 33→24 FPS — D-FINE-N은 GPU 효율 우수).

DETR 계열 2차 — D-FINE-L@960 결과

클라우드 주력 모델. D-FINE-N(온디바이스 검토용)에서 모델 스케일 상향(N 3.7M → L 30.7M) + 학습 해상도 960.

  • 학습: merged_drone · 960 · seed 0 · COCO ckpt tuning · 120ep · 3×RTX4090, total_batch 24 · 학습 시간 2일 4시간. 설정 configs/dfine/·dfine_l960_3gpu_pod.yml.
  • 2-스테이지: stop_epoch 108 — ep0–107(stg1, 강증강) → ep108–119(stg2, 증강 off·EMA restart). release = best_stg2.pth(stg2 최고점 ep115) → 배포 파일명 dfine_l_drone_960_mergedataset_120epoch.pth, 비교용 best_stg1.pth 병존.
  • 추론: DETR 계열 스케일 특화로 960 고정(1280 상향은 붕괴 — 위 D-FINE-N 분석과 동일 성질).

학습 곡선 요약 (DUT-val · COCO eval) — 수렴 best = ep115 (stg2). held-out test 비교 수치는 마스터표:

지표 AP@[.50:.95] AP50 AP75 AP_s AP_m AP_l AR@100
best_stg2 0.7346 0.9696 0.8442 0.6655 0.8078 0.8424 0.7969

AP / epoch 곡선 (DUT-val COCO, reports/dfine_l960_train_log.txt → scripts/plot_dfine_l_curve.py) — mAP50는 초반 수렴, mAP50-95는 ep~30까지 상승 후 plateau. ep108 stg2 전환(증강 off·EMA restart) 후 best ep115:

dfine-l ap curve

ℹ️ 위 수치는 DUT-val COCO eval 기준 — 학습 검증 분할은 DUT만(Maciullo는 train에만 포함, reports/merge_stats.json). repo 상단 yolo·D-FINE-N 표는 held-out test(DUT-test·Maciullo-test)라 split·평가기(ultralytics vs faster-coco-eval)가 달라 직접 비교 불가. held-out test(DUT-test·Maciullo-test)에서 faster-coco-eval로 측정한 동일-조건 수치는 상단 마스터표의 D-FINE-L 행(reports/dfine_l960_eval.json = reports/unified/dfine_l_960_m120.json) 참조.

  • 산출물: 학습 로그 reports/dfine_l960_train_log.txt (epoch별 COCO eval · best AP 포함).
  • 가중치 배포: 릴리스 dfine_l_drone_960_mergedataset_120epoch.pth (=학습 산출물 best_stg2.pth ep115, 477MB) — GitHub 100MB 한도 초과로 repo 미포함, Drive 전용. 다운로드 링크는 가중치 다운로드 표 참조. (보조 best_stg1.pth·last.pth·checkpoint00XX.pth는 학습 볼륨 runs/merged_dfine_l_960/에 보관.)

추론 입력(화질) 설정 가이드 — 계열별 양상

추론 imgsz ↑ : yolo26 = 이득(+far 0.876→0.944), D-FINE = 마이너스(−far 0.944→0.369) — 정반대.

두 계열의 입력 화질 반응이 정반대 → 추론 입력은 모델별 규칙으로 고정한다. 실측 근거: yolo reports/far_drone_p2_960.md · D-FINE reports/dfine_n_eval_{960,1280,letterbox,rect736x1280}.json.

입력 변화 (재학습 없음) yolo26 계열 D-FINE 계열 (실측 D-FINE-N)
해상도 상향 ✅ far 이득 — 640학습→1280추론 0.876→0.944 / P2-960→1280 +1pt ❌ 붕괴 — 960: far −22pt · 1280: −58pt
해상도 상한 1920 과도 상향 시 대형 붕괴(스케일 갭) 학습 해상도 = 최적이자 상한
전처리 letterbox(표준) 정사각 resize 고정 — letterbox 시 far −4.7pt(학습 분포 이탈)
직사각 네이티브(736×1280) 1280 추론과 동등 ❌ far 0.648 (DUT)
ML2 1280×720 입력 온디바이스 640 letterbox / 클라우드 1280 640×640 정사각 다운스케일(왜곡 포함 = 학습 분포) — 호환 문제 없음

모델별 추론 입력 설정 (권장):

모델 추론 입력 근거
yolo26n merged (온디바이스) 640 letterbox ML2 CPU/Vulkan 속도
yolo26l-P2 (클라우드) 1280 letterbox H→I 무비용 far 이득
D-FINE-N 640×640 정사각(=학습) 학습 화질과 동일하게
D-FINE-L@960 960×960 정사각(=학습) 학습 화질과 동일하게 (test 실측 = 마스터표)
  • 원리: YOLO = 순수 conv → 스케일 일반화. DETR = 쿼리·anchor 학습 스케일 특화 → 해상도 잠금.
  • 한 줄 규칙: D-FINE은 학습 입력을 그대로, YOLO는 최적 추론 해상도를 별도 탐색.

라이선스 / 비고

데이터셋(DUT-Anti-UAV, Maciullo DroneDetectionDataset)은 각자 원 라이선스를 따른다. 여기서 재배포하지 않는다(위 출처 링크 참조).

About

2026 K-UAM confex - Drone object detection AR glasses

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages