domingo, 6 de mayo de 2018

TensorFlow, primeros pasos: Algebra matricial y fractales


Inicio una serie dedicada exclusivamente a TensorFlow, siendo esta la primera entrada con los aspectos más básicos. La intención es describir la potencia matemática de esta herramienta, atendiendo principalmente a la API de bajo nivel.


TensorFlow y el álgebra matricial:

 

Antes de nada describiremos el termino tensor, ampliamente usado en el ámbito de la física y que guarda muchas similitudes con el definido por la gente de TensorFlow.

"Un tensor es una forma de representar un valor o una magnitud y que además permanece invariante con los cambios de coordenadas".

Desde este punto de vista todas las magnitudes son tensores:
  • Un escalar es un tensor de orden 0. 
  • Un vector es un tensor de orden 1 ya que requiere n dimensiones para ser descrito.
  • Una matriz es un tensor de orden 2 ya que requiere nxn componentes.
  • Un tensor de orden m requiere $n^{m}$ componentes para ser descrito. 
Para TensorFlow los tensores son contenedores de datos, al igual que el tensor físico-matemático, y donde todos los datos son de un mismo tipo.  Algunos ejemplos podrían ser:

import tesnorflow as ts
order0 = ts.constant(12, ts.float32)
order1 = ts.constant([12, 4], ts.int8)
order2 = ts.constant([[2, 3], [0, 1]])


Sabiendo esto ya estamos en disposición de realizar ciertos cálculos matemáticos, por ejemplo entre matrices.

A = ts.constant([[2, 3], [2, 1]], ts.float32)
A_transpuesta = ts.transpose(A)
A_traza = ts.trace(A)
A_solve = ts.matrix_solve(A, [[10, 1], [1,10]])
print(sess.run(A_solve))
print(sess.run(A_traza))
  
Para evaluar los resultados es necesario crear una sesión para ejecutar las operaciones:

sess = ts.Session()
sess.run(A_transpuesta) 

Estas y otras muchas operaciones pueden consultase en https://www.tensorflow.org/api_guides/python/math_ops.


Un pequeño ejemplo:


Como ejercicio intentaremos dibujar un fractal empleando las herramientas matemáticas que nos proporciona TersorFlow.  Sin entrar en los detalles matemáticos un conjunto fractal se puede representar iterando los puntos del plano -en su expresión de plano complejo- y luego representando los puntos en función de la velocidad de escape de un circulo de radio r a lo largo de la iteración. Para el caso que nos ocupa iteraremos según la fórmula $Z_{n+1}=f(Z_{n})+c$. La función f y la constante c pueden dar lugar o no a un conjunto fractal, es decir, el resultado puede cumplir los criterios fractales -autosemejanza, dimensión no entera- o bien no cumplirlos. 

import numpy as np
x, y = np.mgrid[-2:1:0.005, -1.3:1.3:0.005]
points = x+1j*y
z = ts.Variable(points)
fractal = ts.Variable(ts.zeros_like(z, ts.float32))
ts.global_variables_initializer().run(session=sess)
z_ = ts.exp(z*z*z) - 0.59-0.0041j
no_diverge = ts.cast(ts.abs(z_) < 10, ts.float32)
step = ts.group(z.assign(z_), fractal.assign_add(no_diverge))
for cont in range(150):
   step.run(session=sess)
   fractal_ = fractal.eval(session=sess)
1:  Importamos numpy.
2:  Generamos los puntos del plano como una malla.
3:  Pasamos estos puntos al plano complejo.
4:  Pasamos los puntos del plano complejo a un tensor variable.
5: Creamos un tensor de ceros, este se usará para clasificar los puntos del plano complejo según un criterio métrico determinado -ver línea 7-.
6:  Antes de usar los tensores variable deben ser incializados para una sesión.
7:  Función a iterar, en ese caso dará como resultado un conjunto Julia. Aquí se puede jugar con la función a iterar y comprobar que tipo de fractal resulta.
8:   El método cast transforma un tensor en otro de tipos diferentes, en este caso $ts.abs(z$_$)<10$ es un tensor de booleanos True cuando el punto z_ -en módulo- es menor que 10.
9:   Cada uno de los pasos iterativos, group agrupa varias operaciones, en ese caso asignamos a z el valor de z_ y por otro lado añadimos al valor anterior de fractal el valor de no_diverge.
10:  Iteración de los cálculos.
10:  Corremos el iterador 150 veces.

Para representar el fractal podemos por ejemplo usar matplolib -usando from matplotlib import pyplot as plt-:

plt.imshow(fractal_, interpolation='gaussian')
plt.show()
De este ejemplo podemos extraer algunas peculiaridades de TF.
  1. Existe una diferencia notable entre variables -línea 3- y tensores normales. Una variable es un tensor mutable, es decir, se pude ir actualizando su valor, como en 8 que se actualiza z y fractal. Tienen también métodos propios que no pueden empelarse con los tensores y es imprescindibles inicializarlos antes de usarlos -línea 5-.
  2.  Los algoritmos se apilan antes de realizarse, como en la línea 8. Algoritmos, tensores, etc... deben evaluarse sobre una sesión o correrse sobre ella -líneas 10 y 11-.
 En la siguiente entrada realizaremos un perceptrón multicapa y lo ilustraremos con un curioso ejemplo.



No hay comentarios:

Publicar un comentario