Validación cruzada K-Fold con Ultralytics#
Introducción#
Esta guía completa explica cómo implementar la validación cruzada K-Fold en conjuntos de datos de detección de objetos dentro del ecosistema Ultralytics. Usaremos el formato de detección YOLO y bibliotecas clave de Python como sklearn, pandas y PyYAML para guiarte en la configuración necesaria, el proceso de generación de vectores de características y la creación de una partición K-Fold del conjunto de datos.
Tanto si tu proyecto utiliza el conjunto de datos Fruit Detection como una fuente de datos personalizada, este tutorial te ayudará a comprender y aplicar la validación cruzada K-Fold para mejorar la fiabilidad y la robustez de tus modelos de aprendizaje automático. En este tutorial aplicamos k=5 particiones, pero ten en cuenta que el número óptimo de particiones puede variar según tu conjunto de datos y las características de tu proyecto. La validación cruzada K-Fold aporta más valor cuando el conjunto de datos es pequeño, ruidoso o muy variable; para conjuntos de datos grandes y diversos, suele bastar con una partición train/val/test bien diseñada.
Empecemos.
Configuración#
-
Las anotaciones deben estar en el formato de detección YOLO.
-
Esta guía presupone que los archivos de anotaciones están disponibles localmente.
-
Para nuestra demostración, usamos el conjunto de datos Fruit Detection.
- Este conjunto de datos contiene un total de 8479 imágenes.
- Incluye 6 etiquetas de clase, cuyos recuentos totales de instancias se indican a continuación.
| Etiqueta de clase | Recuento de instancias |
|---|---|
| Manzana | 7049 |
| Uvas | 7202 |
| Piña | 1613 |
| Naranja | 15549 |
| Plátano | 3536 |
| Sandía | 1976 |
-
Los paquetes de Python necesarios son:
ultralyticssklearnpandaspyyaml
-
En este tutorial se trabaja con
k=5particiones. Sin embargo, debes determinar el número de particiones más adecuado para tu conjunto de datos.
-
Crea un entorno virtual nuevo de Python (
venv) para tu proyecto y actívalo. Usapip(o tu gestor de paquetes preferido) para instalar:- La biblioteca Ultralytics:
pip install -U ultralytics. También puedes clonar el repositorio oficial. - Scikit-learn, pandas y PyYAML:
pip install -U scikit-learn pandas pyyaml.
- La biblioteca Ultralytics:
-
Comprueba que tus anotaciones estén en el formato de detección YOLO.
- En este tutorial, todos los archivos de anotaciones se encuentran en el directorio
Fruit-Detection/labels.
- En este tutorial, todos los archivos de anotaciones se encuentran en el directorio
Generación de vectores de características para un conjunto de datos de detección de objetos#
-
Empieza creando un nuevo archivo de Python
example.pypara seguir los pasos que se indican a continuación. -
Carga las imágenes de entrenamiento y validación configuradas mediante el propietario del conjunto de datos del paquete, sin modificar la partición de prueba.
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] -
A continuación, lee el contenido del archivo YAML del conjunto de datos y extrae los índices de las etiquetas de clase.
classes = data["names"] cls_idx = sorted(classes.keys()) -
Inicializa un DataFrame
pandasvacío.import pandas as pd labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images) -
Cuenta las instancias de cada etiqueta de clase presentes en los archivos de anotaciones.
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()) -
A continuación se muestra un ejemplo del DataFrame con datos:
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 usan rutas absolutas de imágenes y las columnas corresponden a los índices de las etiquetas de clase. Las etiquetas ausentes se representan mediante filas con ceros en todas las columnas. Esta estructura de datos permite aplicar la validación cruzada K-Fold a un conjunto de datos de detección de objetos.
Partición K-Fold del conjunto de datos#
-
Ahora usaremos la clase
KFolddesklearn.model_selectionpara generarkparticiones del conjunto de datos.- Importante:
- Al establecer
shuffle=True, se garantiza una distribución aleatoria de las clases en las particiones. - Si estableces
random_state=MconMcomo un entero elegido, obtendrás resultados reproducibles.
- Al establecer
from sklearn.model_selection import KFold ksplit = 5 kf = KFold(n_splits=ksplit, shuffle=True, random_state=20) # establecer random_state para obtener resultados reproducibles kfolds = list(kf.split(labels_df)) - Importante:
-
El conjunto de datos ya está dividido en
kparticiones, cada una con una lista de índicestrainyval. Crearemos un DataFrame para mostrar estos resultados con más claridad.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" -
Ahora calcularemos la distribución de las etiquetas de clase de cada partición como la proporción entre las clases presentes en
valy las 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}"] = ratioLo ideal es que las proporciones de todas las clases sean razonablemente similares en cada partición y entre las distintas clases. Sin embargo, esto dependerá de las características de tu conjunto de datos.
-
Escribe listas de imágenes y un YAML del conjunto de datos para cada partición. Las listas de texto evitan copiar el conjunto de datos
kveces.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 registros (opcional)#
Si quieres, puedes guardar como archivos CSV los registros de la partición K-Fold y los DataFrames de distribución de etiquetas para consultarlos más adelante.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")Entrenar YOLO con particiones de datos K-Fold#
-
Primero, carga el modelo YOLO.
from ultralytics import YOLO weights_path = "path/to/weights.pt" # usa yolo26n.pt para un modelo pequeño model = YOLO(weights_path, task="detect") -
A continuación, recorre los archivos YAML del conjunto de datos para ejecutar el entrenamiento. Los resultados se guardarán en el directorio especificado por los argumentos
projectyname. De forma predeterminada, 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 puedes usar la función Ultralytics data.split.autosplit para dividir automáticamente el conjunto de datos:
from ultralytics.data.split import autosplit # Dividir automáticamente el conjunto de datos en 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 explicado cómo usar la validación cruzada K-Fold para entrenar el modelo YOLO de detección de objetos. Hemos aprendido a dividir el conjunto de entrenamiento y validación en K particiones y a utilizar la tabla de proporciones generada para analizar el equilibrio entre clases después de la división aleatoria.
También hemos explicado cómo crear DataFrames de informes para visualizar las particiones de datos y la distribución de etiquetas entre ellas, lo que nos permite comprender claramente la estructura de los conjuntos de entrenamiento y validación.
De forma opcional, hemos guardado nuestros registros para consultarlos más adelante, algo que puede resultar especialmente útil en proyectos a gran escala o al investigar problemas de rendimiento del modelo.
Por último, hemos implementado el entrenamiento del modelo propiamente dicho mediante un bucle que utiliza cada partición y guarda los resultados del entrenamiento para analizarlos y compararlos más adelante.
Esta técnica de validación cruzada K-Fold permite aprovechar al máximo los datos disponibles y ayuda a garantizar que el rendimiento del modelo sea fiable y uniforme en distintos subconjuntos de datos. Así se obtiene un modelo más generalizable y fiable, con menos probabilidades de sobreajustarse a patrones de datos concretos.
Ten en cuenta que, aunque en esta guía hemos usado YOLO, estos pasos se pueden aplicar en gran medida a otros modelos de aprendizaje automático. Si los comprendes, podrás aplicar la validación cruzada con eficacia en tus propios proyectos de aprendizaje automático.
Preguntas frecuentes#
La validación cruzada K-Fold es una técnica que divide el conjunto de datos en «k» subconjuntos (particiones) para evaluar el rendimiento del modelo de forma más fiable. Cada partición se utiliza tanto como datos de entrenamiento como de validación. En la detección de objetos, la validación cruzada K-Fold ayuda a garantizar que el rendimiento de tu modelo Ultralytics YOLO sea sólido y generalizable en distintas particiones de datos, lo que mejora su fiabilidad. Para consultar instrucciones detalladas sobre cómo configurar la validación cruzada K-Fold con Ultralytics YOLO, visita Validación cruzada K-Fold con Ultralytics.
Para implementar la validación cruzada K-Fold con Ultralytics YOLO, sigue estos pasos:
- Comprueba que las anotaciones estén en el formato de detección YOLO.
- Usa bibliotecas de Python como
sklearn,pandasypyyaml. - Crea vectores de características a partir de tu conjunto de datos.
- Divide el conjunto de datos con
KFolddesklearn.model_selection. - Entrena el modelo YOLO con cada partición.
Encontrarás una guía completa en la sección Partición K-Fold del conjunto de datos de nuestra documentación.
El flujo de trabajo de esta guía está pensado para el formato de detección YOLO, pero el mismo enfoque se adapta a todas las tareas de YOLO: la tarea determina cómo se componen las particiones, no si la validación cruzada resulta útil:
Tarea Diseño de las particiones detectDivide las imágenes y equilibra la distribución de objetos y clases entre las particiones. Mantén las imágenes relacionadas (del mismo paciente, secuencia de vídeo, cámara o ubicación) en una sola partición. segmentUsa la misma estrategia por imagen que en detección y, además, garantiza que haya máscaras y clases representadas en cada partición. classifyDa preferencia a las particiones estratificadas para mantener equilibrada la frecuencia de las clases entre el entrenamiento y la validación. poseDivide por sujeto o secuencia para que la misma persona o animal nunca aparezca en ambos lados de una partición. obbDivide las imágenes y mantén juntos los mosaicos o recortes de una misma escena; esto es especialmente importante en las imágenes aéreas. Sea cual sea la tarea, evita que las muestras casi duplicadas o relacionadas queden en particiones opuestas: ese tipo de fuga de datos infla las métricas de validación mucho más allá del rendimiento que alcanzará el modelo en producción.
Ultralytics YOLO ofrece detección de objetos en tiempo real de última generación, con gran precisión y eficiencia. Es versátil y admite varias tareas de visión artificial, como la detección, la segmentación de instancias, la segmentación semántica y la clasificación. Además, se integra sin problemas con herramientas como Ultralytics Platform para entrenar y desplegar modelos sin código. Para obtener más información, consulta las ventajas y funciones en nuestra página de Ultralytics YOLO.
Tus anotaciones deben seguir el formato de detección YOLO. Cada archivo de anotaciones debe indicar la clase del objeto y las coordenadas de su caja delimitadora en la imagen. El formato YOLO garantiza un procesamiento de datos optimizado y estandarizado para entrenar modelos de detección de objetos. Para obtener más información sobre el formato correcto de las anotaciones, consulta la guía del formato de detección YOLO.
Sí, puedes usar 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. Sustituye las rutas del conjunto de datos y las etiquetas de clase por las correspondientes a tu conjunto de datos personalizado. Esta flexibilidad permite que cualquier proyecto de detección de objetos se beneficie de una evaluación robusta del modelo mediante la validación cruzada K-Fold. Para ver un ejemplo práctico, consulta la sección Generación de vectores de características.