3 puntos por GN⁺ 2023-11-13 | 1 comentarios | Compartir por WhatsApp
  • El desarrollo de IA ya no puede depender solo de la ejecución secuencial típica de la CPU; para manejar bien el rendimiento de entrenamiento e inferencia, hay que entender el modelo de procesamiento masivo en paralelo de la GPU
  • Una CPU, por lo general, tiene entre 2 y 16 núcleos en equipos de consumo y destaca en tareas de un solo hilo y con bifurcaciones condicionales, mientras que una GPU, con miles de núcleos pequeños, resulta ventajosa para operaciones matriciales, procesamiento de imágenes y deep learning
  • AWS ofrece entornos de ejecución con GPU como P3/P4, P5/Inf1, G4 y Amazon SageMaker; p3.2xlarge cuesta $3.06 por hora, p5.48xlarge $98.32 y g4dn.xlarge alrededor de $0.526
  • CUDA de NVIDIA permite a los desarrolladores manejar directamente el flujo de ejecución en paralelo, desde la asignación de memoria en GPU y la copia de datos hasta la ejecución de kernels y la compilación
  • Los ejemplos de suma de arreglos, generación de Mandelbrot y una CNN para clasificar gatos y perros muestran cómo dividir bucles secuenciales en hilos de GPU; en Mandelbrot, el tiempo baja de 4.07 segundos en CPU a 0.0046 segundos en GPU

Por qué no basta con saber solo de CPU

  • Muchos desarrolladores han aprendido a programar y resolver problemas con un enfoque centrado en la CPU, pero la CPU funciona en esencia apoyándose en una arquitectura secuencial
  • Una CPU tradicional ejecuta instrucciones de forma lineal y optimiza unos pocos núcleos potentes para el rendimiento de un solo hilo
  • Cuando hay que procesar varias tareas al mismo tiempo, el costo de atenderlas una por una crece debido al enfoque de ejecución secuencial
  • Es posible mejorar el rendimiento con multithreading, pero la filosofía de diseño base de la CPU sigue estando más cerca de la ejecución secuencial

Modelos de IA y procesamiento en paralelo

  • Las arquitecturas modernas de IA, como Transformer, mejoran el rendimiento del entrenamiento aprovechando el procesamiento en paralelo
  • Mientras que una RNN opera de forma secuencial, un Transformer como GPT puede procesar varias palabras al mismo tiempo, lo que mejora la eficiencia del entrenamiento y la capacidad del modelo
  • El entrenamiento en paralelo hace posibles modelos más grandes, y los modelos más grandes sientan la base para producir mejores resultados
  • El paralelismo no solo se aplica al procesamiento de lenguaje natural, sino también al reconocimiento de imágenes
    • AlexNet es un ejemplo de cómo procesar varias partes de una imagen al mismo tiempo para identificar patrones
  • Debido a que la CPU está diseñada en torno al rendimiento de un solo hilo, le resulta difícil distribuir y ejecutar de manera eficiente la gran cantidad de cómputo paralelo que exigen los modelos de IA complejos

Cómo la GPU reduce los cuellos de botella

  • La GPU está diseñada con una estructura que usa muchos núcleos pequeños y especializados en lugar de los núcleos grandes y potentes de la CPU
  • El paralelismo de la GPU se luce especialmente en cargas de trabajo donde se repite masivamente el mismo tipo de operación, como el renderizado gráfico y los cálculos matemáticos complejos
  • Frameworks de deep learning como TensorFlow están optimizados para aprovechar el rendimiento de la GPU y acelerar el entrenamiento y la inferencia de modelos
  • El entrenamiento de redes neuronales incluye muchas operaciones matriciales, y la GPU destaca al paralelizarlas gracias a su gran cantidad de núcleos

