GÖREV KAYDI

GÖREV KAYDI

KAYIT AÇIK

ATILIM UAV · YER KONTROL İSTASYONU

GÖREV KAYDI / DETAY

Arşiv

CANLI · OKUMA MODU

Yazılım

Tespit Veri Seti Kurmak: Etiketleme Kuralları, Hard Negative'ler ve Güvenebileceğiniz Doğrulama

Tespit Veri Seti Kurmak: Etiketleme Kuralları, Hard Negative'ler ve Güvenebileceğiniz Doğrulama

FIG. 01

KAYIT KÜNYESİ

Kategori

Yazılım

TARİH

TÜM KAYITLAR

Eğitim biter ve terminalde 0.9'un üzerinde bir mAP50 (0.50 IoU eşiğinde ortalama kesinlik) görünür. Ertesi gün sahaya çıkarsınız: model bir ağaç gölgesine hedef der, bir karede 20 metreden yakaladığı manken bir sonraki karede kaybolur. İlk refleks neredeyse her zaman aynıdır: daha büyük model, daha çok epoch, daha agresif augmentation.

Bizim deneyimimizde sorun neredeyse hiçbir zaman orada değildi. SUAS 2026 için kurduğumuz veri setinin ilk sürümünde karelerin ezici çoğunluğu web'den toplanmış yer seviyesi fotoğraflarıydı; havadan çekilmiş kareler setin %15'inden azını oluşturuyordu ve iki sınıf kabaca 7:1 oranında dengesizdi. Bu setten rastgele ayrılan bir doğrulama kümesi yüksek bir sayı üretiyordu, ama o sayı uçuş performansı hakkında bize neredeyse hiçbir şey söylemiyordu.

Bu yazıda veri setine bir yazılım bileşeni gibi yaklaşıyoruz: bir sözleşmesi vardır (etiketleme kuralları), test kapsaması vardır (doğrulama kümesi) ve sessizce bozulabilir (leakage/sızıntı: bilginin eğitimden teste kaçması). Sayısal hedefler Ultralytics dokümantasyonundan geliyor; yapısal kararlar ise sahada geçirdiğimiz zamandan.

İyi bir veri setinin anatomisi

Ultralytics'in "En İyi Eğitim Sonuçları İçin İpuçları" rehberi, üretim kalitesi için sınıf başına en az 1500 görüntü ve sınıf başına en az 10.000 etiketli örnek öneriyor. Bir öğrenci takımı için bu sayılar çoğu zaman ulaşılabilir değil; asıl önemli olan, açığı hangi eksende kapattığınızı bilmek.

Bileşen

Hedef

Neden önemli

Sınıf başına görüntü

≥ 1500 (Ultralytics önerisi)

Temel görsel çeşitlilik

Sınıf başına örnek

≥ 10.000 (Ultralytics önerisi)

Ölçek ve duruş çeşitliliği

Nesne içermeyen arka plan görüntüsü

Toplamın %0-10'u

Yanlış pozitifleri (FP) aşağı çeker

Doğrulama kümesinin kaynağı

%100 uçuş benzeri görüntü

Metriği anlamlı kılar

Bozuk veya yinelenen etiket

0

Metrik kalitesinin sessizce kaybı

Öncelik de önemli: bizim için iyi etiketlenmiş 500 havadan kare, kötü etiketlenmiş 5000 web fotoğrafından daha değerli çıktı.

Etiketleme: sıkı kutular, yazılı kurallar

Ultralytics üç temel kuralı açıkça söylüyor: bir görüntüdeki her sınıfın her örneği etiketlenmeli, kutular nesneyi boşluk bırakmadan sarmalı ve hiçbir nesne etiketsiz kalmamalı. Kısmi etiketleme işe yaramaz, çünkü etiketlenmemiş bir nesne modele "burada hiçbir şey yok" der.

Etiket dosyası, görüntüyle aynı adı taşıyan bir .txt dosyasıdır; her satır bir nesnedir ve koordinatlar x_center y_center width height biçiminde 0-1 aralığına normalize edilir. Sınıf indeksleri sıfırdan başlar.

