martes, 8 de octubre de 2013

Neuronas y banqueros tacaños



Volver al trabajo después de todo un año es una tarea difícil. Tras un año sabático, en el que había estado tratando de encontrarme a mí mismo, regresaba al trabajo con exactamente las mismas dudas existenciales que cuando me marché, y lo que es peor, todo en la empresa seguía exactamente donde lo dejé un año antes. Sofía con sus cascos y sus pintas de hacker ochentera. Gaznápiro odiándome un poco más si cabía y el Sr. Lego, entrando como siempre en la oficina cabizbajo y mascullando. En fin, la vida seguía por el mismo lugar que la dejé.
Habían pasado ya tres días desde mi regreso y Gaznápiro ya me había asignado el mismo marrón del que hacía un año pensé que me había librado, así que aquella mañana no estaba yo de muy buen ánimo. Para mejorar la estampa, el Sr. Lego llegó mascullando como siempre, pero esta vez un poco más colorado y enfurecido que de costumbre.

- ¡Malditos bancos! Me voy a llevar la nómina y la hipoteca a otro lado... -gruñó el Sr. Lego.

- Hombre, buenos días. -dije- Veo que a usted tampoco le hace gracia que le roben.

El Sr. Lego me miró de soslayo y terminó diciendo: Quince años de cliente y ahora me deniegan un mísero préstamo de 6.000 euros. Y encima me vienen con la milonga de que es el ordenador el que me ha clasificado como cliente de riesgo y que no pueden hacer nada.

Vaya, otra víctima de una fría y malévola red neuronal. -dije tratando de quitar hierro al asunto.

- Pues no le veo la gracia. -protestó el Sr. Lego- Además, no sé como un ordenador, o una red neuronal de esas que usted dice, puede decidir si yo soy o no un cliente de riesgo.

Miré al Sr. Lego con una mezcla de lástima y desesperación por la promesa de una larga mañana de lamentos.

- Está bien Sr. Lego, si se tranquiliza y mejora su humor, prometo explicárselo.


A ver... -comencé tratando de ganar tiempo para ordenar un poco las ideas e ir presentándolas con cierto sentido lógico- Supongo que sabe usted lo que es una neurona ¿no?

Bueno, más o menos, son las células que hay en el cerebro o algo así.

Si, o algo así -repliqué mientras pensaba lo complicado que me iba a resultar explicarle esto al Sr. Lego. Realmente -continué- nuestro cerebro está compuesto por un número astronómico de neuronas, del orden de los 100.000 millones. Pero para entender como trabajan juntas vamos a fijarnos en el comportamiento de una sola neurona.



Las neuronas se comunican entre sí mediante conexiones llamadas sinapsis por las que se envían impulsos eléctricos. De media podríamos decir que cada neurona se conecta a otras mil, aunque dependiendo de la zona del cerebro será más o menos numerosas. Simplificando mucho (muchísimo) podemos afirmar que una neurona se comporta aproximadamente como un transitor, que en un momento dado permite el paso de corriente dependiendo de los valores de la corriente de entrada. En este caso, a la neurona le llegan una serie de conexiones desde otras neuronas a través de los axónes. Si la cantidad de señales que le llegan a través del axón supera un cierto umbral, la neurona se activa (se dice que está muy excitada) y envía impulsos eléctricos a través de las dendritas hacia otras neuronas. Es decir, de nuevo simplificando mucho, la neurona puede tomar dos estados al igual que un Bit: activa o no activa.

Vaya -interrumpió el Sr. Lego- entonces será fácil simularlas en un ordenador.

Si y no -continué. Como le digo, estamos adoptando un modelo muy simplificado de neurona que no se corresponde al 100% con la realidad, y además, no tenemos ordenadores capaces de simular un número tan elevado de neuronas y conexiones como las que hay en un cerebro (humano u animal). En cualquier caso, este modelo de neurona nos va a permitir realizar ciertos procesos computacionales simulando de alguna manera la forma de trabajar del cerebro. Antes de nada comencemos por establecer un modelo computacional de neurona. Usaremos una de las más simples y estudiadas: el perceptrón simple.

Cogí un bolígrafo y hice el siguiente garabato a modo de esquema.


Nuestro perceptrón dispone de n entradas (cuatro en este caso llamadas x1, x2, x3 y x4) y cada una de las entradas tiene un peso asignado (en nuestro caso w1, w2, w3 y w4). Además, cada neurona tiene un nivel de excitación o umbral de disparo (lo representaremos con la letra griega theta), que es el nivel de actividad de entrada necesario para que se dispare la salida. Para calcular la salida z, que en este caso será 0 o 1 (al menos en este modelo de neurona) hacemos lo siguientes. Calculamos el sumatorio de todas las entradas multiplicadas por sus respectivos pesos.


