Python vs Numpy vs GPU (TensorFlow): Tiempos de Ejecución


El Notebook con el código de este artículo lo puedes descargar pulsando AQUI.

Tod@s aquell@s que se dedican al mundo del Deep Learning hacen uso de unos dispositivos llamados GPUs (Graphics Processing Unit – Unidad de Procesamiento Gráfico) para entrenar de una manera mucho más rápida las redes neuronales de cómo se entrenarían haciendo uso de una CPU (Central Processing Unit – Unidad Central de Procesamiento)

En este artículo no vamos a hablar de redes neuronales, pero si que vamos a comentar que estas están definidas por una serie de parámetros (un parámetro no es más que un número decimal) que deben de ser calculados (ajustados) de tal manera que la red neuronal resuelva la tarea para la que esta encomendada con el menor error posible. El calculo de estos parámetros (simplificándolo mucho) se realizan mediante operaciones sencillas; como son sumas y multiplicaciones. El problema, es que una red neuronal puede tener desde cientos de parámetros a miles de millones de parámetros y es ahí donde reside el problema; en la gran cantidad de cálculos que hay que realizar para entrenar una red neuronal.

Realicemos el ejercicio de multiplicar 2 matrices que al fin y al cabo se resuelven con las operaciones de sumas y multiplicaciones: !!anda igual que las redes neuronales¡¡.

Computacionalmente esta operación tiene una complejidad cúbica 0(n3) ya que para calcular la matriz resultante se necesitan 3 bucles <<for>>. Si esto lo programamos en Python el código sería el siguiente:

import numpy as np

# Tamaño de la matriz SIZExSIZE
SIZE = 500
a = np.random.rand(SIZE, SIZE)
b = np.random.rand(SIZE, SIZE)

result = np.zeros((SIZE, SIZE))
for i in range(SIZE):
    for j in range(SIZE):
        for k in range(SIZE):
            result[i,j] += a[i,k] * b[k,j]

Si multiplicamos 2 matrices de 500 x 500, tendremos que realizar 125 Millones de operaciones “Suma-Producto” y estoy Python tarda algo de tiempo en computarlo (en mi ordenador concretamente 65 segundos):

Si utilizamos la librería de Numpy para realizar la misma operación, esta tarda considerablemente menos tiempo; en particular en mi ordenador, tardo 1,81 ms o lo que es lo mismo 1,81·10-3 segundos; es decir, 359.112 veces más rápido que en Python:

Tanto en Python como en Numpy se usa la CPU para realizar los cálculos de “Suma-Producto”. ¿Pero qué pasaría si ahora usamos una GPU para multiplicar matrices?

Para realizar esta prueba haremos uso de la librería de TensorFlow con la GPU correctamente configurada en el PC: Drivers de Nvidia + CUDA + CUDNN (dejamos para otro tutorial esta configuración, ya que es un auténtico dolor de cabeza). Para ello nos definiremos 2 tensores (matrices) y realizaremos la multiplicación. Esta prueba ha tardado 28,8 µs o lo que es lo mismo 28,8·10-6 segundos; es decir, 63 veces más rápido que Numpy y 2,25 Millones de veces más rápido que en Python:

Podemos resumir la prueba en la siguiente imagen:

¿Y porque hay tanta diferencia en tiempos de cálculo? Para entenderlos tenemos que saber las diferencias entre una CPU y una GPU.

NOTA: Para entender bien las diferencias entre CPU, GPU y su rendimiento, deberíamos de tener unos conocimientos bastante avanzados sobre arquitectura de computadores y sistemas operativos, materias en las que se invierten muchísimas horas dentro de las diferentes carreras de Ingeniería Informática. Como el objetivo de este tutorial es entender el concepto (y no el detalle) se explicarán las cosas de manera poco ortodoxa y con palabras quizás inadecuadas, pero enfocado a entender las diferencias.

Las CPUs son unidades de proceso de “alto rendimiento” que son capaces de realizar de manera muy rápida operaciones (instrucciones) muy complejas. Supongamos una calculadora científica muy cara; pues podríamos decir que una CPU tiene en su interior unas pocas calculadoras científicas muy potentes y caras llamadas Cores (núcleos en español). Un ordenador personal puede tener entre 2 y 16 Cores, aunque podemos encontrar procesadores con más Cores.

