Ultralytics YOLO27:

Validación cruzada K-Fold con Ultralytics#

Introducción#

Esta guía completa muestra la implementación de la validación cruzada K-Fold para conjuntos de datos de detección de objetos dentro del ecosistema de Ultralytics. Utilizaremos el formato de detección de YOLO y bibliotecas clave de Python, como sklearn, pandas y PyYAML, para guiarte por la configuración necesaria, el proceso de generación de vectores de características y la ejecución de una división K-Fold del conjunto de datos.

K-fold cross validation data splitting

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 la robustez de tus modelos de aprendizaje automático. Aunque en este tutorial aplicamos k=5 particiones, ten en cuenta que el número óptimo de particiones puede variar en función de tu conjunto de datos y de las características específicas de tu proyecto. La validación cruzada K-Fold resulta más útil cuando el conjunto de datos es pequeño, ruidoso o muy variable; para conjuntos de datos grandes y diversos, normalmente basta con una división train/val/test bien construida.

Empecemos.

Configuración#

  • Tus anotaciones deben estar en el formato de detección de YOLO.

  • Esta guía presupone que los archivos de anotaciones 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, con el recuento total de instancias de cada una indicado a continuación.
Etiqueta de claseRecuento de instancias
Manzana7049
Uvas7202
Piña1613
Naranja15549
Plátano3536
Sandía1976
  • Los paquetes de Python necesarios incluyen:

    • ultralytics
    • sklearn
    • pandas
    • pyyaml
  • Este tutorial funciona con k=5 particiones. Sin embargo, debes determinar el número óptimo de particiones para tu conjunto de datos específico.

  1. Inicia un nuevo entorno virtual de Python (venv) para tu proyecto y actívalo. Utiliza pip (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.
  2. 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.

Generación de vectores de características para un conjunto de datos de detección de objetos#

  1. Empieza creando un nuevo archivo de Python example.py para los pasos siguientes.

  2. Carga las imágenes de entrenamiento y validación configuradas mediante el propietario del conjunto de datos del paquete, dejando intacta la divisió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]
  3. 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())
  4. Inicializa un DataFrame pandas vacío.

    import pandas as pd
    
    labels_df = pd.DataFrame(0.0, columns=cls_idx, index=images)
  5. Cuenta las instancias de cada etiqueta de clase presente 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())
  6. A continuación se muestra un ejemplo 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 utilizan rutas absolutas de las imágenes y las columnas corresponden a los índices de las etiquetas de clase. Las etiquetas que faltan permanecen como filas de fondo con todos los valores a cero. Esta estructura de datos permite aplicar la validación cruzada K-Fold a un conjunto de datos de detección de objetos.

División K-Fold del conjunto de datos#

  1. Ahora utilizaremos la clase KFold de sklearn.model_selection para generar k divisiones del conjunto de datos.

    • Importante:
      • Establecer shuffle=True garantiza una distribución aleatoria de las clases en tus divisiones.
      • Al establecer random_state=M, donde M es un entero elegido, puedes obtener resultados reproducibles.
    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))
  2. El conjunto de datos ya se ha dividido en k particiones, cada una con una lista de índices train y val. 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"
  3. Ahora calcularemos la distribución de las etiquetas de clase para cada partición como una proporción entre las clases presentes en val y las presentes en train.

    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 en cada división y entre las distintas clases. Sin embargo, esto dependerá de las características específicas de tu conjunto de datos.

  4. Escribe listas de imágenes y un YAML del conjunto de datos para cada división. Las listas de texto evitan copiar el conjunto de datos k veces.

    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 consultarlos en el futuro.

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#

  1. 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")
  2. A continuación, itera sobre los archivos YAML del conjunto de datos para ejecutar el entrenamiento. Los resultados se guardarán en un directorio especificado por los argumentos project y name. 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
  3. También puedes utilizar la función Ultralytics data.split.autosplit para dividir automáticamente el 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 uso de la validación cruzada K-Fold para entrenar el modelo de detección de objetos YOLO. Hemos aprendido a dividir el conjunto de entrenamiento y validación en K particiones y a utilizar la tabla de proporciones generada para examinar el equilibrio de clases tras la división aleatoria.

También hemos explorado el procedimiento para crear DataFrames de informes que permitan visualizar las divisiones de datos y las distribuciones de etiquetas entre 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 consultarlos en el futuro, lo que puede resultar especialmente útil en proyectos a gran escala o al solucionar problemas de rendimiento del modelo.

Por último, implementamos el entrenamiento real del modelo utilizando cada división en un bucle y guardando los resultados del entrenamiento para analizarlos y compararlos posteriormente.

Esta técnica de validación cruzada K-Fold es una forma robusta de aprovechar al máximo los datos disponibles y ayuda a garantizar que el rendimiento del modelo sea fiable y coherente en distintos subconjuntos de datos. El resultado es un modelo más generalizable y fiable, menos propenso a sobreajustarse a patrones de datos específicos.

Recuerda que, aunque en esta guía hemos utilizado YOLO, estos pasos se pueden transferir en su mayor parte a otros modelos de aprendizaje automático. Comprender estos pasos te permite aplicar la validación cruzada de forma eficaz en tus propios proyectos de aprendizaje automático.

Preguntas frecuentes#

  • 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 validación. En el contexto de la detección de objetos, utilizar la validación cruzada K-Fold ayuda a garantizar que el rendimiento de tu modelo Ultralytics YOLO sea robusto y generalizable en distintas divisiones 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, debes seguir estos pasos:

    1. Verifica que las anotaciones están en el formato de detección de YOLO.
    2. Utiliza bibliotecas de Python como sklearn, pandas y pyyaml.
    3. Crea vectores de características a partir de tu conjunto de datos.
    4. Divide tu conjunto de datos utilizando KFold de sklearn.model_selection.
    5. Entrena el modelo YOLO en cada división.

    Para consultar una guía completa, visita la sección División K-Fold del conjunto de datos de nuestra documentación.

  • El flujo de trabajo de esta guía está dirigido al formato de detección de YOLO, pero el mismo enfoque se adapta a todas las tareas de YOLO: la tarea cambia la forma de componer las particiones, no si la validación cruzada resulta útil:

    TareaDiseño de particiones
    detectDivide a nivel de imagen, equilibrando 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) dentro de una única partición.
    segmentUtiliza la misma estrategia a nivel de imagen que en detección y conserva además la cobertura de máscaras y clases en cada partición.
    classifyDa preferencia a las particiones estratificadas para que las frecuencias de clase 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 una partición.
    obbDivide a nivel de imagen y mantén juntos los mosaicos o recortes de una misma escena, algo especialmente importante en imágenes aéreas.

    Independientemente de la tarea, mantén las muestras casi duplicadas y relacionadas fuera de particiones opuestas: este tipo de fuga infla las métricas de validación muy por encima de lo que el modelo logrará en producción.

  • Ultralytics YOLO ofrece detección de objetos de última generación en tiempo real, con gran precisión y eficiencia. Es versátil y admite varias tareas de visión artificial, 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 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 de YOLO. Cada archivo de anotaciones debe indicar la clase del objeto y las coordenadas de su cuadro delimitador 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 robusta del modelo mediante la validación cruzada K-Fold. Para ver un ejemplo práctico, consulta nuestra sección Generación de vectores de características.

Comentarios