如下图所示,强化学习有 3 个组成部分:演员(actor)、环境和奖励函数。

智能体玩视频游戏时,演员负责操控游戏的摇杆, 比如向左、向右、开火等操作;环境就是游戏的主机,负责控制游戏的画面、负责控制怪兽的移动等;奖励函数就是当我们做什么事情、发生什么状况的时候,可以得到多少分数, 比如打败一只怪兽得到 20 分等。
同样的概念用在围棋上也是一样的,演员就是 Alpha Go,它要决定棋子落在哪一个位置;环境就是对手;奖励函数就是围棋的规则,赢就是得一分,输就是负一分。
在强化学习里,环境与奖励函数不是我们可以控制的,它们是在开始学习之前给定的。我们唯一需要做的就是调整演员里面的策略,使得演员可以得到最大的奖励。演员里面的策略决定了演员的动作,即给定一个输入,它会输出演员现在应该要执行的动作。
策略一般记作 π \pi π 。假设我们使用深度学习来做强化学习,策略就是一个网络。网络里面有一些参数,我们用 θ \theta θ 来代表 π \pi π 的参数。
网络的输入是智能体看到的东西,如果让智能体玩视频游戏,智能体看到的东西就是游戏的画面。智能体看到的东西会影 响我们训练的效果。
例如,在玩游戏的时候,也许我们觉得游戏的画面是前后相关的,所以应该让策略去看从游戏开始到当前 这个时间点之间所有画面的总和。
因此我们可能会觉得要用到看环神经网络 (recurrent neural network,RNN) 来处理它,不 过这样会比较难处理。我们可以用向量或矩阵来表示智能体的观测,并将观测输入策略网络,策略网络就会输出智能体要采取 的动作。
如下图所示,策略是一个网络; 输入是游戏的画面,它通常是由像素组成的; 输出是我们可以执行的动 作,有几个动作,输出层就有几个神经元。假设我们现在可以执行的动作有 3 个,输出层就有 3 个神经元,每个神经元对应一 个可以采取的动作。输入一个东西后,网络会给每一个可以采取的动作一个分数。我们可以把这个分数当作概率,演员根据概 率的分布来决定它要采取的动作,比如 0.7 0.7 0.7 的概率向左走、 0.2 0.2 0.2 的概率向右走、 0.1 0.1 0.1 的概率开火等。概率分布不同,演员采取的 动作就会不一样。

一场游戏称为一个回合。将这场游戏里面得到的所有奖励都加起来,就是总奖励(total reward),也就是回报,我们用R来表示它。演员要想办法来最大化它可以得到的奖励。

如下图所示,首先,环境是一个函数,我们可以把游戏的主机看成一个函数,虽然它不一定是神经网络,可能是基于规则的 (rule-based) 模型,但我们可以把它看作一个函数。这个函数一开始先“吐”出一个状态(游戏画面
s
1
s_1
s1 ),接下来演员看到游 戏画面
s
1
s_1
s1 以后,它“吐”出动作
a
1
a_1
a1 。环境把动作
a
1
a_1
a1 当作它的输入,再“吐”出新的游戏画面
s
2
s_2
s2 。演员看到新的游戏画面
s
2
s_2
s2 ,再 采取新的动作
a
2
a_2
a2 。环境看到
a
2
a_2
a2 ,再"吐”出
s
3
…
…
s_3 \ldots \ldots
s3…….这个过程会一直持续下去,直到环境觉得应该要停止为止。