# labels/train/flight_042_0187.txt
0 0.412500 0.633333 0.031250 0.055556
1 0.784375 0.211111 0.048437 0

# labels/train/flight_042_0187.txt
0 0.412500 0.633333 0.031250 0.055556
1 0.784375 0.211111 0.048437 0

Zor olan biçim değil, tutarlılık. İki kişi aynı kareyi etiketleyip farklı kutular çizerse model gürültü öğrenir. Bu yüzden kararlarımızı tek bir yazılı kurallar sayfasında topladık:

Durum

Kuralımız

Gerekçe

Nesne kare kenarından kesiliyor

Görünen kısmı etiketle

Kenar tespitleri gerçek bir senaryo

Nesnenin %50'sinden fazlası kapalı

Etiketleme, kareyi arka plan olarak da sayma

Belirsiz bir sinyal üretir

Gölge dahil edilsin mi?

Hayır, yalnızca nesne

Gölgeler irtifayla değişir

Çok küçük nesne (< 8 px)

Etiketle, ama ayrı bir listede takip et

Metrikleri okuma biçimini bozar

Belirsiz kare

"review" klasörüne gider; kararı tek kişi verir

Tutarlılık > hız

Kurallar sayfası olmadan geçirdiğiniz her etiketleme haftası, sonradan bir haftalık yeniden etiketleme olarak geri döner.

Sınıf dengesi: 7:1 sizi sessizce cezalandırır

Sınıf oranımız kabaca 7:1'di. Böyle olduğunda genel mAP çoğunluk sınıfının performansına hâkim olur ve azınlık sınıfının kötü gittiğini hiç fark etmezsiniz. En basit teşhis: metrikleri her zaman sınıf bazında okuyun ve val çıktısındaki sınıf satırlarına bakın.

Yöntem

Ne zaman tercih edilir

Risk

Hedefli veri toplama

Vaktiniz varsa: en iyi çözüm

Yavaş; saha uçuşu gerektirir

Azınlık sınıfını fazla örnekleme (oversampling)

Daha fazla veri toplamak mümkün değilse

Aynı kareleri ezberleme

Sınıfları birleştirme

Görev bu ayrımı gerektirmiyorsa

Bir görev gereksinimini ihlal edebilir

Copy-paste augmentation

Nesneler küçük ve arka plan tekdüzeyse

Gerçekçi olmayan yerleşim

Biz önce hedefli toplamayı, ardından ölçülü bir oversampling'i tercih ettik; sınıfları birleştirmek görev gereksinimleri nedeniyle masada değildi.

Hard negative'ler: neden içinde hiçbir şey olmayan karelere ihtiyacınız var

Hard negative (zor negatif), modelin nesne sandığı ama içinde hiçbir hedef bulunmayan karedir: çimenlikteki bir çöp poşeti, çadıra benzeyen bir tente, insan siluetine benzeyen bir trafik levhası. Bu kareleri veri setine etiketsiz eklersiniz: arka plan görüntüleri etiket dosyası istemez.

Ultralytics, arka plan görüntülerinin veri setinin toplamının %0-10'u kadar olmasını öneriyor; karşılaştırma için, COCO yaklaşık 1000 arka plan karesi içerir ve bu da setin yaklaşık %1'idir. Bazı alanlarda daha yüksek oranlar yanlış pozitifleri gözle görülür biçimde azaltabilir, ama bu tamamen alana özgüdür ve yalnızca özenle seçilmiş karelerle işe yarar; körlemesine arka plan görüntüsü yığmak size recall kaybı olarak geri döner.

En değerli arka planlar rastgele toplanmaz; modelin kendi hatalarından çıkarılır. İş akışı:

from ultralytics import YOLO

model = YOLO("runs/detect/train/weights/best.pt")
# Scan flight recordings we know contain no targets
results = model.predict("flights/clean_pass/", conf=0.25, stream=True)

for r in results:
    if len(r.boxes):                     # there should be no detection here -> FP
        r.save(f"hard_negatives/{r.path.stem}.jpg")
# These frames go into the dataset WITHOUT a label file.
from ultralytics import YOLO

