Ultralytics YOLO27: Inscreve-te na lista de espera.
Get Started
Aulainiciante

Controlo de qualidade do conjunto de dados

Audita os rótulos, o equilíbrio entre classes, as duplicatas e a cobertura das divisões com um script; pede a um modelo que assinale possíveis rótulos em falta e corrige o que encontrares antes do treino.

Corrigir rótulos costuma aumentar a precisão mais do que ajustar hiperparâmetros. As equipas passam muitas vezes semanas a ajustar configurações para ganhar um ou dois pontos de mAP, mas depois descobrem, numa tarde de auditoria, que parte de uma classe estava mal rotulada desde o início. O controlo de qualidade deteta o que o guia de anotação não identificou antes que uma execução de treino transforme erros nos rótulos num número em que ninguém deve confiar.

Resultado

Executa uma auditoria automática dos rótulos e uma revisão de casos suspeitos assistida por um modelo, corrige o que for encontrado e regista o resultado em notas breves de CQ.

Via rápida
Se já sabes orientar-te, aqui está a versão resumida.
  1. Automatiza as contagens: instâncias por classe, proporção de imagens de fundo, classes inválidas, duplicatas e classes ausentes do treino.

  2. Pede a um modelo pré-treinado que assinale deteções confiáveis sem um rótulo correspondente e revê-as visualmente.

  3. Corrige o desequilíbrio entre classes pela ordem certa: primeiro recolhe dados, depois sobreamostra e, por último, pondera a função de perda.

  4. Regista o que verificaste e corrigiste.

Prático

As sete verificações#

Executa as sete verificações antes do primeiro treino e novamente após cada lote importante de anotações. O guia de pré-processamento de dados anotados explica várias delas em detalhe.

#VerificaçãoO que detetaComo detetá-lo
1Rótulos em faltaObjetos ignorados por quem anotouRevisão de casos suspeitos abaixo; amostra visual
2Classes erradas ou inválidasClasses trocadas, IDs fora da lista de classesScript de auditoria; matriz de confusão após a primeira execução
3Caixas imprecisasCaixas com margens excessivas ou bordas cortadasAmostra visual; sinalizações de caixas minúsculas pela auditoria
4DuplicatasCópias exatas e fotogramas quase idênticosScript de auditoria para cópias exatas; vista Clustering da Ultralytics Platform, colorida por divisão, para quase duplicatas
5Desequilíbrio entre classesClasses muito abaixo das metas da especificaçãoScript de auditoria; separador Charts da Platform
6Cobertura das divisões e fuga de dadosClasses ausentes de uma divisão; um clipe em duas divisõesScript de auditoria; divisões agrupadas (próxima lição)
7Proporção de imagens de fundoPoucas ou demasiadas imagens vaziasScript de auditoria; objetivo de 0–10%

Automatiza as contagens#

As verificações 2, 4, 5, 6 e 7 envolvem contagens, por isso automatiza-as com um script. Esta auditoria lê um YAML de dados de deteção cujos valores train: e val: apontam para pastas de imagens. Conta as instâncias por classe e as imagens de fundo em cada divisão, assinala IDs de classe inválidos, caixas minúsculas, linhas de rótulos duplicadas e imagens idênticas byte a byte, e lista as classes que aparecem em val, mas nunca em train:

import hashlib
from collections import Counter
from pathlib import Path

from ultralytics.data.utils import IMG_FORMATS, check_det_dataset, img2label_paths

data = check_det_dataset("coco8.yaml")  # downloads COCO8 on first run; swap in your data YAML
names, seen = data["names"], {}

for split in ("train", "val"):
    images = sorted(p for p in Path(data[split]).rglob("*.*") if p.suffix[1:].lower() in IMG_FORMATS)
    instances, hashes, problems, background = Counter(), Counter(), [], 0
    for img, label in zip(images, img2label_paths([str(p) for p in images])):
        hashes[hashlib.md5(img.read_bytes()).hexdigest()] += 1
        rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
        background += not rows
        if len(rows) != len({tuple(r) for r in rows}):
            problems.append(f"{img.name}: duplicate label rows")
        for r in rows:
            cls, w, h = int(r[0]), float(r[3]), float(r[4])
            if cls not in names:
                problems.append(f"{img.name}: class {cls} is not in names")
            elif w * h < 1e-4:
                problems.append(f"{img.name}: tiny {names[cls]} box")
            instances[cls] += 1
    seen[split] = set(instances)
    duplicates = sum(n - 1 for n in hashes.values())
    print(f"{split}: {len(images)} images, {background} background, {duplicates} duplicate images")
    for cls, n in instances.most_common():
        print(f"  {names.get(cls, cls):>12}  {n}")
    for problem in problems:
        print("  !", problem)

