Archivo de la categoría: Machine learning

Como calcular distancias entre dos arrays con variables binarias

El computo de distancias es un procedimiento básico para poder procesar datos. Las distancias nos sirven en el mundo real para poder desplazarnos con efectividad pero también podemos usar las distancias en el mundo no-físico para medir la similitud entre dos entidades. Cuando hablamos de entidades no-físicas nos referirnos por ejemplo a palabras, objetos o conceptos abstractos. Por ejemplo, ¿Cual es la similitud entre dos palabras? Dependiendo del enfoque podemos obtener resultados muy distintos. En este caso estoy interesado en los distintos métodos para calcular distancias entre dos vectores binarios por ejemplo [0,1,0,0,0,1,1] y [0,0,1,0,0,1,0]. Si fueran vectores normales los podríamos tratar en el plano euclidiano calculando distancias como lo haríamos sobre un mapa pero usando 7 dimensiones, pero al ser binarios cada dimensión solo puede ser 0 ó 1 (y ningún otro valor intermedio). La solución no pasa por asignar otros números a las variables porque substituyendo el “uno” por un “tres” no cambia nada. Por ejemplo, las palabras binarias pueden servir para codificar si eres hombre (0) o mujer (1). ¿Cual es la distancia entre un hombre y una mujer? Lo mismo pasa con palabras, cual es la distancia entre “hola” y “guacamole” No hay un solo método universal que nos sirva para todos los problemas, por esta razón he hecho esta lista para que os pueda servir en un futuro. Los métodos de esta lista calculan la distancia entre vectores con elementos binarios. Cada método – o algoritmo – tiene sus ventajas e inconvenientes obligando al usuario a entender el problema y escoger la solución más adecuada para cada caso.

Todas las formulas siguen el mismo patrón. En la siguiente tabla hay todas las opciones que las variables pueden tomar:

. y = 1 y = 0 Total
x = 1 n11 n10 n1•
x = 0 n01 n00 n0•
Total n•1 n•0 N

Coeficiente de Jaccard

El coeficiente de Jaccard o el índice de Jaccard es una de las distancias mas intuitivas. Computa la cardinalidad de la intersección de los dos conjuntos (el numero de elementos que están en los dos grupos a la vez) dividido por su unión (todos los elementos que forman parte de un grupo o del otro). Dividiendo usando la unión de los conjuntos evita que conjuntos grandes con poca similitud dominen el espacio.

$$ Coeficente de Jaccard = \frac{n11}{n1•+n•1}$$

Coeficiente de Sørensen–Dice

Este método fue originalmente desarrollado para la botánica. Sørensen y Diece lo desarrollaron de forma independiente con 3 años de diferencia. Muy parecido al anterior, este se basa en dos veces la intersección dividido por la cardinalidad de los conjuntos. El numerador es multiplicado por dos porque en el denominador la unión de estos conjuntos contiene la intersección de los conjuntos duplicada, hay un conjunto de elementos que están en el grupo A y el grupo B.

$$ Sørensen-Diece = \frac{2*n11}{n•1+n1•}$$

Coeficiente de correlación de pearson

Quizás esta sea la métrica más avanzada de este post. A pesar de que algunos estadistas la han usado no tengo tan claro que sea una buena métrica para comparar arrays binarios. A pesar de esto lo voy a dejar al gusto del lector comprobar su validez con los datos.

$$ Coef pearson = \frac{ (n11-\frac{n01*n10}{N})}{sqrt(n10-N*n10/n^2)*sqrt(n01-N*n01/n^2)}$$

Coeficiente de Phi

El coeficiente Phi o coeficiente de correlación de Matthews es una medida para la asociación de dos variables binarias. En teoría si calculamos el coeficiente de correlación pearson para dos variables binarias nos dará el mismo resultado que con el coeficiente Phi.

$$Phi = \frac{n11*n00 – n10*n01}{sqrt(n0•*n1•*n•0*n•1)} $$

Distancia de Yule