Diferencias de rol entre CPU y GPU

  • CPU

    • La CPU está diseñada con enfoque en el procesamiento secuencial, por lo que destaca en tareas que ejecutan un flujo de instrucciones de manera lineal
    • Es adecuada para cómputo de propósito general, tareas del sistema y procesamiento de algoritmos complejos con bifurcaciones condicionales
    • Una CPU de consumo suele tener una cantidad relativamente baja de núcleos, normalmente en el rango de 2 a 16 núcleos
    • Cada núcleo puede procesar de forma independiente su propio conjunto de instrucciones
  • GPU

    • La GPU está diseñada con una arquitectura paralela, por lo que es eficiente para manejar muchas subtareas al mismo tiempo
    • Es ventajosa para renderizado gráfico, cálculos matemáticos complejos y ejecución de algoritmos paralelizables
    • Divide el trabajo en unidades paralelas más pequeñas para procesar múltiples operaciones de forma simultánea
    • Los núcleos de una GPU suelen contarse por miles y se organizan en streaming multiprocessors (SMs) o estructuras similares
    • Es adecuada para tareas que manejan muchos datos al mismo tiempo, como procesamiento de imagen y video, deep learning y simulaciones científicas

Entornos con GPU que puedes elegir en AWS

  • AWS ofrece varias instancias con GPU que pueden usarse para tareas como machine learning
  • Instancias GPU de propósito general

    • P3 y P4 son instancias GPU de propósito general adecuadas para diversas cargas de trabajo
    • Pueden usarse para entrenamiento e inferencia de machine learning, procesamiento de imágenes y codificación de video
    • p3.2xlarge cuesta $3.06 por hora y ofrece 1 GPU NVIDIA Tesla V100 con 16 GB de memoria GPU
  • Instancias optimizadas para inferencia

    • La inferencia es el proceso de introducir datos en tiempo real en un modelo de IA ya entrenado para hacer predicciones o resolver tareas
    • P5 e Inf1 están orientadas a inferencia de machine learning, donde importan la baja latencia y la eficiencia en costos
    • p5.48xlarge cuesta $98.32 por hora y ofrece 8 GPU NVIDIA H100, cada una con 80 GB de memoria, para un total de 640 GB de memoria de video
  • Instancias optimizadas para gráficos

    • Las G4 instances están diseñadas para manejar tareas intensivas en gráficos
    • Los desarrolladores de videojuegos pueden usar instancias G4 para renderizar gráficos 3D para juegos
    • g4dn.xlarge cuesta $0.526 por hora y usa 1 GPU NVIDIA T4 con 16 GB de memoria
  • Servicio administrado de machine learning

    • Amazon SageMaker es un servicio administrado para machine learning que brinda acceso a instancias basadas en GPU como P3, P4 y P5
    • SageMaker es adecuado para organizaciones que quieren empezar con machine learning sin administrar directamente la infraestructura subyacente
    • También se ofrece por separado la documentación de precios de Amazon SageMaker

Uso básico de NVIDIA CUDA

  • CUDA es una plataforma de computación paralela y un modelo de programación desarrollado por NVIDIA, que permite ejecutar aplicaciones más rápido aprovechando aceleradores GPU
  • Los ejemplos muestran el flujo de desarrollo con CUDA: asignación de memoria en GPU, copia de datos, ejecución del kernel y recuperación de resultados
  • Flujo de instalación

    • Descarga el base installer y el driver installer desde CUDA
    • Agrega las siguientes variables de entorno al archivo .bashrc de tu carpeta personal
      • export PATH="/usr/local/cuda-12.3/bin:$PATH"
      • export LD_LIBRARY_PATH="/usr/local/cuda-12.3/lib64:$LD_LIBRARY_PATH"
    • Ejecuta los siguientes comandos
      • sudo apt-get install cuda-toolkit
      • sudo apt-get install nvidia-gds
    • Reinicia el sistema para aplicar los cambios
  • Comandos útiles de verificación

    • lspci | grep VGA: identifica y enumera las GPU del sistema
    • nvidia-smi: proporciona información detallada sobre uso, temperatura y memoria de las GPU NVIDIA
    • sudo lshw -C display: muestra información de los controladores de pantalla, incluidas las tarjetas gráficas
    • inxi -G: muestra información del subsistema gráfico, incluida la GPU y la pantalla
    • sudo hwinfo --gfxcard: se usa para consultar información detallada sobre la tarjeta gráfica del sistema

