Workflow i CLI

Po ostatnim refaktorze Easy-RT-DETR ma juz jeden glowny workflow eksperymentow zamiast wielu rozlacznych skryptow.

Glowne elementy

Nowy stack sklada sie z:

  • YAML configow w configs/,
  • loadera configow i override'ow w easy_rtdetr/configuration.py,
  • wspolnego solvera w easy_rtdetr/engine/solver.py,
  • wspolnych builderow danych w easy_rtdetr/data/,
  • builderow optimizera, schedulerow, warmupu i EMA w easy_rtdetr/optim/,
  • glownego CLI w easy_rtdetr/cli.py,
  • cienkich wrapperow w scripts/.

To jest teraz preferowana sciezka uruchamiania projektu.

Glowne entrypointy

W repo pozostaly uniwersalne skrypty:

  • scripts/train.py
  • scripts/eval.py
  • scripts/visualize.py
  • scripts/benchmark_video.py
  • scripts/calibrate.py
  • scripts/prepare_dataset.py
  • scripts/run_remote.py

Skrypty dataset-specific zostaly usuniete z glownego workflow.

Trening

Przyklad treningu na VOC car:

.venv/bin/python scripts/train.py --config configs/voc_car/base.yaml

Przyklad treningu z override:

.venv/bin/python scripts/train.py \
  --config configs/voc_car/base.yaml \
  --set solver.epochs=5 \
  --set model.backbone_name=hgnetv2_s

Przyklad gotowego configu dla HGNet-V2-S:

.venv/bin/python scripts/train.py --config configs/voc_car/hgnetv2_s.yaml

Mozna tez wymusic zapis finalnego checkpointu pod konkretna sciezka:

.venv/bin/python scripts/train.py \
  --config configs/voc_car/base.yaml \
  --output artifacts/voc_car_best.pt

Ewaluacja

Przyklad:

.venv/bin/python scripts/eval.py \
  --config configs/voc_car/base.yaml \
  --checkpoint runs/voc_car/.../checkpoints/best.pt

Wynik obejmuje miedzy innymi:

  • AP50
  • AP75
  • mAP@0.50:0.95
  • avg_best_iou
  • proxy precision/recall
  • duplikaty boxow

Wizualizacja

Przyklad:

.venv/bin/python scripts/visualize.py \
  --config configs/voc_car/base.yaml \
  --checkpoint runs/voc_car/.../checkpoints/best.pt \
  --input car_on_street.avif \
  --output-dir artifacts/inference_vis

Benchmark wideo

Do lokalnego pomiaru predkosci inferencji na CPU/GPU sluzy:

.venv/bin/python scripts/benchmark_video.py \
  --config configs/bdd_vehicle3/resnet50_lr1e4.yaml \
  --checkpoint artifacts/remote/custom/artifacts/bdd_vehicle3_resnet50_overfit64_512_200e.pt \
  --input-video artifacts/videos/input.mp4 \
  --device cpu \
  --max-frames 100 \
  --warmup-frames 5 \
  --threads 4 \
  --summary artifacts/video_benchmarks/run.json

Opcjonalnie mozna zapisac krotki podglad z bboxami:

.venv/bin/python scripts/benchmark_video.py \
  --config configs/bdd_vehicle3/resnet50_lr1e4.yaml \
  --checkpoint artifacts/remote/custom/artifacts/bdd_vehicle3_resnet50_overfit64_512_200e.pt \
  --input-video artifacts/videos/input.mp4 \
  --output-video artifacts/videos/preview.mp4 \
  --device cpu \
  --max-frames 30 \
  --frame-stride 5 \
  --set data.image_size=512

Skrypt wymaga lokalnych binarek ffmpeg i ffprobe. Nie wymaga opencv.

Filmy z YouTube nalezy pobierac tylko wtedy, gdy material mozna legalnie wykorzystac. Pomocniczo mozna uzyc lokalnie zainstalowanego yt-dlp:

.venv/bin/python -m yt_dlp -f "bv*[height<=480]+ba/b[height<=480]/best[height<=480]/worst" \
  --merge-output-format mp4 \
  -o "artifacts/videos/input.%(ext)s" \
  "https://www.youtube.com/watch?v=..."

Benchmark TensorRT na Jetsonie

Do pomiaru surowej wydajnosci engine'a TensorRT na Jetsonie sluza:

scripts/jetson_tensorrt_benchmark.sh
scripts/benchmark_tensorrt_engine.py

Pierwszy skrypt uruchamia kontener z obrazem camera-worker, konwertuje ONNX przez trtexec, zapisuje .engine, log trtexec i odpala drugi skrypt. Drugi skrypt mierzy juz gotowy engine przez TensorRT Python API + CuPy.

Przyklad dla modelu openimages_traffic5_raw umieszczonego w /data/test_model na Jetsonie:

cd /data/test_model

IMAGE=10.10.0.1:5000/camera-worker:latest-jetson \
./scripts/jetson_tensorrt_benchmark.sh easy_rtdetr_openimages_traffic5_raw.onnx

Domyslne parametry:

  • input tensor: images
  • input shape: 1x3x640x640
  • precyzja: fp16
  • warmup trtexec: 1000 ms
  • benchmark trtexec: 30 s
  • benchmark Python API: 50 warmup iterations + 500 measured iterations

W przypadku ONNX z external data obok pliku .onnx musi lezec rowniez plik .onnx.data, np.:

easy_rtdetr_openimages_traffic5_raw.onnx
easy_rtdetr_openimages_traffic5_raw.onnx.data

Ten benchmark mierzy sam model/engine. Nie obejmuje RTSP, dekodowania, letterbox/resize, postprocessingu, rysowania bboxow ani WebRTC.

Przygotowanie datasetow