在一场游戏里面,我们把环境输出的
s
s
s 与演员输出的动作
a
a
a 全部组合起来,就是一个轨迹,即
τ
=
{
s
1
,
a
1
,
s
2
,
a
2
,
⋯
,
s
t
,
a
t
}
\tau=\left\{s_1, a_1, s_2, a_2, \cdots, s_t, a_t\right\}
τ={s1,a1,s2,a2,⋯,st,at}
给定演员的参数
θ
\theta
θ ,我们可以计算某个轨迹
τ
\tau
τ 发生的概率为
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
)
pθ(τ)=p(s1)pθ(a1∣s1)p(s2∣s1,a1)pθ(a2∣s2)p(s3∣s2,a2)⋯=p(s1)T∏t−1pθ(at∣st)p(st+1∣st,at)
我们先计算环境输出
s
1
s_1
s1 的概率
p
(
s
1
)
p\left(s_1\right)
p(s1) ,再计算根据
s
1
s_1
s1 执行
a
1
a_1
a1 的概率
p
θ
(
a
1
∣
s
1
)
,
p
θ
(
a
1
∣
s
1
)
p_\theta\left(a_1 \mid s_1\right) , p_\theta\left(a_1 \mid s_1\right)
pθ(a1∣s1),pθ(a1∣s1) 是由策略里面的网络参数
θ
\theta
θ 所决 定的。
策略网络的输出是一个分布,演员根据这个分布进行采样,决定实际要采取的动作。接下来环境根据 a 1 a_1 a1 与 s 1 s_1 s1 产生 s 2 s_2 s2,因为 s 2 s_2 s2 与 s 1 s_1 s1 是有关系的 (游戏画面是连续的,下一个游戏画面与上一个游戏画面通常是有关系的),所以给定上一个游戏 画面 s 1 s_1 s1 和演员采取的动作 a 1 a_1 a1 ,就会产生 s 2 s_2 s2 。主机在决定输出游戏画面的时候,可能有概率,也可能没有概率, 这取决于环境 (主机内部设定) 。
如果主机输出游戏画面的时候没有概率,游戏的每次的画面都一样,我们只要找到一条路径就可以过关了,这样的游戏没有意 义。
所以输出游戏画面时通営有一定概率,给定同样的前一个画面,我们采取同样的动作,下次产生的画面不一定是一样的。 反复执行下去,我们就可以计算一个轨迹 τ \tau τ 出现的概率有多大。
某个轨迹出现的概率取决于环境的动作和智能体的动作。环境 的动作是指环境根据其函数内部的参数或内部的规则采取的动作。 p ( s t + 1 ∣ s t , a t ) p\left(s_{t+1} \mid s_t, a_t\right) p(st+1∣st,at) 代表的是环境,通常我们无法控制环境,因 为环境是设定好的。我们能控制的是 p θ ( a t ∣ s t ) p_\theta\left(a_t \mid s_t\right) pθ(at∣st) 。
给定一个 s t s_t st ,演员要采取的 a t a_t at 取决于演员的参数 θ ~ \theta_{\text {~}} θ~ ,所以智能体的动作是 演员可以控制的。演员的动作不同,每个同样的轨迹就有不同的出现的概率。
在强化学习里面,除了环境与演员以外,还有奖励函数。如下图所示,奖励函数根据在某一个状态采取的某一个动作决定这 个动作可以得到的分数。对奖励函数输入 s 1 、 a 1 s_1 、 a_1 s1、a1 ,它会输出 r 1 r_1 r1 ;输入 s 2 、 a 2 s_2 、 a_2 s2、a2 ,奖励函数会输出 r 2 r_2 r2 。我们把轨迹所有的奖励 r r r 都加起来,就得到了 R ( τ ) R(\tau) R(τ) ,其代表某一个轨迹 τ \tau τ 的奖励。

