Saltar para o conteúdo

K-Fold Cross Validation com Ultralytics

Introdução

Este guia abrangente ilustra a implementação do K-Fold Cross Validation para conjuntos de dados de deteção de objectos no ecossistema Ultralytics . Aproveitaremos o formato de deteção YOLO e as principais bibliotecas Python , como sklearn, pandas e PyYaml, para o guiar através da configuração necessária, do processo de geração de vectores de caraterísticas e da execução de uma divisão de conjunto de dados K-Fold.

Visão geral da validação cruzada K-Fold

Quer o seu projeto envolva 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 fiabilidade e a robustez do seu aprendizagem automática modelos. Enquanto estivermos a aplicar k=5 dobras para este tutorial, tenha em mente que o número ideal de dobras pode variar dependendo do seu conjunto de dados e das especificidades do seu projeto.

Sem mais demoras, vamos a isto!

Configuração

  • As suas anotações devem estar no formato de deteçãoYOLO .

  • Este guia pressupõe que os ficheiros de anotações 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 número total de instâncias listado abaixo.
Etiqueta de classe Contagem de instâncias
Apple 7049
Uvas 7202
Ananás 1613
Laranja 15549
Banana 3536
Melancia 1976
  • Necessário Python pacotes incluem:

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

  • Iniciar um novo ambiente virtual Python (venv) para o seu projeto e activá-lo. Utilizar pip (ou o seu gestor de pacotes preferido) para instalar:

    • A biblioteca Ultralytics : pip install -U ultralytics. Em alternativa, pode clonar o ficheiro oficial 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 deteçãoYOLO .

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

Geração de vectores de caraterísticas para o conjunto de dados de deteção de objectos

  1. Comece por criar um novo example.py Python para os passos seguintes.

  2. Prossiga para recuperar todos os ficheiros de etiquetas do 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 ficheiro YAML do conjunto de dados e extraia os índices das etiquetas das classes.

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

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

    from collections import Counter
    
    for label in labels:
        lbl_counter = Counter()
    
        with open(label, "r") 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(" ")[0])] += 1
    
        labels_df.loc[label.stem] = lbl_counter
    
    labels_df = labels_df.fillna(0.0)  # replace `nan` values with `0.0`
    
  6. Segue-se uma vista de amostra 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 ficheiros de etiquetas, cada um correspondendo a uma imagem no seu conjunto de dados, e as colunas correspondem aos índices das etiquetas de classe. Cada linha representa um pseudo-vetor de caraterísticas, com a contagem de cada etiqueta de classe presente no seu conjunto de dados. Esta estrutura de dados permite a aplicação de K-Fold Cross Validation a um conjunto de dados de deteção de objectos.

Divisão do conjunto de dados K-Fold

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

    • Importante:
      • Definição shuffle=True garante uma distribuição aleatória de classes nas suas divisões.
      • Por definição random_state=M onde M é um número inteiro escolhido, é possível 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 dobras, cada uma com uma lista de train e val índices. Iremos construir um DataFrame para apresentar estes resultados de forma mais clara.

    folds = [f"split_{n}" for n in range(1, ksplit + 1)]
    folds_df = pd.DataFrame(index=indx, columns=folds)
    
    for idx, (train, val) in enumerate(kfolds, start=1):
        folds_df[f"split_{idx}"].loc[labels_df.iloc[train].index] = "train"
        folds_df[f"split_{idx}"].loc[labels_df.iloc[val].index] = "val"
    
  3. Agora vamos calcular a distribuição das etiquetas das classes para cada dobra como um rácio das classes presentes em val aos presentes no 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 todos os rácios de classe sejam razoavelmente semelhantes para cada divisão e entre classes. No entanto, isto estará sujeito às especificidades do seu conjunto de dados.

  4. Em seguida, criamos os diretórios e os ficheiros 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 (unchanged)
    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 as etiquetas para o respetivo diretório ('train' ou 'val') para cada divisão.

    • NOTA: O tempo necessário para esta parte do código varia consoante o tamanho do conjunto de dados e o hardware do sistema.
    import shutil
    
    for image, label in zip(images, labels):
        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)
    

Guardar registos (opcional)