Con la distancia distancia de yule no es extrapolable a otras métricas. Como estamos pudiendo ver todas las métricas se parecen mucho. Se busca lo común entre los conjuntos y se divide por el computo global o similar para corregir para el tamaño de los grupos.

$$ Yule distance = \frac{2*(n10+n01)}{n11+n01+n10+n01} $$

Russell-rao

En este caso la métrica es algo distinta. Las otras métricas miran lo que tienen en común los grupos, este algoritmo hace lo contrario. Rusell-rao buscan lo que no tienen en común. Aunque siguen normalizando el resultado para conseguir valores relativos.

$$Russel-Rao = \frac{N-n11}{N}$$

Sokal-Michener

Por esta distancia y la siguiente no encontré mucha información, por lo que usé los detalles de implementación de la libreria de python scipy.

$$ Skoal-Michener = \frac{2*(n1•+n•1)}{(2*(n1•+n•1)+n11+n00)}$$

Rogers-Tanmoto

Al igual que el anterior aquí os dejo el link a la implementación de scipy.

$$ Rogers-Tanmoto = \frac{n11+n00}{n11+n00+2*(n10+n01)} $$

Mutual information

O en español información mutua mide la interconexión entre dos variables. Puesto de otra forma, mide la incertidumbre de una variable habiendo observado otra (conocido como entropía). Esta métrica también es avanzada y aunque no se como funciona en comparación a pearson me da mejores vibraciones. Me ha resultado difícil/ineficiente implementarlo yo mismo completamente así que os dejo un snippet con el código en python.

[code language=”python”]

from sklearn.metrics import mutual_info_score

def calc_MI(x, y, bins):
c_xy = np.histogram2d(x, y, bins)[0]
mi = mutual_info_score(None, None, contingency=c_xy)
return mi

[/code]

Kulczynski-2

En teoría Kulczynski consiguió mejorar la facilidad de crear clusters. Lo mismo que hace Jaccard y que Russell-Rao mejoraron. Este es la segunda versión, más sofisticada.

$$ Kulczynski-2 = \frac{\frac{n11^2}{(n11+n01)*(n11+n10)}}{2} $$

Ochiai

En teoría ochiai – también conocido como cosine similarity– consigue mejorar Kulczynski-2 y consecuentemente Jaccard y Rusell-Rao.