在某一场游戏的某一个回合里面,我们会得到
R
(
τ
)
R(\tau)
R(τ) 。我们要做的就是调整演员内部的参数
θ
\theta
θ ,使得
R
(
τ
)
R(\tau)
R(τ) 的值越大越好。但 实际上
R
(
τ
)
R(\tau)
R(τ) 并不只是一个标量(scalar),它是一个随机变量,因为演员在给定同样的状态下会采取什么样的动作,这是有随机性的。环境在给定同样的观测时要采取什么样的动作,要产生什么样的观测,本身也是有随机性的,所以
R
(
τ
)
R(\tau)
R(τ) 是一个随机 变量。我们能够计算的是
R
(
τ
)
R(\tau)
R(τ) 的期望值。给定某一组参数
θ
\theta
θ ,我们可计算
r
θ
r_\theta
rθ 的期望值为
R
ˉ
θ
=
∑
τ
R
(
τ
)
p
θ
(
τ
)
\bar{R}_\theta=\sum_\tau R(\tau) p_\theta(\tau)
Rˉθ=τ∑R(τ)pθ(τ)
我们要穷举所有可能的轨迹
τ
\tau
τ ,每一个轨迹
τ
\tau
τ 都有一个概率。
因为我们要让奖励越大越好,所以可以使用梯度上升(gradient ascent)来最大化期望奖励。要进行梯度上升,我们先要计算 期望奖励 R ˉ θ \bar{R}_\theta Rˉθ 的梯苺。我们对 R ˉ θ \bar{R}_\theta Rˉθ 做梯度运算(约等于是因为实际期望无法计算,我们只能用大数定律估计期望值)
∇
R
ˉ
θ
=
∑
τ
R
(
τ
)
∇
p
θ
(
τ
)
=
∑
τ
R
(
τ
)
p
θ
(
τ
)
∇
p
θ
(
τ
)
p
θ
(
τ
)
=
∑
τ
R
(
τ
)
p
θ
(
τ
)
∇
log
p
θ
(
τ
)
=
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
)
∇ˉRθ=∑τR(τ)∇pθ(τ)=∑τR(τ)pθ(τ)∇pθ(τ)pθ(τ)=∑τR(τ)pθ(τ)∇logpθ(τ)=Eτ∼pθ(τ)[R(τ)∇logpθ(τ)]≈1NN∑n−1R(τn)∇logpθ(τn)=1NN∑n−1Tn∑t−1R(τn)∇logpθ(ant∣snt)
这时我们就可以把采样到的数据代入下式中,把梯度算出来。也就是把每一个 s s s 与 a a a 的对拿进来,计算在某一个状态下采取 某一个动作的对数概率(log probability) log p θ ( a t n ∣ s t n ) \log p_\theta\left(a_t^n \mid s_t^n\right) logpθ(atn∣stn) 。对这个概率取梯度,在梯度前面乘一个权重,权重就是这场游戏的 奖励。我们计算出梯度后,就可以更新模型。
∇
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 \mid s_t^n\right)
∇Rˉθ=N1n=1∑Nt−1∑TnR(τn)∇logpθ(atn∣stn)

更新完模型以后,我们要重新采样数据再更新模型。注意,一般策略梯度(policy gradient,PG)采样的数据只会用一次。
我们采样这些数据,然后用这些数据更新参数,再丢掉这些数据。接着重新采样数据,才能去更新参数。

接下来讲一些实现细节。我们可以把强化学习想成一个分类问题,分类问题的输入就是状态s,输出就是执行每个动作的概率(相当于每个分类的概率)。
在解决分类问题时,我们要有输入和正确的输出,要有训练数据。但在强化学习中,我们通过采样来获得训练数据。假设在采样的过程中,在某个状态下,我们采样到要采取动作 a, 那么就把动作 a 当作标准答案(ground truth)。
在一般的分类问题里面,我们在实现分类的时候,目标函数都会写成最小化交叉熵(cross entropy),最小化交叉熵就是最大化对数似然(log likelihood)。

我们在解决分类问题的时候,目标函数就是最大化或最小化的对象,因为我们现在是最大化似然 (likelihood),所以其实是最 大化,我们要最大化
1 N ∑ n = 1 N ∑ t − 1 T n log p θ ( a t n ∣ s t n ) \frac{1}{N} \sum_{n=1}^N \sum_{t-1}^{T_n} \log p_\theta\left(a_t^n \mid s_t^n\right) N1n=1∑Nt−1∑Tnlogpθ(atn∣stn)
我们可在 PyTorch 里调用现成的函数来自动计算损失函数,并且把梯度计算出来。这是一般的分类问题,强化学习与分类问题 唯一不同的地方是损失前面乘一个权重一-一-整场游戏得到的总奖励 R ( τ ) R(\tau) R(τ) ,而不是在状态 s s s 采取动作 a a a 的时候得到的奖励,即
1 N ∑ n = 1 N ∑ t − 1 T n R ( τ n ) log p θ ( a t n ∣ s t n ) \frac{1}{N} \sum_{n=1}^N \sum_{t-1}^{T_n} R\left(\tau^n\right) \log p_\theta\left(a_t^n \mid s_t^n\right) N1n=1∑Nt−1∑TnR(τn)logpθ(atn∣stn)
我们要把每一笔训练数据,都使用 R ( τ ) R(\tau) R(τ) 进行加权。如下图 所示,我们使用 PyTorch 或 TensorFlow 之类的深度学习框架计算 梯度就结束了,与一般分类问题差不多。