Opcionalmente, pode guardar os registos dos DataFrames de divisão K-Fold e de distribuição de 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 K-Fold Data Splits

  1. Primeiro, carregue o modelo YOLO .

    from ultralytics import YOLO
    
    weights_path = "path/to/weights.pt"
    model = YOLO(weights_path, task="detect")
    
  2. Em seguida, itere sobre os ficheiros YAML do conjunto de dados para executar o treino. Os resultados serão salvos em um diretório especificado pelo parâmetro project e name argumentos. Por defeito, este diretório é 'exp/runs#' onde # é um índice inteiro.

    results = {}
    
    # Define your additional arguments here
    batch = 16
    project = "kfold_demo"
    epochs = 100
    
    for k in range(ksplit):
        dataset_yaml = ds_yamls[k]
        model = YOLO(weights_path, task="detect")
        model.train(data=dataset_yaml, epochs=epochs, batch=batch, project=project)  # include any train arguments
        results[k] = model.metrics  # save output metrics for further analysis
    

Conclusão

Neste guia, explorámos o processo de utilização da validação cruzada K-Fold para treinar o modelo de deteção de objectos YOLO . Aprendemos a dividir o nosso conjunto de dados em K partições, garantindo uma distribuição equilibrada das classes nas diferentes dobras.

Também explorámos o procedimento de criação de DataFrames de relatório para visualizar as divisões de dados e as distribuições de rótulos entre essas divisões, fornecendo-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 projectos de grande escala ou na resolução de problemas de desempenho do modelo.

Por fim, implementámos o treino do modelo real 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 modelo é fiável e consistente em diferentes subconjuntos de dados. Isto resulta num modelo mais generalizável e fiável que tem menos probabilidades de se ajustar excessivamente a padrões de dados específicos.

Lembre-se de que, embora tenhamos utilizado YOLO neste guia, estes passos são, na sua maioria, transferíveis para outros modelos de aprendizagem automática. A compreensão destes passos permite-lhe aplicar a validação cruzada de forma eficaz nos seus próprios projectos de aprendizagem automática. Boa programação!

FAQ

O que é a validação cruzada K-Fold e por que razão é útil na deteção de objectos?

A validação cruzada K-Fold é uma técnica em que o conjunto de dados é dividido em "k" subconjuntos (dobras) para avaliar o desempenho do modelo de forma mais fiável. Cada dobra serve como dados de treino e validação. No contexto da deteção de objectos, a utilização da validação cruzada K-Fold ajuda a garantir que o desempenho do seu 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 , consulte Validação cruzada K-Fold com Ultralytics.

Como é que implemento a validação cruzada K-Fold utilizando Ultralytics YOLO ?

Para implementar a validação cruzada K-Fold com Ultralytics YOLO , é necessário seguir estes passos:

  1. Verificar se as anotações estão no formato de deteçãoYOLO .
  2. Utilize as bibliotecas Python como sklearn, pandase pyyaml.
  3. Crie vectores de caraterísticas a partir do seu conjunto de dados.
  4. Divida o seu conjunto de dados utilizando KFold de sklearn.model_selection.
  5. Treinar o modelo YOLO em cada divisão.

Para obter um guia completo, consulte a secção K-Fold Dataset Split na nossa documentação.

Por que razão devo utilizar Ultralytics YOLO para a deteção de objectos?

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

Como posso garantir que as minhas anotações estão no formato correto para Ultralytics YOLO ?

As suas 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 objectos. Para obter mais informações sobre a formatação correta das anotações, visite o guia de formato de deteçãoYOLO .

Posso utilizar a validação cruzada K-Fold com conjuntos de dados personalizados para além da deteção de frutos?

Sim, é possível 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 . Substitua os caminhos do conjunto de dados e os rótulos de classe por aqueles específicos do seu conjunto de dados personalizado. Esta flexibilidade garante que qualquer projeto de deteção de objectos pode beneficiar de uma avaliação robusta do modelo utilizando o K-Fold Cross Validation. Para um exemplo prático, consulte a nossa secção Gerar vectores de caraterísticas.

📅C riado há 1 ano ✏️ Atualizado há 1 mês

Comentários