🌐 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.
- 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·낮은 지연). yolo26n = 최속·최경량, D-FINE-L = 최고 AP. D-FINE-N은 CPU 트레이드오프 양호.
전 모델(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 가속 못 받음 |
INT8 GPU 가속은 TensorRT 엔진 별도 빌드 필요.
⚠️ 이전 환경(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)는 미측정.
| 정밀도 | 파일 | 크기 | 비고 |
|---|---|---|---|
| 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).
구성별 기여 분해 — 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 |
- 곡선(검증셋 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이 정체(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 과소 |
|---|---|---|
![]() |
![]() |
![]() |
FN 상위 — 라벨 오차·특수 케이스 혼재:
| fn_000281 · GT 과대 | fn_000807 · 드론 일부만 프레임 | fn_000808 · 자막이 드론 가림 | fn_002018 · 강한 조명(LED) |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
- 결론: Maciullo 0.89 천장은 **라벨 품질(박스 크기 오차·누락)+특수 케이스(조명)**의 영향. 모델 추가 개선의 Maciullo AP 기대치는 이 천장 기준으로 해석할 것.
- 전체 시각화(506장):
reports/label_audit_maciullo/(로컬 생성물, 미커밋).
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) |
|---|---|---|
![]() |
![]() |
![]() |
재현: 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
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 (기본): https://github.com/wangdongdut/DUT-Anti-UAV
- Maciullo DroneDetectionDataset (비교적 큰 데이터셋): 원본 https://github.com/Maciullo/DroneDetectionDataset · 사용한 HF mirror https://huggingface.co/datasets/pathikg/drone-detection-dataset
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).
학습 데이터 확장용(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.mdDocker 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] 확인.
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).
| 증상 | 원인 | 해결 |
|---|---|---|
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 셋 필요
- 선정: 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-FINEtrain.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·소형에서 우위(아래).
위 곡선은 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× 확대 인셋.
![]() |
![]() |
![]() |
|---|---|---|
| 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) 전용 |
- trade-off: far +7.1pt(vs yolo26n-D) ↔ fps ↓(4090 340→195 FPS · CPU t8 33→24 FPS — D-FINE-N은 GPU 효율 우수).
클라우드 주력 모델. 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:
ℹ️ 위 수치는 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.pthep115, 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)은 각자 원 라이선스를 따른다. 여기서 재배포하지 않는다(위 출처 링크 참조).


