$$ Ochiai = sqrt(\frac{a^2}{(n11+n10)*(n11+01)} $$

Yo lo dejo aquí pero podéis encontrar más métricas en los siguientes enlaces:

Search Results Binary Vector Dissimilarity Measures for Handwriting Identification consta de varias métricas, algunas ya señaladas en los anteriores apartados. Tengo que decir que las formulas que exponen me parecen algo raras, no se si las han adaptado al caso en concreto.

Para una descripción en profundidad de Kulczynski y Ochiai

Spearman’s rank correlation coefficient or Spearman’s rho para vuestro disfrute.

Instalando tensorflow en un container de docker

Con el docker instalado podremos instalar el tensor flow en un container de docker. Ejecutaremos el siguiente comando para iniciar el container con tensor flow. La primera vez que lo ejecutemos docker se bajará todos los archivos necesarios para poder correr el container, por lo que por lo que puede tardar un poco en estar listo. A partir de la segunda vez ya tendremos todo el software requerido en el ordenador y se ejecutará sin ningún problema.

docker run -it -p 8888:8888 gcr.io/tensorflow/tensorflow

la opción -p 8888:8888 es usada para conectar el puerto interno de docker con el de la maquina física. Importante si queremos usar Jupyter notebooks. El formato es hostPort:containerPort. Y la url gcr.io/tensorflow/tensorflow contiene la imagen binaria para CPU. Hay otra con GPU, y sus respectivas con el código fuente. Aquí dejo una lista por si quieres la imagen con el codigo fuente o usar la versión GPU.

  • gcr.io/tensorflow/tensorflow: TensorFlow CPU
  • gcr.io/tensorflow/tensorflow:latest-devel: CPU y codigo fuente
  • gcr.io/tensorflow/tensorflow:latest-gpu: TensorFlow GPU
  • gcr.io/tensorflow/tensorflow:latest-devel-gpu: GPU codigo fuente

Instalando docker en Ubuntu

Esa va a a formar parte de una serie de posts. En los que voy a implementar algoritmos de inteligencia artificial en tensor flow. Y porque docker? Docker es un programa que crea virtualizaciones de sistemas operativos con un overhead muy reducido. Las virtualizaciones se llaman containers y requieren pocos recursos para poder virtualizar el entorno correctamente. La ventaja principal es que me permite crear automáticamente una instalación del sistema en cualquier ordenador sin mucho problema. La idea era actualmente hacerlo en el ordenador normal – por lo que seria innecesario usar docker – pero más adelante puede ser que use sistemas externos para realizar los trainings. Además permite separar distintas instalaciones en caso que hayas hecho modificaciones o quieras usar distintas versiones a la vez.

Primero empezaremos instalando la clave GPG oficial de docker

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

y añadiremos el repositorio en nuestras fuentes

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

Seguidamente actualizaremos la lista de paquetes disponibles para nuestra distribución

sudo apt-get update

y finalmente instalaremos docker (nótese que es docker-ce, las versiones anteriores tenian distintos nombres)

sudo apt-get install -y docker-ce

para comprobar que la instalación ha finalizado correctamente pondremos en la terminal:

sudo docker run hello-world

Que nos verificará que la instalación funciona correctamente. Cómo podéis comprobar requiere de sudo para poder usar el socket TCP para comunicarse con el sistema. Si queréis evitar esto. Tendremos que realizar unos pasos extra que pueden comprometer la seguridad del sistema.

El primer paso será crear un grupo

sudo groupadd docker

añadir el usuario al grupo

sudo usermod -aG docker $USER

para verificar que funciona y podemos evitar el uso de sudo cada vez tendremos que cerrar sesión y volver a logearnos. Una vez dentro ejecutaremos el “hello world” pero esta vez sin el sudo

docker run hello-world

Si nos sale un “permission denied” significa que algo no ha funcionado. Por el contrario si el comando se ha ejecutado correctamente se imprimirá por pantalla un mensaje algo largo incitandote a usar docker.

Optimizadores de tensor flow

Continuando el anterior post dónde introducí tensor flow hoy vengo con los optimizadores de funciones. Tensor flow ofrece optimizadores que cambian las variables para minimizar la funcion de perdida (loss function). El más simple es el de gradiente descendiente. Computan las derivadas simbólicas (symbolic derivatives) simplemente usando el modelo y la función tf.gradients. Por ejemplo:

[code language=”python”]
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
sess.run(train, {x:[1,2,3,4], y:[0,-1,-2,-3]})
print(sess.run([W, b]))
los resultados finales
[array([-0.9999969], dtype=float32), array([ 0.99999082],
dtype=float32)]
[/code]

El modelo completo para la regresión linal es:

[code language=”python”]
import numpy as np
import tensorflow as tf
# Model parameters
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W * x + b
y = tf.placeholder(tf.float32)
# loss
loss = tf.reduce_sum(tf.square(linear_model – y)) # suma de los cuadrados
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
# training data
x_train = [1,2,3,4]
y_train = [0,-1,-2,-3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
sess.run(train, {x:x_train, y:y_train})
# evaluate training accuracy
curr_W, curr_b, curr_loss  = sess.run([W, b, loss], {x:x_train, y:y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))
# When run, it produces
#W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11
[/code]
tf.contrib.learn te simplifica la vida con las funciones: ejecución, entrenamiento, iteraciones, evaluaciones entre otros

[code language=”python”]
import tensorflow as tf
import numpy as np
features = [tf.contrib.layers.real_valued_column("x", dimension=1)]
estimator = tf.contrib.learn.LinearRegressor(feature_columns=features)
x = np.array([1., 2., 3., 4.])
y = np.array([0., -1., -2., -3.])
input_fn = tf.contrib.learn.io.numpy_input_fn({"x":x}, y, batch_size=4,
num_epochs=1000)
estimator.fit(input_fn=input_fn, steps=1000)
print(estimator.evaluate(input_fn=input_fn))
# Result: {‘global_step’: 1000, ‘loss’: 1.9650059e-11}
[/code]

Introducción a TensorFlow

La web oficial de TensorFlow tiene muy buenos recursos. En esencia lo que hay en este post proviene del “get started” de la web oficial.

En el primer ejemplo importaremos TensorFlow. Crearemos dos constantes y las imprimiremos en pantalla.

[code language=”python”]
import tensorflow as tf
node1 = tf.constant(3.0, tf.float32)
node2 = tf.constant(4.0) # También tf.float32 de forma implícita
sess = tf.Session()
print(sess.run([node1, node2]))
node3 = tf.add(node1, node2)
print("node3: ", node3) #Esta linea muestra las propiedades del tensor
print("sess.run(node3): ",sess.run(node3)) # Aquí se imprime el resultado[/code]

También podemos aplicar operaciones a los tensors. Las operaciones producen más tensores. En el siguiente ejemplo sumamos dos variables (tensores) y luego las sumamos. Un tensor es capaz de procesar listas también como veremos.

[code language=”python”]
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b
print(sess.run(adder_node, {a: 3, b:4.5})) # 7.5
print(sess.run(adder_node, {a: [1,3], b: [2, 4]})) # Itera sobre la lista: 3 y 7
[/code]

Podemos multiplicar

[code language=”python”]
add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b:4.5}))
[/code]

