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.
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. Usepip
(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
.
- A biblioteca Ultralytics:
-
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.
- Para este tutorial, todos os arquivos de anotação são encontrados no
Gerando Vetores de Características para Conjunto de Dados de Detecção de Objetos
-
Comece criando um novo
example.py
Arquivo Python para os passos abaixo. -
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'
-
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())
-
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)
-
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`
-
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
-
Agora vamos usar o
KFold
classe desklearn.model_selection
para gerark
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
ondeM
é um número inteiro escolhido, o que permite obter resultados repetíveis.
- Configurando
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 foi agora dividido em
k
folds, cada um com uma lista detrain
eval
í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"
-
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 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}"] = 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.
-
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, )
-
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
-
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")
-
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
ename
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
-
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:
- Verifique se as anotações estão no formato de detecção YOLO.
- Use bibliotecas Python como
sklearn
,pandas
, epyyaml
. - Criar vetores de características a partir do seu conjunto de dados.
- Divida seu conjunto de dados usando
KFold
desklearn.model_selection
. - 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).