下图中可以看到,定义损失函数的时候乘了权重Reward,并且在前面加上了负号(目的是为了讲最大化问题转化为最小化问题)

让我们先来描述这样一个场景
在很多游戏里面,奖励总是非负的,最低都是 0。比如打乒乓球游戏, 分数为 0 ~ 21 分,所以 R ( τ ) R(\tau) R(τ) 总是非负的。
但我们真正在学习的时候,只是采样了少量的s与a的对。 因为我们做的是采样,所以有一些动作可能从来都没有被采样到。
如下图所示:在某一个状态,虽然可以执行的动作有 a、b、c,但我们可能只采样到动作 b 或者 只采样到动作 c,没有采样到动作 a。但现在所有动作的奖励都是正的。
我们会遇到的问题是,因为 a 没有被采样到,所以其他动作的概率如果都要提高,a 的概率就要下降。 但是a不一定是一个不好的动作, 它只是没有被采样到。但因为 a 没有被采样到,它的概率就会下降
这显然是有问题的。

为了解决奖励总是正的的问题,我们可以把奖励减 b,即
∇
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 \mid s_t^n\right)
∇Rˉθ≈N1n=1∑Nt=1∑Tn(R(τn)−b)∇logpθ(atn∣stn)
其中,
b
b
b 称为基线。通过这种方法,我们就可以让
R
(
τ
)
−
b
R(\tau)-b
R(τ)−b 这一项有正有负。
如果我们得到的总奖励 R ( τ ) > b R(\tau)>b R(τ)>b ,就让 ( s , a ) (s, a) (s,a) 的概率上升。
如果 R ( τ ) < b R(\tau)R(τ)<b ,就算 R ( τ ) R(\tau) R(τ) 是正的,值很小也是不好的,我们就让 ( s , a ) (s, a) (s,a) 的概率下降,让这个状态采取这 个动作的分数下降。
b b b 怎么设置呢? 我们可以对 τ \tau τ 的值取期望,计算 τ \tau τ 的平均值,令 b ≈ E [ R ( τ ) ] b \approx E[R(\tau)] b≈E[R(τ)] 。所以在训练的时候,我 们会不断地把 R ( τ ) R(\tau) R(τ) 的值记录下来,会不断地计算 R ( τ ) R(\tau) R(τ) 的平均值,把这个平均值当作 b b b 来使用。这样就可以让我们在训练 的时候, R ( τ ) − b R(\tau)-b R(τ)−b 是有正有负的,这是第一个技巧。
同样让我们先描述一个场景
如下图(a)所示,假设游戏都很短,只有 3 ∼ 4 3 \sim 4 3∼4 个交互,在 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 -2 −2 分。整场游戏下来,我们得到 + 3 +3 +3 分,那我们得到 + 3 +3 +3 分 代表在 s b s_b sb 执 行 a 2 a_2 a2 是好的吗? 这并不一定代表在 s b s_b sb 执行 a 2 a_2 a2 是好的。因为这个正的分数,主要来自在 s a s_a sa 执行 了 a 1 a_1 a1 ,与在 s b s_b sb 执行 a 2 a_2 a2 是没有关系的,也许在 s b s_b sb 执行 a 2 a_2 a2 反而是不好的, 因为它导致我们接下来 会进入 s c s_c sc ,执行 a 3 a_3 a3 被扣分。所以整场游戏得到的结果是好的,并不代表每一个动作都是好的。

