Internals de PyTorch (2019)
(blog.ezyang.com)- Una explicación de la estructura interna de PyTorch, como guía para quienes quieran contribuir al codebase en C++ de PyTorch
- El objetivo de este artículo es ayudar a entender la estructura de la biblioteca de tensores de PyTorch y la técnica de diferenciación automática (autograd), además de orientarse dentro del codebase
Estructura básica de los tensores de PyTorch
- En PyTorch, el tensor es la estructura de datos más básica
- Un tensor es una estructura de datos de n dimensiones que puede almacenar valores escalares como punto flotante (
float), enteros (int), etc. - Los tensores incluyen los siguientes metadatos:
- tamaño (size): información sobre las dimensiones del tensor
- dtype: tipo de dato almacenado (por ejemplo,
float32,int64, etc.) - device: ubicación donde se almacenan los datos (
CPU,CUDA, etc.) - stride: información de desplazamiento en la memoria física de los datos
-
El papel de
stridestridese usa para convertir índices lógicos en ubicaciones de memoria físicastrideestablece un desplazamiento para cada dimensión y determina la ubicación de memoria física multiplicando el índice por el valor destride- Gracias a
stride, es posible ver los mismos datos de otra forma como una view sin crear un tensor nuevo
Concepto de tensor y almacenamiento (Storage)
- En PyTorch, el tensor no guarda directamente los datos reales → los datos se administran en el almacenamiento (
Storage) - Tensor = tamaño + dtype + device + stride + offset
- Varios tensores pueden compartir un mismo almacenamiento → esto habilita el concepto de vista (View)
- La separación entre almacenamiento y tensor permite usar la memoria de forma eficiente
Proceso de dispatch en las operaciones de tensor
- En PyTorch, las operaciones pasan por dos etapas de dispatch:
- Dispatch basado en tipo de dispositivo y layout
- Se ejecuta código de implementación distinto según sea un tensor de
CPUo deCUDA
- Se ejecuta código de implementación distinto según sea un tensor de
- Dispatch basado en dtype
- Se llama a distintos kernels según el tipo de dato, como
floatvs.int
- Se llama a distintos kernels según el tipo de dato, como
- Dispatch basado en tipo de dispositivo y layout
Modelo de extensión de tensores en PyTorch
-
Los tres elementos principales de extensión de un tensor:
- Device: define cómo se asigna la memoria en
CPU,GPU,TPU, etc. - Layout: define cómo se almacena el tensor en memoria (por ejemplo, almacenamiento contiguo, almacenamiento disperso (sparse), etc.)
- dtype: define el tipo de dato que se almacenará en cada elemento del tensor
- Device: define cómo se asigna la memoria en
-
Opciones de extensión:
- Es posible extender tensores modificando directamente el código de PyTorch
- Es posible escribir una clase wrapper que envuelva tensores existentes
- Si se necesita el wrapper durante la diferenciación automática, hace falta una extensión directa
Cómo funciona la diferenciación automática (Autograd)
- PyTorch realiza diferenciación automática basada en reverse-mode differentiation
- Durante la operación forward se crea un grafo → durante backpropagation se recorre ese grafo para realizar la diferenciación
- Autograd administra información adicional como la siguiente:
- AutogradMeta: metadatos conectados al tensor que se usan en backpropagation
- Registra los resultados de las operaciones y realiza la diferenciación durante backpropagation
Estructura del código de PyTorch y ubicación de archivos
- Directorios principales dentro del codebase de PyTorch:
torch/→ módulo de Python (código Python)torch/csrc/→ bindings entre Python y C++, motor de diferenciación automática, compilador JIT, etc.aten/→ definición de operaciones de tensor (incluye la mayoría de las operaciones principales)c10/→ definición de estructuras de datos base como tensor y almacenamiento
Proceso de ejecución de operaciones en PyTorch
- Ejemplo: proceso de ejecución al llamar
torch.add():- Conversión de argumentos desde Python a código C++
- Se realiza el dispatch en
VariableType - Se realiza el dispatch basado en
device/layout - Ejecución final del kernel
Proceso y herramientas para escribir kernels
- En PyTorch, un kernel se escribe siguiendo pasos como estos:
- Escritura de metadatos de la operación: definir la firma de la función, dispositivos compatibles y tipos de dato soportados
- Validación de entrada: verificar entradas como dimensiones, tipos, etc.
- Asignación del tensor de salida
- Dispatch por dtype: ejecutar el kernel según el tipo de dato
- Procesamiento en paralelo: en
CPUse usaOpenMP, y enCUDAse usa paralelización integrada - Acceso a datos y cálculo: uso de
TensorAccessor,TensorIterator, etc.
Macros principales de dispatch
- AT_DISPATCH_ALL_TYPES → realiza dispatch según el
dtype - Hay soporte mediante macros para diversos tipos de dato → esto permite optimizar el rendimiento
Consejos para optimización de rendimiento y eficiencia de trabajo
- Minimizar cambios en archivos de encabezado → al modificarlos se fuerza un rebuild completo del código
- Configurar un entorno de desarrollo local → minimiza el tiempo perdido al depender de CI
- Usar
ccache→ puede ahorrar tiempo de recompilación - Usar un servidor potente → puede reducir el tiempo de compilación en C++ y de build de
CUDA
Guía para contribuir a PyTorch
- Buenos puntos de inicio para contribuir:
- Issues con la etiqueta
triaged→ issues ya revisados por desarrolladores de PyTorch - Mejoras de documentación y ayuda para reproducir bugs
- Dar opinión sobre los RFC de PyTorch (propuestas de funcionalidad)
- Issues con la etiqueta
- PyTorch ha crecido gracias a contribuyentes de código abierto y da la bienvenida a la participación de la comunidad
Aún no hay comentarios.