Do konwersji datasetow sluzy:

.venv/bin/python scripts/prepare_dataset.py --dataset vehicles3

Aktualnie istotny wariant to vehicles3, czyli Roboflow vehicles v2 release zmapowany do trzech klas:

  • car
  • bus
  • truck

Po konwersji root datasetu to:

data/vehicles3

Config treningowy:

configs/vehicles3/resnet50.yaml

Alternatywny, szerszy wariant dla generalizacji to openimages_traffic4, czyli podzbior Open Images z klasami:

  • person
  • car
  • bus
  • truck

Przyklad przygotowania limitowanego subsetu:

.venv/bin/python scripts/prepare_dataset.py \
  --dataset openimages_traffic4 \
  --raw-root data/openimages_raw \
  --output-root data/openimages_traffic4 \
  --max-train-samples-per-class 2000 \
  --max-val-samples-per-class 500 \
  --min-area-ratio 0.0005 \
  --download-workers 16

Config treningowy:

configs/openimages_traffic4/hgnetv2_b0.yaml

Kalibracja score

Model zwraca surowe score po sigmoid, ale repo zawiera tez prosty etap kalibracji precision:

.venv/bin/python scripts/calibrate.py \
  --config configs/voc_car/base.yaml \
  --checkpoint runs/voc_car/.../checkpoints/best.pt \
  --output artifacts/voc_calibration.json

To jest przydatne do wizualizacji typu:

  • raw=...
  • p=...

gdzie p jest empiryczna precyzja oszacowana na zbiorze ewaluacyjnym.

Co robi solver

Wspolny solver obsluguje:

  • train loop,
  • eval loop,
  • checkpointing best/last,
  • AMP,
  • DDP przy uruchomieniu przez torchrun,
  • gradient clipping,
  • scheduler learning rate,
  • warmup,
  • EMA wag modelu,
  • logowanie i prosty profiling.

Przy AMP solver zapisuje w metrykach optimizer_steps. To jest wazne dla krotkich smoke testow: jesli optimizer_steps=0, pipeline mogl przejsc poprawnie, ale wagi nie zostaly zaktualizowane, bo GradScaler odrzucil kroki optymalizatora.

Gdy solver.use_ema=true, solver ewaluuje teraz dwa warianty modelu:

  • EMA jako wariant glowny, zapisywany w metrykach bez prefiksu i uzywany do wyboru best.pt,
  • raw model jako diagnostyka, zapisywany z prefiksem raw_, np. raw_AP50.

Dzieki temu w krotkich i srednich treningach widac, czy EMA dogania raw model. Dla krotkich runow praktyczniejsze jest ema_decay=0.999; 0.9999 jest sensowne dopiero przy dluzszych treningach, bo EMA aktualizuje sie wtedy bardzo wolno.

Po poprawce DFL w auxiliary dense head dodano test regresyjny na broadcasting wag:

tests/test_engine_utils.py::test_auxiliary_dense_dfl_loss_does_not_broadcast_weights

Jesli po kolejnych zmianach training loss znowu zacznie byc nienaturalnie wysoki, ten test jest pierwszym miejscem do sprawdzenia.

Struktura wynikow

Kazdy run tworzy katalog z:

  • checkpoints/best.pt
  • checkpoints/last.pt
  • metrics.json
  • config_resolved.yaml
  • train.log

To upraszcza porownywanie eksperymentow i integracje ze zdalnym runnerem.

Zdalny runner

scripts/run_remote.py nie powinien byc traktowany jako rsync calego repo. Domyslnie wysyla tylko minimalna allowliste potrzebna do treningu:

  • easy_rtdetr/
  • configs/
  • scripts/
  • pyproject.toml
  • README.md

Przed startem zdalnego kodu wypisywane sa sync_files i sync_size_mb. Jezeli te wartosci sa duze, nalezy sprawdzic --sync-path i wykluczenia, zanim uruchomi sie dlugi trening.

Dwie karty GPU na Kaggle wlacza sie przez --nproc-per-node 2, co po stronie zdalnej uruchamia torchrun i powinno dac w logu distributed=True world_size=2.

Import wag backbone'u z Paddle

Repo zawiera tez utility do importu wag PPHGNetV2 z .pdparams. Wspierane warianty: S (wlasny duzy wariant) oraz B0 (kompatybilny z PaddleClas):

.venv/bin/python scripts/import_hgnetv2.py \
  --source /path/to/PPHGNetV2_B0_ssld_stage1_pretrained.pdparams \
  --output artifacts/hgnetv2_b0_pretrained.pt \
  --arch B0

Tak zapisany checkpoint mozna potem podac przez:

  • model.backbone_name=hgnetv2_b0 (lub hgnetv2_s dla wariantu duzego),
  • model.backbone_pretrained_path=artifacts/hgnetv2_b0_pretrained.pt.

Pretrenowane wagi dla B0 sa dostepne publicznie na PaddleClas (Apache 2.0). Wariant S w naszym repo nie ma bezposrednio pasujacych wag - jego startup byl od zera.

Gradient accumulation

Solver przyjmuje solver.grad_accum_steps (default 1). Przy grad_accum_steps=N:

  • forward + backward jest robiony co krok,
  • optimizer step jest robiony co N krokow lub na ostatnim batchu epoki,
  • loss jest skalowany przez 1/N przed backward, zeby zachowac semantyke wielkiego batcha,
  • przy DDP model.no_sync() jest uzywane na niepelnych krokach zeby uniknac zbyt czestych all-reduce.

Uzywaj grad_accum_steps=2..4 gdy nie miescisz batcha w pamieci GPU. Przyklad - przy P2 na T4: batch_size: 2 + grad_accum_steps: 2 daje efektywny batch 4 per GPU.