martes, 9 de febrero de 2021

9) Arrays temporales.










 Arrays Temporales.



Cuando Numpy tiene que realizar cálculos complejos es posible que utilice arrrays temporales. Esto puede suponer un alto consumo de recursos y memoria.

Analicemos el siguiente ejemplo. 

Creamos dos arrays. Una matriz llamda a y otra llamada b, con una forma de  (1024,1024,50) que contiene 52_428_800 elementos aleatorios.

>>> import numpy as np
>>> a = np.random.random((1024, 1024, 50))
>>> b = np.random.random((1024, 1024, 50))

>>> print(a.size(a))
52428800
Ahora creamos otra matriz llamada c con esta fórmula. c = 2.0 * a - 4.5 * b y lo ejecutamos.

>>> c = 2.0 * a - 4.5 * b
Killed

Para calcular esta última línea, se crearán dos matrices temporales para ir almacenando los resultados intermedios. Primero se almacenará 2.0 * a y luego 4.5 * b. Luego intentará realizar la operación pero,
como ves, para mi ordenador esto supone un desbordamiento de memoria ya que las matrices que se generan son muy grandes. Por lo tanto vamos a replantear el ejemplo, haciendo los arrays más pequeños para poder ver el concepto que nos interesa.

>>> import numpy as np
>>> a = np.random.random((1000, 1000, 40))
>>> b = np.random.random((1000, 1000, 40))

>>> print(a.size(a))
40_000_000

Numpy reutiliza las matrices temporales que va creando para no tener que volver a usar unas nuevas. Así, si añadimos nuevas operaciones al ejemplo previo:

 c = 2.0 * a - 4.5 * b + np.sin(a)+ np.cos(b) 

Solo se necesitan dos matrices temporales para realizar el cálculo, porque previamente ya se han creado en 2.0 * a - 4.5 * b . Ahora bien, si añadimos algunos paréntesis (que no son necesarios) a la fórmula, la situación cambia y entonces se crean tres arrays temporales.

 c =  2.0 * a - 4.5 * b +(numpy.sin(a) + numpy.cos(b))

Esto no nos interesa porque es desperdiciar recursos de memoria, con lo que o bien eliminamos los paréntesis innecesarios o podríamos mover el paréntesis y colocarlo como primer elemento de la suma, lo que nos permitiría reutilizar mejor las matrices temporales que se crean.


 c = (numpy.sin(a) + numpy.cos(b)) + 2.0 * a - 4.5 * b


La moraleja de todo esto es que si quieres ahorrar memoria (sobre todo cuando la matrices son pero que muy, muy, muy grandes) y que los cálculos se realicen más rápidamente suele ser buena idea aplicar las operaciones sobre una matriz existente uno por uno. En nuestro ejemplo anterior

 c = 2.0 * a - 4.5 * b + np.sin(a)+np.cos(b)


Es mucho más eficiente hacerlo por partes en vez de todo de una vez:

c = 2.0 * a
c -= 4.5 * b
c += np.sin(a)
c += np.cos(b)



No hay comentarios:

Publicar un comentario