print("in val but never in train:", sorted(names[c] for c in seen["val"] - seen["train"] if c in names))
train: 4 images, 0 background, 0 duplicate images
        orange  4
          bowl  3
       giraffe  2
...
val: 4 images, 0 background, 0 duplicate images
        person  10
         horse  2
...
in val but never in train: ['dog', 'elephant', 'horse', 'person', 'umbrella']

A última linha mostra o resultado. person tem 10 instâncias em val e nenhuma em train, pelo que val avalia uma classe com a qual o modelo nunca foi treinado. COCO8 é um conjunto de teste rápido com 8 imagens, por isso é esperado nesse caso; no teu conjunto de dados, significa que deves corrigir a divisão ou recolher mais dados antes do treino. Quando há problemas, estes aparecem sob a respetiva divisão como ! 000000000025.jpg: class 99 is not in names ou ! 000000000009.jpg: duplicate label rows.

Pede a um modelo que assinale rótulos suspeitos#

Os rótulos em falta são o erro mais prejudicial e o mais difícil de detetar percorrendo as imagens. Um modelo pré-treinado pode assinalá-los: uma deteção confiável sem nenhum rótulo nas proximidades pode indicar um rótulo em falta ou um erro do modelo, e vale a pena analisar qualquer uma das hipóteses. Este exemplo é executado em COCO128:

from pathlib import Path

import torch

from ultralytics import YOLO
from ultralytics.data.utils import check_det_dataset, img2label_paths
from ultralytics.utils.metrics import box_iou
from ultralytics.utils.ops import xywh2xyxy

data = check_det_dataset("coco128.yaml")  # swap in your data YAML
model = YOLO("yolo26n.pt")  # a pretrained model, or your own first fine-tune
images = sorted(Path(data["train"]).glob("*.jpg"))

suspects = []
for result, label in zip(model(images, conf=0.6, stream=True, verbose=False), img2label_paths(map(str, images))):
    rows = [r.split() for r in Path(label).read_text().splitlines() if r.strip()] if Path(label).exists() else []
    boxes = xywh2xyxy(torch.tensor([[float(v) for v in r[1:5]] for r in rows]).reshape(-1, 4))
    iou = box_iou(result.boxes.xyxyn, boxes)  # model boxes vs labeled boxes
    for j, (cls, conf) in enumerate(zip(result.boxes.cls.int().tolist(), result.boxes.conf.tolist())):
        if not rows or iou[j].max() < 0.3:  # confident detection with no label anywhere near it
            suspects.append((conf, Path(result.path).name, model.names[cls]))

print(f"{len(suspects)} confident detections with no matching label; top 5:")
for conf, name, cls in sorted(suspects, reverse=True)[:5]:
    print(f"  {conf:.2f}  {cls:<12} {name}")
4 confident detections with no matching label; top 5:
  0.77  donut        000000000438.jpg
  0.75  person       000000000531.jpg
  0.71  donut        000000000438.jpg
  0.66  bottle       000000000436.jpg

Das 128 imagens, o script reduz a revisão a três. Ao abri-las, encontras os dois tipos de resultado:

  • 000000000438.jpg mostra uma caixa com uma dúzia de donuts, em que duas deteções confiáveis não se sobrepõem a nenhum donut rotulado: possíveis rótulos em falta ou mal posicionados.
  • 000000000531.jpg assinala uma «pessoa» que é a sombra do fotógrafo num campo de ténis. O rótulo está correto e o modelo está errado, precisamente por isso uma pessoa deve rever cada caso suspeito em vez de o adicionar automaticamente.

Reduz conf para ampliar a procura e, quando tiveres o teu primeiro ajuste fino, substitui-o por esse modelo, pois conhecerá as tuas classes.

Lê os gráficos do conjunto de dados#

Separador Charts da Ultralytics Platform para o conjunto de dados medical-pills: Split Distribution mostra 80 imagens de treino e 20 de validação, e Top Classes mostra pill com 1,755 anotações e tablet com 0