解决这个问题主要有两种方法:
- 采样状态-动作对的数量足够多
- 分配合适的分数
然而,通常情况下,我们采样的次数是不够多的。在采样的次数不够多的情况下,我们只能采取第二种做法,给每一个状态-动作对分配合理的分数,要让大家知道它合理的贡献。
一个做法是计算某个状态-动作对的奖励的时候,不把整场游戏得到的奖励全部加起来,只计算从 这个动作执行以后得到的奖励。
因为这场游戏在执行这个动作之前发生的事情是与执行这个动作 是没有关系的,所以在执行这个动作之前得到的奖励都不能算是这个动作的贡献。我们把执行这 个动作以后发生的所有奖励加起来,才是这个动作真正的贡献。
所以上图(b)中,在 s b s_b sb 执行 a 2 a_2 a2 这 件事情,也许它真正会导致我们得到的分数应该是 − 2 -2 −2 分而不是 + 3 +3 +3 分,因为前面的 + 5 +5 +5 分 并不是 执行 a 2 a_2 a2 的功劳。实际上执行 a 2 a_2 a2 以后,到游戏结束前,我们只被扣了 2 分,所以分数应该是 2。同理,图 4.12b 中,执行 a 2 a_2 a2 实际上不应该是扣 7 分,因为前面扣 5 分,与在 s b s_b sb 执行 a 2 a_2 a2 是没 有关系的。在 s b s_b sb 执行 a 2 a_2 a2 ,只会让我们被扣两分而已。
分配合适的分数这一技巧可以表达为
∇ 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^{\prime}-t}^{T_n} r_{t^{\prime}}^n-b\right) \nabla \log p_\theta\left(a_t^n \mid s_t^n\right) ∇Rˉθ≈N1n=1∑Nt−1∑Tn(t′−t∑Tnrt′n−b)∇logpθ(atn∣stn)
原来的权重是整场游戏的奖励的总和,现在改成从某个时刻 t t t 开始,假设这个动作是在 t t t 开始执行 的,从 t t t 一直到游戏结束所有奖励的总和才能代表这个动作的好坏。
接下来更进一步,我们把末来的奖励做一个折扣,即
∇ 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 ) \nabla \bar{R}_\theta \approx \frac{1}{N} \sum_{n=1}^N \sum_{t-1}^{T_n}\left(\sum_{t^{\prime}-t}^{T_n} \gamma^{t^{\prime}-t} r_{t^{\prime}}^n-b\right) \nabla \log p_\theta\left(a_t^n \mid s_t^n\right) ∇Rˉθ≈N1n=1∑Nt−1∑Tn(t′−t∑Tnγt′−trt′n−b)∇logpθ(atn∣stn)
Q:为什么要把末来的奖励做一个折扣呢?
A:因为虽然在某一时刻,执行某一个动作,会影响接下来所 有的结果(有可能在某一时刻执行的动作,接下来得到的奖励都是这个动作的功劳),但在一般 的情况下,时间拖得越长,该动作的影响力就越小。
实际上,我们会在 R R R 前面乘一个折扣因子 γ ( γ ∈ [ 0 , 1 ] \gamma(\gamma \in[0,1] γ(γ∈[0,1] ,一般会设为 0.9 0.9 0.9 或 0.99),如果 γ = 0 \gamma=0 γ=0 ,这表示我们只关心即时奖励;如果 γ = \gamma= γ= 1 ,这表示末来奖励等同于即时奖励。时刻 t ′ t^{\prime} t′ 越大,它前面就多次乘 γ \gamma γ ,就代表现在在某一个状态 s t s_t st ,执行某一个动作 a t a_t at 的时候,它真正的分数是执行这个动作之后所有奖励的总和,而且还要乘 γ \gamma γ 。
例如,假设游戏有两个回合,我们在游戏的第二回合的某一个 s t s_t st 执行 a t a_t at 得到 + 1 +1 +1 分,在 s t + 1 s_{t+1} st+1 执行 a t + 1 a_{t+1} at+1 得到 + 3 +3 +3 分,在 s t + 2 s_{t+2} st+2 执行 a t + 2 a_{t+2} at+2 得到 − 5 -5 −5 分,第二回合结束。 a t a_t at 的分数应该是
1 + γ × 3 + γ 2 × ( − 5 ) 1+\gamma \times 3+\gamma^2 \times(-5) 1+γ×3+γ2×(−5)
实际上就是这么实现的。 b b b 可以是依赖状态 (state-dependent) 的,事实上 b b b 通常是一个网络估计 出来的,它是一个网络的输出。
我们把 R − b R-b R−b 这一项称为优势函数 (advantage function),用 A θ ( s t , a t ) A^\theta\left(s_t, a_t\right) Aθ(st,at) 来代表优势函数。
优势函数取决于 s s s 和 a a a ,我们就是要计算在某个状态 s s s 采取某个动 作 a a a 的时候,优势函数的值。
在计算优势函数值时,我们要计算 ∑ t ′ − t T n r t ′ n \sum_{t^{\prime}-t}^{T_n} r_{t^{\prime}}^n ∑t′−tTnrt′n ,需要有一个模型与环 境交互,才能知道接下来得到的奖励。
优势函数 A θ ( s t , a t ) A^\theta\left(s_t, a_t\right) Aθ(st,at) 的上标是 θ , θ \theta , \theta θ,θ 代表用模型 θ \theta θ 与环境 交互。从时刻 t t t 开始到游戏结束为止,所有 r r r 的加和减去 b b b ,这就是优势函数。
优势函数的意义 是,假设我们在某一个状态 s t s_t st 执行某一个动作 a t a_t at ,相较于其他可能的动作, a t a_t at 有多好。
优势函数在意的不是绝对的好,而是相对的好,即相对优势(relative advantage)。因为在优势函数中, 我们会减去一个基线 b b b ,所以这个动作是相对的好,不是绝对的好。
A θ ( s t , a t ) A^\theta\left(s_t, a_t\right) Aθ(st,at) 通常可以由一个 网络估计出来,这个网络称为评论员(critic)。
如下图所示,蒙特卡洛方法可以理解为算法完成一个回合之后,再利用这个回合的 数据去学习,做一次更新。

因为我们已经获得了整个回合的数据,所以也能够获得每个步骤的奖励,我们可以很方便地计算每个步骤的未来总奖励,即回报 G t G_t Gt。 G t G_t Gt是未来总奖励,代表从这个步骤开始,我们能获得的奖励之和。
G 1 G_1 G1 代表我们从第一步开 始,往后能够获得的总奖励。 G 2 G_2 G2 代表从第二步开始,往后能够获得的总奖励。
相比蒙特卡洛方法一个回合更新一次,时序差分方法是每个步骤更新一次,即每走一 步,更新一次,时序差分方法的更新频率更高。时序差分方法使用 Q Q Q 函数来近似地表示 末来总奖励 G t 0 G_{t_0} Gt0
接下来介绍一下策略梯度中最简单的也是最经典的一个算法REINFORCE。REINFORCE 用 的是回合更新的方式,它在代码上的处理上是先获取每个步骤的奖励,然后计算每个 步㖩的末来总奖励 G t G_t Gt ,将每个 G t G_t Gt 代入
∇ R ˉ θ ≈ 1 N ∑ n − 1 N ∑ t − 1 T n G t n ∇ log π θ ( a t n ∣ s t n ) \nabla \bar{R}_\theta \approx \frac{1}{N} \sum_{n-1}^N \sum_{t-1}^{T_n} G_t^n \nabla \log \pi_\theta\left(a_t^n \mid s_t^n\right) ∇Rˉθ≈N1n−1∑Nt−1∑TnGtn∇logπθ(atn∣stn)
优化每一个动作的输出。所以我们在编写代码时会设计一个函数,这个函数的输入是
G
t
=
∑
k
−
t
+
1
T
γ
k
−
t
−
1
r
k
=
r
t
+
1
+
γ
G
t
+
1
Gt=T∑k−t+1γk−t−1rk=rt+1+γGt+1
即上一个步骤和下一个步骤的末来总奖励的关系如式(4.8)所示,所以在代码的计算上, 我们是从后往前推,一步一步地往前推,先算 G T G_T GT ,然后往前推,一直算到 G 1 G_1 G1 。
如下图所示,REINFORCE 的伪代码主要看最后 4 行,先产生一个回合的数据,比如
(
s
1
,
a
1
,
G
1
)
,
(
s
2
,
a
2
,
G
2
)
,
⋯
,
(
s
T
,
a
T
,
G
T
)
\left(s_1, a_1, G_1\right),\left(s_2, a_2, G_2\right), \cdots,\left(s_T, a_T, G_T\right)
(s1,a1,G1),(s2,a2,G2),⋯,(sT,aT,GT)

然后针对每个动作计算梯度 ∇ log π ( a t ∣ s t , θ ) \nabla \log \pi\left(a_t \mid s_t, \theta\right) ∇logπ(at∣st,θ) 。在代码上计算时,我们要获取神经网 络的输出。神经网络会输出每个动作对应的概率值 (比如0.2、0.5、0.3),然后我们还 可以获取实际的动作 a t a_t at ,把动作转成独热 (one-hot) 向量 (比如 [ 0 , 1 , 0 ] [0,1,0] [0,1,0] ) 与 log [ 0.2 , 0.5 , 0.3 ] \log [0.2,0.5,0.3] log[0.2,0.5,0.3] 相乘就可以得到 log π ( a t ∣ s t , θ ) \log \pi\left(a_t \mid s_t, \theta\right) logπ(at∣st,θ) 。
类似地,如下图所示,策略梯度预测每一个状态下应该要输出的动作的概率,即输 入状态 s t s_t st ,输出动作 a t a_t at 的概率,比如 0.02 、 0.08 、 0.9 0.02 、 0.08 、 0.9 0.02、0.08、0.9 。实际上输出给环境的动作是随 机选择一个动作,比如我们选择向右这个动作,它的独热向量就是 ( 0 , 0 , 1 ) (0,0,1) (0,0,1) 。
我们把 神经网络的输出和实际动作代入交叉樀的公式就可以求出输出动作的概率和实际动作 的概率之间的差距。但实际的动作
a
t
a_t
at 只是我们输出的真实的动作,它不一定是正确的 动作,它不能像手写数字识别一样作为一个正确的标签来指导神经网络朝着正确的方 向更新,所以我们需要乘一个奖励回报
G
t
G_t
Gt 。
G
t
G_t
Gt 相当于对真实动作的评价。如果
G
t
G_t
Gt 越 大,末来总奖励越大,那就说明当前输出的真实的动作就越好,损失就越需要重视。 如果
G
t
G_t
Gt 越小,那就说明动作
a
t
a_t
at 不是很好,损失的权重就要小一点儿,优化力度也要 小一点儿。通过与手写数字识别的一个对比,我们就知道为什么策略梯度损失会构造 成这样。

如下图所示,实际上我们在计算策略梯度损失的时候,要先对实际执行的动作取独 热向量,再获取神经网络预测的动作概率,将它们相乘,我们就可以得到 log π ( a t ∣ s t , θ ) \log \pi\left(a_t \mid s_t, \theta\right) logπ(at∣st,θ) ,这就是我们要构造的损失。
因为我们可以获取整个回合的所有的轨 迹,所以我们可以对这一条轨迹里面的每个动作都去计算一个损失。把所有的损失加 起来,我们再将其“扔”给 Adam 的优化器去自动更新参数就好了。

下图所示为REINFORCE 算法示意,首先我们需要一个策略模型来输出动作概率,输 出动作概率后,通过 sample() 函数得到一个具体的动作,与环境交互后,我们可以得 到整个回合的数据。得到回合数据之后,我们再去执行 learn() 函数,在 learn() 函数里 面,我们就可以用这些数据去构造损失函数,“扔”给优化器优化,更新我们的策略模型。

4-1 如果我们想让机器人自己玩视频游戏, 那么强化学习中的 3 个组成部分 (演员、环境、奖励函数) 具体分别代表什么?
4-2 在一个过程中, 一个具体的轨迹
s
1
,
a
1
,
s
2
,
a
2
s_1, a_1, s_2, a_2
s1,a1,s2,a2 出现的概率取决于什么?
4-3 当我们最大化期望奖励时,应该使用什么方法?
4-4 我们应该如何理解策略梯度的公式呢?
4-5 我们可以使用哪些方法来进行梯度提升的计算?
4-6 进行基于策略梯度的优化的技巧有哪些?
4-7 对于策略梯度的两种方法, 蒙特卡洛强化学习和时序差分强化学习两种方法有什么联系和区别?
4-8 请详细描述 REINFORCE 算法的计算过程。
4-1 友善的面试官:同学来吧, 给我手动推导一下策略梯度公式的计算过程。
4-2 友善的面试官:可以说一下你所了解的基于策略梯度优化的技巧吗?