"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# **Aprendizaje por refuerzo 1**\n",
"\n",
"El **aprendizaje por refuerzo** agrupa al conjunto de métodos del **aprendizaje automático** en el que un **agente** aprende a tomar decisiones sobre las **acciones** a ejecutar en un **entorno** que lo llevan a maximizar una **recompensa**. Lo hace mediante la **exploración** de nuevas vías y soluciones y la **explotación** del conocimiento que adquiere mediante repetidas pruebas.\n",
"\n",
- "\n",
+ "
\n",
+ " \n",
+ "
\n",
"\n",
"Se asume que el comportamiento del sistema es discreto, es decir, que está formado por una secuencia de pasos. El caso continuo se trataría con intervalos temporales. Cada paso consiste en estudiar el estado $s$ del entorno y seleccionar una acción $a$. El entorno responde con un nuevo estado $s'$ y una recompensa $r$. El comportamiento del entorno es, en general, desconocido y puede ser estocástico, es decir, que la evolución del entorno y la recompensa generada pueden obedecer a una cierta función de probabilidad.\n",
"\n",
@@ -21,11 +32,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Q-Learning\n",
+ "## **Q-Learning**\n",
"\n",
"Supongamos que tenemos un robot cuyo objetivo es aprender a salir de una casa, como la que muestra el plano de la figura siguiente. Para ello, va a realizar una serie de múltiples intentos, obteniendo **recompensa** únicamente cuando consiga salir. En cada intento el robot partirá desde alguna habitación aleatoria. A estos \"intentos\" le daremos el nombre de \"episodios\". Denominaremos **episodio** al conjunto de **acciones** que el robot toma desde que parte inicialmente de una habitación hasta que consigue salir.\n",
"\n",
- ""
+ "
\n",
+ " \n",
+ "
"
]
},
{
@@ -34,110 +47,340 @@
"source": [
"Las puertas que dan directamente al exterior tienen una recompensa de 100. El resto de puertas no tienen recompensa. El plano de la casa puede ser visto como un grafo (figura siguiente). Cuando el robot llega al **estado** número 5 del grafo el **episodio** finaliza.\n",
"\n",
- ""
+ "
\n",
+ " \n",
+ "
"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Este grafo puede ser representado también por una matriz donde las filas representan los **estados** y las columnas las **acciones** que se pueden tomar. En este caso en particular, las acciones corresponden a los estados a los que se puede ir. Así que, en este caso, la matriz es cuadrada.\n",
- "Vamos a llamar a esta matriz $R$, **matriz de recompensas**. En este caso, vamos a denotar con el valor $-1$ a una acción que no es posible ejecutar para un determinado estado. Ojo, en otro tipo de problemas el valor $-1$ puede corresponder con una recompensa negativa (o castigo).\n",
- "\n",
+ "Este grafo puede ser representado también por una tabla donde las filas representan los **estados** y las columnas las **acciones** que se pueden tomar. Vamos a llamar a esta tabla $R$, **tabla de recompensas**. En este caso, vamos a denotar con el valor $-1$ a una acción que no es posible ejecutar para un determinado estado. Ojo, en otro tipo de problemas el valor $-1$ puede corresponder con una recompensa negativa (o castigo).\n",
+ "\n",
+ "\n",
+ "
\n",
"\n",
"\n",
"A medida que el robot vaya deambulando por la casa y completando episodios irá acumulando recompensas. \n",
"\n",
- "Supongamos que el robot parte de la habitación (estado) 1. En ese momento, el robot no tiene ningún tipo de conocimiento de la casa, no sabe qué puerta elegir para llegar antes a la salida. Por supuesto, no tiene acceso a la matriz de recompensas. En esas condiciones, el robot solo puede hacer una elección aleatoria de una de las dos puertas, supongamos que elige la inferior (véase el plano de la casa). Es decir, elige la acción “ir al estado 3”. \n",
+ "Supongamos que el robot parte de la habitación (estado) 1. En ese momento, el robot no tiene ningún tipo de conocimiento de la casa, no sabe qué puerta elegir para llegar antes a la salida. Por supuesto, no tiene acceso a la matriz de recompensas. En esas condiciones, el robot solo puede hacer una elección aleatoria de una de las dos puertas, supongamos que elige la inferior (véase el plano de la casa). Es decir, elige la acción “Down”. \n",
"\n",
- "Una vez en la habitación 3 descubre que no recibe ninguna recompensa y que vuelve a encontrarse en la misma situación, ¿qué puerta elegir? Todas, incluso ir de nuevo al estado 1, son para el robot elecciones aceptables, puesto que todas le proporcionan la misma incertidumbre. De nuevo, mediante una selección totalmente aleatoria, elige la puerta izquierda, “ir al estado 4”.\n",
+ "Una vez en la habitación 3 descubre que no recibe ninguna recompensa y que vuelve a encontrarse en la misma situación, ¿qué puerta elegir? Todas, incluso ir de nuevo al estado 1, son para el robot elecciones aceptables, puesto que todas le proporcionan la misma incertidumbre. De nuevo, mediante una selección totalmente aleatoria, elige la puerta izquierda, acción “Left”.\n",
"\n",
- "Ya en el estado 4 la situación se repite. De nuevo, no recibe ninguna recompensa. Otra vez de forma aleatoria, elige la puerta inferior “ir al estado 5”.\n",
+ "Ya en el estado 4 la situación se repite. De nuevo, no recibe ninguna recompensa. Otra vez de forma aleatoria, elige la puerta inferior, acción “Down”.\n",
"\n",
- "Llegado al estado 5, el robot descubre que recibe 100 puntos (puntos, dinero, gallifantes... cualquier cosa vale) de recompensa. En ese momento el robot actualizará su tabla $Q$, dado que hay algo de información nueva. Actualizará, por tanto, la entrada $(4,5)$ con el valor $100$. Démonos cuenta de que $4$ representa el estado en el que se encontraba el robot y $5$ es la acción que tomó (\"ir al estado 5\"). En otras palabras significaría que debemos apuntar en una libreta que si estamos en la habitación 4 y vamos por la puerta inferior obtendremos 100 puntos de recompensa. La próxima vez que estemos en la habitación $4$, ya sabremos qué puerta elegir para obtener alguna recompensa. Ten en cuenta que no hemos explorado otras puertas de la habitación 4, luego no sabemos si esas otras puertas nos llevan a recompensas mayores.\n",
+ "Llegado al estado 5, el robot descubre que recibe 100 puntos (puntos, dinero, gallifantes... cualquier cosa vale) de recompensa. En ese momento el robot actualizará su tabla $Q$, dado que hay algo de información nueva. Actualizará, por tanto, la entrada $(4, Down)$ con el valor $100$. Démonos cuenta de que $4$ representa el estado en el que se encontraba el robot y $Down$ es la acción que tomó. En otras palabras significaría que debemos apuntar en una libreta que si estamos en la habitación 4 y vamos por la puerta inferior obtendremos 100 puntos de recompensa. La próxima vez que estemos en la habitación $4$, ya sabremos qué puerta elegir para obtener alguna recompensa. Ten en cuenta que no hemos explorado otras puertas de la habitación 4, luego no sabemos si esas otras puertas nos llevan a recompensas mayores.\n",
"\n",
"\n",
"Finalmente, como el robot ya ha salida de la casa, el episodio termina.\n",
"\n",
"La tabla $Q$ actualizada será:\n",
"\n",
- "$$Q = \\begin{pmatrix}\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 100 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0\n",
- "\\end{pmatrix}$$\n",
- "\n",
- "\n",
- "\n",
- "Empezamos, por tanto, un nuevo episodio. Supongamos que, por una cuestión aleatoria, el robot parte de la habitación 3. Y, de nuevo, el azar nos lleva a seleccionar la puerta de la izquierda, (ir al estado 4).\n",
- "Tengo que confesarte que, por simplificar, en el episodio anterior no expliqué completamente cómo se actualiza la tabla $Q$. Ahora sí que la vamos a ir actualizando correctamente. Para ello, vamos a hacer uso de esta fórmula, denominada **ecuación de Bellman**:\n",
+ "
\n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
Q
\n",
+ "
Up
\n",
+ "
Right
\n",
+ "
Down
\n",
+ "
Left
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0
\n",
+ "
0
\n",
+ "
100
\n",
+ "
0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "Matriz Q\n",
+ "
\n",
+ "\n",
+ "\n",
+ "\n",
+ "Empezamos, por tanto, un nuevo episodio. Supongamos que, por una cuestión aleatoria, el robot parte de la habitación 3. Y, de nuevo, el azar nos lleva a seleccionar la puerta de la izquierda.\n",
+ "\n",
+ "Por simplificar, en el episodio anterior no se explicó con detalle cómo se actualiza la tabla $Q$. Ahora sí que lo vamos a explicar. Para ello, vamos a hacer uso de esta fórmula, denominada **ecuación de Bellman**:\n",
"\n",
"$$Q(s,a) = R(s,a) + \\gamma max[Q(s',a')]$$\n",
"\n",
- "Significa lo siguiente, cuando el robot se encuentra en el estado $s$ y toma la acción $a$ pasa al estado $s’$. Una vez en el estado $s’$ podemos consultar la tabla $Q$ para ver qué acción $a’$ es la que tiene la recompensa máxima, $max[Q(s’,a’)]$. Por tanto, la actualización de $Q(s,a)$ se compone de dos partes. En primer lugar, la recompensa directa por haber pasado de $s$ a $s’$ mediante la acción $a$, que en este caso es $R(3,4) = 0$. Y, en segundo lugar, la recompensa máxima que se puede obtener desde $s’$ tomando la acción $a’$ adecuada. El factor $\\gamma$ debe tener un valor mayor que $0$ y menor que $1$, pongámosle $0.8$. Su cometido es rebajar proporcionalmente la recompensa que está dos pasos más allá del estado $s$. Por tanto, la nueva actualización de $Q(3,4)$ será:\n",
+ "Significa lo siguiente, cuando el robot se encuentra en el estado $s$ y toma la acción $a$ pasa al estado $s’$. Una vez en el estado $s’$ podemos consultar la tabla $Q$ para ver qué acción $a’$ es la que tiene la recompensa máxima, $max[Q(s’,a’)]$. Por tanto, la actualización de $Q(s,a)$ se compone de dos partes. En primer lugar, la recompensa directa por haber pasado de $s$ a $s’$ mediante la acción $a$, que en este caso es $R(3, Left) = 0$. Y, en segundo lugar, la recompensa máxima que se puede obtener desde $s’$ tomando la acción $a’$ adecuada. El factor $\\gamma$ debe tener un valor mayor que $0$ y menor que $1$, pongámosle $0.8$. Su cometido es rebajar proporcionalmente la recompensa que está dos pasos más allá del estado $s$. Por tanto, la nueva actualización de $Q(3, Left)$ será:\n",
"\n",
- "$$Q(3,4) = R(3,4) + \\gamma \\cdot max[Q(4,0),Q(4,3),Q(4,5)]$$\n",
+ "$$Q(3, Left) = R(3, Left) + \\gamma \\cdot max[Q(4, Up),Q(4, Right),Q(4, Down)]$$\n",
"\n",
"Que es:\n",
"\n",
- "$$Q(3,4) = 0 + 0.8 \\cdot max[0, 0, 100]$$\n",
+ "$$Q(3, Left) = 0 + 0.8 \\cdot max[0, 0, 100]$$\n",
"\n",
"Los nuevos valores de $Q$ serán:\n",
"\n",
- "$$Q = \\begin{pmatrix}\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 80 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 100 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0\n",
- "\\end{pmatrix}$$\n",
+ "
\n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
Q
\n",
+ "
Up
\n",
+ "
Right
\n",
+ "
Down
\n",
+ "
Left
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
80
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0
\n",
+ "
0
\n",
+ "
100
\n",
+ "
0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "Matriz Q\n",
+ "
\n",
"\n",
"Por tanto, ¿qué almacena la tabla $Q$? Almacena la recompensa que podría obtenerse a partir de cada estado, pero disminuida exponencialmente en función de la lejanía a la que la recompensa se encuentra. Desde un determinado estado podríamos tener dos opciones con el mismo valor de Q, yendo por un camino podríamos conseguir una recompensa pequeña pero cercana, mientras que por el otro podríamos conseguir una recompensa mucho mayor pero nos obligaría a dar muchos pasos intermedios.\n",
"\n",
- "Aún no hemos terminado este segundo episodio. Nos encontramos en el estado 4. Si el robot consulta la tabla $Q$ puede ver que si elige la acción “ir al estado 5” obtendrá mayor recompensa que si toma cualquiera de las otras opciones, que, por el momento, están a $0$. Supongamos que elige “ir al estado 5” y el episodio termina.\n",
+ "Aún no hemos terminado este segundo episodio. Nos encontramos en el estado 4. Si el robot consulta la tabla $Q$ puede ver que si elige la acción “Down” obtendrá mayor recompensa que si toma cualquiera de las otras opciones, que, por el momento, están a $0$. Supongamos que elige “Down” y el episodio termina.\n",
"\n",
- "Comencemos con el tercer episodio. El robot parte de la habitación 1 (por azar). Si escoge la acción “ir al estado 3” deberá actualizar la tabla $Q$ de la siguiente forma:\n",
+ "Comencemos con el tercer episodio. El robot parte de la habitación 1 (por azar). Si escoge la acción “Down” deberá actualizar la tabla $Q$ de la siguiente forma:\n",
"\n",
- "$$Q(1,3) = R(1,3) + \\gamma \\cdot max[Q(3,1), Q(3,2), Q(3,4)]$$\n",
+ "$$Q(1, Down) = R(1, Down) + \\gamma \\cdot max[Q(3, Up), Q(3, Right), Q(3, Left)]$$\n",
"\n",
"Lo cual es:\n",
"\n",
- "$$Q(1,3) = 64 = 0 + 0.8 \\cdot max[0, 0, 80] $$\n",
+ "$$Q(1, Down) = 64 = 0 + 0.8 \\cdot max[0, 0, 80] $$\n",
"\n",
"Con lo que la tabla $Q$ quedaría:\n",
"\n",
- "$$Q = \\begin{pmatrix}\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 64 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 80 & 0 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 100 \\\\\n",
- "0 & 0 & 0 & 0 & 0 & 0\n",
- "\\end{pmatrix}$$\n",
+ "
\n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
Q
\n",
+ "
Up
\n",
+ "
Right
\n",
+ "
Down
\n",
+ "
Left
\n",
+ "
\n",
+ " \n",
+ " \n",
+ "
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
1
\n",
+ "
0
\n",
+ "
0
\n",
+ "
64
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
2
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
\n",
+ "
\n",
+ "
3
\n",
+ "
0
\n",
+ "
0
\n",
+ "
0
\n",
+ "
80
\n",
+ "
\n",
+ "
\n",
+ "
4
\n",
+ "
0
\n",
+ "
0
\n",
+ "
100
\n",
+ "
0
\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "Matriz Q\n",
+ "
\n",
"\n",
"A partir de aquí el robot podría ir utilizando la información de la tabla $Q$ para guiar su toma de decisiones, pasando del estado $3$ al $4$ y del $4$ al $5$, finalizando el tercer episodio.\n",
"\n",
@@ -147,161 +390,113 @@
"\n",
"\n",
"\n",
- "### Explotación vs. exploración\n",
+ "### **Explotación vs. exploración**\n",
"\n",
"El algoritmo Q-Learning debe acompasar una estrategia que combine cierta **explotación** con cierta **exploración**. Al principio, es evidente que lo único que se puede hacer es explorar, puesto que nuestra tabla $Q$ está vacía, no hay información. Pero, a medida que vamos completando episodios, debemos explotar esta información para obtener recompensas seguras.\n",
"\n",
"La forma de combinar exploración y explotación es lo que se denomina **política**.\n",
"\n",
- "### Convergencia\n",
+ "### **Convergencia**\n",
"\n",
"¿Cuándo termina el algoritmo Q-Learning? La primera respuesta sería: cuando la tabla $Q$ converja. Esto significa que cuando hayamos hecho los suficientes episodios, la tabla $Q$ ya no modificará más sus valores, a esta tabla la llamaremos $Q^*$. Esto ocurre fácilmente en casos como el de nuestro ejemplo. Pero en casos complejos, la tabla puede ser muy grande y sería necesario mucho tiempo (más del disponible) para que la tabla llegue a converger. Por tanto, la segunda respuesta es que no termina nunca. Siempre se estará ejecutando una determinada política que alterne, de la manera más eficiente posible, explotación y exploración.\n",
"\n",
- "### Implementación del algoritmo\n",
+ "### **Implementación del algoritmo**\n",
"\n",
"Establecemos los parámetros del algoritmo"
]
},
{
"cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import random\n",
- "\n",
- "discount = 0.8 # gamma\n",
- "final_state = 5"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Inicializamos la tabla de recompensas"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "rewards = [[-1., -1., -1., -1., 0., -1.],\n",
- " [-1., -1., -1., 0., -1., 100.],\n",
- " [-1., -1., -1., 0., -1., -1.],\n",
- " [-1., 0., 0., -1., 0., -1.],\n",
- " [0., -1., -1., 0., -1., 100.],\n",
- " [-1., 0., -1., -1., 0., 100.]]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Inicializamos la tabla $Q$ a cero."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
+ "execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+---+---+---+---+---+---+\n"
+ "Tabla Q:\n",
+ "[0.0, 0.0, 80.0, 0.0]\n",
+ "[100, 0.0, 64.0, 0.0]\n",
+ "[0.0, 0.0, 0.0, 64.0]\n",
+ "[80.0, 51.2, 0.0, 80.0]\n",
+ "[64.0, 64.0, 100, 0.0]\n"
]
}
],
"source": [
- "Q = [[0., 0., 0., 0., 0., 0.],\n",
- " [0., 0., 0., 0., 0., 0.],\n",
- " [0., 0., 0., 0., 0., 0.],\n",
- " [0., 0., 0., 0., 0., 0.],\n",
- " [0., 0., 0., 0., 0., 0.],\n",
- " [0., 0., 0., 0., 0., 0.]]\n",
- "\n",
"import random\n",
+ "\n",
+ "# Definimos las transiciones de estados basadas en las acciones\n",
+ "state_transitions = {\n",
+ " 0: {\"Down\": 4}, # Estado 0\n",
+ " 1: {\"Up\": 5, \"Down\": 3}, # Estado 1\n",
+ " 2: {\"Left\": 3}, # Estado 2\n",
+ " 3: {\"Up\": 1, \"Right\": 2, \"Left\": 4}, # Estado 3\n",
+ " 4: {\"Up\": 0, \"Right\": 3, \"Down\": 5}, # Estado 4\n",
+ "}\n",
+ "\n",
+ "Q_rows = {\"Up\": 0, \"Right\": 1, \"Down\": 2, \"Left\": 3} # Codificación numérica de las acciones\n",
+ "\n",
+ "# Establecemos las recompensas de cada estado\n",
+ "rewards = [[-1, -1, 0, -1],\n",
+ " [100, -1, 0, -1],\n",
+ " [-1, -1, -1, 0],\n",
+ " [0, 0, -1, 0],\n",
+ " [0, 0, 100, -1]]\n",
+ "\n",
+ "# Definimos e inicializamos la tabla Q\n",
+ "Q = [[0., 0., 0., 0.],\n",
+ " [0., 0., 0., 0.],\n",
+ " [0., 0., 0., 0.],\n",
+ " [0., 0., 0., 0.],\n",
+ " [0., 0., 0., 0.]]\n",
+ "\n",
+ "# Definimos el factor de descuento\n",
+ "gamma = 0.8\n",
+ "\n",
+ "# Definimos el número de episodios\n",
+ "num_episodes = 100\n",
+ "\n",
+ "# Definimos la función Q-Learning\n",
+ "def Q_learning(state, action):\n",
+ " next_state = state_transitions[state][action]\n",
+ " if next_state == 5:\n",
+ " Q[state][Q_rows[action]] = rewards[state][Q_rows[action]]\n",
+ " else:\n",
+ " Q[state][Q_rows[action]] = rewards[state][Q_rows[action]] + gamma * max(Q[next_state])\n",
+ " return\n",
+ "\n",
+ "# Ejecutamos el algoritmo Q-Learning\n",
+ "for i in range(num_episodes):\n",
+ " state = random.randint(0, 4)\n",
+ " action, next_state = random.choice(list(state_transitions[state].items()))\n",
+ " while True:\n",
+ " Q_learning(state, action)\n",
+ " if next_state == 5:\n",
+ " break\n",
+ " else:\n",
+ " state = next_state\n",
+ " action, next_state = random.choice(list(state_transitions[state].items()))\n",
" \n",
- "from tabulate import tabulate\n",
- "print(tabulate(Q, tablefmt=\"grid\"))"
+ "\n",
+ "# Mostramos la tabla Q\n",
+ "print(\"Tabla Q:\")\n",
+ "for i in range(5):\n",
+ " print(Q[i])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "Fórmula de actualización de la matriz $Q$ \n",
+ "### **Algoritmo Q-Learning con tasa de aprendizaje**\n",
"\n",
- "$$Q(s,a) = R(s,a) + \\gamma max[Q(s',a')]$$"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "def qlearning(s, a):\n",
- " Q[s][a] = rewards[s][a] + discount * max(Q[a])\n",
- " return"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "+----+----+------+----+----+-----+\n",
- "| 0 | 0 | 0 | 0 | 80 | 0 |\n",
- "+----+----+------+----+----+-----+\n",
- "| 0 | 0 | 0 | 64 | 0 | 100 |\n",
- "+----+----+------+----+----+-----+\n",
- "| 0 | 0 | 0 | 64 | 0 | 0 |\n",
- "+----+----+------+----+----+-----+\n",
- "| 0 | 80 | 51.2 | 0 | 80 | 0 |\n",
- "+----+----+------+----+----+-----+\n",
- "| 64 | 0 | 0 | 64 | 0 | 100 |\n",
- "+----+----+------+----+----+-----+\n",
- "| 0 | 0 | 0 | 0 | 0 | 0 |\n",
- "+----+----+------+----+----+-----+\n"
- ]
- }
- ],
- "source": [
- "for _ in range(100):\n",
- "\n",
- " s = random.randint(0, 4)\n",
- " \n",
- " keep = True\n",
- " while keep:\n",
- " a = random.randint(0, 5)\n",
- " while rewards[s][a] == -1:\n",
- " a = random.randint(0, 5)\n",
- " qlearning(s, a)\n",
- " s = a\n",
- " if s == final_state:\n",
- " keep = False \n",
- " \n",
- "print(tabulate(Q, tablefmt=\"grid\"))"
+ "El algoritmo Q-Learning con tasa de aprendizaje es una variante del algoritmo Q-Learning básico. La diferencia radica en que se introduce un factor de aprendizaje $\\alpha$ que modula la actualización de la tabla $Q$. La fórmula de actualización de la tabla $Q$ es:\n",
+ "\n",
+ "$$Q(s,a) = (1-\\alpha) Q(s,a) + \\alpha[R(s,a) + \\gamma max[Q(s',a')]]$$\n",
+ "\n",
+ "donde $\\alpha$ es la tasa de aprendizaje. La utilidad de la tasa de aprendizaje es que permite que la tabla $Q$ se actualice de forma más suave, evitando oscilaciones bruscas en los valores de la tabla. Además, permite que el entorno pueda cambiar y que el agente pueda adaptarse a esos cambios.\n",
+ "\n"
]
},
{
@@ -310,7 +505,7 @@
"collapsed": true
},
"source": [
- "### Ejercicios prácticos"
+ "### **Ejercicios prácticos**"
]
},
{
@@ -327,20 +522,11 @@
"\n",
"* ¿Qué pasaría si el factor $\\gamma$ fuera $1$?\n"
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": []
}
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "env",
"language": "python",
"name": "python3"
},
diff --git a/ia/nbpy/rl-02.ipynb b/ia/nbpy/rl-02.ipynb
index 3deb1c61..401d7082 100644
--- a/ia/nbpy/rl-02.ipynb
+++ b/ia/nbpy/rl-02.ipynb
@@ -4,7 +4,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Aprendizaje por refuerzo\n",
+ "
\n",
+ " \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# **Aprendizaje por refuerzo 2**\n",
"\n",
"Hemos visto ya cómo funciona el algoritmo **Q-Learning** en su versión simplificada. Ahora dotaremos de mayor formalismo al aprendizaje por refuerzo. Comenzaremos estudiando lo que significa que un sistema tenga la **propiedad de Markov**. Esto nos permitirá modelar un problema de aprendizaje por refuerzo como un conjunto de estados y transiciones probabilísticas entre estados por donde un agente va a transitar.\n",
"\n",
@@ -19,7 +28,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Propiedad de Markov\n",
+ "## **Propiedad de Markov**\n",
"\n",
"Consiste en que la evolución de un sistema dependa exclusivamente de su estado y de la acción realizada. Es decir, su evolución no depende de los estados anteriores ni de las acciones anteriores. Se dice que el sistema \"no tiene memoria\". Matemáticamente se expresaría así:\n",
"\n",
@@ -44,7 +53,7 @@
"Esto puede generar cadenas de estados, como por ejemplo \"$soleado \\rightarrow nublado \\rightarrow nublado \\rightarrow lluvioso$\" con una cierta probabilidad de ocurrencia. A estas cadenas las denominamos **cadenas de Markov**.\n",
"\n",
"\n",
- "## Recompensa y retorno\n",
+ "## **Recompensa y retorno**\n",
"\n",
"Las recompensas son los valores numéricos que recibe el agente al realizar alguna acción en algunos estados del entorno. El valor numérico puede ser positivo o negativo en función de las acciones del agente. En el aprendizaje por refuerzo nos preocupamos por maximizar la recompensa acumulada (todas las recompensas que el agente recibe del entorno) en lugar de las recompensas que recibe en el estado actual (también llamada recompensa inmediata). Esta suma total de recompensas que el agente recibe del entorno lo denominaremos **retorno**.\n",
"\n",
@@ -55,7 +64,7 @@
"donde $r_{t+1}$ es la recompensa que recibirá el agente en $t$ al realizar la acción $a$. $T$ es el instante final. El objetivo del aprendizaje por refuerzo es maximizar el retorno esperado.\n",
"\n",
"\n",
- "### Retorno con descuento\n",
+ "### **Retorno con descuento**\n",
"\n",
"El **factor de descuento $\\gamma$** determina cuánta importancia se debe dar a la recompensa inmediata y cuánta a las recompensas futuras. Básicamente, esto nos ayuda a evitar un valor de retorno infinito en tareas continuas. El factor $\\gamma$ debe tener un valor mayor que 0 y menor que 1. Un valor cercano al 0 significa que se le da más importancia a la recompensa inmediata y un valor cercano a 1 significa que se le da más importancia a las recompensas futuras. Por lo tanto, los valores comunes para el factor de descuento se encuentran entre 0.2 y 0.8. Por tanto, definimos el **retorno** con **factor de descuento** como: \n",
"\n",
@@ -74,26 +83,24 @@
},
{
"cell_type": "code",
- "execution_count": 939,
+ "execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Retorno: 134.98205542205693\n"
+ "Retorno: 134.9820554220569\n"
]
},
{
"data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEKCAYAAAD9xUlFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAgAElEQVR4nO3dd3gd9Z3v8fdXzSqWu9xtZIML1eAITAkltBBCgLAJJZB1WLIke0myyW6WwE2ehezd3Es2/QbILgkkhCSAQ8ji5KZgTHEK7thYtnFvcpFkybJsdel87x8zEsdCsmXhoznl83oePXPmN3PO+XpA89HMb+Y35u6IiIgAZEVdgIiIJA+FgoiIdFEoiIhIF4WCiIh0USiIiEgXhYKIiHRJWCiY2RNmVmVm5T0s+6KZuZmNimu738w2m9kGM3t/ouoSEZHeJfJI4SfANd0bzWwScBWwM67tNOBW4PTwPY+aWXYCaxMRkR4kLBTcfRFQ28Oi7wD3AvF3zd0APOPuLe6+DdgMnJeo2kREpGc5A/llZnY9sNvdV5tZ/KIJwOK4+Yqw7ahGjRrlpaWlJ7RGEZF0t2LFiv3uXtLTsgELBTMrBL4MXN3T4h7aehx/w8zuBu4GmDx5MsuXLz9hNYqIZAIz29HbsoG8+uhkYAqw2sy2AxOBlWY2luDIYFLcuhOBPT19iLs/5u5l7l5WUtJj0ImISD8NWCi4+xp3H+3upe5eShAEs919HzAfuNXMBpnZFGAasHSgahMRkUAiL0l9GngdmGFmFWZ2V2/ruvtaYB6wDvgDcI+7dySqNhER6VnC+hTc/bZjLC/tNv814GuJqkdERI5NdzSLiEgXhYKIiHQZ0PsURESkb7o/FbOnh2RmZfV0Nf+7o1AQSUMdMae1PUZre4yW9g5a2mO0dcRo63DaOmK0dsRoaw+nYXt7h9Me63wdoy3mdHTEaI857TGnIxas0xGLvT0fTjtiToc7se6vHWIxJ+ZBe8ydmBP32onFIOaOeziFrvU8bh3n7fmudT2+HZywLXxvsCyunc6d65Hvfbv97fnOO6Xi16Hb58WtFre+d63XbdERO/r4z+uP684ax8Mfm92/Nx+FQkEkAu5OQ2sHh5rbONTczqHmdhpb22lo6aCxtZ3G1o53zDe1ddDc1kFzW4ymI+aDnX5Le4yWto5wR5/YZ6/nZBnZWUZOlpEVTrOzjCw7chq8pqvt7eXBX7lZZmSbYeE6OeE6ZnRNO5ebBe8zjKysYN6Ibw/bwnWsqy1u3oDeloXv79S57O3Xne9/+7s7b7uNXy+umbiPe8c68evFN8b/7X/kwA9vfwbA9DGDj/4fqZ8UCiLvQizmHGxqo/pwC7UNrdQ1tlHX2MqBxjbqmlqpa2jjQGMrdU1t1Dd1BkAbh1vaifVxv12Yl01hXjb5ucFPQW42+blZFOfnUFI8iILcbAblZDEoN4tBOcHrvJwjX+flZJGXHUxzs7PIzTbysrPIDedzsiyYZhu5WcE0J8vIiWuLDwFJXwoFkR64OzUNreyta2bPwSb21jVReaiF/YdaqD7cwv7DLVQfaqHmcCvtvezd87KzGFaYy/DCPIYW5jJxeCFD8nMozs+hOD/3iOng/BwGD8qhMC+borwcCgcF04LcbO2EZUApFCRj1TW2sr2mke37G9he08DO2kb21jWz92ATew4209oeO2L9nCxj1OBBjCrOo2TwIE4dO4SS4kFh2yBGFuUxtCCX4UV5DC/MpSA3G+t+/C+S5BQKktY6Ys6Omgbe2neIjZWHwgBoZHtNA3WNbV3rmcHYIfmMH1bAGROGcvXpYxk3NJ9xQwuYMKyAccPyGVGYp7/aJe0pFCRt1Da08tbeetbvO8SGffVdQdDcFvzFbwbjhxZQOqqQD545jtKRRZSOKqJ0ZCGTRhSSn6vnOokoFCQltXfEeGvfId7YeYCVO+tYufMAO2oau5aPLMpj5rhibp9zEjPHFjNz7BCmjRmsHb/IMSgUJCU0trazZFsty7fXsnJHHasr6mhsDcZMLCkexOzJw7jtvMmcPn4IM8Nz/SJy/BQKkpTcnQ2Vh3htQzWLNlWzbNsBWjti5GQZp40fws1lkzhn8jBmTx7OxOEF6tAVOUEUCpI0Dja28dqmahZtrOZPm6qprG8BYMaYYuZeeBKXTC+h7KQRFOTpFJBIoigUJFKNre0sWFfJ/FV7WLSpmrYOZ2hBLu+dNopLp5Vw8fRRjBtaEHWZIhlDoSADrrU9xmsbq5m/eg8vraukqa2DsUPy+cSFpVxzxjjOnjSMbF36KRIJhYIMCHdn5c46frl8F79bs5f65naGF+Zy0+wJXD9rPOeWjtA9ACJJQKEgCdXaHuP35Xt54i/bWb2rjqK8bK4+fSzXzxrPe6eNIjdbj/QQSSYKBUmImsMtPL10Jz99fQdVh1qYOqqI/3XD6dw0eyJFg/S/nUiy0m+nnFDr99bz479s479X7aG1PcbF00bx9Y+cxaXTSnR6SCQFKBTkhNhSfZj/+MNb/HFtJfm5WXz0PRO586JSThldHHVpInIcFAryrlQdaua7L23i2WW7yM/J4gtXTmfuhScxrDAv6tJEpB8UCtIvh1vaeWzRVn70p620tse4Y85kPnvFNEYN1vASIqlMoSDHpa0jxjNLd/K9hZvYf7iVD545jn95/wxKRxVFXZqInAAKBemzJVtruP/5NWzd38CcKSP40dxTOXvSsKjLEpETSKEgx9Tc1sE3/riBJ/6yjUnDC3l8bhmXzxytQehE0pBCQY5q1a46/nneKrZUN/Dx80/ivg/M1H0GImlMv93So9b2GN9/eROPvrqF0cWDeOqu87h4WknUZYlIgiVsjAEze8LMqsysPK7tG2b2lpm9aWa/NrNhccvuN7PNZrbBzN6fqLrk2NbvreeGR/7C91/ezIfPmcAfPn+JAkEkQyRy4JmfANd0a1sAnOHuZwEbgfsBzOw04Fbg9PA9j5qZBs0fYO7OD17dwvUP/5nqQy388G/L+OZHZzG0IDfq0kRkgCTs9JG7LzKz0m5tL8bNLgY+Er6+AXjG3VuAbWa2GTgPeD1R9cmRmts6uPe5N5m/eg/XnjmWf7/xTEYU6QY0kUwTZZ/C3wHPhq8nEIREp4qw7R3M7G7gboDJkycnsr6Msf9wC596agUrdhzgS9fM5NOXTtWVRSIZKpJQMLMvA+3AzzubeljNe3qvuz8GPAZQVlbW4zrSd5sqD3HnT5ZRfaiFR2+fzbVnjou6JBGJ0ICHgpnNBa4DrnD3zp16BTApbrWJwJ6Bri3T/GlTNf/jZysZlJvNs5+6QDeiiUhCO5rfwcyuAb4EXO/ujXGL5gO3mtkgM5sCTAOWDmRtmebnS3bwiR8vY8LwAl74zEUKBBEBEnikYGZPA5cBo8ysAniA4GqjQcCC8Jz1Ynf/tLuvNbN5wDqC00r3uHtHomrLZB0x53//bj2P/3kbl80o4fu3nUNxvq4uEpGAvX0GJ/WUlZX58uXLoy4jZbR3xPjs02/w+/J9fOLCUr7ywVPJ0eMwRTKOma1w97KelumO5gwRizn3Pvcmvy/fx5evPZW/v2Rq1CWJSBLSn4kZwN15YP5ann9jN/981XQFgoj0SqGQAb7xxw08tXgHd18ylc9cfkrU5YhIElMopLkfvLqFR1/dwm3nTeb+D8zUTWkiclQKhTT21OIdfP0Pb/GhWeP59xvPUCCIyDEpFNLUr9+o4F9fKOeKmaP59s2zyM5SIIjIsSkU0tCCdZV88Zdvcv6UkTxy+2xyddmpiPSR9hZp5vUtNdzzi5WcMWEoP5xbRn6uRiAXkb5TKKSRyvpmPvOLlUweUciTd57LYD02U0SOk/YaaaK9I8Znf/EGTW0dPHvHexhWqGchiMjxUyikie+8tJGl22v5zi2zOGX04KjLEZEUpdNHaeDVDVU88soWbj13Eh8+Z2LU5YhIClMopLi9B5v4wrOrmDm2mAevPz3qckQkxSkUUlhb2I/Q2h7jkdtn60ojEXnX1KeQwr754gaW7zjA9249m5NL1I8gIu+ejhRS1MtvVfJfr23lY3Mmc8PZE6IuR0TShEIhBe2ua+Kf5q3m1HFD+NfrTou6HBFJIwqFFBP0I6ykvcN5VP0IInKCqU8hxfznq1tYubOO7992DlNGFUVdjoikGR0ppJBdtY08/MpmPnjmOD40a3zU5YhIGlIopJCv/mYd2VnGV647NepSRCRNKRRSxML1lby0vpLPXTGNcUMLoi5HRNKUQiEFNLd18OBv1nLK6MH83UVToi5HRNKYOppTwH++toVdtU384pNzyMtRjotI4mgPk+R21jTy6Ktb+NCs8Vx4yqioyxGRNKdQSGLuzoO/WUtulvHla9W5LCKJp1BIYi+tr+Llt6r4/JXTGTs0P+pyRCQDJCwUzOwJM6sys/K4thFmtsDMNoXT4XHL7jezzWa2wczen6i6UkVTawcPzl/L9DGD+cRFpVGXIyIZIpFHCj8BrunWdh+w0N2nAQvDeczsNOBW4PTwPY+aWUaP3/CDVzezu66Jf7vhDHKzdUAnIgMjYXsbd18E1HZrvgF4Mnz9JHBjXPsz7t7i7tuAzcB5iaot2W3f38B/vraVG88ez/lTR0ZdjohkkIH+E3SMu+8FCKejw/YJwK649SrCtozj7jwwfy15OVn8T3Uui8gAS5bzEtZDm/e4otndZrbczJZXV1cnuKyB96dN+3ltYzWfv3Iao4eoc1lEBtZAh0KlmY0DCKdVYXsFMCluvYnAnp4+wN0fc/cydy8rKSlJaLFRePjlzYwdks/HLzgp6lJEJAMNdCjMB+aGr+cCL8S132pmg8xsCjANWDrAtUVuydYalm6v5VOXTmVQTkb3s4tIRBI2zIWZPQ1cBowyswrgAeAhYJ6Z3QXsBD4K4O5rzWwesA5oB+5x945E1ZasHn5lMyOL8rj13MlRlyIiGSphoeDut/Wy6Ipe1v8a8LVE1ZPsVu+q40+b9nPvNTMoyNNRgohEI1k6mjPeI69sZkh+Dh8/X30JIhIdhUISeGtfPS+uq+QTF02hOD836nJEJIMpFJLAo69soTAvmzsvLI26FBHJcAqFiG3b38Bv39zDx88/ieFFeVGXIyIZTqEQsR+8upmc7CzuulhPVBOR6CkUIrS7ronnV+7mtnMnMbpYdy+LSPQUChF67LUtANx96ckRVyIiElAoRKTqUDNPL9vFTbMnMGFYQdTliIgACoXIPP6nbbR3xPiHy06JuhQRkS4KhQgcaGjlZ4t3cN1Z45kyqijqckREuigUIvDjv26nobWDe96nowQRSS4KhQHW3NbBk3/dztWnjWHG2OKoyxEROYJCYYD9vnwvB5vauPMi3ZcgIsmnz6OkmlkeMD2c3eDubYkpKb09u2wXk0cUMmfKiKhLERF5hz4dKZjZZcAm4BHgUWCjmV2SwLrS0o6aBhZvreXmsolkZfX0BFIRkWj19UjhW8DV7r4BwMymA08D70lUYenol8sryDL4m/dMjLoUEZEe9bVPIbczEADcfSOgMZ6PQ0fMeW5FBZdML2HcUN2sJiLJqa+hsNzMHjezy8KfHwIrEllYulm0qZp99c3cUjYp6lJERHrV19NH/wDcA3wOMGARQd+C9NG8ZbsYUZTHFaeOiboUEZFe9SkU3L0F+Hb4I8ep5nALL62v5G8vKCUvR1cBi0jy6lMomNlFwIPASfHvcfepiSkrvfz6jd20dTi3nKtTRyKS3Pp6+uhx4AsE/QgdiSsn/bg785bv4uxJw5g+Rncwi0hy62soHHT33ye0kjS1alcdGysP839uOjPqUkREjqmvofCKmX0DeB5o6Wx095UJqSqNzFteQUFuNtedNS7qUkREjqmvoTAnnJbFtTlw+YktJ700trbzm9V7uPbMcRTn67YOEUl+xwwFM8sG5rv7dwagnrTyuzX7ONzSrg5mEUkZx7w+0t07gOsHoJa0M2/ZLqaMKuLc0uFRlyIi0id9PX30VzN7GHgWaOhsVJ9C77ZWH2bp9lruvWYGZhr8TkRSQ19D4cJw+m9xbf3uUzCzLwCfDD9jDXAnUEgQOqXAduBmdz/Qn89PBr9cUUF2lvGR2Rr8TkRSR1/vaH7fifpCM5tAMFzGae7eZGbzgFuB04CF7v6Qmd0H3Ad86UR970Bq74jxqxUVXDa9hNFD8qMuR0Skz/r6PIWhZvZtM1se/nzLzIa+i+/NAQrMLIfgCGEPcAPwZLj8SeDGd/H5kXptYzVVh1q4WR3MIpJi+joQzxPAIeDm8Kce+HF/vtDddwPfBHYCewlujHsRGOPue8N19gKje3q/md3dGU7V1dX9KSHh5i3fxajBeVw+s8d/gohI0uprKJzs7g+4+9bw56tAv8Y9MrPhBEcFU4DxQJGZ3dHX97v7Y+5e5u5lJSUl/SkhoRpb23l1QzXXnTWe3GwNficiqaWve60mM3tv50w4QF5TP7/zSmCbu1eHz3l+nqAju9LMxoWfPw6o6ufnR2rRxv20tMe4+jQNkS0iqaevVx99GvhpXD/CAWBuP79zJ3C+mRUSBMsVwHKCS13nAg+F0xf6+fmRWrCukiH5OZw7ZUTUpYiIHLe+hkK9u88ysyEA7l5vZlP684XuvsTMngNWAu3AG8BjwGBgnpndRRAcH+3P50epvSPGy29VcvnM0Tp1JCIpqa+h8CtgtrvXx7U9B7ynP1/q7g8AD3RrbiE4akhZK3Yc4EBjG1edNjbqUkRE+uWooWBmM4HTgaFmdlPcoiGALsDv5qX1leRlZ3HpjOTrABcR6YtjHSnMAK4DhgEfims/BPx9oopKRe7OgnWVXHDySAYP6usBmIhIcjnq3svdXwBeMLML3P31AaopJW2uOsz2mkY+ebGeUCoiqauvvaE1ZrbQzMoBzOwsM/tKAutKOS+uqwTgKl2KKiIprK+h8EPgfqANwN3fJBivSEIL1lUya+JQxmisIxFJYX0NhUJ3X9qtrf1EF5OqquqbWbWrjitP1VGCiKS2vobCfjM7mWCoa8zsIwTjFgnw0vrg5uurTlcoiEhq6+tlMvcQ3GA208x2A9uA2xNWVYpZsG4fk0YUMGNMcdSliIi8K319nsJW4EozKyI4umgCbgF2JLC2lNDQ0s5fttRwx5yT9IQ1EUl5Rz19ZGZDzOx+M3vYzK4CGgnGJdpMMIR2xlu0sZrW9piuOhKRtHCsI4WnCAa/e53gZrV7gTzgRndfleDaUsKCdZUMK8zl3NLhUZciIvKuHSsUprr7mQBm9iNgPzDZ3Q8lvLIU0N4R4+UNVVw+YzQ5GgBPRNLAsfZkbZ0v3L2D4DkICoTQsu0HqGts06kjEUkbxzpSmGVmnSOjGsFzlevD1+7uQxJaXZJbsK6SvJwsLpmuAfBEJD0ca+yj7IEqJNW4OwvW7+Oik0dSpAHwRCRN6ER4P22sPMyu2iY9O0FE0opCoZ8WrNsHwJWnjo64EhGRE0eh0E8L1lVy9qRhjNYAeCKSRhQK/VBZ38zqioO66khE0o5CoR8WhM9OuFqhICJpRqHQD69trGbSiAJOGT046lJERE4ohcJxisWcZdtruWDqSA2AJyJpR6FwnDZUHqKusY05U0ZGXYqIyAmnUDhOS7bWADBn6oiIKxEROfEUCsdp8dZaJgwrYOLwwqhLERE54RQKx8HdWbq9lvOn6tSRiKQnhcJx2FR1mNqGVp06EpG0pVA4Dp39Ceerk1lE0lQkoWBmw8zsOTN7y8zWm9kFZjbCzBaY2aZwmnSPMlu8rZZxQ/OZNKIg6lJERBIiqiOF7wF/cPeZwCxgPXAfsNDdpwELw/mk4e4s2VrLnCkjdH+CiKStAQ8FMxsCXAI8DuDure5eB9wAPBmu9iRw40DXdjRbqhvYf7iFOepkFpE0FsWRwlSgGvixmb1hZj8ysyJgjLvvBQinPY5JbWZ3m9lyM1teXV09YEUv2Rb2JygURCSNRREKOcBs4Afufg7QwHGcKnL3x9y9zN3LSkoG7jGYS7bWMrp4EKUjdX+CiKSvKEKhAqhw9yXh/HMEIVFpZuMAwmlVBLX1yN1Zsq2GORrvSETS3ICHgrvvA3aZ2Yyw6QpgHTAfmBu2zQVeGOjaerOjppHK+hbmTNH9CSKS3qJ64vxngZ+bWR6wFbiTIKDmmdldwE7goxHV9g6LO+9P0E1rIpLmIgkFd18FlPWw6IqBrqUvlmyrZdTgPE4u0fMTRCS96Y7mYwjuT6hhzhT1J4hI+lMoHEPFgSb2HGzWeEcikhEUCsfQ2Z+gh+qISCZQKBzD4q21DC/MZZqexywiGUChcAxLttVw3pQRZGWpP0FE0p9C4Sh21zVRcaBJQ1uISMZQKBzFEvUniEiGUSgcxZKttQwtyGXm2OKoSxERGRAKhaNYsq2Gc0vVnyAimUOh0It9B5vZXtOooS1EJKMoFHrR+fwE9SeISCZRKPRi8dZaigflcNr4IVGXIiIyYBQKvViyrYZzp4wgW/0JIpJBFAo9qDrUzNbqBj0/QUQyjkKhB0u31QIwRzetiUiGUSj0YPWuOvJysjhd/QkikmEUCj0o313PqWOLyc3W5hGRzKK9XjfuTvmeg5wxYWjUpYiIDDiFQjc7axs51NyuUBCRjKRQ6GbN7oMAnKlQEJEMpFDopnx3PbnZxrQxeqiOiGQehUI35bsPMmNsMYNysqMuRURkwCkU4nR1Mo/XqSMRyUwKhTgVB5qoa2xTJ7OIZCyFQpy1e4JOZoWCiGQqhUKcNbsPkp1letKaiGQshUKc8t31TBs9mPxcdTKLSGaKLBTMLNvM3jCz34bzI8xsgZltCqfDB7Ied6d890HdnyAiGS3KI4V/BNbHzd8HLHT3acDCcH7A7KtvpqahVf0JIpLRIgkFM5sIfBD4UVzzDcCT4esngRsHsqY1FepkFhGJ6kjhu8C9QCyubYy77wUIp6MHsqDyPfVkGZw2TsNli0jmGvBQMLPrgCp3X9HP999tZsvNbHl1dfUJq6t890FOGT2Ygjx1MotI5oriSOEi4Hoz2w48A1xuZj8DKs1sHEA4rerpze7+mLuXuXtZSUnJCSuqfLfuZBYRGfBQcPf73X2iu5cCtwIvu/sdwHxgbrjaXOCFgaqpqr6ZqkMt6k8QkYyXTPcpPARcZWabgKvC+QFRrjuZRUQAyInyy939VeDV8HUNcEUUdaypqMcMPZNZRDJeMh0pRKZ8z0GmjiqiaFCkGSkiEjmFAmEns04diYgoFPYfbmHvwWYNbyEigkKB8vCZzKfrclQREYXC2j31AJw+QZ3MIiIZHwprKg5SOrKQIfm5UZciIhK5jA+F8j3qZBYR6ZTRoXCgoZWKA00KBRGRUEaHQmd/gq48EhEJZHQorOm68kidzCIikOGhUL7nIJNGFDCsMC/qUkREkkJmh4KGyxYROULGhsLBpjZ21DSqk1lEJE7GhsJaDZctIvIOmRsKu4Mrj85QJ7OISJeMDYU1uw8yfmg+IwcPiroUEZGkkbGhoDuZRUTeKSND4XBLO9v2NygURES6ychQWLenHnfdySwi0l1GPn9y9uRh/PHzlzBxeEHUpYiIJJWMDIWc7CxmjC2OugwRkaSTkaePRESkZwoFERHpolAQEZEuCgUREemiUBARkS4KBRER6aJQEBGRLubuUdfQb2ZWDex4Fx8xCth/gso50VRb/6i2/lFt/ZOqtZ3k7iU9LUjpUHi3zGy5u5dFXUdPVFv/qLb+UW39k4616fSRiIh0USiIiEiXTA+Fx6Iu4ChUW/+otv5Rbf2TdrVldJ+CiIgcKdOPFEREJI5CQUREumRkKJjZNWa2wcw2m9l9UdcTz8y2m9kaM1tlZssjruUJM6sys/K4thFmtsDMNoXT4UlU24NmtjvcdqvM7NqIaptkZq+Y2XozW2tm/xi2R77tjlJb5NvOzPLNbKmZrQ5r+2rYngzbrbfaIt9ucTVmm9kbZvbbcL5f2y3j+hTMLBvYCFwFVADLgNvcfV2khYXMbDtQ5u6R3xBjZpcAh4GfuvsZYdt/ALXu/lAYqMPd/UtJUtuDwGF3/+ZA19OttnHAOHdfaWbFwArgRuATRLztjlLbzUS87czMgCJ3P2xmucCfgX8EbiL67dZbbdeQBP/PAZjZPwFlwBB3v66/v6uZeKRwHrDZ3be6eyvwDHBDxDUlJXdfBNR2a74BeDJ8/STBDmXA9VJbUnD3ve6+Mnx9CFgPTCAJtt1RaoucBw6Hs7nhj5Mc26232pKCmU0EPgj8KK65X9stE0NhArArbr6CJPmlCDnwopmtMLO7oy6mB2PcfS8EOxhgdMT1dPcZM3szPL0UyamteGZWCpwDLCHJtl232iAJtl14CmQVUAUscPek2W691AZJsN2A7wL3ArG4tn5tt0wMBeuhLWkSH7jI3WcDHwDuCU+TSN/8ADgZOBvYC3wrymLMbDDwK+Dz7l4fZS3d9VBbUmw7d+9w97OBicB5ZnZGFHX0pJfaIt9uZnYdUOXuK07E52ViKFQAk+LmJwJ7IqrlHdx9TzitAn5NcLormVSG56U7z09XRVxPF3evDH9xY8APiXDbheedfwX83N2fD5uTYtv1VFsybbuwnjrgVYJz9kmx3TrF15Yk2+0i4PqwP/IZ4HIz+xn93G6ZGArLgGlmNsXM8oBbgfkR1wSAmRWFnX+YWRFwNVB+9HcNuPnA3PD1XOCFCGs5QucvQOjDRLTtwk7Jx4H17v7tuEWRb7veakuGbWdmJWY2LHxdAFwJvEVybLcea0uG7ebu97v7RHcvJdifvezud9Df7ebuGfcDXEtwBdIW4MtR1xNX11RgdfizNuragKcJDonbCI6w7gJGAguBTeF0RBLV9hSwBngz/IUYF1Ft7yU4JfkmsCr8uTYZtt1Raot82wFnAW+ENZQD/xq2J8N26622yLdbtzovA377brZbxl2SKiIivcvE00ciItILhYKIiHRRKIiISBeFgoiIdFEoSFoys3vCG7RE5DgoFCSlmJmb2bfi5r8YDoQXv87HCS6/O9z9/VGxYPTbUVHXIXIsCgVJNS3ATcfYwWYD/56ILzeznER8rkiyUChIqmknePbsF7ovMLOfmNlH3P0n7u5mdjhsv8zMXsN9flMAAANnSURBVDOzeWa20cweMrPbw/Hx15jZyeF6JWb2KzNbFv5cFLY/aGaPmdmLwE/N7CQzWxgOgrbQzCb3UMtIM3sxHN/+v4gbc8vM7gi/e5WZ/Vc4nHv39283s6+H6y01s1PC9g+Z2ZLwc18yszFh+6X29pj+b5hZsQW+YWbl4b/zlnDdcWa2KFy33Mwufvf/WSRdKBQkFT0C3G5mQ4/jPbMIxr8/E/g4MN3dzyMYaviz4TrfA77j7ucCf8ORwxC/B7jB3T8GPEzwHIezgJ8D/7eH73sA+LO7n0Nwp+tkADM7FbiFYODDs4EO4PZeaq4Pa3yYYBRMCMbxPz/83GcIRsYE+CJwT/iZFwNNBM8hODv8t18JfCMcluFjwB/DdWcR3NUsAoAOhSXluHu9mf0U+BzBzq8vlnk4jLCZbQFeDNvXAO8LX18JnBYMDwTAkM6xqID57t75XRcQ7HAhGObgP3r4vks613H3/2dmB8L2KwgCZln4PQX0PlDZ03HT74SvJwLPhjv3PGBb2P4X4Ntm9nPgeXevMLP3Ak+7ewfB4GivAecSjP/1RDgw3n+7u0JBuuhIQVLVdwnGOyqKa2sn/H86HPgtL25ZS9zrWNx8jLf/OMoCLnD3s8OfCR48iAag4Si19DZWTE/tBjwZ9x0z3P3BPry/8/X3gYfd/UzgU0A+gLs/BHySIGQWm9lMeh4mHg8eUHQJsBt4ysz+ttd/mWQchYKkJHevBeYRBEOn7QR/hUPw1Knc4/zYF4HPdM6Y2dm9rPdXgtEoITj18+ce1lkULsPMPgB0PnxlIfARMxsdLhthZif18j23xE1fD18PJdiZw9sjYGJmJ7v7Gnf/OrAcmBnWcIsFD4cpIQiCpeH3Vbn7DwlGTJ3dy/dLBtLpI0ll3yJuJ04wnv0LZraUYOd7tL/ue/I54BEze5Pgd2MR8Ole1nvCzP4FqAbu7GGdrwJPm9lK4DVgJ4C7rzOzrxA8XS+LYJTXe4AdPXzGIDNbQvDH221h24PAL81sN7AYmBK2f97M3kfQR7EO+D3QSnCqazXBkca97r7PzOYC/2JmbQTPudaRgnTRKKkiSciCB6aUufv+qGuRzKLTRyIi0kVHCiIi0kVHCiIi0kWhICIiXRQKIiLSRaEgIiJdFAoiItJFoSAiIl3+Pw/z8NcmTTAgAAAAAElFTkSuQmCC\n",
+ "image/png": "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AAB8GmEHAAD4NMIOAADwaYQdAADg0wg7Lej5559Xly5dFBISouHDh+vbb7+1uiS3evDBB2Wz2VwevXv3trqsU7ZixQqNHTtWSUlJstlsWrhwoctyY4xmz56tjh07KjQ0VOnp6crOzram2GY62T7ecMMNxxzb0aNHW1PsKZgzZ45+9atfqV27doqPj9dll12mrKwsl3UqKys1bdo0xcXFKSIiQpdffrny8/MtqrjpGrOP55133jHHc+rUqRZV3DwvvviiBg4c6Lyqblpamj799FPncm8/jg1Otp++cCyP57HHHpPNZtPMmTOdbe48poSdFvLOO+/ojjvu0AMPPKD169dr0KBBGjVqlAoKCqwuza369eun/fv3Ox8rV660uqRTVl5erkGDBun5558/7vInnnhCc+fO1bx585SRkaHw8HCNGjVKlZWVrVxp851sHyVp9OjRLsf2rbfeasUK3WP58uWaNm2aVq9erc8//1w1NTW66KKLVF5e7lzn9ttv10cffaT33ntPy5cv1759+zR+/HgLq26axuyjJE2ePNnleD7xxBMWVdw8nTp10mOPPaZ169Zp7dq1uuCCCzRu3Dht2bJFkvcfxwYn20/J+4/lz61Zs0YvvfSSBg4c6NLu1mNq0CLOOOMMM23aNOdru91ukpKSzJw5cyysyr0eeOABM2jQIKvLaFGSzIIFC5yvHQ6HSUxMNH/961+dbUVFRSY4ONi89dZbFlR46n6+j8YYM3HiRDNu3DhL6mlJBQUFRpJZvny5Mabu2AUGBpr33nvPuc7WrVuNJLNq1SqryjwlP99HY4z59a9/bW677TbrimohMTEx5h//+IdPHsejNeynMb53LEtLS02PHj3M559/7rJv7j6mnNlpAdXV1Vq3bp3S09OdbX5+fkpPT9eqVassrMz9srOzlZSUpK5du2rChAnavXu31SW1qJycHOXl5bkc26ioKA0fPtznju2yZcsUHx+vXr166eabb9bBgwetLumUFRcXS5JiY2MlSevWrVNNTY3L8ezdu7dSUlK89nj+fB8bvPnmm2rfvr369++vWbNmqaKiwory3MJut+vtt99WeXm50tLSfPI4SsfuZwNfOpbTpk3TJZdc4nLsJPf/3eSu5y3gwIEDstvtSkhIcGlPSEjQDz/8YFFV7jd8+HDNnz9fvXr10v79+/XQQw/pnHPO0ebNm9WuXTury2sReXl5knTcY9uwzBeMHj1a48ePV2pqqnbs2KE//elPuvjii7Vq1Sr5+/tbXV6zOBwOzZw5UyNGjFD//v0l1R3PoKAgRUdHu6zrrcfzePsoSddcc406d+6spKQkbdy4Uffee6+ysrL0wQcfWFht023atElpaWmqrKxURESEFixYoL59+yozM9OnjuOJ9lPynWMpSW+//bbWr1+vNWvWHLPM3X83CTtotosvvtj5fODAgRo+fLg6d+6sd999VzfddJOFleFUXXXVVc7nAwYM0MCBA9WtWzctW7ZMI0eOtLCy5ps2bZo2b97sE+PKTuRE+zhlyhTn8wEDBqhjx44aOXKkduzYoW7durV2mc3Wq1cvZWZmqri4WP/3f/+niRMnavny5VaX5XYn2s++ffv6zLHMzc3Vbbfdps8//1whISEt/nl0Y7WA9u3by9/f/5hR4/n5+UpMTLSoqpYXHR2tnj17avv27VaX0mIajl9bO7Zdu3ZV+/btvfbYTp8+XYsWLdKXX36pTp06OdsTExNVXV2toqIil/W98XieaB+PZ/jw4ZLkdcczKChI3bt319ChQzVnzhwNGjRIzz77rE8dR+nE+3k83nos161bp4KCAg0ZMkQBAQEKCAjQ8uXLNXfuXAUEBCghIcGtx5Sw0wKCgoI0dOhQLVmyxNnmcDi0ZMkSl35XX1NWVqYdO3aoY8eOVpfSYlJTU5WYmOhybEtKSpSRkeHTx3bPnj06ePCg1x1bY4ymT5+uBQsWaOnSpUpNTXVZPnToUAUGBrocz6ysLO3evdtrjufJ9vF4MjMzJcnrjufPORwOVVVV+cRx/CUN+3k83nosR44cqU2bNikzM9P5GDZsmCZMmOB87tZj6p7x1Pi5t99+2wQHB5v58+eb77//3kyZMsVER0ebvLw8q0tzmzvvvNMsW7bM5OTkmK+//tqkp6eb9u3bm4KCAqtLOyWlpaXmu+++M999952RZJ566inz3XffmV27dhljjHnsscdMdHS0+c9//mM2btxoxo0bZ1JTU83hw4ctrrzxfmkfS0tLzV133WVWrVplcnJyzBdffGGGDBlievToYSorK60uvUluvvlmExUVZZYtW2b279/vfFRUVDjXmTp1qklJSTFLly41a9euNWlpaSYtLc3CqpvmZPu4fft28/DDD5u1a9eanJwc85///Md0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",
"text/plain": [
- ""
+ ""
]
},
- "metadata": {
- "needs_background": "light"
- },
+ "metadata": {},
"output_type": "display_data"
}
],
@@ -140,7 +147,7 @@
"$$ G = \\frac{27}{0.2} = 135 $$\n",
"\n",
"\n",
- "### Proceso de recompensa de Markov (MRP)\n",
+ "### **Proceso de recompensa de Markov (MRP)**\n",
"\n",
"La siguiente figura muestra un grafo con varias transiciones entre estados y sus probabilidades de ocurrencia. \n",
"\n",
@@ -177,17 +184,17 @@
},
{
"cell_type": "code",
- "execution_count": 905,
+ "execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "Retorno medio para el estado 0 : 90.95110629090061\n",
- "Retorno medio para el estado 1 : 108.20844548983852\n",
- "Retorno medio para el estado 2 : 57.60614596515345\n",
- "Retorno medio para el estado 3 : 57.74389486364507\n"
+ "Retorno medio para el estado 0 : 90.99582369994424\n",
+ "Retorno medio para el estado 1 : 107.93998665924593\n",
+ "Retorno medio para el estado 2 : 57.21597346531888\n",
+ "Retorno medio para el estado 3 : 57.900204444056364\n"
]
}
],
@@ -225,17 +232,17 @@
" hist.append(GE/(episode+1))\n",
"\n",
" print(\"Retorno medio para el estado\",s,\":\", hist[-1])\n",
- " #plt.plot(hist)\n",
- " #plt.xlabel(\"Número de episodios\")\n",
- " #plt.ylabel(\"Retorno medio\")\n",
- " #plt.show()"
+ " # plt.plot(hist)\n",
+ " # plt.xlabel(\"Número de episodios\")\n",
+ " # plt.ylabel(\"Retorno medio\")\n",
+ " # plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Problemas episódicos y problemas continuos\n",
+ "### **Problemas episódicos y problemas continuos**\n",
"\n",
"Un problema **episódido** es aquel en el que existe un estado final. Por ejemplo, un juego en el que se gana o se pierde. Un **episodio** consiste en una secuencia de pasos desde un estado inicial a un estado final.\n",
"\n",
@@ -248,7 +255,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Ecuaciones de Bellman\n",
+ "## **Ecuaciones de Bellman**\n",
"\n",
"Podemos llegar a otra forma de calcular lo anterior mediante las ecuaciones de Bellman. Para esto, calcularemos una función de valor $V(s)$ que nos dará para cada estado $s \\in S$ el retorno esperado.\n",
"\n",
@@ -276,14 +283,14 @@
},
{
"cell_type": "code",
- "execution_count": 909,
+ "execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
- "V = [ 90.80343965 108.29467228 57.40289979 58.02570534]\n"
+ "V = [ 90.7298298 108.20152879 57.32091389 57.94255022]\n"
]
}
],
@@ -308,10 +315,10 @@
"\n",
"while True:\n",
" V_new = R + gamma * np.dot(P,V) # np.dot(P,V) es la multiplicación de la matriz P por el vector V\n",
- " conv = np.abs(V - V_new).sum()\n",
+ " conv = ((V - V_new)**2).sum()\n",
" # print(\"Convergencia:\", conv)\n",
" V = V_new\n",
- " if conv < 0.1:\n",
+ " if conv < 0.01:\n",
" break\n",
"\n",
"print(\"V =\", V)"
@@ -345,7 +352,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "#### Matriz de recompensas\n",
+ "#### **Matriz de recompensas**\n",
"\n",
"Hasta ahora hemos utilizado el vector de recompensas $R$, lo cual indica que llegados a un estado $s$ obtendremos la recompensa $R(s)$. A partir de ahora generalizaremos las recompensas con la matriz $R^a_{s,s'}$ que indica que la recompensa vendrá dada por el estado $s$ del que se parte, por la acción $a$ que se tome y por el estado $s'$ al que llegue\n"
]
@@ -354,7 +361,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Función $V$ y función $Q$\n",
+ "### **Función $V$ y función $Q$**\n",
"\n",
"Hemos introducido la función $V$, que denominaremos **función de valor de estado**. Introduciremos ahora la función $Q$ que llamaremos **función de valor de estado-acción**. Como ya hemos visto, $V(s)$ representa el retorno esperado que tendríamos a partir de un estado $s$. $Q(s,a)$ es el retorno esperado que tendríamos a partir del estado $s$ si ejecutamos la acción $a$. Por tanto:\n",
"\n",
@@ -401,7 +408,7 @@
"$$ \n",
"\n",
"\n",
- "#### Transiciones no deterministas\n",
+ "#### **Transiciones no deterministas**\n",
"\n",
"Hasta ahora hemos supuesto que al estar en el estado $s$ y ejecutar la acción $a$ nos vamos al estado $s’$. Pero esto no siempre tiene por qué ser así. Si estamos programando un agente para que aprenda a jugar al ajedrez y le decimos que ejecute la acción “mover el peón ‘x’ una casilla hacia adelante”, pasaremos de un estado del juego a otro de una forma totalmente determinista. Pero si estamos enseñando al agente a jugar al parchís y ejecutamos la acción “tirar el dado” podemos irnos a seis estados distintos de una manera estocástica. Por lo tanto, en muchos contextos distintos, ejecutar la acción $a$ no nos garantiza llegar al estado $s’$, sino solo una probabilidad de llegar a ese estado.\n",
"\n",
@@ -423,7 +430,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Value Iteration\n",
+ "## **Value Iteration**\n",
"\n",
"Ha llegado el momento de concretar nuestra política $\\pi$. ¿Cómo vamos a seleccionar las acciones a realizar para que el retorno sea máximo desde cada estado? Teniendo presente lo que acabamos de ver sobre las transiciones no deterministas, volvamos a retomar, por sencillez, la funcion $V$ y $Q$ con transiciones deterministas.\n",
"\n",
@@ -457,7 +464,7 @@
"\n",
"\n",
"\n",
- "## Policy Iteration\n",
+ "## **Policy Iteration**\n",
"\n",
"El método **value iteration** termina cuando los valores de $V$ convergen. En ese momento, la tabla $Q$ actuará como política y podrá guiar las acciones del agente de manera óptima. Sin embargo, podríamos obtener una política óptima incluso antes de que la tabla $V$ converja. Esto es lo que intenta hacer el método **policy iteration**.\n",
"\n",
@@ -564,18 +571,11 @@
"\n",
"En la mayoría de las ocasiones, **policy iteration** llega a una política correcta antes que **value iteration** ya que no es necesario que la tabla $V$ converja completamente.\n"
]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
}
],
"metadata": {
"kernelspec": {
- "display_name": "Python 3 (ipykernel)",
+ "display_name": "env",
"language": "python",
"name": "python3"
},
diff --git a/ia/nbpy/rl-03.ipynb b/ia/nbpy/rl-03.ipynb
index 7692f8fb..671ae9ad 100644
--- a/ia/nbpy/rl-03.ipynb
+++ b/ia/nbpy/rl-03.ipynb
@@ -4,7 +4,16 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Aprendizaje por refuerzo\n",
+ "
\n",
+ " \n",
+ "
"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# **Aprendizaje por refuerzo 3**\n",
"\n",
"Todo lo que hemos visto en los apuntes anteriores corresponden a soluciones basadas en [programación dinámica]( https://es.wikipedia.org/wiki/Programación_dinámica). Su principal característica es que son solamente aplicables cuando se conoce *a priori* todo el entorno en el que el agente se va a desenvolver. Es decir, conocemos cuáles son las recompensas en cada estado y las probabilidades de transición entre estados. Como imaginarás, esto no es siempre posible en situaciones reales, por tanto, necesitamos desarrollar métodos que busquen políticas óptimas mediante la exploración y la exploración. Veremos los métodos basados en estrategias de Monte Carlo y basadas en diferencias temporales."
]
@@ -13,7 +22,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Monte Carlo\n",
+ "## **Monte Carlo**\n",
"\n",
"La **Programación Dinámica** exige un conocimiento *a priori* del comportamiento del entorno $P_{s,s'}^a$ y $R_{s,s'}^a$ para poder evaluar y actualizar las políticas. Pero, ¿qué ocurre cuando el comportamiento del entorno es desconocido? Los métodos de **Monte Carlo** consisten en evaluar una política estudiando las recompensas obtenidas por el agente al actuar sobre el entorno. Para eso es necesario realizar numerosos episodios partiendo de diferentes estados iniciales, lo que permite obtener una estimación del retorno esperado a partir de los diferentes estados.\n",
"\n",
@@ -44,7 +53,7 @@
"- Para evitar lo segundo, se utilizan políticas con carácter exploratorio, como $\\epsilon$*-greedy*.\n",
"\n",
"\n",
- "#### Algoritmo\n",
+ "#### **Algoritmo**\n",
"\n",
"El algoritmo para estimar la función acción-valor es el siguiente:\n",
"\n",
@@ -76,7 +85,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "## Diferencias temporales\n",
+ "## **Diferencias temporales**\n",
"\n",
"Los métodos de **Monte Carlo** permiten aprender la política óptima sin necesidad de conocer el comportamiento del entorno, pero requiere realizar episodios completos para poder actualizar la estimación de $V(s)$ o de $Q(s,a)$. La **programación dinámica** permite actualizar los valores de $V(s)$ o de $Q(s,a)$ estudiando los estados vecinos, sin necesidad de realizar episodios completos, pero necesita conocer el comportamiento del entorno.\n",
"\n",
@@ -108,19 +117,25 @@
"Este algoritmo se conoce como **TD(0)**. El factor $\\alpha$ garantiza, además, que el agente se adapte a entornos no estacionarios, es decir, entornos en los que $P_{s,s'}^a$ y $R_{s,s'}^a$ varíen en el tiempo.\n",
"\n",
"\n",
- "#### Algoritmo\n",
+ "#### **Algoritmo**\n",
"\n",
- "```python\n",
- "while noConvergencia:\n",
- " Inicializar episodio\n",
- " while duraEpisodio:\n",
- " a = Π(s) # Acción elegida por la política en el estado s\n",
- " r, s’ = EjecutarAcción(a) # Recompensa y estado tras la transición\n",
- " V[s] = V[s] + alpha * ( r + gamma * V(s’) – V(s) )\n",
- " s = s’\n",
+ "```raw\n",
+ "Inicializar el valor de los estados V(s) arbitrariamente para todos los estados s\n",
+ "Repetir para cada episodio:\n",
+ " Inicializar el estado s\n",
+ " \n",
+ " Mientras el estado s no sea terminal:\n",
+ " Seleccionar una acción a basada en la política actual π(s)\n",
+ " Tomar la acción a y observar la recompensa r y el nuevo estado s'\n",
+ " Actualizar el valor del estado V(s):\n",
+ " V(s) = V(s) + α * [r + γ * V(s') - V(s)]\n",
+ " s = s' # Moverse al nuevo estado\n",
+ "\n",
+ "Fin del episodio\n",
"```\n",
"\n",
- "### Sarsa: State–action–reward–state–action \n",
+ "\n",
+ "### **Sarsa: State–action–reward–state–action**\n",
"\n",
"Este algoritmo es similar al anterior, pero haciendo uso de la **función acción-valor** $Q(s,a)$ en lugar de la **función valor** $V(s)$. Para ello, actualizamos $Q$ de la siguiente forma:\n",
"\n",
@@ -129,22 +144,28 @@
"$$\n",
"\n",
"\n",
- "#### Algoritmo\n",
+ "#### **Algoritmo**\n",
+ "\n",
+ "```raw\n",
+ "Inicializar la tabla Q(s, a) arbitrariamente\n",
+ "Repetir para cada episodio:\n",
+ " Inicializar el estado s\n",
+ " Seleccionar una acción a usando la política derivada de Q (por ejemplo, ϵ-greedy)\n",
+ "\n",
+ " Mientras el estado s no sea terminal:\n",
+ " Tomar la acción a y observar la recompensa r y el nuevo estado s'\n",
+ " Seleccionar una acción a' en el nuevo estado s' usando la política derivada de Q (por ejemplo, ϵ-greedy)\n",
+ " Actualizar Q(s, a):\n",
+ " Q(s, a) = Q(s, a) + α [r + γ * Q(s', a') - Q(s, a)]\n",
+ " s = s' # Moverse al nuevo estado\n",
+ " a = a' # Moverse a la nueva acción seleccionada\n",
+ "\n",
+ "Fin del episodio\n",
"\n",
- "```python\n",
- "while noConvergencia:\n",
- " Inicializar episodio\n",
- " a = Π(s) # Acción elegida por la política en el estado s\n",
- " while duraEpisodio:\n",
- " r, s’ = EjecutarAcción(a) # Recompensa y estado tras la transición\n",
- " a’ = Π(s’) # Acción elegida por la política en el estado s’\n",
- " Q[s,a] = Q[s,a] + alpha * ( r + gamma * Q(s’,a’) – Q(s,a) )\n",
- " s = s’\n",
- " a = a’\n",
"```\n",
"\n",
"\n",
- "### Q-learning\n",
+ "### **Q-learning**\n",
"\n",
"El algoritmo Q-learning se basa en no tomar directamente el valor de $Q(s’,a’)$ dada por la política $\\pi$ (como en *Sarsa*), sino escoger el máximo valor de $Q$ en es el estado $s’$.\n",
"\n",
@@ -154,19 +175,25 @@
"$$\n",
"\n",
"\n",
- "#### Algoritmo\n",
+ "#### **Algoritmo**\n",
+ "\n",
+ "```raw\n",
+ "Inicializar la tabla Q(s, a) arbitrariamente\n",
+ "Repetir para cada episodio:\n",
+ " Inicializar el estado s\n",
+ "\n",
+ " Mientras el estado s no sea terminal:\n",
+ " Seleccionar una acción a usando la política derivada de Q (por ejemplo, ϵ-greedy)\n",
+ " Tomar la acción a y observar la recompensa r y el nuevo estado s'\n",
+ " Actualizar Q(s, a):\n",
+ " Q(s, a) = Q(s, a) + α [r + γ * max(Q(s', a')) - Q(s, a)]\n",
+ " s = s' # Moverse al nuevo estado\n",
+ "\n",
+ "Fin del episodio\n",
"\n",
- "```python\n",
- "while noConvergencia:\n",
- " Inicializar episodio\n",
- " while duraEpisodio:\n",
- " a = Π(s) # Elegir acción utilizando una política (p.e. ε-greedy)\n",
- " (r, s’) = EjecutarAcción(a) # Recompensa y estado tras la transición\n",
- " Q(s,a) = Q(s,a) + α · [ r + γ · maxa’( Q(s’,a’)) – Q(s,a) ]\n",
- " s = s’\n",
"```\n",
"\n",
- "### TD(1), TD(2),...\n",
+ "### **TD(1), TD(2),...**\n",
"\n",
"Los algoritmos TD(0), Sarsa y Q-learning realizan una estimación del retorno esperado $R_t$ de un estado en un único paso:\n",
"\n",
@@ -198,7 +225,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "### Métodos on-policy y off-policy\n",
+ "### **Métodos on-policy y off-policy**\n",
"\n",
"La filosofía de TD(0) y *Sarsa* es desarrollar muchos episodios para evaluar una política. Una vez evaluada correctamente se actualiza la política y se vuelve a evaluar. Para generar los nuevos episodios se utiliza la política a evaluar. Esto se conoce como métodos **on-policy**. Por el contrario, se denominan métodos **off-policy** a aquellos en los que la actualización de los valores no se basa en la política a evaluar sino en una búsqueda directa de la política óptima, como, por ejemplo, el método *Q-Learning*.\n",
"\n",
@@ -220,6 +247,11 @@
"La diferencia está en que el algoritmo *Q-Learning* ha actualizado $Q$ haciendo uso de la acción $a’$ sobre $s’$ que mayor valor le ofrece. Por tanto, la acción $a’$ no ha venido seleccionada por la política $\\pi$, (*off-policy*). Sin embargo, en el algoritmo *Sarsa* la actualización se lleva a cabo por $Q(s’,a’)$ en donde $a’$ sí ha venido dada por la política $\\pi$, (*on-policy*).\n",
"\n"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": []
}
],
"metadata": {
diff --git a/ia/rl/.DS_Store b/ia/rl/.DS_Store
index 9e2267a4..5d7c638c 100644
Binary files a/ia/rl/.DS_Store and b/ia/rl/.DS_Store differ