model = YOLO("runs/detect/train/weights/best.pt")
# Scan flight recordings we know contain no targets
results = model.predict("flights/clean_pass/", conf=0.25, stream=True)

for r in results:
    if len(r.boxes):                     # there should be no detection here -> FP
        r.save(f"hard_negatives/{r.path.stem}.jpg")
# These frames go into the dataset WITHOUT a label file.

Arka plan oranı

Beklenen etki

Ne zaman

0%

Yüksek FP, sahada gereksiz bırakmalar

Asla

1-5%

Dengeli: bizim başlangıç noktamız

Genel kullanım

5-10%

Daha düşük FP, ama recall'a dikkat

Yanlış pozitiflerin pahalıya patladığı durumlar

%10 üzeri

Kaçırılan tespit riski artar

Yalnızca ölçümle destekleniyorsa

Kilit nokta: bu oranı da ölçerek değiştirin. Arka plan eklemek doğrulama kümenizdeki FP sayısını düşürmüyorsa, eklediğiniz kareler baştan "zor" değildi.

Doğru bölmek: rastgele ayırma bir tuzaktır

Alışılmış tavsiye 70/20/10 ya da 70/15/15'tir (eğitim/doğrulama/test). Oranın kendisi ikincildir; asıl önemli olan, bölmenin birimi olarak neyi aldığınızdır.

Bir videodan çıkarılan ardışık kareler birbirinin neredeyse kopyasıdır. Bunları görüntü seviyesinde rastgele bölerseniz 187. kare eğitime, 188. kare doğrulamaya düşer. Model doğrulama karenizi zaten görmüştür; ölçtüğünüz şey genelleme değil, ezberdir. Literatür bunu videodan türetilen veri setlerindeki uzay-zamansal korelasyondan doğan bir leakage (sızıntı) olarak tanımlar ve bu, metrikleri sistematik biçimde şişirir.

Çözüm, kareleri değil grupları bölmektir: aynı uçuş, aynı video, aynı sahne tek bir tarafa gider.

from sklearn.model_selection import GroupShuffleSplit

# groups: the flight/video ID each image belongs to
gss = GroupShuffleSplit(n_splits=1, test_size=0.30, random_state=42)
train_idx, rest_idx = next(gss.split(images, groups=groups))
# rest_idx is later split into val/test with the same logic
from sklearn.model_selection import GroupShuffleSplit

# groups: the flight/video ID each image belongs to
gss = GroupShuffleSplit(n_splits=1, test_size=0.30, random_state=42)
train_idx, rest_idx = next(gss.split(images, groups=groups))
# rest_idx is later split into val/test with the same logic

Sınıf dengesizliğiniz de varsa, StratifiedGroupKFold grupları ayrık tutarken sınıf oranlarını korur.

Doğrulama kümesi, çalışacağınız ortamı temsil etmek zorundadır

Bir metrik, ancak ölçtüğü dağılım kadar anlamlıdır. Web fotoğraflarıyla dolu bir doğrulama kümesinde alınan yüksek skor, 81° HFOV'lu bir gimbal kameranın 100 metre irtifadan gördüğü sahne hakkında size hiçbir şey söylemez.

Doğrulama kümesini yalnızca havadan karelerden yeniden kurduğumuzda ölçtüğümüz değer mAP50 0.950 oldu. Bu sayıyı değerli kılan büyüklüğü değil, neyi ölçtüğünü biliyor olmamız: aynı model bir saha testinde yaklaşık 20 metre irtifadan iki hedefi tespit etti ve otonom bir bırakmayı tamamladı. Web ağırlıklı bir doğrulama kümesiyle bu bağlantıyı asla kuramazdık.

Pratik kural: doğrulama ve test kümeleri uçuş koşullarını temsil etmeli (irtifa aralığı, günün saati, zemin dokusu, kamera). Test kümesini kurduğunuz gün kilitleyin. Bir karar vermek için ona bir kez baktığınız an, o küme eğitim verisine dönüşmüş demektir.

Veri hijyeni: yinelenenler ve bozuk etiketler