Las GPUs (que nacieron para videojuegos y procesamientos gráficos) tienen en su interior unidades de proceso de “bajo rendimiento”; pero a diferencia de las CPUs, las GPUs cuentan con cientos o miles de estas unidades de proceso de “bajo rendimiento” que resuelven operaciones muy sencillas (como sumas y multiplicaciones) y lo hacen de manera más lenta. Supongamos en este caso la “calculadora más cutre y barata” que puedas encontrar en una tienda; pues la GPU tendría en su interior cientos o miles de esas “calculadoras cutres”.

Explicadas las “características” de la CPU y GPU uno ya puede intuir el porque de la diferencia de tiempos de las ejecuciones entre Python, Numpy y GPU (con TensorFlow):

  • Python, hace uso de la CPU para realizar los cálculos; y al haber implementado nosotros la operación de multiplicación de matrices (implementación poco optima), mandará una a una las operaciones de sumas-productos que le indicamos en el último bucle <<for>> a un único Core de la CPU. Para el ejemplo de multiplicar dos matrices de 500 por 500 tendrá que hacer 125 Millones de operaciones de sumas-producto, lo cual tiene sentido que tarde mucho tiempo en procesarse.
  • Numpy, es una librería de Python especializada en el cálculo numérico y su implementación esta optimizada de tal manera que aproveche al máximo los recursos de la CPU. Como hemos comentado las CPUs cuentas con unos pocos Cores (entre 2 y 16) de “alto rendimiento” y Numpy, hace uso de todos los Cores de la CPU para hacer las operaciones necesarias en la multiplicación de matrices; dicho de otra manera, Numpy aprovecha el “paralelismo” (Multitarea) que la CPU le ofrece, usando de manera independiente cada uno de los Cores para realizar las operaciones. Es por este motivo (a parte de tener un código más optimizados) que Numpy tarda mucho menos tiempo que Python en obtener el resultado.
  • GPU (TensorFlow) hace uso del “paralelismo” que le otorga el tener cientos de Cores de “bajo rendimiento”. Aunque un Core de la GPU tarden más tiempo en ejecutar una operación que en un Core de la CPU; realizar cientos de operaciones “lentas” a la vez, será más rápido que realizar entre 2 y 16 operaciones “rápidas” a la vez. Esta es la explicación de porque la GPU (TensorFlow) tarda mucho menos tiempo en obtener el resultado que en Numpy y obviamente que en Python.

 

BONUS TRACK

Cuando se haga uso de TensorFlow con GPU es recomendable limitar el uso de la memoria de la GPU (para que nuestro entrenamientos no «casquen») y también es interesante cerciorarse que estamos usando la GPU (y no la CPU). En el siguiente fragmento de código mostramos como limitar la memoria y como activar la GPU, imprimiendo la información de la GPU y de las versiones de CUDA y CUDNN:

import tensorflow

from tensorflow.python.client import device_lib

# Limitación la memoria de la GPU
config = tensorflow.compat.v1.ConfigProto(allow_soft_placement=True)
config.gpu_options.per_process_gpu_memory_fraction = 0.6
tensorflow.compat.v1.keras.backend.set_session(tensorflow.compat.v1.Session(config=config))

# Permitir crecimiento de la memoria
physical_devices = tensorflow.config.list_physical_devices('GPU')
try:
    tensorflow.config.experimental.set_memory_growth(physical_devices[0], True)
except:
    print('Invalid device or cannot modify virtual devices once initialized.')

print('#### INFORMACIÓN ####')
print('  Versión de TensorFlow: {}'.format(tensorflow.__version__))
print('  GPU: {}'.format([x.physical_device_desc for x in device_lib.list_local_devices() if x.device_type == 'GPU']))
print('  Versión Cuda  -> {}'.format(tensorflow.sysconfig.get_build_info()['cuda_version']))
print('  Versión Cudnn -> {}\n'.format(tensorflow.sysconfig.get_build_info()['cudnn_version']))
Comparte esta entrada en:
Safe Creative #1401310112503
Python vs Numpy vs GPU (TensorFlow): Tiempos de Ejecución por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies