Ultralytics YOLO27:
Get Started

Validação cruzada K-Fold com Ultralytics#

Introdução#

Este guia completo ilustra a implementação da validação cruzada K-Fold em conjuntos de dados de deteção de objetos no ecossistema Ultralytics. Vamos usar o formato de deteção YOLO e bibliotecas Python importantes, como sklearn, pandas e PyYAML, para orientar-te pela configuração necessária, pelo processo de geração de vetores de características e pela execução de uma divisão K-Fold do conjunto de dados.

K-fold cross validation data splitting

Quer o teu projeto envolva o conjunto de dados Fruit Detection ou uma fonte de dados personalizada, este tutorial pretende ajudar-te a compreender e aplicar a validação cruzada K-Fold para reforçar a fiabilidade e a robustez dos teus modelos de aprendizagem automática. Embora usemos k=5 folds neste tutorial, lembra-te de que o número ideal de folds pode variar consoante o teu conjunto de dados e as características específicas do teu projeto. A validação cruzada K-Fold é mais útil quando o conjunto de dados é pequeno, ruidoso ou muito variável; para conjuntos de dados grandes e diversificados, normalmente basta uma divisão train/val/test bem concebida.

Vamos começar.

Configuração#

  • As tuas anotações devem estar no formato de deteção YOLO.

  • Este guia pressupõe que os ficheiros de anotação estão disponíveis localmente.

  • Para a nossa demonstração, usamos o conjunto de dados Fruit Detection.

    • Este conjunto de dados contém um total de 8479 imagens.
    • Inclui 6 rótulos de classe, cujas contagens totais de instâncias são apresentadas abaixo.
Rótulo da classeContagem de instâncias
Maçã7049
Uvas7202
Ananás1613
Laranja15549
Banana3536
Melancia1976
  • Os pacotes Python necessários incluem:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Este tutorial usa k=5 folds. No entanto, deves determinar o melhor número de folds para o teu conjunto de dados específico.

  1. Cria um novo ambiente virtual Python (venv) para o teu projeto e ativa-o. Usa pip (ou o gestor de pacotes da tua preferência) para instalar:

    • A biblioteca Ultralytics: pip install -U ultralytics. Em alternativa, podes clonar o repositório oficial.
    • Scikit-learn, pandas e PyYAML: pip install -U scikit-learn pandas pyyaml.
  2. Confirma que as tuas anotações estão no formato de deteção YOLO.

    • Neste tutorial, todos os ficheiros de anotação encontram-se no diretório Fruit-Detection/labels.

Gerar vetores de características para um conjunto de dados de deteção de objetos#

  1. Começa por criar um novo ficheiro Python example.py para os passos abaixo.

  2. Carrega as imagens de treino e validação configuradas através do proprietário do conjunto de dados do pacote, deixando a divisão de teste intacta.

    from pathlib import Path
    
    from ultralytics.data.dataset import YOLODataset
    from ultralytics.data.utils import check_det_dataset
    
    yaml_file = "path/to/data.yaml"
    data = check_det_dataset(yaml_file)
    dataset_path = Path(data["path"])
    sources = []
    for split in ("train", "val"):
        source = data.get(split)
        if source:
            sources.extend(source if isinstance(source, list) else [source])
    dataset = YOLODataset(sources, data=data, augment=False)
    images = [Path(p) for p in dataset.im_files]
  3. Agora, lê o conteúdo do ficheiro YAML do conjunto de dados e extrai os índices dos rótulos de classe.

    classes = data["names"]
    cls_idx = sorted(classes.keys())
  4. Inicializa um DataFrame pandas vazio.

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. Conta as instâncias de cada classe presente nos ficheiros de anotação.

    from collections import Counter
    
    for image, label in zip(images, dataset.labels):
        lbl_counter = Counter(label["cls"].flatten().astype(int))
        labels_df.loc[image, list(lbl_counter)] = list(lbl_counter.values())
  6. Segue-se uma amostra da vista do DataFrame preenchido:

                                                           0    1    2    3    4    5
    '0000a16e4b057580_jpg.rf.00ab48988370f64f5ca8ea4...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.7e6dce029fb67f01eb19aa7...'  0.0  0.0  0.0  0.0  0.0  7.0
    '0000a16e4b057580_jpg.rf.bc4d31cdcbe229dd022957a...'  0.0  0.0  0.0  0.0  0.0  7.0
    '00020ebf74c4881c_jpg.rf.508192a0a97aa6c4a3b6882...'  0.0  0.0  0.0  1.0  0.0  0.0
    '00020ebf74c4881c_jpg.rf.5af192a2254c8ecc4188a25...'  0.0  0.0  0.0  1.0  0.0  0.0
     ...                                                  ...  ...  ...  ...  ...  ...
    'ff4cd45896de38be_jpg.rf.c4b5e967ca10c7ced3b9e97...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff4cd45896de38be_jpg.rf.ea4c1d37d2884b3e3cbce08...'  0.0  0.0  0.0  0.0  0.0  2.0
    'ff5fd9c3c624b7dc_jpg.rf.bb519feaa36fc4bf630a033...'  1.0  0.0  0.0  0.0  0.0  0.0
    'ff5fd9c3c624b7dc_jpg.rf.f0751c9c3aa4519ea3c9d6a...'  1.0  0.0  0.0  0.0  0.0  0.0
    'fffe28b31f2a70d4_jpg.rf.7ea16bd637ba0711c53b540...'  0.0  6.0  0.0  0.0  0.0  0.0