Paralelizar la suma de arreglos con CUDA

  • La suma de arreglos es un problema adecuado para explicar la paralelización con GPU
  • Los arreglos de ejemplo son A = [1,2,3,4,5,6], B = [7,8,9,10,11,12] y el resultado es C = [8,10,12,14,16,18]
  • En el enfoque con CPU, la suma se realiza recorriendo uno por uno los elementos del arreglo
  • A medida que crecen los datos, aumenta el tiempo del enfoque secuencial, mientras que la GPU puede ejecutar al mismo tiempo operaciones como 1+7, 2+8 y 3+9
  • El ejemplo en CUDA usa un archivo de kernel .cu
    • __global__ indica una función kernel que se ejecuta en la GPU
    • vectorAdd recibe tres punteros enteros a, b y c y realiza la suma de vectores
    • threadIdx.x obtiene el índice del hilo actual
    • Cada hilo guarda la suma del elemento correspondiente en c[i]
  • La función main sigue el orden de asignación de memoria en GPU, copia de datos, ejecución del kernel y copia del resultado
    • cudaMalloc asigna en la GPU la memoria para cudaA, cudaB y cudaC
    • cudaMemcpy copia a y b del host a la GPU
    • vectorAdd <<<1, sizeof(a) / sizeof(a[0])>>> ejecuta el kernel
    • El vector resultado cudaC se copia de la GPU al host
  • Para compilar y ejecutar se usa el comando nvcc
  • Se proporciona el código completo

Uso de GPU para generar imágenes en Python

  • La generación del conjunto de Mandelbrot consiste en crear patrones visuales complejos a partir del comportamiento de números en una ecuación específica, y es una tarea intensiva en recursos
  • El ejemplo en Python basado en CPU recorre cada píxel para calcular el valor de Mandelbrot, y tarda 4.07 segundos en generar una imagen de 1024×1536
  • La versión acelerada con GPU usa la biblioteca Numba
    • El decorador @jit realiza compilación Just-In-Time para convertir código Python en código máquina
    • cuda.jit se usa para crear mandel_gpu, y se especifica device=True para que se ejecute en la GPU
    • mandel_kernel se ejecuta en una GPU CUDA y distribuye la generación de Mandelbrot entre hilos de GPU
  • create_fractal_gpu realiza la asignación de memoria en GPU, la configuración de hilos y bloques, la ejecución del kernel, la sincronización y la copia de resultados
    • Usa threadsperblock = (16, 16)
    • cuda.synchronize() espera a que termine el trabajo de la GPU
    • d_image.copy_to_host(image) copia el resultado al lado de la CPU
  • El tiempo de ejecución en GPU es de 0.0046 segundos, mucho más rápido que el código basado en CPU
  • Se proporciona el código completo

Entrenar una red neuronal para clasificar gatos y perros con GPU

  • Para mostrar cómo se usa la GPU en IA, se utiliza como ejemplo una red neuronal que distingue entre gatos y perros
  • Los prerrequisitos son CUDA y TensorFlow
    • TensorFlow puede instalarse con pip install tensorflow[and-cuda]
    • El dataset usado es Kaggle Dogs vs. Cats
    • Después de descargarlo, se organizan las imágenes de gatos y perros en subcarpetas separadas dentro de la carpeta de entrenamiento
  • El modelo usa una red neuronal convolucional (CNN)
    • pandas y numpy se usan para manipulación de datos
    • Sequential se usa para apilar capas de la red neuronal de forma lineal
    • Convolution2D, MaxPooling2D, Dense y Flatten son capas que componen la CNN
    • ImageDataGenerator se utiliza para aumento de datos en tiempo real durante el entrenamiento
  • Los datos de entrenamiento se cargan con ImageDataGenerator
    • A los datos de entrenamiento se les aplica rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True
    • Las imágenes de entrada se configuran con tamaño (64, 64), batch size 32 y modo de clasificación binaria
  • La estructura de la CNN se compone de capas convolucionales, max pooling, flatten, capas Dense y una salida sigmoid
  • El modelo se compila con el optimizador adam, la pérdida binary_crossentropy y la métrica accuracy
  • El entrenamiento se ejecuta con epochs=25 y validation_steps=2000, y se guarda en un archivo .h5 con classifier.save('trained_model.h5')
  • El código de inferencia carga trained_model.h5, convierte la imagen a (64, 64) y, si la predicción es de 0.5 o más, imprime dog; de lo contrario, cat
  • Se proporciona el código completo