Eğitim başlarken Ultralytics veri setini tarar ve ... images, ... backgrounds, ... corrupt biçiminde bir özet basar; ayrıca normalize edilmemiş ya da sınır dışı koordinatlar ve yinelenen etiketler konusunda sizi uyarır. O satırı atlamayın; arkasında sessizce elenmiş yüzlerce kare saklı olabilir.

Kontrol listemiz:

# 1) Images with no label / labels with no image
# 2) Out-of-bounds coordinates (0-1 range violations)
python - <<'PY'
from pathlib import Path
for f in Path("labels/train").glob("*.txt"):
    for i, line in enumerate(f.read_text().split("\n")):
        if not line.strip(): continue
        c, *v = line.split()
        if any(not (0.0 <= float(x) <= 1.0) for x in v):
            print("OUT OF BOUNDS:", f.name, "line", i + 1)
PY
# 1) Images with no label / labels with no image
# 2) Out-of-bounds coordinates (0-1 range violations)
python - <<'PY'
from pathlib import Path
for f in Path("labels/train").glob("*.txt"):
    for i, line in enumerate(f.read_text().split("\n")):
        if not line.strip(): continue
        c, *v = line.split()
        if any(not (0.0 <= float(x) <= 1.0) for x in v):
            print("OUT OF BOUNDS:", f.name, "line", i + 1)
PY

Bayt bazında birebir aynı kareler için bir dosya hash'i yeterlidir. Yakın yinelenenler için FiftyOne Brain'in compute_exact_duplicates() ve compute_near_duplicates() fonksiyonları işi kolaylaştırır. Bir sahnenin bir kopyası eğitimde, diğeri doğrulamada duruyorsa ölçümünüz inandırıcılığını kaybeder.

Sık düşülen tuzaklar

Tuzak

Belirti

Çözüm

Kare seviyesinde rastgele bölme

Val mAP çok yüksek, saha performansı kötü

Grup bazlı bölme

Kısmi etiketleme

Düşük recall, kararsız loss

Her örneği etiketle

Gevşek kutular

Konumlandırma sapması, düşük mAP50-95

Kutuyu nesneye oturt

Hiç arka plan görüntüsü yok

Boş arazide tespit üretir

%1-5 hard negative ekle

Temsil gücü olmayan val kümesi

Metrikler sahayla uyuşmuyor

Val kümesini uçuş görüntülerinden kur

Test kümesine sürekli göz atmak

Nihai sayı iyimser çıkar

Test kümesini kilitle

Bölmeden önce augmentation uygulamak

Aynı karenin varyantları iki tarafta birden

Önce böl, sonra augment et

Pratik özet

  1. Önce bölmenin birimini seçin, oranı sonra. Uçuş/videoya göre gruplayın; GroupShuffleSplit ya da StratifiedGroupKFold kullanın. 70/15/15 makul bir başlangıç noktasıdır.

  2. Doğrulama ve test kümelerini yalnızca uçuş görüntülerinden kurun. Web fotoğrafları eğitimde kalabilir; ölçümünüzde kalamaz.

  3. Etiketleme kurallarını tek bir sayfaya yazın ve iki kişinin aynı kareyi etiketleyip sonucu karşılaştırmasıyla doğrulayın. Kutular nesneyi boşluk bırakmadan sarmalı.

  4. Toplamın %1-5'i oranında hard negative ekleyin, bunları modelin kendi yanlış pozitiflerinden çıkarın ve oranı yalnızca ölçüme dayanarak yükseltin (tavan yaklaşık %10).

  5. Her eğitim koşusundan önce bir temizlik turu atın: yineleme hash kontrolü, sınır dışı koordinat taraması ve Ultralytics tarama özetinde corrupt sayısının sıfır olması.

Kaynaklar: Ultralytics: En İyi Eğitim Sonuçları İçin İpuçları, Ultralytics: Tespit Veri Setleri, Ultralytics: Veri Toplama ve Etiketleme, Ultralytics: Etiketli Veriyi Ön İşleme, scikit-learn: Çapraz Doğrulama, FiftyOne Brain

©2026 ATILIM UAV TEAM

©2026 ATILIM UAV TEAM