Ir para o conteúdo

Validação Cruzada K-Fold com Ultralytics

Introdução

Este guia abrangente ilustra a implementação da Validação Cruzada K-Fold para conjuntos de dados de detecção de objetos dentro do ecossistema Ultralytics. Utilizaremos o formato de detecção YOLO e bibliotecas-chave de python, como sklearn, pandas e PyYaml, para guiá-lo através da configuração necessária, o processo de geração de vetores de características e a execução de uma divisão de conjunto de dados K-Fold.

Visão Geral da Validação Cruzada K-Fold

Se o seu projeto envolve o conjunto de dados Fruit Detection ou uma fonte de dados personalizada, este tutorial tem como objetivo ajudá-lo a compreender e aplicar a Validação Cruzada K-Fold para reforçar a confiabilidade e a robustez do seu aprendizado de máquina modelos. Enquanto estamos aplicando k=5 folds para este tutorial, lembre-se de que o número ideal de folds pode variar dependendo do seu conjunto de dados e das especificidades do seu projeto.

Sem mais delongas, vamos mergulhar!

Configuração

  • Suas anotações devem estar no formato de detecção YOLO.

  • Este guia assume que os arquivos de anotação estão disponíveis localmente.

  • Para nossa demonstração, usamos o conjunto de dados de Detecção de Frutas.

    • Este conjunto de dados contém um total de 8479 imagens.
    • Inclui 6 rótulos de classe, cada um com suas contagens totais de instâncias listadas abaixo.
Rótulo da Classe Contagem de Instâncias
Apple 7049
Uvas 7202
Abacaxi 1613
Laranja 15549
Banana 3536
Melancia 1976
  • Os pacotes Python necessários incluem:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Este tutorial opera com k=5 folds. No entanto, você deve determinar o melhor número de folds para seu conjunto de dados específico.

  • Iniciar um novo ambiente virtual Python (venv) para o seu projeto e ative-o. Use pip (ou seu gerenciador de pacotes preferido) para instalar:

    • A biblioteca Ultralytics: pip install -U ultralytics. Alternativamente, você pode clonar o repositório.
    • Scikit-learn, pandas e PyYAML: pip install -U scikit-learn pandas pyyaml.
  • Verifique se as suas anotações estão no formato de detecção YOLO.

    • Para este tutorial, todos os arquivos de anotação são encontrados no Fruit-Detection/labels diretório.

