Etiketleme Kalitesi
Bir YOLO etiketinin neler içerdiğini öğren, bozuk satırlar için etiket dosyalarını lint işleminden geçir ve bir yönergeyle ölçümlü bir kalibrasyon turu kullanarak etiketleyicilerin tutarlı çalışmasını sağla.
Tutarsız etiketlerle eğitilen model tutarsızlığı öğrenir. Tek ve açık bir kurala göre çizilmiş elli bin kutu, kişiye özel yirmi farklı yoruma göre çizilmiş yarım milyon kutudan daha iyidir; etiketleri yeniden oluşturmak neredeyse her zaman daha büyük bir modelden ucuza gelir. Bu derste bir etiket linter'ı, tek sayfalık bir yönerge ve etiketleyiciler arası uyum için bir ölçüm elde edeceksin.
Uygulamalı
Etiket aslında nedir?#
YOLO biçiminde her görüntünün, nesne başına bir satır içeren eşleşen bir .txt dosyası vardır. Koordinatlar görüntünün genişlik ve yüksekliğine göre 0–1 aralığında normalize edilir; böylece etiketler yeniden boyutlandırma sonrasında da geçerliliğini korur. Satır düzeni göreve bağlıdır:
| Görev | Bir etiket | Notlar |
|---|---|---|
| Algılama | class x_center y_center width height | Kutu merkezi ve boyutu |
| Örnek segmentasyonu | class x1 y1 x2 y2 ... xn yn | Çokgen dış çizgisi |
| Poz | class x y w h px1 py1 v1 ... pxn pyn vn | Kutu, ardından isteğe bağlı görünürlük bilgisiyle kilit noktaları |
| OBB | class x1 y1 x2 y2 x3 y3 x4 y4 | Döndürülmüş kutunun dört köşesi |
| Sınıflandırma | Etiket dosyası yok | Görüntüler her sınıf için bir klasöre ayrılmış |
| Anlamsal segmentasyon | Görüntü başına bir PNG maskesi | Her pikselin değeri bir sınıf indeksidir |
| Derinlik tahmini | Görüntü başına bir derinlik PNG'si veya NPY dosyası | Her pikselin değeri bir mesafedir |
Sınıf indeksleri 0'dan başlar ve veri kümesi YAML dosyasındaki adlarla eşleşir. Nesne içermeyen bir görüntünün etiket dosyasına ihtiyacı yoktur; arka plan görüntüleri bu şekilde çalışır. Nesne algılama biçimi, anlamsal ve derinlik veri kümesi sayfalarında çalışan örnekler bulunur.
Etiket dosyalarını lint işleminden geçir#
Bozuk satırlar sessizce sorun çıkarır: 1'den büyük bir koordinat, YAML dosyasında tanımlanmamış bir sınıf kimliği veya aynı nesnenin iki kez kutulanması, modelin öğrenmeye çalıştığı gürültüye dönüşür. Kısa bir linter bunları eğitimden önce yakalar:
from itertools import combinations
from pathlib import Path
from ultralytics.data.utils import check_det_dataset
def iou(a, b):
"""IoU of two normalized (x_center, y_center, width, height) boxes."""
ax1, ay1, ax2, ay2 = a[0] - a[2] / 2, a[1] - a[3] / 2, a[0] + a[2] / 2, a[1] + a[3] / 2
bx1, by1, bx2, by2 = b[0] - b[2] / 2, b[1] - b[3] / 2, b[0] + b[2] / 2, b[1] + b[3] / 2
inter = max(0, min(ax2, bx2) - max(ax1, bx1)) * max(0, min(ay2, by2) - max(ay1, by1))
return inter / (a[2] * a[3] + b[2] * b[3] - inter)
def check_labels(text, names):
"""Return a list of problems in one YOLO detection label file."""
problems, boxes = [], []
for i, row in enumerate(text.split("\n"), 1):
values = row.split()
if not values:
continue
if len(values) != 5:
problems.append(f"row {i}: {len(values)} values, expected 5")
elif int(values[0]) not in names:
problems.append(f"row {i}: unknown class {values[0]}")
elif not all(0 <= float(v) <= 1 for v in values[1:]):
problems.append(f"row {i}: coordinates outside 0-1")
else:
boxes.append((i, int(values[0]), [float(v) for v in values[1:]]))
for (i, ca, a), (j, cb, b) in combinations(boxes, 2):
if ca == cb and iou(a, b) > 0.8:
problems.append(f"row {j}: duplicate of row {i} (IoU {iou(a, b):.2f})")
return problems
broken = "0 0.50 0.50 0.20 0.30\n0 0.51 0.50 0.20 0.30\n3 0.40 0.40 0.10 0.10\n0 1.20 0.50 0.10 0.10\n0 0.50 0.50 0.10"
print("\n".join(check_labels(broken, {0: "pill"})))
data = check_det_dataset("medical-pills.yaml")
files = sorted(Path(data["path"], "labels").rglob("*.txt"))
found = sum(len(check_labels(f.read_text(), data["names"])) for f in files)
print(f"medical-pills: {found} problems in {len(files)} label files")row 3: unknown class 3
row 4: coordinates outside 0-1
row 5: 4 values, expected 5
row 2: duplicate of row 1 (IoU 0.90)
medical-pills: 0 problems in 115 label filesElle oluşturulan örnek, linter'ın yakaladığı her hatayı gösterir; gerçek veri kümesinde ise sorun yoktur. Her etiketleme grubundan sonra ve her eğitim çalıştırmasından önce linter'ı çalıştır; işlem saniyeler sürer.
Etiketler neden kayar?#

