Saltar al contenido

Primeros pasos en aprendizaje por refuerzo

    Primeros pasos en aprendizaje reforzado

    Últimamente siempre escuchamos hablar de que una inteligencia artificial ha sido capaz de ganar a personas en determinados juegos y quizá no sepas del todo bien que hace para conseguir esta inteligencia una aplicación. Todo empezó por el ajedrez (donde se pueden jugar 10100.000 partidas diferentes), continuó por el famoso juego go y de lo último que hemos oído es como han derrotado al campeón del dota 2, uno de los juegos más jugados actualmente con miles de jugadores jugando desde todo el mundo.

    En el siguiente vídeo podemos ver como una inteligencia artificial aprende a jugar al escondite, ¡incluso llegando a hacer trampas con fallos en el sistema!

    Nosotros no vamos a ir tan lejos (al menos ahora), pero vamos a entrar un poco en detalle de qué es y cómo dar los primeros pasos en aprendizaje por refuerzo y como está cambiando el mundo con un ejemplo muy simple. El tres en raya.

    ¿Cómo funciona aprendizaje por refuerzo?

    Se puede decir que el aprendizaje por refuerzo es una rama del machine learning en la que una inteligencia artificial va a entender su entorno, tomará acciones y aprenderá la forma optima de afrontar el problema. Este aprendizaje se lleva a cabo mediante recompensas, en las que se premian los buenos comportamientos y se castigan los malos.

    Al igual que en las redes neuronales la inteligencia artificial necesita un entrenamiento, en el cual el agente experimentará con el ambiente e irá poco a poco optimizando su comportamiento. La gran diferencia con estas últimas es que en estas no se le dice cómo tiene que ser la salida, sino que es la misma inteligencia artificial la que descubre cual es la mejor forma de conseguir un resultado.

    Fundamentos

    Antes de empezar vamos a empezar describiendo un poco la jerga que se utiliza en el esta rama para que no nos perdamos más adelante. Es importante comprender bien cada uno de ellos, no solo por esta entrada sino por futuras entradas (o que leas más sobre el tema). Así que vamos a ello:

    • Agente: Así llamaremos a la inteligencia artificial que toma las decisiones. En nuestro futuro caso nuestro agente será un jugador de tres en raya que irá aprendiendo.
    • Entorno: El ambiente como su nombre indica es el lugar donde se realiza el aprendizaje del agente. en nuestro futuro caso será el tablero del juego.
    • Política: Es lo que define el comportamiento que va a tener nuestro agente en el tiempo. En la práctica es un mapa donde se ven los estados del entorno a las acciones que se pueden dar en un determinado estado.
    • Recompensa: Es el objetivo del problema que estamos intentando resolver. El objetivo del agente es encontrar la recompensa más grande. En nuestro ejemplo es el conseguir formar tres en línea.
    • Función de valor: Especifica cual es el total de recompensas que se puede dar a través del tiempo desde un estado dado.
    • Modelo: El lo que imita el comportamiento del entorno, y se usan para planear las estrategias a seguir.

    Primeros pasos en aprendizaje por refuerzo: El tres en raya

    El juego es el típico juego de tres en línea que todos conocemos y no creo que haga falta explicar mucho más que es un juego de dos jugadores (uno pone X y el otro pone O) donde el primero en conseguir poner tres X o O en línea gana.

    Tic tac toe

    Aunque el juego es muy simple la realización de un algoritmo que sea capaz de jugar ganándote no es tan simple (como se puede ver en este ejemplo). En nuestro ejemplo utilizamos aprendizaje por refuerzo desde cero para que aprenda a jugar y al final nos acabe venciendo.

    Nuestro agente buscará las políticas con alta probabilidad de ganar para aplicarla e intentar ganar a un adversario humano. En este ejemplo una política le dice al agente como tiene que jugar para ganar. Para cada política se obtendrá una probabilidad de ganar.

    Así que vamos a utilizar una función de valor para ver cual sería la jugada que deberíamos hacer en cada uno de los estados. Para ello crearemos una tabla con números, uno para cada uno de los estados del juego. Cada número será la probabilidad de que ganemos en ese estado.

    Si asumimos que siempre jugamos con las Xs entonces si se da el caso de que se encuentras 3 Xs seguidas la probabilidad de ganar es de 1. Si por el contrario, nos encontramos con tres Os seguidas la probabilidad de ganar es de 0. Y establecemos todos los demás estados con una probabilidad de ganar de 0.5.

    Mientras vamos jugando vamos cambiando los valores, para que nuestro juego vaya mejorando mediante predicciones más precisas y lo vamos guardando en la tabla. Vamos actualizando la tabla de la siguiente manera: El valor actual del estado anterior se ajusta para estar más cerca del estado posterior. Esto se hace por medio una “tasa de aprendizaje” que se llama alpha y lo hacemos así: Si llamamos s al estado antes del movimiento y s0 al estado después del movimiento la actualización del valor estimado de s, que se representa como V(s) se puede escribir como:

    (1)   \begin{equation*} V(s) \leftarrow V(s) + \alpha [V(s\, ')-V(s)] \end{equation*}

    Este modelo es un buen ejemplo de como se puede realizar un método de aprendizaje diferencial, llamado así porque sus cambios se basan en una diferencia, V(s0)−V(s), entre estimaciones en dos momentos diferentes.

    El código

    No voy a entrar en la programación completa del juego ya que no me parece necesario, pero sí os dejo el código completo de la aplicación para que lo probéis y podáis empezar con el aprendizaje por refuerzo. El código lo podéis encontrar en mi GitHub:

    https://github.com/igarciaferreira/tres-en-raya

    Lo que si voy a explicar es la parte en la que se realiza el aprendizaje de la inteligencia artificial. Está basado en la ecuación que hemos visto anteriormente así que será muy sencillo de entender. Iré haciendo la explicación por pasos para que se más sencillo de seguir.

    La lógica de nuestro agente

    La función donde se realizá toda la lógica de la inteligencia artificial es la función “turno” dentro de la clase “jugador_IA”. Antes de entrar a explicar que hace realmente esa función creo importante repasar unas inicializaciones que se dan al principio de la clase.

    self.tasa_aprendizaje = 0.2
    self.estado_valor = {}
    self.cargar_politica()
    self.ultima_posicion_jugada = np.zeros((FILAS, COLUMNAS), dtype=int)

    La tasa de aprendizaje es el alfa que vimos en la fórmula anterior que ibamos a utilizar. Como vemos está multiplicando a la diferencia de la nueva probalidad de ganar entre la actual, así que cuanto mayor sea este valor mayor será lo que aprenda al hacer esta operación. Aunque se pueda pensar que esto es bueno y deberiamos ponerlo siempre a uno no es así.

    Una tasa de aprendizaje demasiado alta hará que el aprendizaje fluctúe muchísimo entre una decisión u otra y eso no es bueno. Pero una tasa de aprendizaje demasiado baja tardará demasiado en aprender una buena combinación. En este caso he optado por el 0.2 pero se puede cambiar e ir jugando con él y ver cual es el valor ideal.

    La variable “estado_valor” es el valor donde se iran guardando los pares de “posición-posibilidad de ganar” y que se irán consultando para cada poder saber cual es probabilidad de ganar de cada posición y para realizar la fórmula. Las otras dos variables se explica por si mismo.

    Ahora vayamos a ver la función “turno” que es donde se realiza la magia.

    aleatorio = random.randint(0, 1)
    if self.serializar(tablero) != "000000000":
    	self.estado_valor[self.serializar(self.ultima_posicion_jugada)] = self.probabilidad(self.ultima_posicion_jugada)+self.tasa_aprendizaje*(self.probabilidad(tablero)-self.probabilidad(self.ultima_posicion_jugada))

    Al comienzo se saca un valor aleatorio para determinar si la siguiente jugada es de exploración o de razonamiento. Este es un problema también muy amplio dentro de la inteligencia artificial, ya que hay momentos en los cuales la IA tiene que tomar un valor no esperado ya que sino constantemente iría tomando siempre las mismas decisiones y nunca exploraría el tablero (este es un tema muy amplio que tendrá una entrada a parte en el blog). En este trozo de código también se mira si el es la primera posición del tablero y no hay fichas en ella, y si no es así se guarda la probabilidad de la posición actual.

    if aleatorio:
    	colocada = False
    	tablero_antiguo = tablero.copy()
    	while colocada == False:
    		fila = random.randint(1, 3)
    		columna = random.randint(1, 3)
    		if tablero[fila-1, columna-1] == 0:
    			tablero[fila-1, columna-1] = self.ficha
    			colocada = True
    	self.estado_valor[self.serializar(tablero_antiguo)] = self.probabilidad(tablero_antiguo)+self.tasa_aprendizaje*(self.probabilidad(tablero)-self.probabilidad(tablero_antiguo))

    Esta el la parte de exploración que hablamos anteriormente. Simplemente coloca una ficha aleatoria en el tablero y se calcula la probabilidad de ganar de la posición actual.

    else:
    	posiciones = self.conseguir_posiciones(tablero)
    	mejor_valor = 0
    	mejor_jugada = np.zeros((FILAS, COLUMNAS), dtype=int)
    	for posicion in posiciones:
    		valor = self.probabilidad(posicion)
    		if valor > mejor_valor:
    			mejor_valor = valor
    			mejor_jugada = posicion
    	self.estado_valor[self.serializar(tablero)] = self.probabilidad(tablero)+self.tasa_aprendizaje*(self.probabilidad(mejor_jugada)-self.probabilidad(tablero))

    Esta es la parte donde se produce toda la lógica de la IA. Primero se ven las posiciones que quedan disponibles y por cada posición se busca la mejor jugada. Una vez que se ha encontrado la mejor jugada se calcula cual será la nueva probabilidad de ganar de esa posición y se guarda en el diccionario.

    self.guardar_politica()
    self.ultima_posicion_jugada = mejor_jugada.copy()
    return mejor_jugada

    Después de esto se guardan las políticas nuevamente, se guarda en una variable cual ha sido la jugada seleccionada (para futuros cálculos como hemos visto antes) y se devuelve al tablero la jugada seleccionada.

    Recompensas

    Cuando empieza el juego todas las posiciones en el diccionario tendrían un valor de ganar de 0.5, y solo las que son de victoria devuelven un 1. ¿Con esto que se consigue? Pues muy fácil. Cuando una posición es previa a ganar tendrá una probabilidad de ganar inicial de 0.5, pero una vez que haya ganado la formula le dará un nuevo valor:

    (2)   \begin{equation*} Nuevo\;  valor = 0.5 + 0.2\cdot (1-0.5)= 0.5 + 0.1 = 0.6 \end{equation*}

    Con lo cual la siguiente vez que juege y se encuentre con esta posición al recorrer todas las posiciones encontrará que esta es la mejor posición para ganar la partida, ya que anteriormente la ganó. Así que la volverá a jugar para ganar y si vuelve a ganar la probabilidad volverá a subir.

    Esto hecho con todas las posiciones del tablero hará que tengamos en cada posición posible una probabilidad de ganar que hará que la IA escoja esa posición.

    Conclusiones

    ¿Se podría mejorar este algoritmo? Sí, mucho. En este caso solo hemos introducido un sistema de recompensa, pero no de penalización. Si introducimos uno el sistema sería capaz de evitar malas jugadas y quizá evitaría que el contrincante ganase la mayoría de las veces. En este caso solo está centrado en ganar y no se da cuenta de que hay veces que hay que protegerse porque sino pierdes.

    También existen optimizaciones al propio algoritmo, como lo es que al tratarse de un cuadrado puede haber situaciones muy similares y no hace falta guardar todas las posiciones, con el consiguiente gasto de memoria al guardarlo y de CPU al recorrerlo para buscar la mejor posición. Incluso se podría optimizar la función de exploración y explotación para que no fuera solo un aleatorio, sino que de verdad vea la posibilidad de explotar cuando realmente es necesario.

    Todas estas opciones se verán en próximas entradas del blog, así que estar atentos. Esta entrada solo esperaba ser una introducción al aprendizaje por refuerzo, un tema del que espero que te enganches como lo he hecho yo.

    ¿Se os ocurre alguna mejora más? No dudéis en ponerla en los comentarios o enviarme un mensaje para incluirla.

    Deja una respuesta

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