Si la salida s es mayor o igual que el valor de umbral theta, la salida z toma el valor 1. Si no z toma el valor 0.
Para simplificarnos el asunto a la hora de implemetar un perceptrón en un programa de ordenador, podemos usar el siguiente modelo equivalente al que acabamos de ver, sólo que en este caso, al valor s le restamos theta y, por lo tanto, la neurona se activa si su valor de umbral es mayor o igual que cero.


Una vez definido el modelo de neurona que vamos a usar, y para que nos sea útil, hemos de pasar por un proceso llamado entrenamiento por el cual, mediante ejemplos y muestras, hacemos que el perceptrón aprenda cuál debe ser la respuesta correcta z en función de las entradas. Por ejemplo, si se trata de un sistema bancario para calcular el riesgo de dar un crédito, entrenaríamos el perceptrón con una serie de clientes morosos y no morosos.
Una vez entrenado, podemos consultarle sobre un cliente concreto, por ejemplo usted, y el devolverá en la salida si es usted un posible moroso o no. A esta fase se la llama de clasificación.

La cara de el Sr. Lego se iba torciendo por momentos, así que me apresuré a poner un ejemplo sencillo para ilustrar todo lo que acababa de explicarle.

Empecemos por el entrenamiento. El proceso de entrenar al perceptrón consiste en ir ajustando los pesos y el valor umbral. Para ello, asignamos unos valores, que en principio pueden ser aleatorios, a los pesos y al valor umbral. Vamos a usar un parámetro adicional al que llamaremos factor de aprendizaje (lo designaremos con la letra griega lambda) que indica la velocidad a la que aprende nuestro perceptrón. Si el valor de lambda es muy grande el perceptrón realizará ajustes muy bruscos, mientras que si es pequeño, aprenderá de forma más lenta.
Vamos a suponer los siguientes parámetros para nuestro ejemplo bancario:

x1 = Ingresos mensuales del cliente.
x2 = Deuda acumulada del cliente.
w1 = 0.2
w2 = 0.6
theta = 0.4
lambda = 0.2

Los valores de w1, w2 y theta lo hemos escogido de forma arbitraria. Un valor de lambda de 0.2 suele ser una buena cifra para la mayoría de los casos.
El procedimiento de entrenamiento es el siguiente:

1 - Aplicar las entradas x1, x2, ... , xn al perceptrón.
2 - Calcular la salida z.
3 - A la diferencia entre el valor deseado y el obtenido lo llamaremos error.
4 - Modificamos los pesos y el valor umbral según el error obtenido.
5 - Repetimos el proceso hasta que el error sea cero.

El Sr. Lego tenía la cara aun más torcida si cabía. No se preocupe -dije- ahora lo irá viendo más claro.
Comencemos definiendo mejor el error del paso 3 (lo designaremos con la letra e).


Este error lo usamos para ajustar el valor umbral theta de forma proporcional a lambda. Es el paso 4.


Finalmente, también dentro del paso 4, ajustamos los pesos para todas las entradas.


Antes de volver a ejecutar otra iteración en el ciclo de entrenamiento aplicamos los incrementos que hemos calculado sumándoselos tanto a theta como a todos los pesos.

Vemos un ejemplo. Supongamos que uno de los casos con los que vamos a entrenar al perceptrón es el de un cliente que gana 1.000 euros/mes y tiene una deuda de 100 euros. En este caso el cliente ha tenido un mal historial así que no se le considera apto para recibir el préstamo (codificaremos con 1 si es apto y con 0 si no es apto):
x1 = 1000 (ingresos mensuales)
x2 = 100 (deuda)
valor esperado y = 0

Calculamos tal y como vimos antes.



El resultado es z=259.6, que es >= 0, por lo que la salida del perceptrón se activa y su valor de salida será z=1. Como el valor esperado era 0 y z<>y, debemos calcular su error y ajustar pesos y umbral. Si la salida z fuera igual al valor esperado no se realizarían ajustes). Así pues:


Y con los valores obtenidos ajustamos los pesos y el umbral theta.


Y con estos nuevos ajustes continuaríamos entrenando al preceptrón.

Como sabía que el Sr. Lego lo vería más claro con un programa de ejemplo, dediqué buena parte de la mañana a escribir el siguiente código.