Gerando Vetores de Características para Conjunto de Dados de Detecção de Objetos

  1. Comece criando um novo example.py Arquivo Python para os passos abaixo.

  2. Prossiga para recuperar todos os arquivos de rótulos para seu conjunto de dados.

    from pathlib import Path
    
    dataset_path = Path("./Fruit-detection")  # replace with 'path/to/dataset' for your custom data
    labels = sorted(dataset_path.rglob("*labels/*.txt"))  # all data in 'labels'
    
  3. Agora, leia o conteúdo do arquivo YAML do conjunto de dados e extraia os índices dos rótulos de classe.

    import yaml
    
    yaml_file = "path/to/data.yaml"  # your data YAML with data directories and names dictionary
    with open(yaml_file, encoding="utf8") as y:
        classes = yaml.safe_load(y)["names"]
    cls_idx = sorted(classes.keys())
    
  4. Inicializar um vazio pandas DataFrame.

    import pandas as pd
    
    index = [label.stem for label in labels]  # uses base filename as ID (no extension)
    labels_df = pd.DataFrame([], columns=cls_idx, index=index)
    
  5. Contar as instâncias de cada rótulo de classe presente nos arquivos de anotação.

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label) as lf:
            lines = lf.readlines()
    
        for line in lines:
            # classes for YOLO label uses integer at first position of each line
            lbl_counter[int(line.split(" ", 1)[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. A seguir, uma amostra da visualização 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 indexam os arquivos de rótulos, cada um correspondendo a uma imagem no seu conjunto de dados, e as colunas correspondem aos seus índices de rótulos de classe. Cada linha representa um pseudo vetor de características, com a contagem de cada rótulo de classe presente no seu conjunto de dados. Esta estrutura de dados permite a aplicação da Validação Cruzada K-Fold a um conjunto de dados de detecção de objetos.

Divisão de Conjunto de Dados K-Fold

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

    • Importante:
      • Configurando shuffle=True garante uma distribuição aleatória de classes nas suas divisões.
      • Ao definir random_state=M onde M é um número inteiro escolhido, o que permite obter resultados repetíveis.
    import random
    
    from sklearn.model_selection import KFold
    
    random.seed(0)  # for reproducibility
    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 train e val índices. Construiremos um DataFrame para exibir esses resultados de forma mais clara.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=index, columns=folds)
    
    for i, (train, val) in enumerate(kfolds, start=1):
        folds_df[f"split_{i}"].loc[labels_df.iloc[train].index] = "train"
        folds_df[f"split_{i}"].loc[labels_df.iloc[val].index] = "val"
    
  3. Agora vamos calcular a distribuição dos rótulos de classe para cada dobra como uma proporção das classes presentes em val àqueles 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 para cada divisão e entre as classes. No entanto, isso estará sujeito às especificidades do seu conjunto de dados.

  4. Em seguida, criamos os diretórios e os arquivos YAML do conjunto de dados para cada divisão.

    import datetime
    
    supported_extensions = [".jpg", ".jpeg", ".png"]
    
    # Initialize an empty list to store image file paths
    images = []
    
    # Loop through supported extensions and gather image files
    for ext in supported_extensions:
        images.extend(sorted((dataset_path / "images").rglob(f"*{ext}")))
    
    # Create the necessary directories and dataset YAML files
    save_path = Path(dataset_path / f"{datetime.date.today().isoformat()}_{ksplit}-Fold_Cross-val")
    save_path.mkdir(parents=True, exist_ok=True)
    ds_yamls = []
    
    for split in folds_df.columns:
        # Create directories
        split_dir = save_path / split
        split_dir.mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "train" / "labels").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "images").mkdir(parents=True, exist_ok=True)
        (split_dir / "val" / "labels").mkdir(parents=True, exist_ok=True)
    
        # Create dataset YAML files
        dataset_yaml = split_dir / f"{split}_dataset.yaml"
        ds_yamls.append(dataset_yaml)
    
        with open(dataset_yaml, "w") as ds_y:
            yaml.safe_dump(
                {
                    "path": split_dir.as_posix(),
                    "train": "train",
                    "val": "val",
                    "names": classes,
                },
                ds_y,
            )
    
  5. Por fim, copie as imagens e etiquetas para o diretório respectivo ('train' ou 'val') para cada divisão.

    • NOTA: O tempo necessário para esta parte do código irá variar com base no tamanho do seu dataset e no hardware do seu sistema.
    import shutil
    
    from tqdm import tqdm
    
    for image, label in tqdm(zip(images, labels), total=len(images), desc="Copying files"):
        for split, k_split in folds_df.loc[image.stem].items():
            # Destination directory
            img_to_path = save_path / split / k_split / "images"
            lbl_to_path = save_path / split / k_split / "labels"
    
            # Copy image and label files to new directory (SamefileError if file already exists)
            shutil.copy(image, img_to_path / image.name)
            shutil.copy(label, lbl_to_path / label.name)
    

Salvar Registros (Opcional)

Opcionalmente, você pode salvar os registros da divisão K-Fold e os DataFrames de distribuição de rótulos como arquivos 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")

Treine o YOLO usando divisões de dados K-Fold

  1. Primeiro, carregue o modelo YOLO.

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"  # use yolo11n.pt for a small model
    model = YOLO(weights_path, task="detect")
    
  2. Em seguida, itere sobre os arquivos YAML do conjunto de dados para executar o treinamento. Os resultados serão salvos em um diretório especificado pelo project e name argumentos. Por padrã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. Pode também usar a função Ultralytics data.utils.autosplit para divisão automática do conjunto de dados:

    from ultralytics.data.utils 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, exploramos o processo de uso da validação cruzada K-Fold para treinar o modelo de detecção de objetos YOLO. Aprendemos como dividir nosso conjunto de dados em K partições, garantindo uma distribuição de classe equilibrada entre as diferentes dobras.

