Definir lo que debe entregar el cargador
Escribe el contrato de salida antes de optimizar: número de elementos, tipo de cada campo, dimensiones, rango de los objetivos y regla de las entradas incompletas. Distingue el identificador del ejemplo de su posición en un batch. Una transformación puede cambiar una forma o filtrar una entrada; el programa de entrenamiento debe saber si eso está permitido.
Toma una muestra representativa que incluya un archivo común, un caso límite y el último elemento del conjunto. Abre cada elemento con exactamente la misma preparación que el Dataset. Luego examina su ensamblado. Un acceso individual exitoso no demuestra que varios resultados puedan apilarse. Para un texto, documenta el padding y la máscara; para una imagen, los canales, las dimensiones y el orden de los ejes.
Fija el alcance: datos presentes o remotos, decodificación incluida o no, transformaciones fijas o aleatorias. Mantenlo entre dos ajustes; una mejora aparente puede venir de un trabajo eliminado.
Volver a un solo proceso para leer el error
Reproduce primero con num_workers=0, shuffle=False y un batch pequeño. La carga se ejecuta entonces en el proceso principal y la traza del error suele ser más legible. La documentación de DataLoader recomienda esta posibilidad para depurar. Registra el identificador del elemento que falla antes de su decodificación, sin copiar su contenido sensible en los registros.
Procede por separación: acceso sin procesar, transformación, collate_fn y luego transferencia. Si el recorrido falla antes de la transferencia, modificar CUDA no es la primera pista. Si solo se bloquea con varios workers, examina los objetos y recursos transmitidos a esos procesos. Compara la primera iteración y las siguientes: el arranque de los workers puede explicar una espera inicial sin establecer un problema recurrente.
Un timeout puede hacer visible una espera, pero no repara ni una fuente no disponible ni un worker bloqueado. Conserva el último paso conocido y reduce el número de entradas en lugar de aumentar indefinidamente ese tiempo de espera.
Ejemplo desarrollado: tres canales esperados, una imagen diferente
Consideremos cuatro registros didácticos. Los tres primeros dan un tensor de forma [3, 16, 16], el cuarto [1, 16, 16]. Con un contrato que impone tres canales, el cuarto elemento debe identificarse antes del apilado. Este escenario no se ha ejecutado aquí; describe un resultado esperado a partir de las formas elegidas.
La función de abajo supone que cada registro tiene los campos id, x e y, que x es un tensor de CPU y que y es un índice entero. Rechaza la incoherencia en lugar de eliminar discretamente la imagen. Para tu proyecto, decide explícitamente si una imagen monocroma debe convertirse a tres canales o rechazarse en la importación. Esta decisión depende del significado de los datos y del preprocesamiento que espera el modelo.
Tras la corrección, los cuatro identificadores deben seguir presentes y el tensor ensamblado debe tener la forma [4, 3, 16, 16]. Añade una guarda adaptada a los objetivos: una imagen con las dimensiones correctas puede llevar aún una anotación no válida.
import torch
from torch.utils.data import DataLoader
def assemble(records):
for item in records:
if tuple(item["x"].shape) != (3, 16, 16):
raise ValueError(f"Forma inesperada para {item['id']}")
return {
"ids": [item["id"] for item in records],
"x": torch.stack([item["x"] for item in records]),
"y": torch.tensor([item["y"] for item in records],
dtype=torch.long),
}
# dataset es tu Dataset que produce los registros descritos.
# En un script multiproceso, crea el loader bajo el guard main.
if __name__ == "__main__":
loader = DataLoader(dataset, batch_size=4, num_workers=0,
shuffle=False, collate_fn=assemble)
iterator = iter(loader)
batch = next(iterator)Reintroducir los workers sin cambiar los datos
Pasa de cero a un número pequeño de workers conservando batch, orden y transformaciones. Prueba una época completa y luego una segunda: algunos errores solo aparecen al reiniciar un iterador o cuando ya se han consumido recursos. Aumentar el paralelismo solo es útil si el trabajo de preparación puede avanzar realmente en paralelo.
Los métodos de inicio dependen del sistema y de la versión de Python. Con spawn, protege la entrada del programa con if __name__ == '__main__' y define Dataset, collate_fn y las funciones de los workers a nivel de módulo en lugar de en lambdas locales. La documentación de procesos también explica por qué los bloqueos o hilos heredados pueden provocar cuelgues. Mantén la inicialización de los accesos propia de cada proceso cuando la biblioteca lo exija.
Para un IterableDataset, verifica la partición entre workers mediante identificadores: varios trabajadores no deben consumir cada uno todo el mismo flujo. No juzgues solo el número de batches; busca también duplicados y elementos faltantes.
Medir la espera y el rendimiento con una unidad clara
Usa dos observaciones complementarias. Un recorrido del cargador por sí solo cuenta los ejemplos preparados durante un intervalo definido. Un recorrido integrado examina qué ocurre cuando el modelo consume esos datos. El primero ayuda a aislar la preparación; no representa automáticamente el rendimiento del entrenamiento.
En tu protocolo, cuenta los ejemplos realmente entregados y luego divide entre los segundos transcurridos. Declara las pasadas excluidas por arranque, la caché de datos, las transformaciones y el número de repeticiones. Conserva los valores de cada pasada en lugar de seleccionar solo el mejor. La tabla de abajo es una hoja de registro: no está rellenada con ningún rendimiento.
Si las formas varían, un número de ejemplos por segundo puede ocultar un cambio de carga. Añade la unidad pertinente, como píxeles decodificados o tokens realmente preparados, conservando también los ejemplos. Para localizar las esperas en el bucle completo, nombra la lectura del siguiente batch por separado del cálculo.
Desplaza la tabla para leer todas las columnas.| Ajuste | Elementos verificados | Duración observada | Conclusión esperada |
|---|---|---|---|
| workers=0 | Identificadores, formas, objetivos | A medir en segundos | Referencia correcta |
| Número pequeño de workers | Mismo conjunto de entradas | A medir en segundos | Ganancia o sobrecoste real |
| Mismo ajuste, segunda época | Sin pérdidas ni duplicaciones | A medir en segundos | Efecto del arranque y de las cachés |
Tratar memoria, precarga y transferencias por separado
Los workers y los batches en espera consumen memoria del host. Vigílala durante tu prueba antes de concluir que solo cuenta la VRAM. Una precarga más profunda puede desplazar la espera a la vez que aumenta la ocupación; no garantiza más resultados por segundo. Reduce primero la variable sospechosa y compara el mismo alcance.
pin_memory y las transferencias no bloqueantes se refieren al paso de datos hacia un acelerador. La receta de optimización de PyTorch los presenta como palancas que hay que examinar junto con el hardware y la carga. No corrigen una decodificación errónea. Empieza con datos en CPU en los workers y luego organiza la transferencia en el proceso que dirige el cálculo. El beneficio y el solapamiento efectivo deben observarse, no suponerse.
Si usas persistent_workers, ten en cuenta los recursos y estados que se conservan entre dos épocas. Un ajuste que funciona bien con un solo batch no basta para verificar el cierre de archivos o la renovación de la fuente.
Aceptar un ajuste solo si los datos siguen siendo correctos
El resultado esperado es un bucle que recibe todas las entradas previstas, dentro del marco elegido, sin errores silenciosos. Compara los identificadores y las etiquetas antes y después de la optimización. Explica drop_last si descartas el último batch incompleto. Si las transformaciones son aleatorias, controla su política en lugar de exigir una igualdad de píxeles que contradiría esa política.
Conserva el ajuste más simple que responda a la necesidad medida. Un aumento del número de workers puede no mejorar nada si el almacenamiento, la decodificación o el propio modelo ya imponen un límite. Los recursos de CPU, RAM y almacenamiento de un servidor no se deducen del nombre de su GPU: especifica esas necesidades por separado cuando prepares tu entorno de Kernodeck.