策略评估
回顾之前我们提到的状态值函数:
v
π
(
s
)
≐
E
π
[
G
t
∣
S
t
=
s
]
=
E
π
[
R
t
+
1
+
γ
G
t
+
1
∣
S
t
=
s
]
=
E
π
[
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
]
=
∑
a
π
(
a
∣
s
)
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
π
(
s
′
)
]
v_\pi (s) \doteq \mathbb{E}_\pi [G_t | S_t = s] \\[5pt] = \mathbb{E}_\pi [R_{t+1} + \gamma G_{t+1} | S_t = s] \\[5pt] = \mathbb{E}_\pi [R_{t+1} + \gamma v_\pi (S_{t+1}) | S_t = s] \\[5pt] = \sum_a \pi (a | s) \sum_{s', r} p(s', r|s, a) [ r + \gamma v_\pi(s')]
vπ(s)≐Eπ[Gt∣St=s]=Eπ[Rt+1+γGt+1∣St=s]=Eπ[Rt+1+γvπ(St+1)∣St=s]=a∑π(a∣s)s′,r∑p(s′,r∣s,a)[r+γvπ(s′)]
使用迭代法求解:考虑一系列近似值函数
v
0
,
v
1
,
v
2
,
…
v_0, v_1, v_2 , \dots
v0,v1,v2,… 都是从
S
+
S^+
S+ 到
R
\mathbb{R}
R 的映射(
S
+
S^+
S+ 表示包含终止态的所有状态的集合)
利用贝尔曼方程进行迭代更新:
v
k
+
1
(
s
)
≐
E
[
R
t
+
1
+
γ
v
k
(
S
t
+
1
)
∣
S
t
=
s
]
=
∑
a
π
(
a
∣
s
)
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
k
(
s
′
)
]
v_{k+1}(s) \doteq \mathbb{E}[R_{t+1} + \gamma v_k(S_{t+1}) | S_t = s] \\[5pt] = \sum_a \pi(a | s) \sum_{s' ,r} p(s' ,r|s, a)[r + \gamma v_k(s')]
vk+1(s)≐E[Rt+1+γvk(St+1)∣St=s]=a∑π(a∣s)s′,r∑p(s′,r∣s,a)[r+γvk(s′)]
这个算法被称为迭代策略评估
策略提升
考虑在当前状态 s s s 下,改变原有策略,选择一个动作 a ≠ π ( s ) a \not = \pi(s) a=π(s),然后再遵从现有的策略 π \pi π
这种情况下的价值:
q
π
(
s
,
a
)
≐
E
[
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
,
A
t
=
a
]
=
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
π
(
s
′
)
]
q_\pi(s, a) \doteq \mathbb{E} [ R_{t+1} + \gamma v_\pi(S_{t+1}) | S_t = s, A_t = a] \\[5pt] = \sum_{s', r} p(s', r|s, a) [r + \gamma v_\pi(s')]
qπ(s,a)≐E[Rt+1+γvπ(St+1)∣St=s,At=a]=s′,r∑p(s′,r∣s,a)[r+γvπ(s′)]
如果 q π ( s , a ) q_\pi(s, a) qπ(s,a) 大于 v π ( s ) v_\pi(s) vπ(s) ,也就是说在状态 s s s 下选择动作 a a a 并且之后遵循策略 π \pi π 会比一直遵循策略 π \pi π 好,因此,每次到达状态 s s s 时,我们都愿意去选择动作 a a a ,而这个新的策略总体上来说会更好
这就是策略提升的一种特殊情况
考虑确定的策略
π
\pi
π 和
π
′
\pi'
π′,如果有:
q
π
(
s
,
π
′
(
s
)
)
⩾
v
π
(
s
)
.
∀
s
∈
S
q_\pi(s, \pi'(s)) \geqslant v_\pi(s) \ . \quad \forall \ s \in \mathcal{S}
qπ(s,π′(s))⩾vπ(s) .∀ s∈S
因此:
v
π
(
s
)
⩽
q
π
(
s
,
π
′
(
s
)
)
=
E
[
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
,
A
t
=
π
′
(
s
)
]
=
E
π
′
[
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
]
⩽
E
π
′
[
R
t
+
1
+
γ
q
π
(
S
t
+
1
,
π
′
(
S
t
+
1
)
)
∣
S
t
=
s
]
=
E
π
′
[
R
t
+
1
+
γ
E
[
R
t
+
2
+
γ
v
π
(
S
t
+
2
)
∣
S
t
+
1
,
A
t
+
1
=
π
′
(
S
t
+
1
)
]
∣
S
t
=
s
]
=
E
π
′
[
R
t
+
1
+
γ
R
t
+
2
+
γ
2
v
π
(
S
t
+
2
)
∣
S
t
=
s
]
⩽
E
π
′
[
R
t
+
1
+
γ
R
t
+
2
+
γ
2
R
t
+
3
+
γ
3
v
π
(
S
t
+
3
)
∣
S
t
=
s
]
⋮
⩽
E
π
′
[
R
t
+
1
+
γ
R
t
+
2
+
γ
2
R
t
+
3
+
γ
3
R
t
+
4
+
⋯
∣
S
t
=
s
]
=
v
π
′
(
s
)
v_\pi(s) \leqslant q_\pi(s, \pi'(s)) \\[5pt] = \mathbb{E}[R_{t+1} + \gamma v_\pi(S_{t+1}) | S_t = s, A_t = \pi'(s)] \\[5pt] = \mathbb{E}_{\pi'} [R_{t+1} + \gamma v_\pi(S_{t+1}) | S_t = s] \\[5pt] \leqslant \mathbb{E}_{\pi'}[R_{t+1} + \gamma q_\pi(S_{t+1}, \pi'(S_{t+1})) | S_t = s] \\[5pt] = \mathbb{E}_{\pi'}[R_{t+1} + \gamma \mathbb{E}[R_{t+2} + \gamma v_\pi(S_{t+2}) | S_{t+1}, A_{t+1} = \pi'(S_{t+1})] | S_t = s] \\[5pt] =\mathbb{E}_{\pi'}[ R_{t+1} + \gamma R_{t+2} + \gamma^2 v_\pi(S_{t+2}) | S_t = s] \\[5pt] \leqslant \mathbb{E}_{\pi'}[R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + \gamma^3 v_\pi(S_{t+3}) | S_t = s] \\ \vdots \\ \leqslant \mathbb{E}_{\pi'} [R_{t+1} + \gamma R_{t+2} + \gamma^2 R_{t+3} + \gamma^3 R_{t+4} + \cdots | S_t = s] \\[5pt] = v_{\pi'} (s)
vπ(s)⩽qπ(s,π′(s))=E[Rt+1+γvπ(St+1)∣St=s,At=π′(s)]=Eπ′[Rt+1+γvπ(St+1)∣St=s]⩽Eπ′[Rt+1+γqπ(St+1,π′(St+1))∣St=s]=Eπ′[Rt+1+γE[Rt+2+γvπ(St+2)∣St+1,At+1=π′(St+1)]∣St=s]=Eπ′[Rt+1+γRt+2+γ2vπ(St+2)∣St=s]⩽Eπ′[Rt+1+γRt+2+γ2Rt+3+γ3vπ(St+3)∣St=s]⋮⩽Eπ′[Rt+1+γRt+2+γ2Rt+3+γ3Rt+4+⋯∣St=s]=vπ′(s)
我们就说策略
π
′
\pi'
π′ 不劣于
π
\pi
π
现在,给定一个策略和价值函数后,我们可以对在某个状态下的动作改变进行评估
将其扩展到所有状态和所有可能的动作,我们根据
q
π
(
s
,
a
)
q_\pi(s,a)
qπ(s,a) 来选择每个状态下表现最好的动作
因此,新的贪婪策略
π
′
\pi'
π′:
π
′
(
s
)
≐
arg max
a
q
π
(
s
,
a
)
=
arg max
a
E
[
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
,
A
t
=
a
]
=
arg max
a
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
π
(
s
′
)
]
\pi'(s) \doteq \argmax_a q_\pi(s, a) \\[5pt] = \argmax_a \mathbb{E} [R_{t+1} + \gamma v_\pi(S_{t+1}) | S_t = s, A_t = a] \\[5pt] = \argmax_a \sum_{s', r} p (s', r| s, a) [r + \gamma v_\pi(s')]
π′(s)≐aargmaxqπ(s,a)=aargmaxE[Rt+1+γvπ(St+1)∣St=s,At=a]=aargmaxs′,r∑p(s′,r∣s,a)[r+γvπ(s′)]
这种通过 在原始策略的价值函数上选取贪婪策略 的方式来制定新策略的过程就是策略提升
策略迭代
π
0
→
E
v
π
0
→
I
π
1
→
E
v
π
1
→
I
π
2
→
E
⋯
→
I
π
∗
→
E
v
∗
\pi_0 \xrightarrow{E} v_{\pi_0} \xrightarrow{I} \pi_1 \xrightarrow{E} v_{\pi_1} \xrightarrow{I} \pi_2 \xrightarrow{E} \cdots \xrightarrow{I} \pi_* \xrightarrow{E} v_*
π0Evπ0Iπ1Evπ1Iπ2E⋯Iπ∗Ev∗
→
E
\xrightarrow{E}
E 表示策略评估 ,
→
I
\xrightarrow{I}
I 表示策略提升
书中给出的伪代码:
价值迭代
策略迭代过程中的策略评估可以用几种方式进行截断而不会失去其收敛性
一种方式是策略评估在一次迭代后就停止,这就是价值迭代
它可以写成:
v
k
+
1
(
s
)
≐
max
a
E
[
R
t
+
1
+
γ
v
k
(
S
t
+
1
)
∣
S
t
=
s
,
A
t
=
a
]
=
max
a
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
k
(
s
′
)
]
,
∀
s
∈
S
v_{k+1}(s) \doteq \max_a \mathbb{E} [R_{t+1} + \gamma v_k (S_{t+1}) | S_t = s, A_t = a] \\[5pt] = \max_a \sum_{s', r} p(s', r|s, a) [r + \gamma v_k(s')] \ , \quad \forall \ s \in \mathcal{S}
vk+1(s)≐amaxE[Rt+1+γvk(St+1)∣St=s,At=a]=amaxs′,r∑p(s′,r∣s,a)[r+γvk(s′)] ,∀ s∈S
注意到这种方式就是将贝尔曼最优方程作为更新方式:
v
∗
(
s
)
=
max
a
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
∗
(
s
′
)
]
v_*(s) = \max_{a} \sum_{s' , r} p(s', r|s, a)[r + \gamma v_*(s')]
v∗(s)=amaxs′,r∑p(s′,r∣s,a)[r+γv∗(s′)]
书中给出的伪代码:
广义策略迭代
术语广义策略迭代(GPI)指代策略评估和策略提升相互交互的一般概念
当评估过程和提升过程都稳定时,价值函数和策略就都是最优的
因为只有当价值函数与当前策略一致时它才会稳定,而策略只有当它关于当前价值函数是贪婪时才会稳定
因此,两个过程都稳定下来意味着,一个策略关于自己的价值函数是贪婪的
这预示着贝尔曼最优方程:
v
∗
(
s
)
=
max
a
∑
s
′
,
r
p
(
s
′
,
r
∣
s
,
a
)
[
r
+
γ
v
∗
(
s
′
)
]
v_*(s) = \max_{a} \sum_{s' , r} p(s', r|s, a)[r + \gamma v_*(s')]
v∗(s)=amaxs′,r∑p(s′,r∣s,a)[r+γv∗(s′)]
的成立,因此这个策略和价值函数都是最优的
实验
赌徒问题
一个赌徒对抛掷硬币的游戏进行下注。如果硬币正面朝上,他将赢得押在这一掷上的所有赌注,如果反面朝上,他将输掉所押的赌注。如果赌徒赢得 100 元,或者输光了钱,那么游戏结束。
每一次抛掷硬币,赌徒必须决定押多少,这些钱必须是整数
状态是赌徒的资本,
s
∈
{
1
,
2
,
…
,
99
}
s\in \{1, 2, \dots, 99 \}
s∈{1,2,…,99},动作是押注多少,
a
∈
{
0
,
1
,
…
,
min
(
s
,
100
−
s
)
}
a \in \{ 0, 1, \dots, \min (s, 100-s) \}
a∈{0,1,…,min(s,100−s)}
赌徒到达目标时奖励为 +1,其他转移过程都为 0
状态价值函数给出从每个状态出发能够赢的概率
策略是从资本多少到押注的一个映射
最优策略最大化达到目标的概率
p
h
p_h
ph 代表硬币正面朝上的概率,事先已知
在这个问题里我们对于环境有着足够的了解,具体地说,可以事先确定四元组 p ( s ′ , r ∣ s , a ) p(s', r|s, a) p(s′,r∣s,a) 的值,因此,可以用动态规划求解
编程求解
Casino.hpp
#ifndef CASINO_H
#define CASINO_H
#include <vector>
#include <random>
// Implement value iteration for the gambler's problem
class Casino
{
public:
Casino(unsigned target,
double ph,
double theta = 0.001,
double gamma = 0.2)
: target_(target)
, ph_(ph)
, theta_(theta)
, gamma_(gamma)
, valueFunc_(target_ + 1)
, policy_(target_ - 1)
{
valueFunc_[0] = 0;
std::random_device rd;
std::knuth_b rng{rd()};
for (size_t i = 1; i < target_; ++i)
valueFunc_[i] = std::generate_canonical<double, 12>(rng);
valueFunc_[target_] = 1;
valueIterate();
}
inline std::vector<double>& ValueFunction() noexcept {return valueFunc_;}
inline std::vector<unsigned>& Policy() noexcept {return policy_;}
private:
void valueIterate() {
for (unsigned s = 1; s < target_; ++s) {
const unsigned maxWager = std::min(s, target_ - s);
/*
* value function
* V(s) = \max_a \sum_{s', r} p(s', r|s, a) [r + \gamma V(s')]
*/
double delta = 0;
do {
delta = 0;
const double raw = valueFunc_[s];
double maxValue = 0;
for (unsigned a = 0; a <= maxWager; ++a) {
double sum = 0;
/*
* There are only two cases:
* head or tail
*/
unsigned nextState_1 = (s + a > target_) ? target_ : s + a;
double p_1 = ph_;
unsigned r_1 = (nextState_1 >= target_) ? 1 : 0;
unsigned nextState_2 = s < a ? 0 : s - a;
double p_2 = 1 - ph_;
unsigned r_2 = (nextState_2 >= target_) ? 1 : 0;
sum += (p_1 * (r_1 + gamma_ * valueFunc_[nextState_1]))
+ (p_2 * (r_2 + gamma_ * valueFunc_[nextState_2]));
if (sum > maxValue)
maxValue = sum;
}
valueFunc_[s] = maxValue;
delta = std::max(delta, std::fabs(raw - valueFunc_[s]));
} while(delta > theta_);
/*
* policy
* \pi(s) = \argmax_z \sum_{s', r} p(s', r|s, a) [r + \gamma V(s')]
*/
double maxValue = 0;
for (unsigned a = 0; a <= maxWager; ++a) {
unsigned nextState_1 = (s + a > target_) ? target_ : s + a;
double p_1 = ph_;
unsigned r_1 = (nextState_1 >= target_) ? 1 : 0;
unsigned nextState_2 = (s < a) ? 0 : s - a;
double p_2 = 1 - ph_;
unsigned r_2 = (nextState_2 >= target_) ? 1 : 0;
double tmpValue = (p_1 * (r_1 + gamma_ * valueFunc_[nextState_1]))
+ (p_2 * (r_2 + gamma_ * valueFunc_[nextState_2]));
if (tmpValue > maxValue) {
maxValue = tmpValue;
policy_[s-1] = a;
}
}
}
}
private:
const unsigned target_;
const double ph_;
const double theta_;
const double gamma_;
std::vector<double> valueFunc_;
std::vector<unsigned> policy_;
};
#endif // CASINO_H
main.cpp
#include <iostream>
#include "Casino.hpp"
using namespace std;
int main()
{
const unsigned target = 100;
const double ph = 0.4;
Casino casino(target, ph);
const auto valueFunc = casino.ValueFunction();
cout << "value function:\n";
for (const auto& i : valueFunc)
cout << i << ", ";
const auto policy = casino.Policy();
cout << "\npolicy:\n";
for (const auto& i : policy)
cout << i << ", ";
}
可视化后的结果:
参考
《Reinforcement Learning An Introduction》(second edition)