**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$.