强化学习笔记专栏传送
上一篇:强化学习RL学习笔记7-表格型方法(tabular methods)
下一篇:强化学习RL学习笔记9-近端策略优化算法(Proximal Policy Optimization, PPO)
目录
前言
强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题 。
本文是笔者对强化学习的一点学习记录,成文于笔者刚开始接触强化学习期间,主要内容参考LeeDeepRL-Notes,学习期间很多概念和理论框架还很不成熟,若文中存在错误欢迎批评指正,也欢迎广大学习者沟通交流、共同进步。
Policy Gradient
在 reinforcement learning 中有 3 个 components:actor、environment 和 reward function。
让机器玩 video game 时:
- actor 做的事情就是去操控游戏的摇杆, 比如说向左、向右、开火等操作;
- environment 就是游戏的主机, 负责控制游戏的画面负责控制说,怪物要怎么移动, 你现在要看到什么画面等等;
- reward function 就是当你做什么事情,发生什么状况的时候,你可以得到多少分数, 比如说杀一只怪兽得到 20 分等等。
- environment 跟 reward function 是在开始学习之前已经给定的。你唯一能做的事是调整 actor 的 policy,使 actor 可以得到最大的 reward。Actor 有 policy, policy 决定了 actor 的行为。
接下来用一个例子来说明 actor 是怎么样跟环境互动的。
首先 actor 会看到一个游戏画面,我们用 s 1 s_1 s1 来表示游戏初始的画面。接下来 actor 根据它内部的 network 即它内部的 policy 来决定一个 action。假设现在决定的 action 是向右,它决定完 action 以后,它就会得到一个 reward ,代表它采取这个 action 以后得到的分数。
- 一场游戏叫做一个 episode(回合) 或者 trial(试验)。
- 把这场游戏里面所有得到的 reward 都加起来,就是 total reward,我们称其为return(回报),用 R 来表示它。
- Actor 要想办法去最大化它可以得到的 reward。
在一场游戏里面,我们把 environment 输出的 s 跟 actor 输出的行为 a 全部串起来, 叫做一个 Trajectory,如下式所示: T r a j e c t o r y τ = { s 1 , a 1 , s 2 , a 2 , . . . , s t , a t } Trajectory\ \tau = \{s_1,a_1,s_2,a_2,...,s_t,a_t\} Trajectory τ={s1,a1,s2,a2,...,st,at}
每一个 trajectory,你可以计算它发生的概率:
p θ ( τ ) = p ( s 1 ) p θ ( a 1 ∣ s 1 ) p ( s 2 ∣ s 1 , a 1 ) p θ ( a 2 ∣ s 2 ) p ( s 3 ∣ s 2 , a 2 ) ⋯ = p ( s 1 ) ∏ t = 1 T p θ ( a t ∣ s t ) p ( s t + 1 ∣ s t , a t ) \begin{aligned} p_{\theta}(\tau) &=p\left(s_{1}\right) p_{\theta}\left(a_{1} | s_{1}\right) p\left(s_{2} | s_{1}, a_{1}\right) p_{\theta}\left(a_{2} | s_{2}\right) p\left(s_{3} | s_{2}, a_{2}\right) \cdots \\ &=p\left(s_{1}\right) \prod_{t=1}^{T} p_{\theta}\left(a_{t} | s_{t}\right) p\left(s_{t+1} | s_{t}, a_{t}\right) \end{aligned} pθ(τ)=p(s1)pθ(a1∣s1)p(s2∣s1,a1)pθ(a2∣s2)p(s3∣s2,a2)⋯=p(s1)t=1∏Tpθ(at∣st)p(st+1∣st,at)
这个概率取决于两部分:
- 一部分是 environment 的行为, environment 的 function 它内部的参数或内部的规则长什么样子。 p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at) 这一项代表的是 environment, environment 这一项通常是无法控制的。
- 另一部分是 agent 的行为。你能控制的是 p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(at∣st) 。给定一个 s t s_t st , actor 要采取什么样的 a t a_t at 会取决于你 actor 的参数 θ \theta θ, 所以这部分是 actor 可以自己控制的。随着 actor 的行为不同,每个同样的 trajectory, 它就会有不同的出现的概率。
在 reinforcement learning 里面,除了 environment 跟 actor 以外, 还有reward function。
Reward function 根据在某一个 state 采取的某一个 action 决定现在这个行为可以得到多少的分数。 它是一个 function,给它 s 1 s_1 s1, a 1 a_1 a1 ,它得到 r 1 r_1 r1 。给它 s 2 s_2 s2 , a 2 a_2 a2 ,它得到 r 2 r_2 r2 。 把所有的 r 都加起来,我们就得到了 R ( τ ) R(\tau) R(τ) ,代表某一个 trajectory τ \tau τ 的 reward。
在某一个 episode 中会得到 R。我们要做的事情就是调整 actor 内部的参数 θ \theta θ, 使得 R 的值越大越好。 但实际上 reward 并不只是一个 scalar,reward 其实是一个 random variable,能够计算的是在给定某一组参数 θ \theta θ 的情况下,会得到的 R 的期望值: R ˉ θ = ∑ τ R ( τ ) p θ ( τ ) \bar{R}_{\theta}=\sum_{\tau} R(\tau) p_{\theta}(\tau) Rˉθ=τ∑R(τ)pθ(τ)
maximize expected reward 的方法使用的是 gradient ascent(梯度上升)
先要计算 expected reward R ˉ \bar{R} Rˉ 的 gradient,这里只有 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 与 θ \theta θ 有关,所以 gradient 就放在 p θ ( τ ) p_{\theta}(\tau) pθ(τ) 上。
取 gradient之后,有公式:
∇ f ( x ) = f ( x ) ∇ log f ( x ) \nabla f(x)=f(x)\nabla \log f(x) ∇f(x)=f(x)∇logf(x)
我们可以对 ∇ p θ ( τ ) \nabla p_{\theta}(\tau) ∇pθ(τ) 使用这个公式,然后会得到
∇ p θ ( τ ) = p θ ( τ ) ∇ log p θ ( τ ) \nabla p_{\theta}(\tau)=p_{\theta}(\tau) \nabla \log p_{\theta}(\tau) ∇pθ(τ)=pθ(τ)∇logpθ(τ)
可以得到下式:
∇ p θ ( τ ) p θ ( τ ) = log p θ ( τ ) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)}=\log p_{\theta}(\tau) pθ(τ)∇pθ(τ)=logpθ(τ)
于是可以将 expected value 转换为下式的形式:
∇ R ˉ θ = ∑ τ R ( τ ) ∇ p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ p θ ( τ ) p θ ( τ ) = ∑ τ R ( τ ) p θ ( τ ) ∇ log p θ ( τ ) = E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] \begin{aligned} \nabla \bar{R}_{\theta}&=\sum_{\tau} R(\tau) \nabla p_{\theta}(\tau)\\&=\sum_{\tau} R(\tau) p_{\theta}(\tau) \frac{\nabla p_{\theta}(\tau)}{p_{\theta}(\tau)} \\&= \sum_{\tau} R(\tau) p_{\theta}(\tau) \nabla \log p_{\theta}(\tau) \\ &=E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right] \end{aligned} ∇Rˉθ=τ∑R(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)pθ(τ)∇pθ(τ)=τ∑R(τ)pθ(τ)∇logpθ(τ)=Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]
但实际问题中,由于问题规模较大,Trajectory 数量较多,无法计算 expected value。只能通过 sample 的方式来 sample 大量的的 τ \tau τ。sample N 个 τ \tau τ, 然后计算每一个的 value,全部加起来(weighted by p θ ( τ ) p_{\theta}(\tau) pθ(τ)),就可以得到 gradient。就可以 update 参数,从而 update agent,如下式所示:
E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log p θ ( τ ) ] ≈ 1 N ∑ n = 1 N R ( τ n ) ∇ log p θ ( τ n ) = 1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) ∇ log p θ ( a t n ∣ s t n ) \begin{aligned} E_{\tau \sim p_{\theta}(\tau)}\left[R(\tau) \nabla \log p_{\theta}(\tau)\right] &\approx \frac{1}{N} \sum_{n=1}^{N} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(\tau^{n}\right) \\ &=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} \mid s_{t}^{n}\right) \end{aligned} Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]≈N1n=1∑NR(τn)∇logpθ(τn)=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
p θ ( τ ) p_{\theta}(\tau) pθ(τ) 中原本有两项, p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at) 来自于 environment, p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(at∣st) 是来自于 agent。 p ( s t + 1 ∣ s t , a t ) p(s_{t+1}|s_t,a_t) p(st+1∣st,at) 由环境决定从而与 θ \theta θ 无关,因此 ∇ log p ( s t + 1 ∣ s t , a t ) = 0 \nabla \log p(s_{t+1}|s_t,a_t) =0 ∇logp(st+1∣st,at)=0 。因此 ∇ p θ ( τ ) = ∇ log p θ ( a t n ∣ s t n ) \nabla p_{\theta}(\tau)= \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right) ∇pθ(τ)=∇logpθ(atn∣stn)。
通过采样获取 reward 从而更新 policy 的过程需要做到如下两点:
- 在 s t s_t st 执行 a t a_t at ,最后发现 τ \tau τ 的 reward 是正的, 那就要增加这一项的概率,增加在 s t s_t st 执行 a t a_t at 的概率。
- 反之,在 s t s_t st 执行 a t a_t at 导致 τ \tau τ 的 reward 变成负的, 就要减少这一项的概率。
以上两点的实现可以用 gradient ascent 来 update 参数,原来有参数 θ \theta θ ,把 θ \theta θ 加上 gradient 这一项,那当然前面要有个 learning rate,learning rate 其实也是要调的,可用 Adam、RMSProp 等方法对其进行调整。
根据policy gradient更新policy的过程
首先套如下公式计算 gradient :
∇ R ˉ θ = 1 N ∑ n = 1 N ∑ t = 1 T n R ( τ n ) ∇ log p θ ( a t n ∣ s t n ) \nabla \bar{R}_{\theta}=\frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} R\left(\tau^{n}\right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right) ∇Rˉθ=N1n=1∑Nt=1∑TnR(τn)∇logpθ(atn∣stn)
再通过 gradient 更新 model parameter:
θ ← θ + η ∇ R ˉ θ \theta \leftarrow \theta + \eta \nabla \bar{R}_{\theta} θ←θ+η∇Rˉθ
根据 sample 计算 gradient 从而 update model,再收集 sample 计算 gradient 从而 update model,反复迭代。注意一下,一般 policy gradient(PG) sample 的 data 就只会用一次。把这些 data sample 起来去 update 参数,这些 data 就丢掉了。接着再重新 sample data,才能 update 参数。
Tips
Tip 1: Add a Baseline
如果 given state s 采取 action a 会给你整场游戏正面的 reward,就要增加它的概率。如果 state s 执行 action a,整场游戏得到负的 reward,就要减少这一项的概率。
但在很多游戏里面, reward 总是正的,即最低为 0。比如乒乓球游戏,分数介于 0 到 21 分之间,所以 R 总是正的。假设直接套用之前的公式更新 gradient ,虽然 R 总是正的,但正的量有大有小,更新的 probability 有大有小,最终 normalize 后还是会存在差异。
但是实际过程中,sample 的过程可能会出现对某个 action 的遗漏情况,导致一些 action 可能从来都没有 sample 到。在某一个 state1,虽然可以执行的 action 有 a/b/c 3 个,但可能只 sample 到 action a 和 action b 而遗漏了 action c。这种情况下因为 a 和 b 的 reward 都是正的,它们对应的 probability 都增加,而 c 没有被 sample,最终 normalize 后就相当于 c 的 probability 减小。
为了避免这种情况,我们希望 reward 不要总是正的。
为了解决 reward 总是正的这个问题,你可以把 reward 减掉一项叫做 b,这项 b 叫 baseline。
∇ R ˉ θ ≈ 1 N ∑ n = 1 N ∑ t = 1 T n ( R ( τ n ) − b ) ∇ log p θ ( a t n ∣ s t n ) b ≈ E [ R ( τ ) ] \nabla \bar{R}_{\theta}\approx \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} \left(R\left(\tau^{n}\right) - b \right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\ \ \ \ b\approx E[R(\tau)] ∇Rˉθ≈N1n=1∑Nt=1∑Tn(R(τn)−b)∇logpθ(atn∣stn) b≈E[R(τ)]
减去 baseline 后,就可以使 R ( τ n ) − b R\left(\tau^{n}\right) - b R(τn)−b 这一项有正有负,得到的 total reward R ( τ n ) R(\tau^n) R(τn) 大于 b 的话,就让它的概率上升。如果这个 total reward 小于 b,就算它是正的,正的很小也是不好的,就要让这一项的概率下降。
baseline 的设定最简单的方法就是设置为 b ≈ E [ R ( τ ) ] b \approx E[R(\tau)] b≈E[R(τ)]
Tip 2: Assign Suitable Credit
原本的更新策略是
∇
R
ˉ
θ
≈
1
N
∑
n
=
1
N
∑
t
=
1
T
n
(
R
(
τ
n
)
−
b
)
∇
log
p
θ
(
a
t
n
∣
s
t
n
)
\nabla \bar{R}_{\theta}\approx \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} \left(R\left(\tau^{n}\right) - b \right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)
∇Rˉθ≈N1n=1∑Nt=1∑Tn(R(τn)−b)∇logpθ(atn∣stn)
但对于同一个 episode ,根据上述公式会利用相同的 R ( τ n ) − b R\left(\tau^{n}\right) - b R(τn)−b 更新当前 episode 的所有 state-action pair ,这显然是不公平的。
举个例子, 假设一个小游戏,只有 3 个互动, 在 s a s_a sa 执行 a 1 a_1 a1 得到 5 分。在 s b s_b sb 执行 a 2 a_2 a2 得到 0 分。在 s c s_c sc 执行 a 3 a_3 a3 得到 -2 分。 整场游戏下来得到 +3 分。
按照刚才的做法,整场游戏得到的分数是 3 分,在 training 的时候, 每一个 state 跟 action 的 pair,都会被乘上 +3,在理想的状态下,只要 sample 足够多,每个 action 的 probability 还是会最终趋于合理的状态,但在 sample 较少的情况下还是会出现问题。
一种解决方案是只累计指定 action 之后的 reward 作为对应 action 的 weight,即:
∇
R
ˉ
θ
≈
1
N
∑
n
=
1
N
∑
t
=
1
T
n
(
∑
t
′
=
t
T
n
r
t
′
n
−
b
)
∇
log
p
θ
(
a
t
n
∣
s
t
n
)
\nabla \bar{R}_{\theta}\approx \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} \left(\sum_{t'=t}^{T_n}r^n_{t'} - b \right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)
∇Rˉθ≈N1n=1∑Nt=1∑Tn(t′=t∑Tnrt′n−b)∇logpθ(atn∣stn)
本来的 weight 是整场游戏的 reward 的总和。那现在改成从某个时间 tt 开始,假设这个 action 是在 t 这个时间点所执行的,从 t 这个时间点,一直到游戏结束所有 reward 的总和,才真的代表这个 action 是好的还是不好的。
接下来再更进一步,把未来的 reward 做一个 discount,由此得到的回报被称为 Discounted Return(折扣回报)。这是因为比较真实的情况下, 如果时间拖得越长,action 的影响力就越小。R 前面乘上一个 discount factor
γ
\gamma
γ,
γ
∈
[
0
,
1
]
\gamma \in [0,1]
γ∈[0,1] ,一般会设个 0.9 或 0.99,即有:
∇
R
ˉ
θ
≈
1
N
∑
n
=
1
N
∑
t
=
1
T
n
(
∑
t
′
=
t
T
n
γ
t
′
−
t
r
t
′
n
−
b
)
∇
log
p
θ
(
a
t
n
∣
s
t
n
)
γ
∈
[
0
,
1
]
\nabla \bar{R}_{\theta}\approx \frac{1}{N} \sum_{n=1}^{N} \sum_{t=1}^{T_{n}} \left(\sum_{t'=t}^{T_n}\gamma ^{t'-t}r^n_{t'} - b \right) \nabla \log p_{\theta}\left(a_{t}^{n} | s_{t}^{n}\right)\ \ \ \ \gamma \in [0,1]
∇Rˉθ≈N1n=1∑Nt=1∑Tn(t′=t∑Tnγt′−trt′n−b)∇logpθ(atn∣stn) γ∈[0,1]
REINFORCE: Monte Carlo Policy Gradient
介绍下策略梯度最简单的也是最经典的一个算法 REINFORCE。REINFORCE 用的是回合更新的方式。它在代码上的处理上是先拿到每个 step 的 reward,然后计算每个 step 的未来总收益 G t G_t Gt 是多少,然后拿每个 G t G_t Gt 代入公式,去优化每一个 action 的输出。所以编写代码时会有这样一个函数,输入每个 step 拿到的 reward,把这些 reward 转成每一个 step 的未来总收益。因为未来总收益是这样计算的:
G t = ∑ k = t + 1 T γ k − t − 1 r k = r t + 1 + γ G t + 1 \begin{aligned} G_{t} &=\sum_{k=t+1}^{T} \gamma^{k-t-1} r_{k} \\ &=r_{t+1}+\gamma G_{t+1} \end{aligned} Gt=k=t+1∑Tγk−t−1rk=rt+1+γGt+1
上一个 step 和下一个 step 的未来总收益可以有上式这样的一个关系。所以在代码的计算上,就是从后往前推,一步一步地往前推,先算 G T G_T GT ,然后往前推,一直算到 G 1 G_1 G1 。
上图是 REINFORCE 的流程图。首先需要一个 policy model 来输出动作概率,输出动作概率后,用 sample 函数去得到一个具体的动作,然后跟环境交互过后,可以得到一整个 episode 的数据。拿到 episode 数据之后,再执行一下 learn() 函数,在 learn() 函数种,可以拿这些数据去构造 loss function,扔给优化器去优化,更新 policy model。
上一篇:强化学习RL学习笔记7-表格型方法(tabular methods)
下一篇:强化学习RL学习笔记9-近端策略优化算法(Proximal Policy Optimization, PPO)