# Perceptrón simple banquero
def recalc(k, weights, t):
  z=-t
  for i in range(len(k)):
    z=z+(k[i]*weights[i])
  if z>=0:
   return 1 # Neurona activada
  else:
   return 0 # Neurona no activada

def train_perceptron(training_data, weights, t, l):
  errors=True
  while errors:
    errors=False
    # entrenar perceptron
    for k,y in training_data.iteritems():
      z=recalc(k, weights, t)
      if z!=y:
        errors=True
        # error
        e=(y-z)
        # calcular ajustes
        delta_t=-(l*e)
        t=t+delta_t
        for i in range(len(k)):
          delta_w=l*e*k[i]
          weights[i]=weights[i]+delta_w

  return weights, t


def clasify(sample, weights, t):
  return recalc(sample, weights, t)


if __name__ == "__main__":
  # (ingresos mensuales, deuda actual):moroso[0=si, 1=no]
  training_data={(1000,800):0, (1600,100):1, (1300,30):1,(800,200):0}
  weights=[0.2,0.6]
  t=0.4
  l=0.2
  weights, t = train_perceptron(training_data, weights, t, l)
  result = clasify((1800,100), weights, t)
  print "Resultado:" + str(result)
  print "Pesos:" + str(weights)
  print "Umbral:" + str(t)


El programa anterior entrena al perceptrón con los cuatro ejemplos siguientes:
(1000,800):0
(1600,100):1
(1300,30):1
(800,200):0

Donde el primer valor dentro del paréntesis son los ingresos mensuales y el segundo la deuda del cliente. El valor esperado puede ser 1 para indicar que se concede el crédito y 0 en caso contrario.
Tras el entrenamiento hemos pedido que clasifique a un cliente con 1800 euros de ingresos y una deuda de 100 euros (1800,100). El resultado es el siguiente:

Resultado:1
Pesos:[20.199999999999989, -289.39999999999998]
Umbral:1.0

Que quiere decir que se le concede el crédito.

Si se fija bien, Sr. Lego, los pesos y el umbral nos dicen algo sobre cómo clasifica el perceptrón a los clientes.
Redondeando los valores de los pesos, podemos escribir el cálculo que hace el perceptrón de forma equivalente a la función siguiente:

20.2x1-289.4x2+1=0

¿No le recuerda a algo? -pregunté.

Si, parece la ecuación de una recta -respondió brillantemente el Sr. Lego.

Efectivamente, y si representamos esta recta gráficamente obtendremos esto:


En esta gráfica, el eje vertical representa la deuda del cliente, y el eje horizontal los ingresos mensuales. He señalado con puntos rojos los valores para los que los clientes no obtenían el crédito y en verde los clientes para los que sí. En azul he marcado el cliente que queríamos clasificar.
Como ve, durante el proceso de entrenamiento, lo que hemos hecho es ir ajustando la pendiente y la posición de la recta para conseguir separar los puntos (clientes) buenos de los malos. Aquellos que están por encima de la línea son malos clientes y los que están por debajo son buenos. Como el cliente representado por el punto azul cae por debajo de la línea, nuestro perceptrón lo ha clasificado como bueno.

El Sr. Lego pareció verlo ahora más claro: entonces, en cada iteración del entrenamiento hemos ido ajustando la recta para ir minimizando el error hasta dejarlo en cero.

Muy bien Sr. Lego. Veo que lo ha pillado. Sin embargo, si se fija, con un perceptrón podemos resolver una serie de problemas muy limitados. Sólo aquellos en los los elementos a clasificar puedan separarse espacialmente por una recta (o un hiperplano si tenemos más de dos variables). A estos elementos que pueden ser así clasificados los llamamos linealmente separables.
Si queremos hacer clasificaciones más complejas necesitaremos usar más de una neurona para formar lo que se llama una red neuronal artificial (RNA). Pero eso ya se lo explicaré otro día, que por hoy ya hemos tenido bastante.

- Si más o menos lo he entendido. Y también he entendido que no le caigo bien a las neuronas de los bancos.
- No se preocupe Sr. Lego. Tengo la impresión de que el problema no está en las neuronas del clasificador del banco, sino en las propias neuronas de los banqueros, desgraciadamente.

4 comentarios:

  1. Qué alegría tenerte de vuelta. No nos abandones tanto tiempo, please!!!!

    ResponderEliminar
  2. Muy bueno tu blog, lo conocí hace algunos días y no puedo dejar de leer las entradas!

    ResponderEliminar
  3. +1
    Muy interesante, siempre quise buscar algun ejemplo de aplicación de neuronas :)

    ResponderEliminar