As linhas usam caminhos absolutos para as imagens, e as colunas correspondem aos índices dos rótulos de classe. Os rótulos em falta permanecem como linhas de fundo compostas apenas por zeros. Esta estrutura de dados permite aplicar a validação cruzada K-Fold a um conjunto de dados de deteção de objetos.

Divisão K-Fold do conjunto de dados#

  1. Agora vamos usar a classe KFold de sklearn.model_selection para gerar k divisões do conjunto de dados.

    • Importante:
      • Definir shuffle=True garante uma distribuição aleatória das classes nas tuas divisões.
      • Ao definir random_state=M como M, um número inteiro à tua escolha, podes obter resultados reproduzíveis.
    from sklearn.model_selection import KFold
    
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # definir random_state para obter resultados reproduzíveis
    
    kfolds = list(kf.split(labels_df))
  2. O conjunto de dados foi agora dividido em k folds, cada um com uma lista de índices train e val. Vamos construir um DataFrame para apresentar estes resultados com mais clareza.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=labels_df.index, columns=folds)
    
    for i, (train, val) in enumerate(kfolds, start=1):
        folds_df.loc[labels_df.index[train], f"split_{i}"] = "train"
        folds_df.loc[labels_df.index[val], f"split_{i}"] = "val"
  3. Agora vamos calcular a distribuição dos rótulos de classe para cada fold, como proporção das classes presentes em val em relação às presentes em train.

    fold_lbl_distrb = pd.DataFrame(index=folds, columns=cls_idx)
    
    for n, (train_indices, val_indices) in enumerate(kfolds, start=1):
        train_totals = labels_df.iloc[train_indices].sum()
        val_totals = labels_df.iloc[val_indices].sum()
    
        # To avoid division by zero, we add a small value (1E-7) to the denominator
        ratio = val_totals / (train_totals + 1e-7)
        fold_lbl_distrb.loc[f"split_{n}"] = ratio

    O cenário ideal é que as proporções de todas as classes sejam razoavelmente semelhantes em cada divisão e entre classes. No entanto, isso dependerá das características específicas do teu conjunto de dados.

  4. Escreve listas de imagens e um ficheiro YAML do conjunto de dados para cada divisão. As listas de texto evitam copiar o conjunto de dados k vezes.

    import yaml
    
    save_path = dataset_path.parent / f"{ksplit}-Fold_Cross-val"
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        for partition in ("train", "val"):
            split_images = folds_df.index[folds_df[split] == partition]
            paths = "\n".join(map(str, split_images))
            (save_path / f"{split}_{partition}.txt").write_text(f"{paths}\n")
    
        dataset_yaml = save_path / f"{split}.yaml"
        ds_yamls.append(dataset_yaml)
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": save_path.as_posix(),
                    "train": f"{split}_train.txt",
                    "val": f"{split}_val.txt",
                    "names": classes,
                },
                ds_y,
            )

Guardar registos (opcional)#

Se quiseres, podes guardar os registos da divisão K-Fold e os DataFrames de distribuição dos rótulos como ficheiros CSV para consulta futura.

folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")

Treinar YOLO usando divisões de dados K-Fold#

  1. Primeiro, carrega o modelo YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # usa yolo26n.pt para um modelo pequeno
    model = YOLO(weights_path, task="detect")
  2. Em seguida, percorre os ficheiros YAML do conjunto de dados para executar o treino. Os resultados serão guardados num diretório especificado pelos argumentos project e name. Por predefinição, esse diretório é 'runs/detect/train#', em que # é um índice inteiro.

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k, dataset_yaml in enumerate(ds_yamls):
        model = YOLO(weights_path, task="detect")
        results[k] = model.train(
            data=dataset_yaml, epochs=epochs, batch=batch, project=project, name=f"fold_{k + 1}"
        )  # include any additional train arguments
  3. Também podes usar a função Ultralytics data.split.autosplit para dividir automaticamente o conjunto de dados:

    from ultralytics.data.split import autosplit
    
    # Dividir automaticamente o conjunto de dados em train/val/test
    autosplit(path="path/to/images", weights=(0.8, 0.2, 0.0), annotated_only=True)

Conclusão#

Neste guia, explorámos o processo de utilização da validação cruzada K-Fold para treinar o modelo YOLO de deteção de objetos. Aprendemos a dividir o conjunto de treino e validação em K partições e a usar a tabela de proporções gerada para inspecionar o equilíbrio entre classes após a divisão aleatória.

