Eksperymenty i Wyniki¶
Ten dokument zbiera najwazniejsze etapy eksperymentow wykonanych do tej pory.
1. Weryfikacja bazowa na CPU¶
Poczatkowo model byl sprawdzany lokalnie na CPU:
- testy shape,
forward/backward,- prosty trening na sztucznym datasecie,
- zapis i odczyt checkpointow.
To potwierdzilo, ze sama logika modelu i treningu dziala bez CUDA.
2. Penn-Fudan Pedestrian¶
Penn-Fudan byl pierwszym realnym datasetem:
- maly,
- prosty,
- jedna klasa,
- bardzo dobry do debugowania boxow i lossow.
Najwazniejsze obserwacje¶
Kluczowe poprawy jakosci przyszly z:
letterboxzamiast deformujacego resize,- lepszego postprocessu,
- dluzszego treningu,
- parity z RT-DETRv3 w query selection i denoising,
- auxiliary branch w stylu PP-YOLOE.
Mocny punkt odniesienia¶
Najbardziej udany checkpoint Penn-Fudan po treningu na GPU dawal profil:
avg_best_iou ~= 0.824pred_precision_proxy@0.50 ~= 0.984gt_recall@0.50 ~= 0.639duplicate_pair_ratio@0.40 = 0.000
W praktyce oznaczalo to:
- bardzo dobre polozenie boxow,
- brak duplikatow,
- sensowny recall jak na maly eksperymentalny setup.
3. VOC 2007 - tylko klasa car¶
VOC car byl pierwszym sensownym testem poza jedna klasa pieszych.
Wczesne biegi¶
Pierwsze checkpointy pokazywaly, ze model:
- poprawnie lokalizuje samochody,
- jest raczej konserwatywny score'owo,
- generalizuje na customowe zdjecia lepiej niz czysto zabawkowy baseline.
Przebudowa architektury¶
W kolejnych iteracjach dodano:
- parity elementow transformera wzgledem
ppdet, - nowy
HybridEncoder, - auxiliary path w stylu PP-YOLOE,
- training recipe z EMA, schedulerem i lepszym backbone.
Mocniejszy wariant VOC¶
Po dluzszym treningu z ResNet34, AMP i nowym training stackiem uzyskano profil:
avg_best_iou ~= 0.662pred_precision_proxy@0.50 ~= 0.747gt_recall@0.50 ~= 0.518duplicate_pair_ratio@0.40 = 0.000AP50 ~= 0.473AP75 ~= 0.357mAP@0.50:0.95 ~= 0.337
To jest obecny formalny punkt odniesienia dla samochodow.
4. COCO2017 traffic-5¶
Dodano standaryzowany subset COCO2017 jako bardziej rozpoznawalny benchmark wieloklasowy dla scen miejskich.
Klasy:
personbicyclecarbustruck
Przygotowany wariant:
train = 5000val = 1000- filtr bardzo malych obiektow:
min_area_ratio = 0.0002 - anotacje w formacie zgodnym z loaderem BDD-like,
- root datasetu:
data/coco_traffic5, - config treningowy:
configs/coco_traffic5/resnet50.yaml.
Statystyki przygotowanego subsetu:
| Split | Obrazy | Boxy | Srednio boxow / obraz | Mediana area ratio |
|---|---|---|---|---|
| train | 5000 | 22836 | 4.57 | 0.01205 |
| val | 1000 | 4598 | 4.60 | 0.01193 |
Historyczny obiekt MinIO:
datasets/coco_traffic5/5k1k-seed0-area0002/coco_traffic5-5k1k-seed0-area0002.tar.gz
Stan aktualny:
- obiekt zostal usuniety z MinIO 2026-05-10, zeby zwolnic miejsce,
- lokalny katalog
data/coco_traffic5zostal usuniety, - wyniki treningowe z tego datasetu byly wykonane przed poprawka bledu DFL w auxiliary dense head, wiec nie powinny byc traktowane jako miarodajny wynik jakosciowy modelu.
Ten dataset nadal jest sensownym pomyslem na benchmark, ale powinien zostac odtworzony i przetrenowany dopiero po przejsciu sanity checku overfit na malym podzbiorze.
5. BDD100K vehicle-3¶
Dodano tez pipeline dla nowoczesniejszego datasetu drogowego.
BDD100K vehicle-3 nie jest pelnym oficjalnym benchmarkiem BDD100K. To roboczy subset przygotowany na potrzeby tego projektu:
- klasy
car truckbus- zrodlo lokalne: dostepne obrazy BDD
10k/train - split deterministyczny:
90/10, seed0 train = 2665obrazowval = 297obrazow
Ten split powstal dlatego, ze lokalne anotacje 100k/val nie pasowaly nazwami do lokalnych obrazow 10k/val. Uzycie ich bez kontroli dawalo puste val. Obecny subset jest wiec poprawny technicznie i nadaje sie do eksperymentow, ale w raporcie trzeba go opisac jako wybrany podzbior BDD100K, a nie jako pelny BDD100K.
Zakres zaimplementowany:
- przygotowanie odfiltrowanego subsetu,
- dataset loader,
- integracja z MinIO,
- cache po stronie Kaggle,
- zdalny trening DDP na
2x Tesla T4, - checkpointy wracajace przez MinIO.
Historyczny obiekt datasetu w MinIO:
datasets/bdd100k_vehicle3/trainval90-seed0/bdd100k_vehicle3-trainval90-seed0.tar.gz
Stan aktualny:
- obiekt zostal usuniety z MinIO 2026-05-10,
- lokalny dataset BDD vehicle-3 nie jest obecnie aktywnym datasetem roboczym,
- wyniki BDD ponizej sa wazne infrastrukturalnie, ale jakosciowo sa ograniczone, bo powstaly przed znalezieniem bledu DFL.
Smoke BDD na Kaggle¶
Pierwszy smoke potwierdzil:
- pobieranie datasetu z MinIO,
- rozpakowanie do
data/bdd100k_vehicle3, - DDP na dwoch kartach:
distributed=True world_size=2, - zapis checkpointu.
Przy runtime.amp=true GradScaler odrzucal kroki optymalizatora w bardzo krotkim smoke tescie:
optimizer_steps = 0
Dlatego kolejne biegi BDD byly prowadzone z runtime.amp=false. To jest wolniejsze, ale daje realne aktualizacje wag.
BDD + HGNet-V2-S, 3 epoki¶
Pierwszy pelniejszy bieg:
- config:
configs/bdd_vehicle3/hgnetv2_s.yaml - model:
HGNet-V2-S - epoki:
3 - batch per proces/GPU:
4 - efektywny batch globalny: okolo
8 - DDP:
2x T4 - AMP:
false - checkpoint:
artifacts/remote/custom/artifacts/bdd_vehicle3_hgnetv2_s_3e.pt
Najwazniejsze obserwacje:
optimizer_steps = 334w kazdej epoce,- loss spadl z okolo
123.6do94.0, - throughput okolo
10 img/s, - pamiec GPU okolo
1.54 GB, mAP,AP50,AP75nadal0,- model po 3 epokach nie produkowal uzytecznych predykcji powyzej progu ewaluacji.
Ten bieg potwierdzil, ze pipeline trenuje poprawnie, ale 3 epoki sa niewystarczajace jakosciowo.
BDD + HGNet-V2-S, batch 16, 10 epok¶
Nastepny bieg zwiekszyl batch, bo poprzedni trening uzywal bardzo malo pamieci.
Ustawienia:
- config:
configs/bdd_vehicle3/hgnetv2_s.yaml - epoki:
10 - batch per proces/GPU:
16 - efektywny batch globalny: okolo
32 - DDP:
2x T4 - AMP:
false train_batches = 84- checkpoint:
artifacts/remote/custom/artifacts/bdd_vehicle3_hgnetv2_s_b16_10e.pt
Wyniki techniczne:
- batch
16miesci sie bez problemu, - maksymalne zuzycie pamieci GPU: okolo
4.56 GB, - throughput: okolo
16.6 img/s, optimizer_steps = 84w kazdej epoce,- trening byl znacznie szybszy niz przy batch
4.
Wyniki jakosciowe po 10 epokach:
- loss spadl z okolo
387.1do337.7, ale byl niestabilny, mAP@0.50:0.95 = 0.0,AP50 = 0.0,AP75 = 0.0,- predykcje zaczely sie pojawiac w czesci epok, ale IoU pozostalo bardzo niskie,
- najlepszy sygnal proxy byl w okolicy epoki
7:avg_best_iou ~= 0.0056,gt_recall@0.50 ~= 0.0023, nadal praktycznie zerowo.
Interpretacja:
- problemem nie jest juz infrastruktura, batch ani DDP,
- model faktycznie aktualizuje wagi i korzysta z obu GPU,
- obecny recipe nie daje jeszcze sensownej detekcji na BDD vehicle-3,
- brak pretrainu dla
HGNet-V2-Si maly subset BDD najpewniej utrudniaja start, - wysokie i niestabilne lossy sugeruja, ze trzeba dopracowac recipe przed dluzszym treningiem.
Wnioski dla BDD¶
To jest baza pod dalsze bardziej realistyczne eksperymenty wieloklasowe.
Najrozsadniejsze kolejne kroki:
- uruchomic ewaluacje tego checkpointu z nizszym
evaluation.score_threshold, np.0.01, zeby zobaczyc, czy predykcje sa tylko zbyt nisko punktowane, - porownac
HGNet-V2-SzResNet50lubResNet34na tym samym splicie, - sprobowac mniejszego LR dla batch
16, np.solver.lr=0.0001, - rozwazyc pretrain backbone'u albo import wag
PPHGNetV2-S, - ograniczyc ewaluacje per epoka przy dluzszych biegach, np.
eval_interval=2, bo walidacja 297 obrazow jest relatywnie kosztowna, - dopiero po stabilizacji recipe uruchamiac
20+epok.
6. Vehicles3 - Roboflow vehicles v2 release¶
Aktualnym czystym datasetem roboczym jest vehicles3, przygotowany z archiwum:
data/vehicles.v2-release.coco.zip
To dataset Roboflow w formacie COCO, licencja CC BY 4.0, z obrazami 640x480. Oryginalne klasy byly zbyt szczegolowe i malo czytelne, dlatego zostaly zmapowane do trzech klas:
carbustruck
Mapowanie:
car->carbig bus,bus-l-,bus-s-,small bus->busbig truck,mid truck,small truck,truck-l-,truck-m-,truck-s-,truck-xl-->truck
Przygotowanie datasetu wykonuje:
.venv/bin/python scripts/prepare_dataset.py --dataset vehicles3
Lokalny root:
data/vehicles3
Config treningowy:
configs/vehicles3/resnet50.yaml
Statystyki po konwersji:
| Split | Obrazy | Boxy | car | bus | truck |
|---|---|---|---|---|---|
| train | 2633 | 31905 | 19083 | 1134 | 11688 |
| valid | 963 | 13450 | 8537 | 342 | 4571 |
| test | 454 | 6222 | 4021 | 149 | 2052 |
Aktualny obiekt MinIO:
datasets/vehicles3/roboflow-v2-release/vehicles3-roboflow-v2-release.tar.gz
SHA256 archiwum:
df67861809359786237779affa2fe3f0334ab48f855494922199c9c3a065be56
Vehicles3 - probe 1 epoka¶
Pierwszy probe potwierdzil, ze dataset, MinIO, rozpakowanie i DDP dzialaja:
- artifact:
artifacts/remote/custom/artifacts/vehicles3_resnet50_remote_probe_1e.pt - DDP:
world_size=2 data.max_train_samples=64data.max_eval_samples=64solver.epochs=1solver.batch_size=4
Wyniki po 1 epoce byly bardzo slabe:
- EMA
AP50 ~= 0.000148 - EMA
mAP ~= 0.0000176 - EMA
avg_best_iou ~= 0.0788 - RAW
AP50 = 0.0 - RAW
avg_best_iou ~= 0.0089
Wniosek: pipeline dziala, ale model po 1 epoce nie wykrywa jeszcze niczego uzytecznego.
Krytyczna poprawka DFL¶
W easy_rtdetr/losses.py znaleziono blad w AuxiliaryDenseCriterion.forward().
Stara wersja mnozyla loss_dfl o ksztalcie [num_pos] przez bbox_weight.unsqueeze(-1) o ksztalcie [num_pos, 1]. Broadcasting tworzyl macierz [num_pos, num_pos], co sztucznie zawyzalo auxiliary DFL.
Efekt na probce przed poprawka:
loss_total ~= 364.3loss_dfl_aux_o2m ~= 293.2
Efekt po poprawce:
loss_total ~= 71.6loss_dfl_aux_o2m ~= 1.42
Dodano test regresyjny:
tests/test_engine_utils.py::test_auxiliary_dense_dfl_loss_does_not_broadcast_weights
Ta poprawka jest istotna: wyniki BDD/COCO/vehicles sprzed niej nie powinny byc uzywane jako ostateczny argument, ze architektura nie dziala.
Vehicles3 - ResNet50 po poprawce DFL, 5 epok¶
Po poprawce DFL uruchomiono test:
- config:
configs/vehicles3/resnet50.yaml - artifact:
artifacts/remote/custom/artifacts/vehicles3_resnet50_dflfix_5e.pt - epoki:
5 data.max_eval_samples=128- DDP:
2x Tesla T4 - EMA wlaczona z
ema_decay=0.999
Najwazniejsze metryki:
| Epoka | RAW AP50 | RAW mAP | RAW avg_best_iou | RAW recall proxy | EMA AP50 | EMA mAP |
|---|---|---|---|---|---|---|
| 1 | ~0.000519 | ~0.000091 | brak pelnego odczytu | brak pelnego odczytu | ~0.000028 | bardzo niskie |
| 3 | ~0.001663 | ~0.000286 | brak pelnego odczytu | ~0.166 | brak pelnego odczytu | brak pelnego odczytu |
| 5 | ~0.002563 | ~0.000462 | ~0.0735 | ~0.1719 | ~0.000175 | ~0.000034 |
Interpretacja:
- poprawka DFL usunela realny blad w lossie,
- metryki po 5 epokach nadal sa zbyt slabe do demo i raportowania jako skuteczny model,
- RAW model uczy sie szybciej niz EMA, co jest normalne w krotkim treningu,
- 5 epok z cosine schedulerem jest zlym testem finalnej jakosci, bo LR dochodzi do zera bardzo szybko,
- przed dlugim treningiem potrzebny jest maly overfit sanity check.
Rekomendowany kolejny test:
32-64obrazy z train,100-200epok,- staly LR
1e-4, runtime.amp=falsealbo nizszyruntime.amp_init_scale,solver.use_ema=falsena czas diagnostyki,- ewaluacja na tym samym malym train subset,
- oczekiwany sygnal: wyrazny wzrost
AP50/IoU. Jesli overfit nadal nie dziala, problem jest w modelu, lossach, assignment albo postprocessie.
7. Lokalny benchmark wideo na CPU¶
Dodano prosty benchmark inferencji na lokalnym pliku wideo:
scripts/benchmark_video.py
Material testowy:
- lokalny plik:
artifacts/videos/youtube_Gr0HpDM8Ki8.mp4 - rozdzielczosc:
640x360 - FPS zrodlowy:
25 - czas:
30 s - liczba klatek:
750
Wyniki na lokalnym CPU laptopa, torch_threads=4:
| Model | Checkpoint | image_size | prog score | FPS modelu | FPS pipeline | Srednio detekcji/klatke | Interpretacja |
|---|---|---|---|---|---|---|---|
| VOC car ResNet34 | voc_car_kaggle.pt |
256 |
0.18 |
3.80 |
3.77 |
1.08 |
najlepszy obecny model do demo detekcji samochodow na filmie |
| BDD ResNet50 overfit64 | bdd_vehicle3_resnet50_overfit64_512_200e.pt |
512 |
0.3 |
1.14 |
1.13 |
0.84 |
wykrywa pojazdy, ale CPU nie jest realtime |
| BDD ResNet50 v2 b8 20e best | bdd_vehicle3_resnet50_v2_b8_20e.pt |
640 |
0.05 |
0.91 |
0.91 |
40.20 |
najnowszy artefakt z Kaggle, ale checkpoint ma epoch=2 i jest bardzo zaszumiony |
| BDD HGNet-V2-S b16 10e | bdd_vehicle3_hgnetv2_s_b16_10e.pt |
256 |
0.05 |
4.89 |
4.85 |
0.00 |
szybszy, ale ten checkpoint nie daje uzytecznych detekcji |
Zapisany podglad wideo z bboxami:
artifacts/videos/youtube_Gr0HpDM8Ki8_voc_car_r34_cpu_preview.mp4
artifacts/videos/youtube_Gr0HpDM8Ki8_bdd_resnet50_cpu_preview.mp4
artifacts/videos/youtube_Gr0HpDM8Ki8_bdd_resnet50_v2_cpu_preview.mp4
Wniosek:
- do demonstracji samochodow najlepszy jest obecnie checkpoint VOC
car, bo faktycznie uczy jedna klasecari daje najbardziej czytelny podglad, - na CPU laptopa obecny ResNet50 jest za wolny do realtime,
- wariant
ResNet50 v2przy640jest jeszcze wolniejszy i w aktualnym checkpointcie generuje duzo nisko punktowanych predykcji, - lzejszy backbone jest szybszy, ale wymaga lepszego treningu/checkpointu,
- BDD overfit jest dobrym dowodem poprawnosci pipeline'u, ale nie powinien byc przedstawiany jako model generalizujacy.
8. Jak interpretowac score modelu¶
Score przy boxie:
- jest scorem po
sigmoid, - sluzy do rankingu i filtrowania,
- nie jest idealnie skalibrowanym prawdopodobienstwem.
Dlatego w repo dodano tez prosty etap kalibracji precision. Dzieki temu wizualizacje moga pokazywac:
raw=...jako surowy score modelu,p=...jako oszacowana precyzja empiryczna na zbiorze ewaluacyjnym.
9. Diagnostyka vehicles3 po poprawkach postprocessingu¶
Dodano narzedzie diagnostyczne:
scripts/diagnose_validation.py
Uruchomienie dla aktualnego checkpointu HGNet-V2-S, z pelnym audytem datasetu i metrykami/obrazkami na 64 obrazach walidacyjnych:
.venv/bin/python scripts/diagnose_validation.py \
--config configs/vehicles3/hgnetv2_s.yaml \
--checkpoint artifacts/remote/custom/artifacts/vehicles3_hgnetv2_s_vfl_fullval_5e.pt \
--output-dir artifacts/diagnostics/vehicles3_hgnetv2_s_vfl_fullval_5e \
--set runtime.device=cpu \
--set runtime.num_workers=0 \
--set runtime.pin_memory=false \
--set solver.eval_batch_size=4 \
--diagnostic-max-eval-samples 64 \
--max-visuals 24 \
--visual-max-preds 30 \
--visual-score-threshold 0.05
Artefakty:
artifacts/diagnostics/vehicles3_hgnetv2_s_vfl_fullval_5e/dataset_audit.json
artifacts/diagnostics/vehicles3_hgnetv2_s_vfl_fullval_5e/validation_metrics.json
artifacts/diagnostics/vehicles3_hgnetv2_s_vfl_fullval_5e/visuals/
Audyt datasetu:
| Split | Obrazy | Obiekty | car | bus | truck | Puste obrazy | Bledne boxy |
|---|---|---|---|---|---|---|---|
| train | 2633 | 31905 | 19083 | 1134 | 11688 | 0 | 0 |
| valid | 963 | 13450 | 8537 | 342 | 4571 | 0 | 0 |
Wniosek z audytu:
- dataset nie wyglada na technicznie uszkodzony: brak pustych obrazow, brak blednych etykiet i brak blednych boxow,
- klasy sa mocno niezbalansowane:
busto tylko okolo3.6%obiektow w train i2.5%w valid, - wiekszosc obiektow jest mala: w train
23844/31905, a w valid10366/13450boxow ma powierzchnie ponizej1%obrazu.
Metryki na 64 obrazach walidacyjnych:
| Metryka | Wartosc |
|---|---|
| AP50 | 0.5805 |
| AP75 | 0.2378 |
| mAP@0.50:0.95 | 0.2883 |
| pred_precision_proxy@0.50 | 0.2858 |
| gt_recall@0.50 | 0.9035 |
| duplicate_pair_ratio@0.40 | 0.0193 |
Metryki per klasa na 64 obrazach:
| Klasa | AP50 | mAP | predykcje | GT | TP | FP | FN | precision | recall |
|---|---|---|---|---|---|---|---|---|---|
| car | 0.6742 | 0.3089 | 2976 | 648 | 563 | 2413 | 85 | 0.1892 | 0.8688 |
| bus | 0.4401 | 0.2900 | 672 | 28 | 25 | 647 | 3 | 0.0372 | 0.8929 |
| truck | 0.6272 | 0.2659 | 2752 | 371 | 336 | 2416 | 35 | 0.1221 | 0.9057 |
Wniosek z diagnostyki:
- model ma wysoki recall, czyli znajduje wiekszosc obiektow,
- glowny problem to za duzo false positives i slaba kalibracja/ranking score,
busjest najslabsza klasa w precyzji, prawdopodobnie przez mala liczbe przykladow i mylenie ztruck,- kolejny techniczny kierunek to poprawa scoringu/assignment/klasyfikacji, a nie dalsze krecenie samym
top-k.
10. Quality-aware scoring head¶
Po diagnostyce false positives dodano opcjonalny quality_score_head w glownym dekoderze DETR.
Mechanizm:
- kazda warstwa dekodera moze przewidywac dodatkowy
quality_logit, - targetem dla matched query jest IoU predykcji z dopasowanym GT,
- targetem dla unmatched query jest
0, - loss jest varifocal-like,
- w inferencji score jest mnozony przez przewidywana jakosc:
final_score = sigmoid(class_logit) * sigmoid(quality_logit)
Dlaczego:
- aktualny model ma wysoki recall, ale niska precyzje,
- zwykly class score nie wystarcza do rankingu detekcji,
- VarifocalNet opisuje IoU-aware classification score jako sposob laczenia obecnosci obiektu i jakosci lokalizacji,
- RT-DETR/D-FINE w praktyce korzystaja z Varifocal Loss jako glownego elementu klasyfikacji/rankingu.
Wazne ustawienia:
model:
use_quality_score_head: true
quality_loss_weight: 1.0
Zmiana jest kompatybilna wstecz: stare checkpointy nie maja tej glowy, wiec domyslne use_quality_score_head=false pozwala je dalej ladowac.
Test 5 epok na Kaggle¶
Uruchomiono krotki test:
configs/vehicles3/hgnetv2_s.yaml
solver.epochs=5
solver.eval_interval=5
solver.save_interval=5
Trening dzialal na 2x Tesla T4 przez DDP. Dataset byl staged przez MinIO:
datasets/vehicles3/quality-run/vehicles3.tar.gz
Artefakty:
artifacts/custom/vehicles3_hgnetv2_s_quality_5e.pt
artifacts/custom/vehicles3_hgnetv2_s_quality_5e_raw.pt
Wazne: wersja quality_5e.pt zawiera tez EMA, ale EMA po 5 epokach byla praktycznie bezuzyteczna. Do dalszych testow nalezy uzywac quality_5e_raw.pt albo ladowac model z use_ema=False.
Metryki RAW po 5 epokach:
| Metryka | Wartosc |
|---|---|
| AP50 | 0.5598 |
| AP75 | 0.2418 |
| mAP@0.50:0.95 | 0.2767 |
| pred_precision_proxy@0.50 | 0.3572 |
| gt_recall@0.50 | 0.8851 |
| avg_preds | 98.73 |
Per klasa RAW:
| Klasa | AP50 | mAP | precision@0.50 | recall@0.50 |
|---|---|---|---|---|
| car | 0.7006 | 0.3442 | 0.1942 | 0.8584 |
| bus | 0.2941 | 0.1563 | 0.0170 | 0.9123 |
| truck | 0.6847 | 0.3294 | 0.1065 | 0.9070 |
Porownanie do poprzedniego HGNet-V2-S 5e bez osobnej quality head:
- AP50:
0.5611 -> 0.5598, praktycznie bez zmiany, - mAP:
0.2845 -> 0.2767, lekko gorzej, - precision proxy:
0.2132 -> 0.3572, wyraznie lepiej, - recall:
0.9239 -> 0.8851, spadek recall w zamian za mniej false positives.
Wniosek: quality head robi to, czego oczekiwalismy kierunkowo: poprawia ranking/precyzje, ale po 5 epokach kosztuje recall i lekko mAP. To nie jest jeszcze finalny model; nastepny test powinien isc bez EMA i z dluzszym treningiem albo z mniejsza waga quality_loss_weight, np. 0.5.
11. Eksperyment P2 - 4 poziomy feature maps (negatywny wynik)¶
W odpowiedzi na obserwacje, ze wiekszosc obiektow vehicles3 ma area < 1% obrazu, sprobowano dodac poziom P2 (stride 4) do feature pyramid. Wariant pelny: backbone zwraca 4 poziomy (P2, P3, P4, P5), encoder + decoder operuja na wszystkich czterech.
Zmiany w kodzie:
TorchvisionResNetBackboneiHGNetV2Backbonedostaly opcjonalny flagreturn_p2build_backboneprzekazuje go gdy wfeat_stridesjest4model.pyautomatycznie wykrywareturn_p2 = 4 in config.feat_strides- konfiguracja YAML:
num_feature_levels: 4,feat_strides: [4, 8, 16, 32],hybrid_encoder_use_idx: [3]
Realny koszt na 640x640:
- token count rosnie z 8.4k do ~34k (~4x wiecej w deformable encoder)
- na T4 16GB pojawil sie OOM przy
batch_size=4 - przy
batch_size=2 + grad_accum_steps=2trening sie miesci, ale memory peak 13.8GB
Wyniki po 5 epokach (HGNet-V2-S, P2, vehicles3)¶
| Metryka | P3 baseline | P2 | Zmiana |
|---|---|---|---|
| AP50 | 0.560 | 0.00054 | -99.9% |
| mAP@0.50:0.95 | 0.277 | 0.0001 | -99.96% |
| gt_recall@0.50 | 0.885 | 0.084 | -91% |
| pred_precision@0.50 | 0.357 | 0.022 | -94% |
Najbardziej diagnostyczny sygnal byl per-klasa: model zaczal kolapsowac na klase bus (3.6% datasetu) i przewidywal 52544 busow na 342 prawdziwe (153x nadprodukcja).
Interpretacja¶
P2 dodaje ~25600 anchorow na poziomie stride 4. W 5 epokach model nie zdazyl nauczyc sie sensownie korzystac z nowych, losowo zainicjalizowanych features na tym poziomie. Auxiliary dense head i assigner taskAligned konkurowaly o sygnal z duza liczba slabych pozycji, co zdestabilizowalo klasyfikacje.
Wniosek¶
P2 mogloby zadzialac przy:
- dluzszym treningu (30+ epok), aby P2 features mialy szanse sie zsyntonizowac,
- mocniejszym pretrenowaniu backbone'u, zeby stage 0 nie startowal od zera,
- rozdzielonej krzywej lr dla nowych poziomow.
W obecnym setupie wniosek jest jednoznaczny: architektura nie byla waskim gardlem na 5 epok. Ograniczeniem byl recipe (pretrain, dlugosc treningu, class balance). Pelny P2 zostal cofniety w configach vehicles3. Kod-level wsparcie pozostalo jako opt-in (feat_strides: [4, 8, 16, 32]), zeby ulatwic kolejne probe w pelniejszym setupie.
12. HGNet-V2-B0 z pretrainem + class reweighting¶
Po negatywnym eksperymencie P2 wrocono do P3 i skupiono sie na recipe. Wprowadzono dwie zmiany jednoczesnie:
- Mniejszy backbone
hgnetv2_b0(~5M params vs ~23M dla "S") z importowanym pretrainem z PaddleClas (PPHGNetV2_B0_ssld_stage1_pretrained.pdparams). - Class reweighting w
SetCriterion.VarifocalLossz wagami inverse-sqrt-frequency.
Wagi klas dla vehicles3¶
| Klasa | Liczba GT (train) | Waga (inverse-sqrt, znormalizowana) |
|---|---|---|
| car | 19083 | 0.47 |
| bus | 1134 | 1.93 |
| truck | 11688 | 0.60 |
Wzor: w_c = (1 / sqrt(n_c)) / mean_c (1 / sqrt(n_c)). Bus dostaje ~2x wieksza wage zarowno na positives jak i negatives w VarifocalLoss. To rownoczesnie nagradza model za TP na bus, jak i karze za FP. Wagi sa wczytywane jako buffer w SetCriterion przez nowe pole config cls_class_weights.
Implementacja w kodzie¶
easy_rtdetr/config.py:cls_class_weights: tuple[float, ...] | None = Noneeasy_rtdetr/losses.py:SetCriterion.__init__przyjmujeclass_weights, rejestrowane jako buffer;_varifocal_loss_with_logitsmnozy element-wise weight tensor przezclass_weights[None, None, :]easy_rtdetr/model.py: przekazujeconfig.cls_class_weightsdoSetCriterion
Wsparcie dla B0 w backbone¶
Dodano nowy wariant arch B0 w _HGNET_V2_ARCH_CONFIGS (stem 16/16, stages mid 16/32/64/128). build_backbone rozumie nazwe hgnetv2_b0. scripts/import_hgnetv2.py przyjmuje teraz --arch B0 obok S.
Trening i wyniki¶
Konfiguracja configs/vehicles3/hgnetv2_s.yaml (mimo nazwy uzywa teraz B0 przez backbone_name: hgnetv2_b0):
backbone_pretrained_path: artifacts/hgnetv2_b0_pretrained.ptcls_class_weights: [0.47, 1.93, 0.60]image_size: 640,batch_size: 8,epochs: 5,lr: 2e-4use_quality_score_head: true
Trening na 2x T4 przez DDP, ~1.5h, throughput 6.96 img/s, memory peak 10.2GB.
Wyniki na pelnym valid (963 obrazow):
| Metryka | Baseline (S 5e, no pretrain) | B0 5e, pretrain + reweight | Zmiana |
|---|---|---|---|
| AP50 | 0.560 | 0.758 | +35% |
| AP75 | 0.242 | 0.490 | +102% |
| mAP@0.50:0.95 | 0.277 | 0.458 | +65% |
| gt_recall@0.50 | 0.885 | 0.917 | +4% |
| pred_precision@0.50 | 0.357 | 0.506 | +42% |
| avg_preds/image | 98 | 72 | -27% |
Per klasa (AP50):
| Klasa | Baseline | B0 + reweight | Zmiana |
|---|---|---|---|
| car | 0.701 | 0.775 | +11% |
| bus | 0.294 | 0.662 | +125% |
| truck | 0.685 | 0.836 | +22% |
Mean score per klasa: car 0.214, bus 0.067, truck 0.178. Bus ma teraz najnizszy mean score, co znaczy ze reweight + pretrain zatrzymaly nadprodukcje bus. Recall na wszystkich klasach > 0.89.
Wniosek¶
Recipe (pretrain + class reweight) byl waskim gardlem, nie architektura. Dwie niezalezne zmiany dlugosci tygodnia kodu daly ~2x lepsze metryki niz rozbudowywanie architektury (P2 dalo regresje 1000x).
Domain shift jako otwarta sprawa¶
Pomimo dobrych metryk na valid, model jest mocno specyficzny dla rodzaju kamery z vehicles3 (klatki CCTV/dashcam z kilku sesji nagraniowych: aditganteng_mp4, malam_04112021_mp4, etc.). Na zdjeciach z innej dystrybucji (np. lokalne pliki street_of_cars.jpeg) detekcje sa zauwazalnie slabsze. To nie jest blad modelu - to natura datasetu. Lek na to to mocniejsza augmentacja (mosaic, random_scale_crop, perspective transforms) lub mieszanie z innym datasetem (BDD100K, COCO traffic-5).
13. Jetson TensorRT benchmark - openimages_traffic5_raw¶
Przetestowano eksportowany model:
easy_rtdetr_openimages_traffic5_raw.onnx
Model byl zapisany jako ONNX z external data, wiec do konwersji potrzebny byl komplet:
easy_rtdetr_openimages_traffic5_raw.onnx
easy_rtdetr_openimages_traffic5_raw.onnx.data
Srodowisko:
- host: Jetson Orin, GPU
Orin, compute capability8.7 - TensorRT:
10.3.0 - obraz Docker:
10.10.0.1:5000/camera-worker:latest-jetson - input tensor:
images - input shape:
1x3x640x640 - precyzja builda:
FP32+FP16przeztrtexec --fp16
Uruchomienie:
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
Wynik konwersji:
| Metryka | Wartosc |
|---|---|
| Czas budowy engine'a | 1516.5 s |
| Rozmiar engine'a | 48.86 MiB |
| Total weights memory | 47.43 MiB |
| Total activation memory | 104.17 MiB |
| Execution context device memory | 99.34 MiB |
Wyjscia engine'a:
| Tensor | Shape | dtype |
|---|---|---|
logits |
1x300x5 |
float32 |
boxes |
1x300x4 |
float32 |
quality |
1x300x1 |
float32 |
Wyniki trtexec:
| Metryka | Wartosc |
|---|---|
| Throughput | 13.4816 qps |
| Latency mean | 74.403 ms |
| Latency median | 74.4094 ms |
| Latency p90 | 74.8887 ms |
| Latency p95 | 75.1406 ms |
| Latency p99 | 75.4912 ms |
| GPU Compute Time mean | 73.9811 ms |
| H2D Latency mean | 0.4138 ms |
| D2H Latency mean | 0.0081 ms |
Wyniki TensorRT Python API + CuPy:
| Metryka | Wartosc |
|---|---|
| Warmup | 50 iteracji |
| Iteracje mierzone | 500 |
| Mean latency | 74.1474 ms |
| FPS | 13.4867 |
| Copy outputs | false |
Interpretacja:
- wynik
trtexeci wynik Python API sa praktycznie identyczne, wiec surowa inferencja engine'a jest stabilna, - realny limit samego modelu na tym Jetsonie to okolo
13.5 FPS/74 msdla1x3x640x640, - wejscie i wyjscia sa
float32, ale TensorRT wykonuje kompatybilne warstwy w FP16 w ramach buildaFP32+FP16, - ten benchmark mierzy tylko surowy engine; pelny
camera-workerbedzie wolniejszy o RTSP/decode, preprocessing, postprocessing, rysowanie i WebRTC, - czas budowy engine'a jest wysoki (~25 minut), dlatego zbudowany
.enginepowinien byc traktowany jako artefakt runtime, a nie cos budowanego przy starcie workerow.
14. Wnioski z wynikow¶
Najwazniejsze wnioski praktyczne:
- model jest juz uzywalnym research prototype, a nie tylko szkicem,
- pipeline dziala lokalnie i zdalnie,
- Penn-Fudan pokazal mocna jakosc lokalizacji,
- VOC
carpokazal sensowna generalizacje i daje juz formalnemAP, - BDD100K i COCO traffic-5 byly wartosciowe infrastrukturalnie, ale ich stare wyniki jakosciowe sa mniej wiarygodne po znalezieniu bledu DFL,
- aktualnym czystym datasetem roboczym jest
vehicles3, - najwazniejszy kolejny krok to overfit sanity check po poprawce DFL, a dopiero potem dluzszy trening na pelnym datasecie.