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.pyreports/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 composedocker-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.pyconfigs/dfine/ → D-FINE train.pyscripts/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.txtscripts/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