Bu düzenleyicideki sınıf paneline bak: 14 koli çokgeni Grapes altında kaydedilmiş, listede daha aşağıda Box sınıfı yer alıyor; ayrıca plane ve Plane iki ayrı sınıf olarak mevcut. Her satır geçerli olduğundan linter bu sorunların hiçbirini yakalayamaz; ancak etiketleri yönergeyle karşılaştıran bir inceleyen ikisini de saniyeler içinde fark eder. Etiket hatalarının çoğu böyledir: dosyalar geçerlidir, kararlar yanlıştır.
Etiketleyiciler dikkatsiz oldukları için nadiren kötü etiketleme yapar. Kurallar belirsiz olduğu ve herkes makul ama farklı bir tahminde bulunduğu için tutarsız etiketleme yaparlar:
- Kutu nerede bitiyor? Gölgede mi, taşınan çantada mı, forklift çatallarındaki yükte mi?
- Kırpılma. Kadrajın yarısının dışında kalan bir kişi hâlâ
personolarak etiketlenir mi? Görünen kısmı ne kadar olmalı? - Örtülme. Yalnızca bir tekerleği görünen otomobil etiketlenir mi, atlanır mı?
- Sınıf sınırları. Bir pikap
carmı, yoksatruckmi? Bir scooterbicyclemi, yoksaothermü? - Etiketlenmemesi gereken durumlar. Nesneyi gösteren yansımalar, posterler ve ekranlar.
Herkes aynı tercihi yaptığı sürece her seçim uygundur.
Tek sayfalık bir yönerge#
Tek sayfaya sığar, her kural için örnek bir görüntü içerir ve önce sınır durumlarını açıklar:
## Class: forklift
Include:
- Forklifts with at least 30% of the body visible, moving or parked, any size or color.
Do not label:
- Pallet jacks (no overhead mast) → class `pallet_jack`.
- Reflections in glass or polished floors, and forklifts on posters or screens.
Box rules:
- Include the mast and the full visible body.
- Exclude the load (pallets, boxes) — those have their own classes.
- For partly occluded forklifts, box the visible part only.Kalibrasyon turunda uyumu ölç#
Bir etiketleyiciden yirmi etiketleyiciye geçmeden önce herkes aynı 50 görüntüyü etiketler ve sen sonuçları karşılaştırırsın:
Uyum, her iki etiketleyicinin de aynı sınıfla çizdiği ve IoU değeri en az 0.5 olan nesnelerin oranıdır; bu, değerlendiriciler arası güvenilirliğin pratik karşılığıdır:
def iou(a, b):
"""IoU of two (x1, y1, x2, y2) pixel boxes."""
w = max(0, min(a[2], b[2]) - max(a[0], b[0]))
h = max(0, min(a[3], b[3]) - max(a[1], b[1]))
inter = w * h
return inter / ((a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter)
def agreement(boxes_a, boxes_b, threshold=0.5):
"""Share of objects both annotators drew with the same class and IoU >= threshold."""
unmatched, matched = list(boxes_b), 0
for cls_a, box_a in boxes_a:
best = max(unmatched, key=lambda b: iou(box_a, b[1]) if b[0] == cls_a else -1, default=None)
if best and best[0] == cls_a and iou(box_a, best[1]) >= threshold:
unmatched.remove(best)
matched += 1
return matched / max(len(boxes_a), len(boxes_b))
# Two annotators label the same calibration image
alice = [("forklift", (100, 200, 400, 600)), ("person", (450, 220, 520, 480)), ("person", (600, 230, 660, 470))]
bob = [("forklift", (100, 120, 400, 600)), ("person", (452, 218, 522, 478)), ("pallet_jack", (600, 230, 660, 470))]
print(f"agreement: {agreement(alice, bob):.0%}")agreement: 67%Bu %67'nin içinde iki tür anlaşmazlık gizli. Bob'un forklift kutusu 80 piksel daha yukarıdan başlıyor; direğin ucunu da dahil etmiş, ancak yine de IoU 0.83 olduğundan eşleşme sayılıyor. Ürünün yüksekliği ölçmesi gerekiyorsa kutu kuralını daralt. Üçüncü nesnede gerçek bir anlaşmazlık var: Alice person, Bob ise pallet_jack diyor. Bunun için daha açık bir kural ve örnek bir görüntü gerekiyor; ardından bir tur daha yap. 50 kalibrasyon görüntüsünü sakla: bunlar her yeni etiketleyici için işe alıştırma testine dönüşür.
Etiketleri sessizce düzelten bir incelemeci geri bildirim döngüsünü ortadan kaldırır ve etiketleyiciler neyi yanlış yaptıklarını asla öğrenemez. Bir gerekçeyle reddet ve düzeltmeyi etiketleyiciye bırak. İlk yüz görüntü için daha yavaş, sonrasında çok daha hızlıdır.
Neleri izleyeceksin?#
| Ölçüt | Sana ne anlatır? |
|---|---|
| Üretkenlik (saat başına nesne) | Kapasite ve maliyet |
| Ret oranı | Kalitenin düşüp düşmediği |
| Ortak bir örtüşme kümesindeki uyum | Etiketleyicilerin hâlâ aynı kuralları uygulayıp uygulamadığı |
Uyum düştüğünde bir şey değişmiştir: yeni bir etiketleyici, yeni bir kamera, belirsiz bir etiketleme grubu veya yorgunluk. Model daha sonra belirli bir koşulda başarısız olduğunda, tam da o görüntüleri etiketleme sürecine geri gönder; aktif öğrenmenin otomatikleştirdiği döngü budur ve gerçek dünya doğruluk kazanımlarının çoğu bu şekilde elde edilir. Ultralytics Platform'daki veri kümelerinde hatalara göz atıp bunları filtrele; bu süreci büyük ölçekte yürütmek için Veri Kümesi Kalite Kontrolü ve Etiketleme için En İyi Uygulamalar bölümlerine bak.
Çözümü göster
10 görüntüdeki kalibrasyon turundan sonra yazılmış, medical-pills pill sınıfı için bir yönerge:
## Class: pill
Include:
- Every tablet inside a blister cavity, including half-visible ones at the image edge.
- Tablets seen through clear plastic, even when glare covers part of them.
Do not label:
- Empty cavities, torn foil, or tablets outside the pack.
- Reflections of tablets in the foil.
Box rules:
- Box the tablet itself, not the plastic bubble around it.
- If two tablets touch, draw two boxes.Uyum puanı şöyle yorumlanır: İlk turun puanının %82 olduğunu varsayalım. Eşleşmeyen nesneleri nedenlerine göre sırala. Blister ambalajlarda sık görülen iki neden şunlardır: tablet yerine plastik kabarcığın çevresine kutu çizilmesi (ilk kutu kuralına bir örnek görüntü eklenerek çözülür) ve bir etiketleyicinin parlamayla kapanan tabletleri atlaması (ikinci dahil etme kuralıyla çözülür). 10 yeni görüntüde tekrar dene; yalnızca puan %90 eşiğini geçtiğinde ölçeği büyüt.