**Algoritmos para Decisiones Secuenciales** SVRAI
Fernando Sancho Caparrini --- ## Objetivos Tras presentar MDP para modelar la incertidumbre, vamos a abordar los algoritmos más sencillos para guiar una toma de decisiones con ciertas garantías respecto a la calidad de los resultados obtenidos. Comenzaremos profundizando un poro en las políticas y su evaluación. --- ## Políticas y Funciones de Valor !!!def:Políticas Una **política** es una función que determina qué acción seleccionar a partir de la historia pasada de estados y acciones. La acción a seleccionar en el momento $t$, dada la **historia** $h_t = (s_{1:t}, a_{1:t-1})$, se escribe $\pi_t(h_t)$. * Si los estados y recompensas futuras sólo dependen del estado y acción actual: $\pi_t(h_t) \rightsquigarrow \pi_t(s_t)$. * En MDP está garantizada la existencia de una política óptima que es determinista. * **Políticas estocásticas**: $\pi_t(a_t | s_t)$, probabilidad que la política asigna a tomar la acción $a_t$ en el estado $s_t$ en el tiempo $t$. * En horizonte infinito podemos restringir a **políticas estacionarias**: $\pi_t(s_t) \rightsquigarrow \pi(s)$. * En horizonte finito, la acción puede depender de cuántos pasos de tiempo queden. --- ## Funciones de Valor y Políticas Óptimas !!!def $U^\pi(s)$: **Utilidad Esperada** de ejecutar $\pi$ desde el estado $s$. En MDP, se denomina **Función de Valor**. **Política óptima** $\pi^*$: si maximiza la utilidad esperada o función de valor: \begin{equation} \label{eq4} \pi^*(s)=\arg \max_\pi U^\pi(s),\ \forall s\in S \end{equation} Puede haber múltiples políticas óptimas (depende del modelo). La función de valor asociada a una política óptima $\pi^*$ se denomina **función de valor óptima** y se denota como $U^*$. Se puede encontrar una **política óptima** utilizando **programación dinámica**. En general, los algoritmos que utilizan programación dinámica para resolver MDPs son mucho más eficientes que los métodos de fuerza bruta. --- ## Valor a partir de Políticas **Evaluación de la política**: calcular la función de valor $U^\pi$. Puede hacerse de forma iterativa: * Si la política se ejecuta para un solo paso, $U^\pi(s) = R(s, \pi(s))$. * Los pasos posteriores pueden obtenerse a partir de la ecuación de **lookahead**: \begin{equation} \label{eq5} U^\pi_{k+1}(s) = R(s, \pi(s)) + \gamma \sum_{s'} T(s' | s, \pi(s))U^\pi_k(s') \end{equation} $U^\pi$ puede calcularse con una precisión arbitraria si se realizan suficientes iteraciones de la ecuación lookahead. Si $\gamma < 1$ y $R$ está acotada, $U^\pi_k$ converge (**Ecuación de Bellman**): \begin{equation} \label{eq6} U^\pi(s) = R(s, \pi(s)) + \gamma \sum_{s'} T(s' | s, \pi(s))U^\pi(s') \end{equation} --- ## Valor a partir de Políticas !!!alg:Lookahead ~~~~C function lookahead(𝒫::MDP, U, s, a) 𝒮, T, R, γ = 𝒫.𝒮, 𝒫.T, 𝒫.R, 𝒫.γ return R(s,a) + γ*sum(T(s,a,s')*U(s') for s' in 𝒮) end function lookahead(𝒫::MDP, U::Vector, s, a) 𝒮, T, R, γ = 𝒫.𝒮, 𝒫.T, 𝒫.R, 𝒫.γ return R(s,a) + γ*sum(T(s,a,s')*U[i] for (i,s') in enumerate(𝒮)) end ~~~~ !!!alg: Iterative Policy Evaluation ~~~~C function iterative_policy_evaluation(𝒫::MDP, π, k_max) 𝒮, T, R, γ = 𝒫.𝒮, 𝒫.T, 𝒫.R, 𝒫.γ U = [0.0 for s in 𝒮] for k in 1:k_max U = [lookahead(𝒫, U, s, π(s)) for s in 𝒮] end return U end ~~~~ --- ## Valor a partir de Políticas Ecuación de Bellman: $|S|$ ecuaciones lineales con $|S|$ incógnitas (los valores de cada estado): \begin{equation} \label{eq7} {\mathbf U}^\pi = {\mathbf R}^\pi + \gamma\mathbf{T}^\pi \mathbf{U}^\pi \end{equation} * ${\mathbf U}^\pi$ y ${\mathbf R}^\pi$ vectores con $|S|$ componentes. * $\mathbf{T}^\pi$ (matriz $|S|\times|S|$): $T^\pi_{ij}$ es la probabilidad de transición del estado $s_i$ al estado $s_j$. Entonces: ${\mathbf U}^\pi - \gamma{\mathbf T}^\pi{\mathbf U}^\pi = {\mathbf R}^\pi \quad \Rightarrow \quad ({\mathbf I}- \gamma {\mathbf T}^\pi) {\mathbf U}^\pi = {\mathbf R}^\pi \quad \Rightarrow \quad {\mathbf U}^\pi = ({\mathbf I} - \gamma {\mathbf T}^\pi)^{-1} {\mathbf R}^\pi$ Requiere $O(|S|^3)$ unidades de tiempo. !!!alg: Exact Policy Evaluation. ~~~~C function policy_evaluation(𝒫::MDP, π) 𝒮, R, T, γ = 𝒫.𝒮, 𝒫.R, 𝒫.T, 𝒫.γ R' = [R(s, π(s)) for s in 𝒮] T' = [T(s, π(s), s') for s in 𝒮, s' in 𝒮] return (I - γ*T')\R' end ~~~~ --- ## Políticas a partir de Valor Dada una función de valor $U$, podemos construir una política $\pi$ que maximice la ecuación de lookahead introducida anteriormente: **Política voraz** con respecto a $s$ $$\pi(s) = \arg \max_a \Big( R(s, a) + \gamma \sum_{s'} T(s' | s, a) U(s') \Big)$$ Si $U$ es la función de valor óptima, entonces la política extraída es óptima. !!!alg:Greedy Policy ~~~~ struct ValueFunctionPolicy 𝒫 # problem U # utility function end function greedy(𝒫::MDP, U, s) u, a = findmax(a->lookahead(𝒫, U, s, a), 𝒫.𝒜) return (a=a, u=u) end (π::ValueFunctionPolicy)(s) = greedy(π.𝒫, π.U, s).a ~~~~ --- ## Funciones $Q$ !!!def:$Q$-función **Función de valor de la acción**, o **$Q$-función**: representa el rendimiento esperado cuando se empieza en el estado $s$, se toma la acción $a$, y luego se continúa con la política voraz con respecto a $Q$: $$Q(s, a) = R(s, a) + \gamma \sum_{s'} T(s' | s, a) U(s')$$ A partir de ella, podemos obtener la función de valor: $U(s) = \max_a Q(s, a)$ Y la política: $\pi(s) = \arg \max_a Q(s, a)$ Almacenar $Q$ explícitamente para problemas discretos requiere $O(|S| \times |\mathscr{A}|)$ de almacenamiento en lugar de $O(|S|)$ para $U$, pero no tenemos que usar $R$ y $T$ para extraer la política. **Función de ventaja**: cuantifica la ventaja de tomar una acción en comparación con la acción voraz: $A(s, a) = Q(s, a) - U(s)$ Las acciones voraces tienen ventaja cero, y las acciones no voraces tienen ventaja negativa. --- ## Iteración de la Política Calcula una política óptima: * Itera entre la evaluación de la política que vimos anteriormente y la mejora de la política mediante una política voraz. * Se garantiza que la iteración de la política converge para cualquier política inicial, y en un número finito de iteraciones cuando hay un número finito de políticas. * Cada iteración mejora la política si puede ser mejorada. * Aunque el número de políticas posibles es exponencial en el número de estados, la iteración de políticas suele converger rápidamente. * Suele ser costosa porque debemos evaluar la política en cada iteración. * Se puede considerar la **iteración de políticas modificada**, que aproxima la función de valor utilizando una evaluación de políticas iterativa en lugar de una evaluación de políticas exacta. Este enfoque es idéntico a la iteración de valores (que veremos a continuación). --- ## Iteración de la Política !!!alg:Policy Iteration ~~~~ struct PolicyIteration π # initial policy k_max # maximum number of iterations end function solve(M::PolicyIteration, 𝒫::MDP) π, 𝒮 = M.π, 𝒫.𝒮 for k = 1:M.k_max U = policy_evaluation(𝒫, π) π′ = ValueFunctionPolicy(𝒫, U) if all(π(s) == π′(s) for s in 𝒮) break end π = π′ end return π end ~~~~ --- ## Iteración del Valor * Alternativa más simple a la iteración de la política. * Actualiza la función de valor directamente. * Comienza con cualquier función de valor $U$ acotada (una inicialización común es $U\equiv 0$). * $U$ se actualiza con la Ec. de Bellman: $U_{k+1}(s) = \max_a \Big( R(s, a) + \gamma \sum_{s'} T(s' | s, a) U_k(s') \Big)$ !!!alg: Backup ~~~~ function backup(𝒫::MDP, U, s) return maximum(lookahead(𝒫, U, s, a) for a in 𝒫.𝒜) end ~~~~ * Converge a la función de valor óptima: $U^*(s) = \max_a \Big( R(s, a) + \gamma \sum_{s'} T(s' | s, a) U^*(s') \Big)$ * Entonces, se puede extraer una política óptima a partir de $U^*$. --- ## Iteración del Valor !!!alg:Value Iteration. ~~~~ struct ValueIteration k_max # maximum number of iterations end function solve(M::ValueIteration, 𝒫::MDP) U = [0.0 for s in 𝒫.𝒮] for k = 1:M.k_max U = [backup(𝒫, U, s) for s in 𝒫.𝒮] end return ValueFunctionPolicy(𝒫, U) end ~~~~ * El algoritmo se detiene después de un número fijo de iteraciones, pero también se puede detener cuando el valor $||U_{k+1} - U_k||_\infty < \delta$ (**residuo de Bellman**). * En este caso, $||U_k - U^*||_\infty < \epsilon=\delta \gamma/(1 - \gamma)$. Si $\gamma\sim 1$, la convergencia es más lenta, si $\gamma\sim 0$, la convergencia es más rápida. * Si $|| U_k - U^*||_\infty < \epsilon$, entonces $||U^\pi - U^*||_\infty < 2\epsilon\gamma/(1-\gamma)$. --- ## Iteración asíncrona del valor * La iteración del valor es computacionalmente intensiva (cada entrada de $U_k$ se actualiza en cada iteración para obtener $U_{k+1}$). * En la **iteración de valor asíncrona**, sólo se actualiza un subconjunto de estados en cada iteración. * Converge a $U^*$ siempre que cada estado se actualice un número infinito de veces. * La **iteración de valor Gauss-Seidel**, hace un ordenamiento de los estados y aplica Bellman. No construye una segunda función de valor en memoria con cada iteración. !!!alg:Gauss-Seidel Value Iteration ~~~~ struct GaussSeidelValueIteration k_max # maximum number of iterations end function solve(M::GaussSeidelValueIteration, 𝒫::MDP) U = [0.0 for s in 𝒫.𝒮] for k = 1:M.k_max for (i, s) in enumerate(𝒫.𝒮) U[i] = backup(𝒫, U, s) end end return ValueFunctionPolicy(𝒫, U) end ~~~~ --- ## Formulación de un programa lineal * Encontrar una política óptima puede formularse como un **programa lineal**: problema de optimización con una función objetivo lineal y un conjunto de restricciones lineales de igualdad o desigualdad. * ... y, entonces, utilizar un solucionador de programación lineal disponible. 1. Expresar la Ec. Bellman como: $$\text{minimizar }\sum_s U(s) \text{, sujeto a } U(s) \geq \max_a \Big( R(s, a) + \gamma \sum_{s'}T(s' | s, a) U(s')\Big), \text{para todo } s$$ 2. Sustituir las maximizaxiones por restricciones lineales (Variables: $\{U(s):\ s\in S\}$, con $|S|\times |A|$ restricciones): $$\text{minimizar }\sum_s U(s) \text{, sujeto a } U(s) \geq R(s, a) + \gamma \sum_{s'} T(s' | s, a) U(s') , \text{para todo } s, \text{ para todo } a$$ A partir de $U$ podemos extraer una política óptima (ya lo vimos). Por tanto, los MDP pueden resolverse en tiempo polinómico. Pero a menudo es más eficiente utilizar la iteración de valores. --- ## Formulación de un programa lineal !!!alg ~~~~ struct LinearProgramFormulation end function tensorform(𝒫::MDP) 𝒮, 𝒜, R, T = 𝒫.𝒮, 𝒫.𝒜, 𝒫.R, 𝒫.T 𝒮′ = eachindex(𝒮) 𝒜′ = eachindex(𝒜) R′ = [R(s,a) for s in 𝒮, a in 𝒜] T′ = [T(s,a,s′) for s in 𝒮, a in 𝒜, s′ in 𝒮] return 𝒮′, 𝒜′, R′, T′ end solve(𝒫::MDP) = solve(LinearProgramFormulation(), 𝒫) function solve(M::LinearProgramFormulation, 𝒫::MDP) 𝒮, 𝒜, R, T = tensorform(𝒫) model = Model(GLPK.Optimizer) @variable(model, U[𝒮]) @objective(model, Min, sum(U)) @constraint(model, [s=𝒮,a=𝒜], U[s] ≥ R[s,a] + 𝒫.γ*T[s,a,:]⋅U) optimize!(model) return ValueFunctionPolicy(𝒫, value.(U)) end ~~~~ --- ## Sistemas lineales con recompensa cuadrática * Hasta ahora, hemos asumido espacios de estado y acción discretos. Ahora relajamos esta suposición, permitiendo estados y acciones continuos y vectoriales. La ec. Bellman para problemas discretos pasa a ser: $U_{h+1}(\mathbf{s}) = \max_a \Big( R(\mathbf{s}, \mathbf{a}) + \int T(\mathbf{s}' | \mathbf{s}, \mathbf{a}) U_h(\mathbf{s}') d\mathbf{s}' \Big)$ donde $s$ y $a$ son vectores, la suma se sustituye por una integral, y $T$ proporciona una densidad de probabilidad en lugar de una masa de probabilidad. El cálculo de esta ecuación no es sencillo para una distribución de transición continua y una función de recompensa arbitrarias. En algunos casos, existen métodos de solución exacta para estos MDP: por ejemplo, si un problema tiene una dinámica lineal y una recompensa cuadrática, entonces la política óptima puede encontrarse eficientemente en forma cerrada. Estos problemas son comunes en la Teoría de Control, donde a menudo se busca regular un proceso de manera que no se desvíe mucho de un valor deseado. El coste cuadrático asigna un coste mucho mayor a los estados alejados del origen que a los cercanos. Muchos MDP pueden aproximarse con MDP cuadráticos lineales y resolverse, lo que a menudo da lugar a políticas razonables para el problema original. --- ## Sistemas lineales con recompensa cuadrática !!!Note Un problema tiene una dinámica lineal si la función de transición tiene la forma: \begin{equation} \label{eq22} T(\mathbf{s}' | \mathbf{s},\mathbf{a}) = \mathbf{T}_s\mathbf{s} + \mathbf{T}_a\mathbf{a} + \mathbf{w} \end{equation} donde $\mathbf{T}_s$ y $\mathbf{T}_a$ son matrices que determinan la media del siguiente estado $\mathbf{s}'$ a partir de $\mathbf{s}$ y $\mathbf{a}$, y $\mathbf{w}$ es una perturbación aleatoria extraída de una distribución de media cero y varianza finita que no depende de $\mathbf{s}$ y $\mathbf{a}$ (una elección común es la gaussiana multivariante). Una función de recompensa es cuadrática si se puede escribir de la forma: \begin{equation} \label{eq23} R(\mathbf{s}, \mathbf{a}) = \mathbf{s}^⊤ \mathbf{R}_s\mathbf{s} + \mathbf{a}^⊤ \mathbf{R}_a\mathbf{a} \end{equation} donde $\mathbf{R}_s$ y $\mathbf{R}_a$ son matrices que determinan cómo contribuyen a la recompensa las combinaciones de componentes de estado y acción. Además, exigimos que $\mathbf{R}_s$ sea semidefinida negativa y $\mathbf{R}_a$ sea definida negativa. Esta función de recompensa penaliza los estados y las acciones que se desvían de $0$.