Também exploramos o procedimento para criar DataFrames de relatório para visualizar as divisões de dados e as distribuições de rótulos nessas divisões, fornecendo-nos uma visão clara da estrutura de nossos conjuntos de treinamento e validação.

Opcionalmente, salvamos nossos registros para referência futura, o que pode ser particularmente útil em projetos de grande escala ou ao solucionar problemas de desempenho do modelo.

Finalmente, implementamos o treinamento real do modelo usando cada divisão em um loop, salvando nossos resultados de treinamento para análise e comparação adicionais.

Esta técnica de validação cruzada K-Fold é uma forma robusta de aproveitar ao máximo os seus dados disponíveis e ajuda a garantir que o desempenho do seu modelo seja confiável e consistente em diferentes subconjuntos de dados. Isso resulta em um modelo mais generalizável e confiável, que é menos propenso a overfit em padrões de dados específicos.

Lembre-se de que, embora tenhamos usado o YOLO neste guia, estas etapas são, em sua maioria, transferíveis para outros modelos de machine learning. Compreender estas etapas permite-lhe aplicar a validação cruzada de forma eficaz nos seus próprios projetos de machine learning. Boa programação!

FAQ

O que é Validação Cruzada K-Fold e por que ela é útil na detecção de objetos?

A Validação Cruzada K-Fold é uma técnica onde o conjunto de dados é dividido em 'k' subconjuntos (folds) para avaliar o desempenho do modelo de forma mais confiável. Cada fold serve como dados de treinamento e validação. No contexto da detecção de objetos, usar a Validação Cruzada K-Fold ajuda a garantir que o desempenho do seu modelo Ultralytics YOLO seja robusto e generalizável em diferentes divisões de dados, aumentando sua confiabilidade. Para obter instruções detalhadas sobre como configurar a Validação Cruzada K-Fold com Ultralytics YOLO, consulte Validação Cruzada K-Fold com Ultralytics.

Como implementar a validação cruzada K-Fold usando Ultralytics YOLO?

Para implementar a validação cruzada K-Fold com Ultralytics YOLO, você precisa seguir estes passos:

  1. Verifique se as anotações estão no formato de detecção YOLO.
  2. Use bibliotecas Python como sklearn, pandas, e pyyaml.
  3. Criar vetores de características a partir do seu conjunto de dados.
  4. Divida seu conjunto de dados usando KFold de sklearn.model_selection.
  5. Treine o modelo YOLO em cada divisão.

Para um guia completo, consulte a seção Divisão de Dataset K-Fold em nossa documentação.

Por que devo usar Ultralytics YOLO para detecção de objetos?

O YOLO da Ultralytics oferece detecção de objetos em tempo real de última geração com alta precisão e eficiência. É versátil, suportando múltiplas tarefas de visão computacional, como detecção, segmentação e classificação. Além disso, integra-se perfeitamente com ferramentas como o HUB da Ultralytics para treinamento e implantação de modelos sem código. Para mais detalhes, explore os benefícios e recursos em nossa página do YOLO da Ultralytics.

Como posso garantir que minhas anotações estejam no formato correto para Ultralytics YOLO?

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

Posso usar Validação Cruzada K-Fold com conjuntos de dados personalizados que não sejam de Detecção de Frutas?

Sim, você pode usar a Validação Cruzada K-Fold com qualquer conjunto de dados personalizado, desde que as anotações estejam no formato de detecção YOLO. Substitua os caminhos do conjunto de dados e os rótulos de classe pelos específicos do seu conjunto de dados personalizado. Essa flexibilidade garante que qualquer projeto de detecção de objetos possa se beneficiar da avaliação robusta do modelo usando a Validação Cruzada K-Fold. Para um exemplo prático, revise nossa seção Generating Feature Vectors (Geração de Vetores de Características).



📅 Criado há 1 ano ✏️ Atualizado há 24 dias

Comentários