Ultralytics YOLO27: Inscreve-te na lista de espera.
Get Started
Fundamentos de visão computacional·Conjuntos de dados e rótulos·Aula 7/10
Aulainiciante

Qualidade das anotações

Descobre o que contém um rótulo YOLO, verifica os ficheiros de rótulos em busca de linhas inválidas e mantém a consistência entre anotadores com uma diretriz e uma ronda de calibração medida.

Um modelo treinado com rótulos inconsistentes aprende essa inconsistência. Cinquenta mil caixas desenhadas segundo uma regra clara superam meio milhão desenhadas com vinte interpretações pessoais, e quase sempre é mais barato voltar a rotular do que usar um modelo maior. Nesta lição, vais criar um verificador de rótulos, uma diretriz de uma página e uma medida de concordância entre anotadores.

Resultado

Verifica os ficheiros de rótulos de um conjunto de dados, escreve uma diretriz de uma página para uma classe e mede a concordância entre dois anotadores.

Via rápida
Se já sabes orientar-te, aqui está a versão resumida.
  1. Conhece a linha de rótulo da tua tarefa: caixa, polígono, pontos-chave ou cantos.

  2. Verifica cada ficheiro de rótulos: número de colunas, IDs de classe, coordenadas de 0 a 1 e caixas duplicadas.

  3. Diretriz de uma página, com uma imagem de exemplo para cada regra e uma lista de elementos a não rotular.

  4. Começa com uma ronda de calibração: todos rotulam as mesmas 50 imagens; só aumenta a escala quando a concordância atingir o limite.

  5. Os revisores rejeitam com uma justificação; nunca corrigem sem avisar.

Prático

O que é realmente um rótulo#

No formato YOLO, cada imagem tem um ficheiro .txt correspondente, com uma linha por objeto. As coordenadas são normalizadas de 0 a 1 pela largura e altura da imagem, para que os rótulos continuem válidos após o redimensionamento. A estrutura da linha depende da tarefa:

TarefaUm rótuloNotas
Detecçãoclass x_center y_center width heightCentro e tamanho da caixa
Segmentação de instânciasclass x1 y1 x2 y2 ... xn ynContorno do polígono
Poseclass x y w h px1 py1 v1 ... pxn pyn vnCaixa, seguida dos pontos-chave com visibilidade opcional
OBBclass x1 y1 x2 y2 x3 y3 x4 y4Quatro cantos da caixa rodada
ClassificaçãoSem ficheiro de rótulosImagens organizadas numa pasta por classe
Segmentação semânticaUma máscara PNG por imagemCada valor de píxel é um índice de classe
Estimativa de profundidadeUm PNG ou NPY de profundidade por imagemCada valor de píxel é uma distância

Os índices de classe começam em 0 e correspondem aos nomes no YAML do conjunto de dados. Uma imagem sem objetos não precisa de ficheiro de rótulos — é assim que funcionam as imagens de fundo. As páginas de conjuntos de dados de formato de deteção, segmentação semântica e profundidade incluem exemplos práticos.

Verifica os teus ficheiros de rótulos#

Linhas inválidas passam despercebidas: uma coordenada superior a 1, um ID de classe não definido no YAML ou o mesmo objeto delimitado duas vezes tornam-se ruído que o modelo tenta aprender. Um verificador simples deteta esses problemas antes do treino:

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 files

O exemplo criado manualmente mostra cada erro que o verificador deteta; o conjunto de dados real passa sem problemas. Executa-o depois de cada lote de anotações e antes de cada treino — demora apenas alguns segundos.

Porque é que os rótulos divergem#

Editor de anotações da Ultralytics Platform em ecrã inteiro: caixas de cartão em robôs de armazém delimitadas por polígonos, com a lista de classes aberta ao lado

Observa o painel de classes neste editor: os 14 polígonos das caixas de cartão estão guardados em Grapes, enquanto uma classe Box aparece mais abaixo na lista — e plane e Plane existem como duas classes distintas. Um verificador não deteta nenhum destes problemas, porque todas as linhas são válidas, mas um revisor que compare os rótulos com a diretriz identificaria ambos em segundos. A maioria dos erros de rotulagem é assim: ficheiros válidos, decisões erradas.

