Validación cruzada K-Fold con Ultralytics
Introducción
Esta guía completa ilustra la implementación de la validación cruzada K-Fold para conjuntos de datos de detección de objetos dentro del ecosistema Ultralytics. Aprovecharemos el formato de detección YOLO y bibliotecas clave de python como sklearn, pandas y PyYaml para guiarte a través de la configuración necesaria, el proceso de generación de vectores de características y la ejecución de una división de conjunto de datos K-Fold.
Ya sea que su proyecto involucre el conjunto de datos Fruit Detection o una fuente de datos personalizada, este tutorial tiene como objetivo ayudarlo a comprender y aplicar la validación cruzada K-Fold para reforzar la confiabilidad y solidez de su aprendizaje automático modelos. Mientras aplicamos k=5
pliegues para este tutorial, tenga en cuenta que el número óptimo de pliegues puede variar dependiendo de su conjunto de datos y los detalles de su proyecto.
¡Sin más preámbulos, vamos a sumergirnos!
Configuración
-
Sus anotaciones deben estar en el formato de detección YOLO.
-
Esta guía asume que los archivos de anotación están disponibles localmente.
-
Para nuestra demostración, utilizamos el conjunto de datos de Detección de Frutas.
- Este conjunto de datos contiene un total de 8479 imágenes.
- Incluye 6 etiquetas de clase, cada una con su recuento total de instancias enumeradas a continuación.
Etiqueta de clase | Número de instancias |
---|---|
Apple | 7049 |
Uvas | 7202 |
Piña | 1613 |
Naranja | 15549 |
Plátano | 3536 |
Sandía | 1976 |
-
Los paquetes de python necesarios incluyen:
ultralytics
sklearn
pandas
pyyaml
-
Este tutorial opera con
k=5
folds. Sin embargo, debes determinar el número óptimo de folds para tu conjunto de datos específico. -
Iniciar un nuevo entorno virtual de Python (
venv
) para tu proyecto y actívalo. Usapip
(o su gestor de paquetes preferido) para instalar:- La biblioteca Ultralytics:
pip install -U ultralytics
. Alternativamente, puede clonar el oficial repo. - Scikit-learn, pandas y PyYAML:
pip install -U scikit-learn pandas pyyaml
.
- La biblioteca Ultralytics:
-
Verifica que tus anotaciones estén en el formato de detección YOLO.
- Para este tutorial, todos los archivos de anotación se encuentran en el
Fruit-Detection/labels
directorio.
- Para este tutorial, todos los archivos de anotación se encuentran en el
Generación de vectores de características para el conjunto de datos de detección de objetos
-
Comienza creando un nuevo
example.py
Archivo Python para los pasos a continuación. -
Proceda a recuperar todos los archivos de etiquetas de su conjunto de datos.
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'
-
Ahora, lea el contenido del archivo YAML del conjunto de datos y extraiga los índices de las etiquetas de clase.
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 un vacío
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)
-
Cuente las instancias de cada etiqueta de clase presente en los archivos de anotación.
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`
-
La siguiente es una vista de muestra del DataFrame rellenado:
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
Las filas indexan los archivos de etiquetas, cada uno correspondiente a una imagen de su conjunto de datos, y las columnas corresponden a los índices de etiquetas de clase. Cada fila representa un pseudo vector de características, con el recuento de cada etiqueta de clase presente en su conjunto de datos. Esta estructura de datos permite la aplicación de la Validación Cruzada K-Fold a un conjunto de datos de detección de objetos.
División del conjunto de datos K-Fold
-
Ahora usaremos el
KFold
clase desklearn.model_selection
para generark
divisiones del conjunto de datos.- Importante:
- Configuración
shuffle=True
asegura una distribución aleatoria de las clases en tus divisiones. - Al establecer
random_state=M
dondeM
es un entero elegido, puede obtener resultados repetibles.
- Configuración
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:
-
El conjunto de datos ahora se ha dividido en
k
folds, cada uno con una lista detrain
yval
índices. Construiremos un DataFrame para mostrar estos resultados más 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"
-
Ahora calcularemos la distribución de las etiquetas de clase para cada pliegue como una proporción de las clases presentes en
val
a los presentes entrain
.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
Lo ideal es que todas las proporciones de clases sean razonablemente similares para cada división y entre clases. Sin embargo, esto estará sujeto a las características específicas de tu conjunto de datos.
-
A continuación, creamos los directorios y los archivos YAML del conjunto de datos para cada división.
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 último, copia las imágenes y las etiquetas en el directorio respectivo ('train' o 'val') para cada división.
- NOTA: El tiempo requerido para esta porción del código variará según el tamaño de su conjunto de datos y el hardware de su 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)
Guardar registros (opcional)
Opcionalmente, puede guardar los registros de la división K-Fold y los DataFrames de distribución de etiquetas como archivos CSV para referencia futura.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")
Entrena YOLO usando Divisiones de Datos K-Fold
-
Primero, cargue el 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")
-
A continuación, itere sobre los archivos YAML del conjunto de datos para ejecutar el entrenamiento. Los resultados se guardarán en un directorio especificado por el
project
yname
argumentos. Por defecto, este directorio es 'runs/detect/train#' donde # es un índice entero.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
-
También puede usar la función Ultralytics data.utils.autosplit para la división automática del conjunto de datos:
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)
Conclusión
En esta guía, hemos explorado el proceso de uso de la validación cruzada K-Fold para entrenar el modelo de detección de objetos YOLO. Aprendimos cómo dividir nuestro conjunto de datos en K particiones, asegurando una distribución de clases equilibrada en los diferentes pliegues.
También exploramos el procedimiento para crear DataFrames de informes para visualizar las divisiones de datos y las distribuciones de etiquetas en estas divisiones, lo que nos proporciona una visión clara de la estructura de nuestros conjuntos de entrenamiento y validación.
Opcionalmente, guardamos nuestros registros para referencia futura, lo cual podría ser particularmente útil en proyectos a gran escala o al solucionar problemas de rendimiento del modelo.
Finalmente, implementamos el entrenamiento del modelo real utilizando cada división en un bucle, guardando nuestros resultados de entrenamiento para su posterior análisis y comparación.
Esta técnica de validación cruzada de K-Fold es una forma sólida de aprovechar al máximo los datos disponibles y ayuda a garantizar que el rendimiento de su modelo sea confiable y consistente en diferentes subconjuntos de datos. Esto da como resultado un modelo más generalizable y confiable que es menos probable que se sobreajuste a patrones de datos específicos.
Recuerde que, aunque hemos utilizado YOLO en esta guía, estos pasos son en su mayoría transferibles a otros modelos de aprendizaje automático. La comprensión de estos pasos le permite aplicar la validación cruzada de forma eficaz en sus propios proyectos de aprendizaje automático. ¡Feliz codificación!
Preguntas frecuentes
¿Qué es la validación cruzada K-Fold y por qué es útil en la detección de objetos?
La validación cruzada K-Fold es una técnica en la que el conjunto de datos se divide en 'k' subconjuntos (folds) para evaluar el rendimiento del modelo de forma más fiable. Cada fold sirve como datos de entrenamiento y validación. En el contexto de la detección de objetos, el uso de la validación cruzada K-Fold ayuda a garantizar que el rendimiento de tu modelo Ultralytics YOLO sea robusto y generalizable en diferentes divisiones de datos, mejorando su fiabilidad. Para obtener instrucciones detalladas sobre cómo configurar la validación cruzada K-Fold con Ultralytics YOLO, consulta Validación cruzada K-Fold con Ultralytics.
¿Cómo implemento la validación cruzada K-Fold utilizando Ultralytics YOLO?
Para implementar la validación cruzada K-Fold con Ultralytics YOLO, debes seguir estos pasos:
- Verifica que las anotaciones estén en el formato de detección YOLO.
- Utiliza bibliotecas de python como
sklearn
,pandas
, ypyyaml
. - Cree vectores de características a partir de su conjunto de datos.
- Divide tu conjunto de datos usando
KFold
desklearn.model_selection
. - Entrena el modelo YOLO en cada división.
Para una guía completa, consulta la sección División del conjunto de datos K-Fold en nuestra documentación.
¿Por qué debería usar Ultralytics YOLO para la detección de objetos?
Ultralytics YOLO ofrece detección de objetos en tiempo real de última generación con alta precisión y eficiencia. Es versátil y admite múltiples tareas de visión artificial como detección, segmentación y clasificación. Además, se integra perfectamente con herramientas como Ultralytics HUB para el entrenamiento e implementación de modelos sin código. Para obtener más detalles, explore los beneficios y las características en nuestra página de Ultralytics YOLO.
¿Cómo puedo asegurar que mis anotaciones estén en el formato correcto para Ultralytics YOLO?
Sus anotaciones deben seguir el formato de detección YOLO. Cada archivo de anotación debe enumerar la clase de objeto, junto con las coordenadas de su cuadro delimitador en la imagen. El formato YOLO garantiza un procesamiento de datos optimizado y estandarizado para el entrenamiento de modelos de detección de objetos. Para obtener más información sobre el formato de anotación adecuado, visite la guía del formato de detección YOLO.
¿Puedo usar la validación cruzada K-Fold con conjuntos de datos personalizados que no sean la detección de frutas?
Sí, puedes utilizar la validación cruzada K-Fold con cualquier conjunto de datos personalizado siempre que las anotaciones estén en el formato de detección YOLO. Reemplaza las rutas del conjunto de datos y las etiquetas de clase con las específicas de tu conjunto de datos personalizado. Esta flexibilidad garantiza que cualquier proyecto de detección de objetos pueda beneficiarse de una evaluación robusta del modelo utilizando la validación cruzada K-Fold. Para obtener un ejemplo práctico, revisa nuestra sección de Generación de vectores de características.