Na Ultralytics Platform, o separador Charts do conjunto de dados apresenta gráficos da distribuição das divisões, das contagens de classes e das estatísticas das caixas (documentação dos conjuntos de dados). Esta captura de ecrã já revela um problema de controlo de qualidade: a lista contém duas classes, mas tablet tem 0 de 1,755 anotações. Não é possível aprender uma classe sem rótulos. Rotula-a ou remove-a da lista de classes antes do treino.

Corrige o desequilíbrio entre classes pela ordem certa#

   forklift  ████████████████████████ 8000 instances
   person    ████████████████ 5400
   pallet    ██ 700                              ← red flag

Uma diferença como esta produz um modelo confiante com empilhadeiras, mas que não deteta paletes. Há três soluções, por esta ordem:

  1. Recolhe e rotula mais exemplos da classe minoritária. É sempre a melhor opção: procura obter ≥ 1,500 imagens e ≥ 10,000 instâncias de cada classe.
  2. Sobreamostra-a apenas em train. Inclui mais do que uma vez, na lista de ficheiros de treino, as imagens que contêm a classe minoritária, para que apareçam com maior frequência em cada época. Não incluas as cópias em val nem em test.
  3. Pondera a função de perda. O argumento de treino cls_pw (0.0 por predefinição, até 1.0 para ponderação inversa à frequência completa) aumenta o peso da perda de classificação para as classes raras. Recorre a esta opção apenas depois das opções 1 e 2.
Nunca exclua imagens difíceis para aumentar a pontuação

Remover imagens desfocadas, cheias ou confusas do conjunto de validação aumenta o mAP sem melhorar o modelo, apenas o torna menos honesto. Se a imagem for um quadro real de implantação, mantenha-a; corrija os rótulos. Remova apenas verdadeiros casos atípicos (erro 9) e duplicatas exatas.

Regista as notas de CQ#

Bastam algumas linhas e permitem responder «o que verificaste?» quando um cliente ou auditor perguntar meses depois:

Dataset: forklift-v1   Audit date: 2026-05-09
Images: 4,820   Labels: 18,442   Background: 380 (7.9%)
Audit script: 3 invalid class IDs fixed, 41 duplicate label rows removed
Suspect review: 62 flagged, 19 missing labels added, 43 model errors ignored
Visual sample: 120 images, 4 mislabeled (3.3%), fixed
Split check: grouped by camera-day, every class in every split
Imbalance: pallet 2.5x below person; collecting more next round
Ready to train: yes
Experimente

Aponta qc_audit.py e suspect_labels.py ao teu próprio YAML de dados (ou a COCO8 e COCO128) e executa-os. Corrige todos os problemas reportados, abre cada imagem suspeita e decide se se trata de um erro de rótulo ou do modelo; depois, executa novamente os dois scripts. Entregável: qc-notes.md com as contagens antes e depois das correções.

Concluído quando
Você concluiu a aula quando todos estes critérios forem atendidos.
  • O script de auditoria não reporta classes inválidas, linhas de rótulos duplicadas nem classes ausentes do train.

  • Todas as imagens suspeitas assinaladas pela revisão assistida pelo modelo foram abertas e resolvidas como correção de rótulo ou erro do modelo.

  • As notas de CQ registam o que foi verificado, o que foi encontrado e o que foi corrigido.

Mostrar solução

Notas de CQ das duas execuções de demonstração desta lição:

Dataset: COCO8 + COCO128 demo   Audit date: 2026-05-09
qc_audit.py (COCO8): 0 invalid classes, 0 duplicate rows, 0 duplicate images
  FINDING: dog, elephant, horse, person, umbrella appear in val only
  ACTION: re-split by group or collect these classes into train before training
suspect_labels.py (COCO128, conf 0.6): 4 suspects in 3 images
  000000000438.jpg: 2 donuts without matching labels -> relabel the donut box
  000000000531.jpg: "person" is the photographer's shadow -> model error, label kept
  000000000436.jpg: bottle -> open and decide
Ready to train: no, fix the val-only classes first

Repara na última linha. Um conjunto de dados com uma classe que aparece apenas em val não passa no controlo de qualidade, mesmo que todos os rótulos individuais estejam corretos, porque a métrica dessa classe avalia algo que o modelo nunca teve oportunidade de aprender.

Próximos passos

Com os rótulos limpos, a próxima lição divide o conjunto de dados para que as pontuações de validação reflitam a produção, não a memorização.

Entre para salvar seu progresso