DataLoader Concurrente

Introducción

En entrenamiento de redes neuronales, el DataLoader prepara los datos (lectura, decodificación, aumentado y batching) mientras la GPU ejecuta los forward/backward passes. Un buen DataLoader:

  • Solapa I/O y CPU con el trabajo de la GPU, evitando que la GPU quede libre.
  • Aprovecha todos los recursos

En esta práctica desarrollarás un DataLoader concurrente a partir de una versión secuencial proporcionada.

Descripción

  • Tienes el código base de la práctica aquí
  • Se proporciona una implementación base secuencial (dataloader.py) de un DataLoader que:
    • Recorre un directorio con imágenes,
    • Carga bytes desde disco,
    • Decodifica y aplica aumentado de datos en CPU,
    • Agrupa en lotes (batching), Y un script de entrenamiento train.py que consume los lotes generados y simula el entrenamiento de una red neuronal.
  • Tu objetivo es diseñar e implementar un nuevo DataLoader concurrente que mejore el tiempo de carga y el throughput respecto del secuencial.
  • Se proporcionan dos scripts para descargar datasets de prueba:
    • download_imagenette.shImagenette (pequeño, para unas pruebas iniciales).
    • download_ffhq_dataset.shFFHQ (más grande, para evaluación final).
  • Debes mantener la interfaz de consumo de datos del DataLoader secuencial (for batch in dataloader:...)

Arquitectura sugerida

Se propone un patrón productor–consumidor en dos etapas:

  1. Carga de ficheros (I/O) con hilos → lectura de ficheros en un buffer.
  2. Decodificación + aumentado (uso intensivo de CPU) con procesos → leer del buffer anterior, procesar y colocarlos en otro buffer.
  3. Batching en el proceso principal, consumiendo del segundo buffer y emitiendo lotes.

I/O suele escalar bien con threads. Las transformaciones intensivas de CPU se benefician de multiprocessing (evitando el GIL). Si prefieres otro diseño (todo hilos, otras etapas, paso de mensajes…), es válido siempre que cumplas los requisitos.

Recomendaciones

  • Buffers limitados: se recomienda usar colas con tamaños limitados para evitar picos de memoria.
  • Sentinel de fin: se recomienda usar un mensaje especial (p. ej., STOP) para indicar a los consumidores que no habrá más trabajo y cerrar el pipeline de forma determinista. Puedes utilizar enfoques distintos si lo prefieres.
  • Cierre limpio: asegúrate de que no haya procesos o hilos abiertos al acabar el entrenamiento.

Requisitos

  1. Correctitud
    • Sin pérdidas ni duplicados: cada imagen de entrada debe contribuir exactamente una vez al total de muestras producidas (o floor(N/B)*B si drop_last=True).
    • Sin deadlocks y sin bloqueos residuales de colas/hilos/procesos.
    • Cierre limpio: al terminar la iteración, no quedan hilos o procesos vivos.
  2. Mejora de tiempo/throughput
    • Debes mostrar mejora medible respecto al secuencial en FFHQ.
  3. Comparabilidad
    • Mantén las mismas transformaciones (resize/augment) para que la comparación con el secuencial sea justa.
  4. Interfaz
    • Tu DataLoader debe poder usarse como iterador de lotes en train.py sin cambios en el bucle principal.

Métricas a reportar

Obligatorias

  • Tiempo total y throughput (imágenes/segundo) en:
    • Imagenette (calibración),
    • FFHQ (evaluación principal).
  • Parámetros usados: n_readers, n_decoders, tamaños de buffers, batch_size, tamaño de imagen.

Adicionales

  • Uso de memoria estimado durante la ejecución (pico o media, justificando el método de medida).
  • Tamaño/ocupación media de los buffers.
  • Tiempo bloqueado en productores y consumidores (tiempo en put() / get()).

Entrega

  • Se realiza en pracdlsi en las fechas allí indicadas. Puedes entregar tantas veces como quieras, solo se corrige la ultima entrega.
  • Crea una carpeta llamada p5 y dentro de ella estarán el código y archivos de texto o PDF donde contestas a las preguntas. Esta carpeta la comprimes en un archivo llamado p5.tgz p.e. así usando el terminal: tar cfz p5.tgz p5