El alcance del uso de GPU

  • En la era de la IA, es difícil ignorar las capacidades de la GPU, y los desarrolladores necesitan entender mejor su potencial
  • A medida que se pasa de algoritmos secuenciales a algoritmos paralelizados, la GPU se convierte en una herramienta para acelerar cálculos complejos
  • La capacidad de procesamiento en paralelo de la GPU es especialmente ventajosa para manejar grandes datasets y arquitecturas complejas de redes neuronales en tareas de IA y machine learning
  • La GPU se usa más allá del machine learning tradicional, en investigación científica, simulaciones y trabajos intensivos en datos
  • La capacidad de procesamiento en paralelo se aprovecha para resolver problemas en campos diversos como descubrimiento de fármacos, modelado climático y simulaciones financieras

1 comentarios

 
GN⁺ 2023-11-13
Opiniones en Hacker News
  • El código de este artículo está mal. No se llama a ningún kernel CUDA: https://github.com/RijulTP/GPUToolkit/blob/f17fec12e008d0d37...
    El 90% del tiempo que el código compilado con JIT dedica a “calcular” el conjunto de Mandelbrot no se va en el cálculo real, sino en compilar la función.
    Si quieres aprender CUDA correctamente, implementar multiplicación de matrices es un buen ejercicio, y hay tutoriales útiles en https://cnugteren.github.io/tutorial/pages/page1.html y https://siboehm.com/articles/22/CUDA-MMM.

    • También está SAXPY, al que se le llama el “Hello World” del código matemático paralelo en CUDA. SAXPY significa “Single-Precision A·X Plus Y”; es una función BLAS estándar y una operación muy simple que combina una multiplicación escalar con una suma de vectores.
      Toma vectores de punto flotante de 32 bits X e Y, y un escalar A; multiplica cada X[i] por A y luego lo suma a Y[i]: https://developer.nvidia.com/blog/six-ways-saxpy/
    • Después de que se lo señalaron, corrigió el código y también actualizó el blog.
  • Aunque afirma que “todo desarrollador debería saberlo”, en realidad parece más un artículo sobre cómo se usan las GPU en IA. La mayoría de los desarrolladores no son desarrolladores de IA, ni interactúan directamente con IA ni usan GPU de forma directa.
    Además, casi no trata los gráficos 3D, que son la razón central por la que existen las GPU.

    • Conocer los fundamentos de campos cercanos ayuda. Especialmente si se trata de un área de aplicación tan amplia como el machine learning: quizá el proyecto que te asignen el mes próximo tenga que usar machine learning, y también te conviene al colaborar con colegas que se encarguen de esa parte.
      Tener conocimientos básicos también ayuda a entender mejor las historias de “IA” que se le venden al gerente.
      La actitud de “no necesito saber nada de áreas vecinas” es algo que veía seguido en la escuela. En administración de sistemas, mis compañeros decían que no hacía falta saber programación, pero se necesitaba scripting; en la escuela de desarrollo de software decían que no hacía falta saber redes, pero unos años después DevOps apareció por todas partes en las ofertas de empleo.
      Si el artículo tiene unas 1500 palabras, incluso leyéndolo como estudio toma alrededor de 12 minutos; y aunque pases unas 2 horas ejecutando los ejemplos de código, no es una gran inversión. Claro, eso supone que el artículo sea una buena introducción.
    • Cuando pasé de una empresa tradicional de embebidos a una startup, recuerdo que un colega me molestó amistosamente porque no sabía enviar una solicitud JSON con curl. Sigo siendo desarrollador embebido, pero desde entonces aprendí bastante de backend, frontend e infraestructura, y parece muy probable que en los próximos años ocurra algo parecido en toda la industria con la IA.
    • Incluso el ejemplo de renderizado del conjunto de Mandelbrot apenas muestra una mejora de velocidad de 10x, cuando este es un caso típico de cómputo atado casi por completo a operaciones de punto flotante. Personalmente, me parece un artículo pésimo.
    • Hay muchas suposiciones incorrectas. Coincido en que la mayoría de los desarrolladores no son desarrolladores de IA, y el autor original parece estar algo desconectado del conjunto de desarrolladores en general, o asumir que todo el mundo es como su entorno cercano.
    • Cada vez que veo un artículo que afirma que “todo desarrollador debería saber” algo, esa afirmación suele ser falsa. Puede que haya artículos con información que realmente todos deberían conocer, pero la mayoría de los que encuentro son clickbait.
  • Creo que la razón por la que Python domina en IA es que la relación Python-C se parece a la relación CPU-GPU.
    Las GPU tienen muy buen rendimiento, pero son difíciles de programar directamente, así que la gente las usa mediante llamadas a APIs de alto nivel como PyTorch.
    C también tiene buen rendimiento, pero es difícil de programar, así que se usa Python como una capa de abstracción sobre C.
    No está claro que la gente tenga que entender las GPU con tanta profundidad. Menos aún si no se mete de lleno en el entrenamiento u operación de IA; y si la ley de Moore termina y el multithreading se convierte en la principal vía para mejorar velocidad, es muy probable que aparezcan nuevos lenguajes adaptados al paradigma de programación paralela. Mojo parece el punto de partida.

    • Me preguntaba si hay espacio para un lenguaje nuevo que maximice el rendimiento de forma invisible, sin importar en qué hardware se ejecute.
      Algo diseñado para que, desde cálculos repetitivos simples, todas las instrucciones aprovechen inteligentemente y en paralelo todos los núcleos de CPU por detrás, y que las tareas posibles se envíen a la GPU.
      Me pregunto si ya hubo intentos así, o si siquiera es posible.
    • Es difícil decir que la ley de Moore ya terminó, y el multithreading tampoco es la respuesta. Dicho eso, la primera frase es correcta.
    • Programar GPU no es tan difícil. CUDA es bastante intuitivo para muchas tareas, y en muchos casos se puede lograr una mejora de 100x en velocidad con menos de 100 líneas de código.
    • Con lenguajes más modernos se puede conservar una expresividad tipo Python y aun así obtener rendimiento de nivel C con bastante facilidad. De hecho, creo que la falta de abstracción de C hace que el código lento pero simple parezca más atractivo.
    • C es una forma de vida. A quienes usan casi exclusivamente C les cuesta aceptar el concepto de “espacios en blanco significativos” de Python.
  • La explicación de que “cuando la CPU se encuentra con varias tareas, asigna recursos para procesar cada tarea una por una” es demasiado simplista. Hasta me hace desear que las CPU todavía fueran así de simples.
    Es válido que el artículo se concentre en el modelo de programación, pero desde el punto de vista del rendimiento, decir que “la CPU ejecuta instrucciones de forma secuencial” es básicamente incorrecto. Los pipelines ejecutan instrucciones en paralelo, también existe SIMD, y varios núcleos pueden trabajar juntos sobre el mismo problema.

    • Este artículo parece haber enfocado mal el tema. Una CPU con AVX-512 también tiene paralelismo de datos a gran escala, y una CPU también puede ejecutar muchas instrucciones al mismo tiempo.
      La gran diferencia es que la CPU usa mucho silicio y energía para manejar el flujo de control de manera eficiente al ejecutar un solo hilo, mientras que la GPU destina esos recursos a más unidades de cómputo y ejecuta muchísimos hilos para ocultar el flujo de control y las latencias de memoria.
    • Las CPU también ejecutan varias instrucciones SIMD al mismo tiempo.
  • Decir que la CPU es buena para código serial y la GPU para código paralelo es cierto hasta cierto punto, pero es una aproximación bastante burda. Si asumimos un presupuesto de energía similar, de cientos de watts, una CPU tiene alrededor de 100 “núcleos” que ejecutan tareas independientes una por una, incluyendo hyper-threading, y oculta la latencia de memoria mediante predicción de saltos y pipelining
    Una GPU tiene alrededor de 100 “unidades de cómputo”, y cada unidad ejecuta de forma intercalada unas 80 tareas independientes, ocultando la latencia de memoria al ejecutar la siguiente instrucción de otra tarea
    La terminología es bastante confusa, y es probable que una CPU tenga unidades vectoriales de 256 bits de ancho y una GPU unidades vectoriales de 2048 bits de ancho, pero si uno toma un poco de distancia, ambas arquitecturas se ven bastante parecidas

    • Las GPU tienen alrededor de 10 veces más ancho de banda de memoria que las CPU, y en los LLM esta diferencia se vuelve importante. Si se procesa por lotes de forma óptima, para generar un token de salida en la práctica hay que leer toda la memoria, y esa memoria se usa para los pesos o para la caché KV
    • Siempre me ha parecido raro que no haya mucho movimiento para combinar unos pocos núcleos de baja latencia con muchos núcleos de alto rendimiento. Bastaría con rodear un núcleo P de Intel con varios núcleos E, y añadir a esos núcleos E muchos núcleos de iGPU o unidades AVX-512
      Podrían llamarlo Xeon Chi
  • La mayoría de los lenguajes de programación están diseñados para procesamiento secuencial, como una CPU, mientras que Erlang/Elixir están diseñados para la paralelización, como una GPU; viéndolo así, me pregunto si Nx / Axon despegarán: https://github.com/elixir-nx/

    • Erlang fue diseñado no para paralelismo intensivo en cómputo, sino para sistemas distribuidos con mucha concurrencia
    • Realmente me da curiosidad qué tan bien funcionarán Elixir y Nx en cargas de trabajo intensivas en cómputo dentro de clústeres de computación de alto rendimiento. Estructuralmente no son muy distintos de MPI, que se usa mucho en este campo, y podrían ser mucho más accesibles, como numpy y el ecosistema científico de Python
    • Estoy viendo si la combinación de Elixir y Nx/Axon encaja bien con arquitecturas como NVIDIA Grace Hopper, que mezclan CPU y GPU
    • Me pregunto si eso se ejecuta en la GPU. Creo que en el futuro harán falta ambas cosas. La programación secuencial sigue siendo la mejor abstracción para la mayoría de las tareas que no requieren una ejecución masivamente paralela
  • Hace falta una guía de compra. Quiero saber cuánto hay que gastar como mínimo y cuál es la mejor opción para cada rango de presupuesto. El problema es que esa información cambia de vez en cuando, y no sé si existe algún recurso que se mantenga actualizado

  • ¿Volvimos otra vez a los artículos clickbait del estilo “lo que todo desarrollador debería saber”?

    • Ese formato de artículo parece que será reemplazado por ChatGPT, pero si está bien escrito, en realidad puede ser bastante valioso
      Me gusta que aborden la complejidad de frente, y como tengo cierto conocimiento tanto de los métodos cuantitativos como de los detalles cualitativos de áreas como el hardware de computadoras, agradezco los textos que explican bien los detalles de un campo
      Por ejemplo, es otra discusión si todos los programadores deberían conocer “What every programmer should know about memory”, pero un buen programador debería tener una idea de cómo funciona realmente una computadora. La idea clave que uno puede llevarse de ese texto, la localidad, suele aparecer de forma natural en buen código que es rápido, fácil de seguir y adecuado al problema
    • Parece que sí. Hay que tomar las afirmaciones de este artículo con pinzas
  • Es un buen artículo, pero las instancias AWS P5, junto con P4d y P4de, están claramente orientadas al entrenamiento, no a la inferencia. Los tipos de instancia más adecuados para inferencia son G4dn y G5, que usan GPU T4 y A10G, respectivamente

    • El artículo original omitió G5
  • Soy casi principiante en programación de GPU, pero este artículo me pareció entretenido. Me sorprende que hayamos avanzado tanto como para poder entrenar con tanta facilidad una red neuronal simple de “perro o gato”