Raramente os anotadores rotulam mal por falta de cuidado. Rotulam de forma inconsistente porque as regras são ambíguas e cada um faz uma suposição razoável, mas diferente:

  • Onde deve terminar a caixa? Na sombra, num saco transportado, na carga dos garfos de um empilhador?
  • Truncamento. Uma pessoa que aparece parcialmente fora do enquadramento continua a ser person? A partir de que fração visível?
  • Oclusão. Um carro visível apenas por uma roda deve ser rotulado ou ignorado?
  • Limites entre classes. Uma carrinha de caixa aberta é car ou truck? Uma scooter é bicycle ou other?
  • Casos a não rotular. Reflexos, cartazes e ecrãs que mostram o objeto.

Cada escolha é válida, desde que todos façam a mesma.

Uma diretriz de uma página#

Cabe numa página, inclui uma imagem de exemplo para cada regra e apresenta primeiro os casos-limite:

## 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.

Mede a concordância numa ronda de calibração#

Antes de passar de um para vinte anotadores, todos rotulam as mesmas 50 imagens e tu comparas os resultados:

A concordância é a proporção de objetos que ambos os anotadores desenharam com a mesma classe e um IoU de, pelo menos, 0.5 — a versão prática da fiabilidade entre avaliadores:

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%

Há dois tipos de divergência escondidos nesses 67%. A caixa do empilhador do Bob começa 80 píxeis mais acima — ele incluiu a ponta do mastro —, mas ainda corresponde com IoU 0.83, por isso conta como concordância; aperfeiçoa a regra das caixas se o teu produto medir a altura. O terceiro objeto é um conflito real: a Alice diz person, o Bob diz pallet_jack. Isso exige uma regra mais clara e uma imagem de exemplo, seguidas de outra ronda. Guarda as 50 imagens de calibração: tornam-se o teste de integração para cada novo anotador.

Revisores rejeitam; não corrigem em silêncio

Um revisor que corrige rótulos discretamente elimina o ciclo de feedback, e os anotadores nunca aprendem o que fizeram de errado. Rejeite com um motivo e deixe o anotador corrigir. É mais lento nas primeiras cem imagens, mas muito mais rápido depois.

O que acompanhar#

MétricaO que indica
Ritmo (objetos por hora)Capacidade e custo
Taxa de rejeiçãoSe a qualidade está a piorar
Concordância num conjunto partilhado de sobreposiçõesSe os anotadores continuam a aplicar as mesmas regras

Quando a concordância diminui, algo mudou: um novo anotador, uma nova câmara, um lote ambíguo ou fadiga. Quando o modelo falhar mais tarde numa condição específica, envia essas mesmas imagens de volta para serem rotuladas — é o ciclo que a aprendizagem ativa automatiza e de onde vêm a maioria dos ganhos de precisão no mundo real. Explora e filtra as falhas nos teus conjuntos de dados na Ultralytics Platform e consulta Controlo de qualidade de conjuntos de dados e Práticas recomendadas para anotações para executar este processo em grande escala.

Experimente

Executa o verificador na pasta de rótulos do teu conjunto de dados. Depois, escreve uma diretriz de uma página para a tua classe mais ambígua, com uma imagem de exemplo para cada regra e uma lista de elementos a não rotular. Entrega-a, juntamente com 10 imagens sem rótulos, a um colega de equipa sem explicar nada; rotula as mesmas imagens tu próprio e calcula a concordância com o script. Regista a pontuação e todas as regras que alteraste devido a divergências.

Concluído quando
Você concluiu a aula quando todos estes critérios forem atendidos.
  • O verificador não encontra problemas nos teus rótulos ou já corrigiste todos os que encontrou.

  • Tens uma diretriz de uma página com um exemplo para cada regra e uma lista de elementos a não rotular.

  • Tens uma pontuação de concordância entre dois anotadores e uma lista das regras que alteraste por causa dessa pontuação.

Mostrar solução

Uma diretriz para a classe pill de medical-pills, escrita após uma ronda de calibração com 10 imagens:

## 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.

Como interpretar a pontuação de concordância: imagina que a primeira ronda obtém 82%. Ordena os objetos sem correspondência por causa. Nos blisters, as duas causas mais comuns são caixas desenhadas à volta da bolha de plástico em vez do comprimido (corrigido pela primeira regra sobre caixas, com uma imagem de exemplo) e comprimidos cobertos por reflexos que um anotador ignorou (corrigido pela segunda regra de inclusão). Repete o processo com 10 imagens novas; só aumenta a escala quando a pontuação ultrapassar o limite de 90%.

Próximos passos

A seguir: dividir os dados rotulados para que as métricas sejam fiáveis — começando por uma fuga de dados escondida neste mesmo conjunto de dados.

Entre para salvar seu progresso