Ultralytics YOLO27:

Validação cruzada K-Fold com Ultralytics#

Introdução#

Este guia abrangente demonstra a implementação da validação cruzada K-Fold para conjuntos de dados de deteção de objetos no ecossistema Ultralytics. Vamos utilizar o formato de deteção YOLO e bibliotecas Python essenciais, como sklearn, pandas e PyYAML, para orientar-te na configuração necessária, no processo de geração de vetores de características e na 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 utilizemos 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 especificidades do teu projeto. A validação cruzada K-Fold oferece mais valor quando o teu conjunto de dados é pequeno, ruidoso ou muito variável; para conjuntos de dados grandes e diversificados, uma divisão train/val/test bem construída costuma ser suficiente.

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, utilizamos o conjunto de dados Fruit Detection.

    • Este conjunto de dados contém um total de 8479 imagens.
    • Inclui 6 etiquetas de classe, cada uma com o respetivo número total de instâncias indicado abaixo.
Etiqueta da classeNúmero de instâncias
Maçã7049
Uvas7202
Ananás1613
Laranja15549
Banana3536
Melancia1976
  • Os pacotes Python necessários incluem:

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

  1. Inicia um novo ambiente virtual Python (venv) para o teu projeto e ativa-o. Utiliza pip (ou o teu gestor de pacotes preferido) 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. Verifica se as tuas anotações estão no formato de deteção YOLO.

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

Geração de 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, mantendo 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 das etiquetas 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 etiqueta de 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 vista de exemplo 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 utilizam caminhos absolutos para as imagens e as colunas correspondem aos índices das etiquetas de classe. As etiquetas em falta permanecem como linhas de fundo totalmente preenchidas com 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 utilizaremos a classe KFold de sklearn.model_selection para gerar k splits do conjunto de dados.

    • Importante:
      • Definir shuffle=True garante uma distribuição aleatória das classes nas tuas divisões.
      • Ao definires random_state=M, em que M é um número inteiro escolhido, podes obter resultados repetíveis.
    from sklearn.model_selection import KFold
    
    ksplit = 5
    kf = KFold(n_splits=ksplit, shuffle=True, random_state=20)  # setting random_state for repeatable results
    
    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 maior 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 calcularemos a distribuição das etiquetas de classe para cada fold como uma proporção entre as classes presentes em val e as 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 todas as proporções de classes sejam razoavelmente semelhantes em cada divisão e entre as classes. No entanto, isso dependerá das especificidades do teu conjunto de dados.

  4. Escreve listas de imagens e um 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)#

Opcionalmente, podes guardar os registos da divisão K-Fold e os DataFrames de distribuição das etiquetas como ficheiros CSV para referência futura.

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

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

  1. Primeiro, carrega o modelo YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # use yolo26n.pt for a small model
    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, este diretório é 'runs/detect/train#', onde # é 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 utilizar a função Ultralytics data.split.autosplit para dividir automaticamente o conjunto de dados:

    from ultralytics.data.split import autosplit
    
    # Automatically split dataset into 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 utilizar a tabela de proporções gerada para analisar o equilíbrio das 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 de dados e as distribuições de etiquetas nessas divisões, proporcionando-nos uma visão clara da estrutura dos nossos conjuntos de treino e validação.

Opcionalmente, guardámos os nossos registos para referência futura, o que pode ser particularmente útil em projetos de grande escala ou ao resolver problemas de desempenho do modelo.

Por fim, implementámos o treino efetivo do modelo utilizando cada divisão num ciclo, guardando 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 teu modelo é fiável e consistente em diferentes subconjuntos de dados. Isto resulta num modelo mais generalizável e fiável, menos propenso a sobreajustar padrões de dados específicos.

Lembra-te de que, embora tenhamos utilizado YOLO neste guia, estes passos são maioritariamente transferíveis para outros modelos de aprendizagem automática. Compreender estes passos permite-te aplicar eficazmente a validação cruzada nos teus próprios projetos de aprendizagem automática.

Perguntas frequentes#

  • A validação cruzada K-Fold é uma técnica em que o conjunto de dados é dividido em 'k' subconjuntos (folds) para avaliar o desempenho do modelo de forma mais fiável. Cada fold funciona como dados de treino e de validação. No contexto da deteção de objetos, utilizar a validação cruzada K-Fold ajuda a garantir que o desempenho do teu modelo Ultralytics YOLO é robusto e generalizável em diferentes divisões de 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, tens de seguir estes passos:

    1. Verifica se as anotações estão no formato de deteção YOLO.
    2. Utiliza 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 utilizando KFold de sklearn.model_selection.
    5. Treina o modelo YOLO em cada divisão.

    Para consultar um guia abrangente, vê a secção Divisão K-Fold do conjunto de dados na nossa documentação.

  • O fluxo de trabalho deste guia visa o 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 o facto de a validação cruzada ser útil:

    TarefaConceção dos folds
    detectDivide ao nível da imagem, equilibrando as distribuições de objetos e classes entre os folds. Mantém imagens relacionadas (do mesmo paciente, sequência de vídeo, câmara ou local) num único fold.
    segmentUtiliza a mesma estratégia ao nível da imagem que na deteção, preservando adicionalmente a cobertura de máscaras e classes em cada fold.
    classifyPrefere folds estratificados para que as frequências das classes permaneçam equilibradas entre o treino e a validação.
    poseDivide por sujeito ou sequência para que a mesma pessoa ou animal nunca apareça em ambos os lados de um fold.
    obbDivide ao nível da imagem, mantendo juntas as tiles ou recortes da mesma cena — 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 aumenta as métricas de validação muito além do que o modelo alcançará 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 suporta 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 perfeitamente com ferramentas como a Ultralytics Platform para treino e implementação de modelos sem código. Para obter mais informações, explora as vantagens e funcionalidades na nossa página do Ultralytics YOLO.

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

  • Sim, podes utilizar 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 as etiquetas de classe pelos específicos do teu conjunto de dados personalizado. Esta flexibilidade garante que qualquer projeto de deteção de objetos pode beneficiar de uma avaliação robusta do modelo utilizando a validação cruzada K-Fold. Para ver um exemplo prático, consulta a nossa secção Geração de vetores de características.

Comentários