Também explorámos o procedimento para criar DataFrames de relatório que permitem visualizar as divisões dos dados e as distribuições dos rótulos entre essas divisões, dando-nos uma visão clara da estrutura dos conjuntos de treino e validação.

Opcionalmente, guardámos os nossos registos para consulta futura, o que pode ser especialmente útil em projetos de grande escala ou ao investigar problemas de desempenho do modelo.

Por fim, implementámos o treino efetivo do modelo usando cada divisão num ciclo e guardámos os resultados do treino para análise e comparação posteriores.

Esta técnica de validação cruzada K-Fold é uma forma robusta de tirar o máximo partido dos dados disponíveis e ajuda a garantir que o desempenho do modelo é fiável e consistente em diferentes subconjuntos de dados. O resultado é um modelo mais generalizável e fiável, com menor probabilidade de sobreajustar a padrões de dados específicos.

Lembra-te de que, embora tenhamos usado YOLO neste guia, estes passos são, na sua maioria, aplicáveis a outros modelos de aprendizagem automática. Compreender estes passos permite-te aplicar a validação cruzada de forma eficaz nos teus próprios projetos de aprendizagem automática.

Perguntas frequentes#

  • A validação cruzada K-Fold é uma técnica que divide o conjunto de dados em «k» subconjuntos (folds) para avaliar o desempenho do modelo com maior fiabilidade. Cada fold serve tanto como dados de validação como de treino. No contexto da deteção de objetos, usar a validação cruzada K-Fold ajuda a garantir que o desempenho do teu modelo Ultralytics YOLO é robusto e generalizável entre diferentes divisões dos dados, aumentando a sua fiabilidade. Para obter instruções detalhadas sobre como configurar a validação cruzada K-Fold com Ultralytics YOLO, consulta Validação cruzada K-Fold com Ultralytics.

  • Para implementar a validação cruzada K-Fold com Ultralytics YOLO, segue estes passos:

    1. Confirma que as anotações estão no formato de deteção YOLO.
    2. Usa bibliotecas Python como sklearn, pandas e pyyaml.
    3. Cria vetores de características a partir do teu conjunto de dados.
    4. Divide o teu conjunto de dados usando KFold de sklearn.model_selection.
    5. Treina o modelo YOLO em cada divisão.

    Para um guia completo, consulta a secção Divisão K-Fold do conjunto de dados na nossa documentação.

  • O fluxo de trabalho deste guia destina-se ao formato de deteção YOLO, mas a mesma abordagem adapta-se a todas as tarefas YOLO — a tarefa altera a forma como compões os folds, não se a validação cruzada é útil:

    TarefaConceção dos folds
    detectDivide ao nível da imagem, equilibrando a distribuição de objetos e classes entre os folds. Mantém imagens relacionadas (do mesmo paciente, sequência de vídeo, câmara ou local) no mesmo fold.
    segmentUsa a mesma estratégia ao nível da imagem que na deteção, preservando também a cobertura de máscaras e classes em cada fold.
    classifyPrefere folds estratificados para manter equilibradas as frequências das classes entre treino e validação.
    poseDivide por indivíduo ou sequência, para que a mesma pessoa ou animal nunca apareça nos dois lados de um fold.
    obbDivide ao nível da imagem, mantendo juntos os mosaicos ou recortes da mesma cena — algo especialmente importante para imagens aéreas.

    Independentemente da tarefa, mantém amostras quase duplicadas e relacionadas fora de folds opostos: esse tipo de fuga de dados infla as métricas de validação muito para além do desempenho que o modelo terá em produção.

  • Ultralytics YOLO oferece deteção de objetos em tempo real de última geração, com elevada precisão e eficiência. É versátil e compatível com várias tarefas de visão computacional, como deteção, segmentação de instâncias, segmentação semântica e classificação. Além disso, integra-se facilmente com ferramentas como a Ultralytics Platform para treinar e implementar modelos sem código. Para mais informações, explora as vantagens e funcionalidades na nossa página Ultralytics YOLO.

  • As tuas anotações devem seguir o formato de deteção YOLO. Cada ficheiro de anotação deve indicar a classe do objeto e as coordenadas da respetiva caixa delimitadora na imagem. O formato YOLO garante um processamento de dados simplificado e padronizado para treinar modelos de deteção de objetos. Para mais informações sobre a formatação correta das anotações, consulta o guia do formato de deteção YOLO.

  • Sim, podes usar a validação cruzada K-Fold com qualquer conjunto de dados personalizado, desde que as anotações estejam no formato de deteção YOLO. Substitui os caminhos do conjunto de dados e os rótulos de classe pelos específicos do teu conjunto de dados personalizado. Esta flexibilidade permite que qualquer projeto de deteção de objetos beneficie de uma avaliação robusta do modelo com validação cruzada K-Fold. Para um exemplo prático, consulta a secção Gerar vetores de características.

Comentários