E incluso podemos crear modelos lineales

[code language=”python”]
W = tf.Variable([.3], tf.float32)
b = tf.Variable([-.3], tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W * x + b
[/code]

Las constantes son inicializadas con tf.constant, y su valor no puede ser cambiado. Contrariamente las variables son creadas usando tf.Variable y no son inicializadas en el inicio. Para inicializar las variables  tenemos que usar una función especial como en el siguiente ejemplo:

[code language=”python”]
init = tf.global_variables_initializer()
sess.run(init)
[/code]

Lo importante es usar init. Es un “handle” que inicia el grafo, inicializando las variables que hasta entonces habían permanecido sin inicializar.

Ya que la X es la variable podemos evaluar el modelo usando distintos valores simultáneamente de la siguiente manera:

[code language=”python”]
print(sess.run(linear_model, {x:[1,2,3,4]}))
# Output: [ 0, 0.30000001, 0.60000002, 0.90000004]
[/code]

La función de pérdida (loss function) puede medir la distancia entre el modelo actual y los datos proporcionados. Usaremos la función de pérdida estándar para hacer una regresión lineal que sume los cuadrados de las distancias entre el modelo actual y los datos. El modelo crea un vector en el que cada posición corresponde a una distancia de error. Cuando usamos tf.square estamos elevando al cuadrado el error. Luego sumamos los errores para crear un simple escalar que abstrae el error de todos los puntos usando tf.reduce_sum:

[code language=”python”]
y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model – y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]}))
[/code]

El valor producido es:  23.66

Podríamos mejorar el modelo manualmente asignando nuevos valores a W para obtener resultados perfectos de -1 y 1. Una variables es inicializada por el valor proporcionado por tf.Variable pero puede ser cambiada si usamos operaciones como tf.assign. Por ejemplo, W=-1 y b=1 son los parámetros óptimos de nuestro modelo. Podemos cambiar W y b acordemente:

[code language=”python”]
fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x:[1,2,3,4], y:[0,-1,-2,-3]})
#resultado: 0
[/code]

Hemos adivinado el valor perfecto para W i b aunque este no es el objetivo de  machine learning. El objetivo es encontrar los parámetros correctos para el modelo automáticamente. Pero esto lo vais a encontrar en la próxima entrega 😉

Bonus: En matemáticas y en física, un tensor es cierta clase de entidad algebraica de varias componentes, que generaliza los conceptos de escalar, vector y matriz de una manera que sea independiente de cualquier sistema de coordenadas elegido. ( wikipedia )