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.
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 classe | Número de instâncias |
|---|---|
| Maçã | 7049 |
| Uvas | 7202 |
| Ananás | 1613 |
| Laranja | 15549 |
| Banana | 3536 |
| Melancia | 1976 |
-
Os pacotes Python necessários incluem:
ultralyticssklearnpandaspyyaml
-
Este tutorial funciona com
k=5folds. No entanto, deves determinar o melhor número de folds para o teu conjunto de dados específico.
-
Inicia um novo ambiente virtual Python (
venv) para o teu projeto e ativa-o. Utilizapip(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.
- A biblioteca Ultralytics:
-
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.
- Para este tutorial, todos os ficheiros de anotação encontram-se no diretório
Geração de vetores de características para um conjunto de dados de deteção de objetos#
-
Começa por criar um novo ficheiro Python
example.pypara os passos abaixo. -
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] -
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()) -
Inicializa um DataFrame
pandasvazio.import pandas as pd labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images) -
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()) -
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#
-
Agora utilizaremos a classe
KFolddesklearn.model_selectionpara gerarksplits do conjunto de dados.- Importante:
- Definir
shuffle=Truegarante uma distribuição aleatória das classes nas tuas divisões. - Ao definires
random_state=M, em queMé um número inteiro escolhido, podes obter resultados repetíveis.
- Definir
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)) - Importante:
-
O conjunto de dados foi agora dividido em
kfolds, cada um com uma lista de índicestraineval. 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" -
Agora calcularemos a distribuição das etiquetas de classe para cada fold como uma proporção entre as classes presentes em
vale as 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 classes sejam razoavelmente semelhantes em cada divisão e entre as classes. No entanto, isso dependerá das especificidades do teu conjunto de dados.
-
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
kvezes.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#
-
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") -
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
projectename. 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 -
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:
- Verifica se as anotações estão no formato de deteção YOLO.
- Utiliza bibliotecas Python como
sklearn,pandasepyyaml. - Cria vetores de características a partir do teu conjunto de dados.
- Divide o teu conjunto de dados utilizando
KFolddesklearn.model_selection. - 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:
Tarefa Conceçã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.