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 Python essenciais como sklearn, pandas e PyYAML para te guiar pela configuração necessária, pelo processo de geração de vetores de características e pela execução de uma divisão de conjunto de dados K-Fold.
Quer seu projeto envolva o conjunto de dados Fruit Detection ou uma fonte de dados personalizada, este tutorial visa ajudar-te a compreender e aplicar a Validação Cruzada K-Fold para reforçar a confiabilidade e a robustez dos teus modelos de aprendizado de máquina. Embora estejamos aplicando k=5 folds para este tutorial, tenha em mente que o número ideal de folds pode variar dependendo do teu conjunto de dados e das especificidades do teu projeto.
Vamos começar.
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 Fruit Detection.
- Este conjunto de dados contém um total de 8479 imagens.
- Ele inclui 6 rótulos de classe, cada um com suas contagens totais de instâncias listadas abaixo.
| Rótulo de Classe | Contagem de Instâncias |
|---|---|
| Apple | 7049 |
| Grapes | 7202 |
| Pineapple | 1613 |
| Orange | 15549 |
| Banana | 3536 |
| Watermelon | 1976 |
-
Os pacotes Python necessários incluem:
ultralyticssklearnpandaspyyaml
-
Este tutorial opera com
k=5folds. No entanto, você deve determinar o melhor número de folds para o teu conjunto de dados específico.
-
Inicie um novo ambiente virtual Python (
venv) para o teu projeto e ative-o. Usepip(ou seu gerenciador de pacotes preferido) para instalar:- A biblioteca Ultralytics:
pip install -U ultralytics. Alternativamente, você pode clonar o repositório oficial. - Scikit-learn, pandas e PyYAML:
pip install -U scikit-learn pandas pyyaml.
- A biblioteca Ultralytics:
-
Verifique se suas anotações estão no formato de detecção YOLO.
- Para este tutorial, todos os arquivos de anotação são encontrados no diretório
Fruit-Detection/labels.
- Para este tutorial, todos os arquivos de anotação são encontrados no diretório
Gerando Vetores de Características para Conjunto de Dados de Detecção de Objetos
-
Comece criando um novo arquivo Python
example.pypara os passos abaixo. -
Prossiga para recuperar todos os arquivos de rótulo para o teu 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' -
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()) -
Inicialize um DataFrame
pandasvazio.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) -
Conte 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` -
Abaixo está uma amostra do DataFrame populado:
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ótulo, cada um correspondendo a uma imagem no teu conjunto de dados, e as colunas correspondem aos teus índices de rótulo de classe. Cada linha representa um pseudo vetor de características, com a contagem de cada rótulo de classe presente no teu 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 do Conjunto de Dados K-Fold
-
Agora usaremos a classe
KFolddosklearn.model_selectionpara gerarkdivisões do conjunto de dados.- Importante:
- Definir
shuffle=Truegarante uma distribuição aleatória das classes nas tuas divisões. - Ao definir
random_state=MondeMé um número inteiro escolhido, você pode obter resultados repetíveis.
- Definir
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)) - Importante:
-
O conjunto de dados agora foi dividido em
kfolds, cada um tendo uma lista de índices detraineval. Construiremos um DataFrame para exibir esses resultados mais claramente.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" -
Agora calcularemos a distribuição dos rótulos de classe para cada fold como uma proporção das classes presentes em
valem relação àquelas presentes emtrain.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}"] = ratioO cenário ideal é que todas as proporções de classe sejam razoavelmente semelhantes para cada divisão e entre as classes. Isso, no entanto, estará sujeito às especificidades do teu conjunto de dados.
-
Em seguida, criamos os diretórios e 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, ) -
Por fim, copie as imagens e os rótulos para o diretório respectivo ('train' ou 'val') para cada divisão.
- NOTA: O tempo necessário para esta parte do código variará com base no tamanho do teu conjunto de dados e no hardware do teu 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
-
Primeiro, carregue 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") -
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 pelos argumentos
projectename. 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 -
Você também pode usar a função Ultralytics data.utils.autosplit para divisão automática de conjuntos 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, 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 os diferentes folds.
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 entre essas divisões, fornecendo-nos uma visão clara da estrutura dos 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.
Por fim, implementamos o treinamento do modelo real usando cada divisão em um loop, salvando nossos resultados de treinamento para análise e comparação posteriores.
Esta técnica de validação cruzada K-Fold é uma maneira robusta de aproveitar ao máximo os teus dados disponíveis, e ajuda a garantir que o desempenho do teu modelo seja confiável e consistente entre diferentes subconjuntos de dados. Isso resulta em um modelo mais generalizável e confiável, menos propenso a sobreajuste a padrões de dados específicos.
Lembre-se de que, embora tenhamos usado o YOLO neste guia, esses passos são em sua maioria transferíveis para outros modelos de aprendizado de máquina. Entender esses passos permite-te aplicar a validação cruzada efetivamente nos teus próprios projetos de aprendizado de máquina.
FAQ
O que é a 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 tanto como treinamento quanto como dados de validação. No contexto da detecção de objetos, usar a Validação Cruzada K-Fold ajuda a garantir que o desempenho do teu modelo Ultralytics YOLO seja robusto e generalizável em diferentes divisões de dados, aumentando sua confiabilidade. Para instruções detalhadas sobre como configurar a Validação Cruzada K-Fold com Ultralytics YOLO, consulte Validação Cruzada K-Fold com Ultralytics.
Como implemento 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:
- Verifique se as anotações estão no formato de detecção YOLO.
- Use bibliotecas Python como
sklearn,pandasepyyaml. - Crie vetores de características a partir do teu conjunto de dados.
- Divida seu conjunto de dados usando
KFolddosklearn.model_selection. - Treine o modelo YOLO em cada divisão.
Para um guia abrangente, veja a seção Divisão do Conjunto de Dados K-Fold em nossa documentação.
Por que devo usar o Ultralytics YOLO para detecção de objetos?
O Ultralytics YOLO oferece detecção de objetos em tempo real de última geração com alta precisão e eficiência. Ele é versátil, suportando múltiplas tarefas de visão computacional como detecção, segmentação e classificação. Além disso, ele se integra perfeitamente com ferramentas como a Plataforma 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 Ultralytics YOLO.
Como posso garantir que minhas anotações estão no formato correto para o Ultralytics YOLO?
Suas anotações devem seguir o formato de detecção YOLO. Cada arquivo de anotação deve listar a classe do objeto, junto com as coordenadas da sua caixa delimitadora na imagem. O formato YOLO garante um processamento de dados simplificado e padronizado para treinar modelos de detecção de objetos. Para mais informações sobre a formatação adequada de anotações, visite o guia do formato de detecção YOLO.
Posso usar a Validação Cruzada K-Fold com conjuntos de dados personalizados além do Fruit Detection?
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 teu conjunto de dados personalizado. Essa flexibilidade garante que qualquer projeto de detecção de objetos possa se beneficiar de uma avaliação robusta do modelo usando a Validação Cruzada K-Fold. Para um exemplo prático, revise nossa seção Gerando Vetores de Características.