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 de Ultralytics. Aprovecharemos el formato de detección de 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 del conjunto de datos mediante K-Fold.
Tanto si tu proyecto utiliza el conjunto de datos Fruit Detection como una fuente de datos personalizada, este tutorial pretende ayudarte a comprender y aplicar la validación cruzada K-Fold para reforzar la fiabilidad y solidez de tus modelos de machine learning. Aunque para este tutorial aplicamos k=5 pliegues, ten en cuenta que el número óptimo de pliegues puede variar según tu conjunto de datos y las especificidades de tu proyecto. La validación cruzada K-Fold aporta el máximo valor cuando tu conjunto de datos es pequeño, ruidoso o muy variable; para conjuntos de datos grandes y diversos, una división de entrenamiento/validación/prueba bien construida suele ser suficiente.
Empecemos.
Configuración#
-
Tus anotaciones deben estar en el formato de detección de YOLO.
-
Esta guía asume que los archivos de anotación están disponibles localmente.
-
Para nuestra demostración, utilizamos el conjunto de datos Fruit Detection.
- Este conjunto de datos contiene un total de 8479 imágenes.
- Incluye 6 etiquetas de clase, cada una con sus recuentos totales de instancias listados a continuación.
| Etiqueta de clase | Recuento de instancias |
|---|---|
| Apple | 7049 |
| Grapes | 7202 |
| Pineapple | 1613 |
| Orange | 15549 |
| Banana | 3536 |
| Watermelon | 1976 |
-
Los paquetes de Python necesarios incluyen:
ultralyticssklearnpandaspyyaml
-
Este tutorial funciona con
k=5particiones (folds). Sin embargo, debes determinar el mejor número de particiones para tu conjunto de datos específico.
-
Inicia un nuevo entorno virtual de Python (
venv) para tu proyecto y actívalo. Utilizapip(o tu gestor de paquetes preferido) para instalar:- La biblioteca de Ultralytics:
pip install -U ultralytics. De forma alternativa, puedes clonar el repositorio oficial. - Scikit-learn, pandas y PyYAML:
pip install -U scikit-learn pandas pyyaml.
- La biblioteca de Ultralytics:
-
Verifica que tus anotaciones estén en el formato de detección de YOLO.
- Para este tutorial, todos los archivos de anotaciones se encuentran en el directorio
Fruit-Detection/labels.
- Para este tutorial, todos los archivos de anotaciones se encuentran en el directorio
Generación de vectores de características para conjuntos de datos de detección de objetos#
-
Empieza creando un nuevo archivo de Python
example.pypara los pasos siguientes. -
Carga las imágenes de entrenamiento y validación configuradas a través del propietario del conjunto de datos del paquete, dejando intacta la división de pruebas.
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] -
Ahora, 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 vacío de
pandas.import pandas as pd labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images) -
Cuenta las instancias de cada etiqueta de clase presente en los archivos de anotación.
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()) -
La siguiente es una vista de muestra del DataFrame completado:
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 de imagen absolutas y las columnas corresponden a índices de etiquetas de clase. Las etiquetas faltantes siguen siendo filas de fondo con todos ceros. Esta estructura de datos permite la aplicación de K-Fold Cross Validation a un conjunto de datos de detección de objetos.
División del conjunto de datos con K-Fold#
-
Ahora utilizaremos la clase
KFolddesklearn.model_selectionpara generarkparticiones del conjunto de datos.- Importante:
- Establecer
shuffle=Truegarantiza una distribución aleatoria de las clases en tus particiones. - Al establecer
random_state=MdondeMes un número entero seleccionado, puedes obtener resultados repetibles.
- Establecer
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:
-
El conjunto de datos se ha dividido ahora en
kparticiones, cada una con una lista de índicestrainyval. Construiremos un DataFrame para mostrar estos resultados con mayor 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 para cada partición como una proporción de las clases presentes en
valfrente a 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}"] = ratioEl escenario ideal es que todas las proporciones de clase sean razonablemente similares para cada división y entre clases. Esto, sin embargo, estará sujeto a las particularidades de tu conjunto de datos.
-
Escribe listas de imágenes y un archivo YAML de conjunto de datos para cada divisió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)#
Opcionalmente, puedes guardar los registros de la división K-Fold y los DataFrames de distribución de etiquetas como archivos CSV para futuras referencias.
folds_df.to_csv(save_path / "kfold_datasplit.csv")
fold_lbl_distrb.to_csv(save_path / "kfold_label_distribution.csv")Entrenar YOLO usando divisiones de datos K-Fold#
-
Primero, carga el 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") -
A continuación, recorre los archivos YAML del conjunto de datos para ejecutar el entrenamiento. Los resultados se guardarán en un 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 utilizar la función Ultralytics data.split.autosplit para la división automática del conjunto de datos:
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)
Conclusión#
En esta guía, hemos explorado el proceso de usar validación cruzada K-Fold para entrenar el modelo de detección de objetos YOLO. Aprendimos a dividir el grupo de entrenamiento y validación en K particiones y a usar la tabla de proporciones generada para inspeccionar el equilibrio de clases después de la división aleatoria.
También exploramos el procedimiento para crear DataFrames de informe para visualizar las divisiones de datos y las distribuciones de etiquetas entre estas divisiones, brindándonos una visión clara de la estructura de nuestros conjuntos de entrenamiento y validación.
Opcionalmente, guardamos nuestros registros para futuras referencias, 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 usando cada división en un bucle, guardando nuestros resultados de entrenamiento para un análisis y comparación posteriores.
Esta técnica de validación cruzada K-Fold es una forma sólida de aprovechar al máximo los datos disponibles y ayuda a garantizar que el rendimiento de tu modelo sea fiable y coherente en diferentes subconjuntos de datos. Esto da como resultado un modelo más generalizable y fiable que es menos propenso al sobreajuste (overfitting) ante patrones de datos específicos.
Recuerda que, aunque usamos YOLO en esta guía, estos pasos son mayormente transferibles a otros modelos de aprendizaje automático. Entender estos pasos te permite aplicar la validación cruzada de manera efectiva en tus propios proyectos de aprendizaje automático.
FAQ#
La validación cruzada K-Fold es una técnica en la que el conjunto de datos se divide en 'k' subconjuntos (particiones) para evaluar el rendimiento del modelo de forma más fiable. Cada partición sirve tanto como datos de entrenamiento como de datos de 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.
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 de YOLO.
- Utiliza bibliotecas de Python como
sklearn,pandasypyyaml. - Crea vectores de características a partir de tu conjunto de datos.
- Divide tu conjunto de datos utilizando
KFolddesklearn.model_selection. - Entrena el modelo YOLO en cada división.
Para ver una guía completa, consulta la sección División del conjunto de datos K-Fold en nuestra documentación.
El flujo de trabajo de esta guía se centra en el formato de detección de YOLO, pero el mismo enfoque se adapta a todas las tareas de YOLO: la tarea cambia la forma en que compones los pliegues, no si la validación cruzada ayuda:
Tarea Diseño de pliegues detectDivide a nivel de imagen, equilibrando las distribuciones de objetos y clases entre los pliegues. Mantén las imágenes relacionadas (mismo paciente, secuencia de vídeo, cámara o sitio) dentro de un único pliegue. segmentUsa la misma estrategia a nivel de imagen que en la detección, preservando además la cobertura de máscaras y clases en cada pliegue. classifyPrefiere pliegues estratificados para que las frecuencias de las clases se mantengan equilibradas 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 un pliegue. obbDivide a nivel de imagen, manteniendo juntas las teselas o recortes de la misma escena, lo cual es especialmente importante para las imágenes aéreas. Sea cual sea la tarea, mantén las muestras casi duplicadas y relacionadas fuera de los pliegues opuestos: ese tipo de fuga infla las métricas de validación mucho más allá de lo que el modelo logrará en producción.
Ultralytics YOLO ofrece detección de objetos en tiempo real de vanguardia con alta precisión y eficiencia. Es versátil y es compatible con múltiples tareas de visión por computador como detección, segmentación de instancias, segmentación semántica y clasificación. Además, se integra sin problemas con herramientas como Ultralytics Platform para el entrenamiento y despliegue de modelos sin código. Para obtener más detalles, explora los beneficios y características en nuestra página de Ultralytics YOLO.
Tus anotaciones deben seguir el formato de detección de YOLO. Cada archivo de anotaciones debe enumerar la clase del objeto, junto con sus coordenadas de cuadro delimitador (bounding box) en la imagen. El formato YOLO garantiza un procesamiento de datos ágil y estandarizado para entrenar modelos de detección de objetos. Para obtener más información sobre el formato correcto de las anotaciones, visita la guía del formato de detección de YOLO.
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 de YOLO. Sustituye las rutas del conjunto de datos y las etiquetas de clase por 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 sólida del modelo mediante la validación cruzada K-Fold. Para ver un ejemplo práctico, revisa nuestra sección Generación de vectores de características.