diff --git a/docs/ch10/main.md b/docs/ch10/main.md index 72e25e0..c8e8550 100644 --- a/docs/ch10/main.md +++ b/docs/ch10/main.md @@ -1,8 +1,8 @@ -# Actor-Critic 算法 +# 第 10 章 Actor-Critic 算法 $\qquad$ 在策略梯度的章节中,实际上我们已经开了一部分 $\text{Actor-Critic}$ 算法的头了,这一章我们将继续深入探讨 $\text{Actor-Critic}$ 算法。 -## 策略梯度算法的缺点 +## 10.1 策略梯度算法的缺点 $\qquad$ 这里策略梯度算法特指蒙特卡洛策略梯度算法,相比于 $\text{DQN}$ 之类的基于价值的算法,策略梯度算法有以下优点: @@ -20,7 +20,7 @@ $\qquad$ 结合了策略梯度和值函数的 $\text{Actor-Critic}$ 算法则能 $\qquad$ 而结合之后呢,$\text{Actor}$ 部分还是负责估计策略梯度和采样,但 $\text{Critic}$ 即原来的值函数部分就不需要采样而只负责估计值函数了,并且由于它估计的值函数指的是策略函数的值,相当于带来了一个更稳定的估计,来指导 $\text{Actor}$ 的更新,反而能够缓解策略梯度估计带来的方差。当然尽管 $\text{Actor-Critic}$ 算法能够缓解方差问题,但并不能彻底解决问题,在接下来的章节中我们也会展开介绍一些改进的方法。 -## Q Actor-Critic 算法 +## 10.2 Q Actor-Critic 算法 $\qquad$ 在策略梯度章节中,我们其实已经对 $\text{Actor-Critic}$ 算法的目标函数进行过推导了,这里就不详细展开,只是简单回顾一下目标函数,如式 $\text(10.1)$ 所示。 @@ -45,11 +45,11 @@ $\qquad$ 这样的算法通常称之为 $\text{Q Actor-Critic}$ 算法,这也 $\qquad$ 如图 $\text{10.1}$ 所示,我们通常将 $\text{Actor}$ 和 $\text{Critic}$ 分别用两个模块来表示,即图中的 策略函数( $\text{Policy}$ )和价值函数( $\text{Value Function}$ )。$\text{Actor}$ 与环境交互采样,然后将采样的轨迹输入 $\text{Critic}$ 网络,$\text{Critic}$ 网络估计出当前状态-动作对的价值,然后再将这个价值作为 $\text{Actor}$ 网络的梯度更新的依据,这也是所有 $\text{Actor-Critic}$ 算法的基本通用架构。
- +
图 $\text{10.1}$ $\text{Actor-Critic}$ 算法架构
-## A2C 与 A3C 算法 +## 10.3 A2C 与 A3C 算法 $\qquad$ 我们知道 $\text{Actor-Critic}$ 架构是能够缓解策略梯度算法的高方差问题的,但是并不能彻底解决问题。为了进一步缓解高方差问题,我们引入一个优势函数( $\text{advantage function}$ )$A^\pi(s_t, a_t)$,用来表示当前状态-动作对相对于平均水平的优势,即式 $\text(10.3)$ 。 @@ -84,7 +84,7 @@ $\qquad$ 如图 $\text{10.2}$ 所示,原先的 $\text{A2C}$ 算法相当于只
图 $\text{10.2}$ $\text{A3C}$ 算法架构
-## 广义优势估计 +## 10.4 广义优势估计 $\qquad$ 上一小节中,我们通过引入优势函数来缓解梯度估计带来的高方差问题,但由于优势函数通本质上来说还是使用蒙特卡洛估计,因此尽管减去了基线,有时候还是会产生高方差,从而导致训练过程不稳定。这时候有读者可能会想到一句话,即“知识一般是通过螺旋式的规律来学习的,也是会螺旋式升级的”,这句话的意思是我们在学某些知识时可能不会马上用到,但是会暂时埋下一个种子,等到后面深入使用的时候会回忆起来并且加深相关知识的理解。当然这句话不是某个名人说的,而是笔者自己总结出来,也是想传达给读者的学习思路。 @@ -107,7 +107,7 @@ $$ \end{aligned} $$ -当 $\lambda = 0$ 时,GAE 退化为单步 $\text{TD}$ 误差,如式 $\text(10.7)$ 所示。 +$\qquad$ 当 $\lambda = 0$ 时,GAE 退化为单步 $\text{TD}$ 误差,如式 $\text(10.7)$ 所示。 $$ \tag{10.7} @@ -116,7 +116,7 @@ A^{\mathrm{GAE}(\gamma, 0)}(s_t, a_t) = \delta_t = r_t + \gamma V^\pi(s_{t+1}) - \end{aligned} $$ -当 $\lambda = 1$ 时,$\text{GAE}$ 退化为蒙特卡洛估计,如式 $\text(10.8)$ 所示。 +$\qquad$ 当 $\lambda = 1$ 时,$\text{GAE}$ 退化为蒙特卡洛估计,如式 $\text(10.8)$ 所示。 $$ \tag{10.8} @@ -125,6 +125,151 @@ A^{\mathrm{GAE}(\gamma, 1)}(s_t, a_t) = \sum_{l=0}^{\infty}(\gamma \lambda)^l \d \end{aligned} $$ -如何选择合适的 $\lambda$ 还请读者回看前面时序差分的相关章节内容,这里就不再赘述。到这里,我们就将 $\text{Actor-Critic}$ 算法的基本原理讲完了,注意广义优势估计并不是 $\text{Actor-Critic}$ 算法的必要组成部分,只是一种改进的方法。相反地,它更像是一种通用的模块,在实践中可以用在任何需要估计优势函数的地方,比如后面章节要讲的 $\text{PPO}$ 算法中就用到了这种估计方法。 +$\qquad$ 如何选择合适的 $\lambda$ 还请读者回看前面时序差分的相关章节内容,这里就不再赘述。到这里,我们就将 $\text{Actor-Critic}$ 算法的基本原理讲完了,注意广义优势估计并不是 $\text{Actor-Critic}$ 算法的必要组成部分,只是一种改进的方法。相反地,它更像是一种通用的模块,在实践中可以用在任何需要估计优势函数的地方,比如后面章节要讲的 $\text{PPO}$ 算法中就用到了这种估计方法。 -## 实战:A2C 算法 +## 10.5 实战:A2C 算法 + +### 10.5.1 定义模型 + +$\qquad$ 通常来讲,$\text{Critic}$ 的输入是状态,输出则是一个维度的价值,而 $\text{Actor}$ 输入的也会状态,但输出的是概率分布,因此我们可以定义两个网络,如代码清单 $\text{10-1}$ 所示。 + +
+
代码清单 $\text{10-1}$ 实现 $\text{Actor}$ 和 $\text{Critic}$
+
+ +```python +class Critic(nn.Module): + def __init__(self,state_dim): + self.fc1 = nn.Linear(state_dim, 256) + self.fc2 = nn.Linear(256, 256) + self.fc3 = nn.Linear(256, 1) + def forward(self, x): + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + value = self.fc3(x) + return value + +class Actor(nn.Module): + def __init__(self, state_dim, action_dim): + self.fc1 = nn.Linear(state_dim, 256) + self.fc2 = nn.Linear(256, 256) + self.fc3 = nn.Linear(256, action_dim) + def forward(self, x): + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + logits_p = F.softmax(self.fc3(x), dim=1) + return logits_p +``` + +$\qquad$ 这里由于是离散的动作空间,根据在策略梯度章节中设计的策略函数,我们使用了 $\text{softmax}$ 函数来输出概率分布。另外,实践上来看,由于 $\text{Actor}$ 和 $\text{Critic}$ 的输入是一样的,因此我们可以将两个网络合并成一个网络,以便于加速训练。这有点类似于 $\text{Duelling DQN}$ 算法中的做法,如代码清单 $\text{10-2}$ 所示。 + +
+
代码清单 $\text{10-2}$ 实现合并的 $\text{Actor}$ 和 $\text{Critic}$
+
+ +```python +class ActorCritic(nn.Module): + def __init__(self, state_dim, action_dim): + self.fc1 = nn.Linear(state_dim, 256) + self.fc2 = nn.Linear(256, 256) + self.action_layer = nn.Linear(256, action_dim) + self.value_layer = nn.Linear(256, 1) + def forward(self, x): + x = F.relu(self.fc1(x)) + x = F.relu(self.fc2(x)) + logits_p = F.softmax(self.action_layer(x), dim=1) + value = self.value_layer(x) + return logits_p, value +``` + +$\qquad$ 注意当我们使用分开的网络时,我们需要在训练时分别更新两个网络的参数,即需要两个优化,而使用合并的网络时则只需要更新一个网络的参数即可。 + +### 10.5.2 动作采样 + +$\qquad$ 与 $\text{DQN}$ 算法不同等确定性策略不同,$\text{A2C}$ 的动作输出不再是 $Q$ 值最大对应的动作,而是从概率分布中采样动作,这意味着即使是很小的概率,也有可能被采样到,这样就能保证探索性,如代码清单 $\text{10-3}$ 所示。 + +
+
代码清单 $\text{10-3}$ 采样动作
+
+ +```python +from torch.distributions import Categorical +class Agent: + def __init__(self): + self.model = ActorCritic(state_dim, action_dim) + def sample_action(self,state): + '''动作采样函数 + ''' + state = torch.tensor(state, device=self.device, dtype=torch.float32) + logits_p, value = self.model(state) + dist = Categorical(logits_p) + action = dist.sample() + return action +``` + +$\qquad$ 注意这里直接利用了 `PyTorch` 中的 `Categorical` 分布函数,这样就能直接从概率分布中采样动作了。 + +### 10.5.3 策略更新 + +我们首先需要计算出优势函数,一般先计算出回报,然后减去网络输出的值即可,如代码清单 $\text{10-4}$ 所示。 + +
+
代码清单 $\text{10-4}$ 计算优势函数
+
+ +```python +class Agent: + def _compute_returns(self, rewards, dones): + returns = [] + discounted_sum = 0 + for reward, done in zip(reversed(rewards), reversed(dones)): + if done: + discounted_sum = 0 + discounted_sum = reward + (self.gamma * discounted_sum) + returns.insert(0, discounted_sum) + # 归一化 + returns = torch.tensor(returns, device=self.device, dtype=torch.float32).unsqueeze(dim=1) + returns = (returns - returns.mean()) / (returns.std() + 1e-5) # 1e-5 to avoid division by zero + return returns + def compute_advantage(self): + '''计算优势函数 + ''' + logits_p, states, rewards, dones = self.memory.sample() + returns = self._compute_returns(rewards, dones) + states = torch.tensor(states, device=self.device, dtype=torch.float32) + logits_p, values = self.model(states) + advantages = returns - values + return advantages +``` + +$\qquad$ 这里我们使用了一个技巧,即将回报归一化,这样可以让优势函数的值域在 $[-1,1]$ 之间,这样可以让优势函数更稳定,从而减少方差。计算优势之后就可以分别计算 $\text{Actor}$ 和 $\text{Critic}$ 的损失函数了,如代码清单 $\text{10-5}$ 所示。 + +
+
代码清单 $\text{10-5}$ 计算损失函数
+
+ +```python +class Agent: + def compute_loss(self): + '''计算损失函数 + ''' + logits_p, states, rewards, dones = self.memory.sample() + returns = self._compute_returns(rewards, dones) + states = torch.tensor(states, device=self.device, dtype=torch.float32) + logits_p, values = self.model(states) + advantages = returns - values + dist = Categorical(logits_p) + log_probs = dist.log_prob(actions) + # 注意这里策略损失反向传播时不需要优化优势函数,因此需要将其 detach 掉 + actor_loss = -(log_probs * advantages.detach()).mean() + critic_loss = advantages.pow(2).mean() + return actor_loss, critic_loss +``` + +到这里,我们就实现了 $\text{A2C}$ 算法的所有核心代码,完整代码请读者参考本书的代码仓库。最后展示一下训练的效果,如图 $\text{10-3}$ 所示。 + + +
+ +
+
图 $\text{10-3}$ $\text{CartPole}$ 环境 $\text{A2C}$ 算法训练曲线
\ No newline at end of file diff --git a/docs/ch10/main.pptx b/docs/ch10/main.pptx new file mode 100644 index 0000000..09ea2b2 Binary files /dev/null and b/docs/ch10/main.pptx differ diff --git a/docs/ch11/main.md b/docs/ch11/main.md index e0bbb82..12da83f 100644 --- a/docs/ch11/main.md +++ b/docs/ch11/main.md @@ -1,11 +1,17 @@ # DDPG 与 TD3 算法 -$\qquad$ 本章开始到接下来的几章,我们将介绍一些非常典型的基于策略梯度的算法,包括 $\text{DDPG}$、$\text{PPO}$、$\text{SAC}$ 等。这些方法的实现方式各不相同,也各有各的特点,因此每个算法都单独形成一个章节来展开。同时它们是目前实践中最最常用的一些算法,甚至有 “遇事不决PPO” 之类的说法,因此读者一定要认真学习。那么本章将 $\text{DDPG}$ 算法作为开篇,严格来说,原论文作者提出 $\text{DDPG}$ 算法的初衷其实是 $\text{DQN}$ 算法的一个扩展,或者说一种连续动作版本的 $\text{DQN}$ 算法。但是由于当时 $\text{Actor-Critic}$ 架构还没有被提出( $\text{A3C}$ 算法是在 $\text{2016}$ 年发表的,比 $\text{DDPG}$ 算法晚了一年),只是后来我们回看 $\text{DDPG}$ 算法的时候发现其在形式上更像 $\text{Actor-Critic}$ 的架构,因此我们就将其归为 $\text{Actor-Critic}$ 算法的一种。 +$\qquad$ 本章开始到接下来的几章,我们将介绍一些非常典型的基于策略梯度的算法,包括 $\text{DDPG}$、$\text{PPO}$、$\text{SAC}$ 等。这些方法的实现方式各不相同,也各有各的特点,因此每个算法都单独形成一个章节来展开。同时它们是目前实践中最最常用的一些算法,甚至有 “遇事不决$\text{PPO}$” 之类的说法,因此读者一定要认真学习。 + +$\qquad$ 那么本章将 $\text{DDPG}$ 算法作为开篇,严格来说,原论文作者提出 $\text{DDPG}$ 算法的初衷其实是 $\text{DQN}$ 算法的一个扩展,或者说一种连续动作版本的 $\text{DQN}$ 算法。但是由于当时 $\text{Actor-Critic}$ 架构还没有被提出( $\text{A3C}$ 算法是在 $\text{2016}$ 年发表的,比 $\text{DDPG}$ 算法晚了一年),只是后来我们回看 $\text{DDPG}$ 算法的时候发现其在形式上更像 $\text{Actor-Critic}$ 的架构,因此我们就将其归为 $\text{Actor-Critic}$ 算法的一种。 ## DPG 方法 -$\qquad$ 深度确定性策略梯度算法( $\text{deep deterministic policy gradient,DDPG}$),是一种确定性的策略梯度算法。为了让读者更好地理解 $\text{DDPG}$ 算法,我们先把 “深度“ 这两个字去掉,即先介绍一下 $\text{DPG}$ 算法,后面我们也会发现 $\text{DPG}$ 算法其实就是 $\text{DDPG}$ 算法的核心,也就是精髓所在。虽然 $\text{DDPG}$ 原论文作者是基于 $\text{DQN}$ 算法来展开的,目前很多相关资料也是如此。但是笔者认为,有了 $\text{Actor-Critic}$ 算法的铺垫之后,从策略梯度的角度来理解 DPG 算法是更容易的。首先我们知道 $\text{DQN}$ 算法的一个主要缺点就是不能用于连续动作空间,这是因为在 $\text{DQN}$ 算法中动作是通过贪心策略或者说 $\text{argmax}$ 的方式来从 $Q$ 函数间接得到的。 +$\qquad$ 深度确定性策略梯度算法( $\text{deep deterministic policy gradient,DDPG}$),是一种确定性的策略梯度算法。为了让读者更好地理解 $\text{DDPG}$ 算法,我们先把 “深度“ 这两个字去掉,即先介绍一下 $\text{DPG}$ 算法,后面我们也会发现 $\text{DPG}$ 算法其实就是 $\text{DDPG}$ 算法的核心,也就是精髓所在。虽然 $\text{DDPG}$ 原论文作者是基于 $\text{DQN}$ 算法来展开的,目前很多相关资料也是如此。 + +$\qquad$ 但是笔者认为,有了 $\text{Actor-Critic}$ 算法的铺垫之后,从策略梯度的角度来理解 DPG 算法是更容易的。首先我们知道 $\text{DQN}$ 算法的一个主要缺点就是不能用于连续动作空间,这是因为在 $\text{DQN}$ 算法中动作是通过贪心策略或者说 $\text{argmax}$ 的方式来从 $Q$ 函数间接得到的。 + +$\qquad$ 要想适配连续动作空间,我们干脆就将选择动作的过程变成一个直接从状态映射到具体动作的函数 $\mu_\theta (s)$,其中 $\theta$ 表示模型的参数,这样一来就把求解 $Q$ 函数、贪心选择动作这两个过程合并成了一个函数,也就是我们常说的 $\text{Actor}$。有读者可能会问,为什么这里用 $\mu_\theta (s)$ 来表示策略而不是 $\text{Actor-Critic}$ 章节中提到的 $\pi_{\theta}(a|s)$ 呢? -$\qquad$ 要想适配连续动作空间,我们干脆就将选择动作的过程变成一个直接从状态映射到具体动作的函数 $\mu_\theta (s)$,其中 $\theta$ 表示模型的参数,这样一来就把求解 $Q$ 函数、贪心选择动作这两个过程合并成了一个函数,也就是我们常说的 $\text{Actor}$。有读者可能会问,为什么这里用 $\mu_\theta (s)$ 来表示策略而不是 $\text{Actor-Critic}$ 章节中提到的 $\pi_{\theta}(a|s)$ 呢?注意,这里 $\mu_\theta (s)$ 输出的是一个值,而 $\pi_{\theta}(a|s)$ 通常输出的是一个概率分布,这是两者的本质区别,与输出概率分布的随机性策略( $\text{stochastic policy}$ )不同,这里输出一个值的策略我们就称作确定性策略( $\text{deterministic policy}$ )。有了这个策略函数之后,类似地,我们也可以推导出 $\text{DPG}$ 算法的目标函数,也就是策略梯度公式,如式 $\text{(11.1)}$ 所示。 +$\qquad$ 注意,这里 $\mu_\theta (s)$ 输出的是一个值,而 $\pi_{\theta}(a|s)$ 通常输出的是一个概率分布,这是两者的本质区别,与输出概率分布的随机性策略( $\text{stochastic policy}$ )不同,这里输出一个值的策略我们就称作确定性策略( $\text{deterministic policy}$ )。有了这个策略函数之后,类似地,我们也可以推导出 $\text{DPG}$ 算法的目标函数,也就是策略梯度公式,如式 $\text{(11.1)}$ 所示。 $$ \tag{11.1} @@ -16,11 +22,17 @@ $\qquad$ 其中 $\rho^\beta$ 是策略的初始分布,用于探索状态空间 ## DDPG 算法 -$\qquad$ 有了 $\text{DPG}$ 算法的铺垫之后,我们再来看看 $\text{DDPG}$ 算法。$\text{DDPG}$ 算法的 “深度” 其实很简单,它就体现在于 $\text{DPG}$ 算法中使用 $\text{Actor}$ 和 $\text{Critic}$ 两个网络来分别表示策略函数(确定性策略)和值函数,它们可以是线性模型或浅层神经网络。而DDPG算法采用深度神经网络来近似 $\text{Actor}$ 和$\text{Critic}$,使得算法可以处理更复杂的任务和高维的状态空间。用现在更流行的话讲,$\text{DDPG}$ 算法同 $\text{DPG}$ 算法相比,就是使用了更大的模型。除此之外,$\text{DDPG}$ 算法还有一些其他的改进,例如使用了经验回放、目标网络等 $\text{DQN}$ 算法中的一些技巧。因此,$\text{DDPG}$ 算法更像是将 $\text{DPG}$ 思路引入到了 $\text{DQN}$ 算法中,而不是简单的将 $\text{DPG}$ 算法扩展到了深度神经网络上。另外,$\text{DDPG}$ 算法还有一项重要的贡献,就是引入了噪声网络来增加策略的探索性,即在训练过程中采用了一种确定性策略加噪声的方式。即在Actor网络的输出动作上加入一定的噪声,使得策略在训练过程中具有一定的探索性,有助于探索更广泛的动作空间。 +$\qquad$ 有了 $\text{DPG}$ 算法的铺垫之后,我们再来看看 $\text{DDPG}$ 算法。$\text{DDPG}$ 算法的 “深度” 其实很简单,它就体现在于 $\text{DPG}$ 算法中使用 $\text{Actor}$ 和 $\text{Critic}$ 两个网络来分别表示策略函数(确定性策略)和值函数,它们可以是线性模型或浅层神经网络。而 $\text{DDPG}$ 算法采用深度神经网络来近似 $\text{Actor}$ 和$\text{Critic}$ ,使得算法可以处理更复杂的任务和高维的状态空间。 + +$\qquad$ 用现在更流行的话讲,$\text{DDPG}$ 算法同 $\text{DPG}$ 算法相比,就是使用了更大的模型。除此之外,$\text{DDPG}$ 算法还有一些其他的改进,例如使用了经验回放、目标网络等 $\text{DQN}$ 算法中的一些技巧。因此,$\text{DDPG}$ 算法更像是将 $\text{DPG}$ 思路引入到了 $\text{DQN}$ 算法中,而不是简单的将 $\text{DPG}$ 算法扩展到了深度神经网络上。 + +$\qquad$ 另外,$\text{DDPG}$ 算法还有一项重要的贡献,就是引入了噪声网络来增加策略的探索性,即在训练过程中采用了一种确定性策略加噪声的方式。即在Actor网络的输出动作上加入一定的噪声,使得策略在训练过程中具有一定的探索性,有助于探索更广泛的动作空间。 $\qquad$ 这里读者顺便插一句题外话,回顾到目前为止的所有章节内容,不知道读者有没有发现,其实我们发现在强化学习基础算法的研究改进当中,无外乎几个亘古不变的主题:一是**如何提高对值函数的估计**,保证其准确性,即尽量无偏且低方差,例如最开始的用深度神经网络替代简单的 $Q$ 表、结合蒙特卡洛和时序差分的 $\text{TD}(\lambda)$ 、引入目标网络以及广义优势估计等等;二是**如何提高探索以及平衡探索-利用的问题**,尤其在探索性比较差的确定性策略中,例如 $\text{DQN}$ 和 $\text{DDPG}$ 算法都会利用各种技巧来提高探索,例如经验回放、$\varepsilon-\text{greedy}$ 策略、噪声网络等等。这两个问题是强化学习算法的基础核心问题,希望能够给读者在学习和研究的过程中带来一定的启发。 -$\qquad$ 回到正题,由于目标网络、经验回放前面章节都讲过了,这里就略过,我们讲讲 $\text{DDPG}$ 引入的噪声。其实引入噪声的方法在前面 $\text{Noisy DQN}$ 算法中就讲到了,只是 $\text{Noisy DQN}$ 算法是在网络中引入噪声,而 $\text{DDPG}$ 算法是在输出动作上引入噪声。本质上来讲,引入噪声的作用就是为了在不破坏系统的前提下,提高系统运行的抗干扰性。这跟我们生活中打疫苗是类似的,通常我们会将灭活的病毒也就是疫苗注入到体内,引发免疫系统的警觉,从而提高免疫系统的抗干扰性,即提高我们身体的免疫力。这里疫苗就相当于轻微的噪声,如果免疫系统一直没见过这种噪声,那么一旦遇到真正的病毒之后是很有可能崩溃的,反之如果经常接触这种轻微的噪声,那么免疫系统就会逐渐适应,从而提高抗干扰性。又好比我们平时做的消防演练,虽然平时的演练都不是真正意义上的灾害,但经过熟练的演练之后一旦遇到真正的灾害不说从容应对,至少也不会过于慌乱了。 +$\qquad$ 回到正题,由于目标网络、经验回放前面章节都讲过了,这里就略过,我们讲讲 $\text{DDPG}$ 引入的噪声。其实引入噪声的方法在前面 $\text{Noisy DQN}$ 算法中就讲到了,只是 $\text{Noisy DQN}$ 算法是在网络中引入噪声,而 $\text{DDPG}$ 算法是在输出动作上引入噪声。本质上来讲,引入噪声的作用就是为了在不破坏系统的前提下,提高系统运行的抗干扰性。 + +$\qquad$ 这跟我们生活中打疫苗是类似的,通常我们会将灭活的病毒也就是疫苗注入到体内,引发免疫系统的警觉,从而提高免疫系统的抗干扰性,即提高我们身体的免疫力。这里疫苗就相当于轻微的噪声,如果免疫系统一直没见过这种噪声,那么一旦遇到真正的病毒之后是很有可能崩溃的,反之如果经常接触这种轻微的噪声,那么免疫系统就会逐渐适应,从而提高抗干扰性。又好比我们平时做的消防演练,虽然平时的演练都不是真正意义上的灾害,但经过熟练的演练之后一旦遇到真正的灾害不说从容应对,至少也不会过于慌乱了。 $\qquad$ 再次回归正题,$\text{DDPG}$ 算法是在输出动作上引入噪声的,由于 $\mu_\theta (s)$ 输出的是单个值,其实最简单的方式就是在输出的值上加上一个随机数,这个随机数可以是正态分布的(即高斯噪声),也可以是均匀分布的,只要能够保证这个随机数的值不要过大就行。 @@ -31,7 +43,9 @@ $\qquad$ 当然简单的噪声引入除了简单这一个优点之外,可能 * **稳定性**:$\text{OU}$ 噪声的回归特性使得噪声在训练过程中具有一定的稳定性。相比于纯粹的随机噪声,在 $\text{DDPG}$ 算法中使用$\text{OU}$ 噪声可以更好地保持动作的连续性,避免剧烈的抖动,从而使得训练过程更加平滑和稳定。 * **可控性**:由于$\text{OU}$ 噪声具有回归特性,它在训练过程中逐渐回归到均值,因此可以控制策略的探索性逐渐减小。这种可控性使得在训练的早期增加探索性,然后逐渐减小探索性,有助于更有效地进行训练。 -$\qquad$ 总的来说,$\text{OU}$ 噪声作为 $\text{DDPG}$ 算法中的一种探索策略,具有平滑、可控、稳定等优点,使得算法能够更好地在连续动作空间中进行训练,探索更广泛的动作空间,并找到更优的策略。它是 $\text{DDPG}$ 算法成功应用于连续动作空间问题的重要因素之一。虽然它有这么多的优点,实际上在简单的环境中,它跟使用简单的高斯噪声甚至不用噪声的效果是差不多的,只有在复杂的环境中才会体现出来区别。因此,如果读者在实际应用中面临的问题比较简单,可以不用OU噪声,而是使用高斯噪声或者不用噪声即可,这样可以减少算法的复杂度,加快算法的收敛速度,正所谓 “杀鸡焉用牛刀”。 +$\qquad$ 总的来说,$\text{OU}$ 噪声作为 $\text{DDPG}$ 算法中的一种探索策略,具有平滑、可控、稳定等优点,使得算法能够更好地在连续动作空间中进行训练,探索更广泛的动作空间,并找到更优的策略。它是 $\text{DDPG}$ 算法成功应用于连续动作空间问题的重要因素之一。 + +$\qquad$ 虽然它有这么多的优点,实际上在简单的环境中,它跟使用简单的高斯噪声甚至不用噪声的效果是差不多的,只有在复杂的环境中才会体现出来区别。因此,如果读者在实际应用中面临的问题比较简单,可以不用OU噪声,而是使用高斯噪声或者不用噪声即可,这样可以减少算法的复杂度,加快算法的收敛速度,正所谓 “杀鸡焉用牛刀”。 $\qquad$ $\text{OU}$ 噪声主要由两个部分组成:随机高斯噪声和回归项,其数学定义如式 $\text{(11.2)}$ 所示。 @@ -40,7 +54,9 @@ $$ d x_t=\theta\left(\mu-x_t\right) d t+\sigma d W_t $$ -$\qquad$ 其中 $x_t$ 是 $\text{OU}$ 过程在时间 $t$ 的值,即当前的噪声值,这个 $t$ 也是强化学习中的时步( $\text{time step}$ )。$\mu$ 是回归到的均值,表示噪声在长时间尺度上的平均值。$\theta$ 是 $\text{OU}$ 过程的回归速率,表示噪声向均值回归的速率。$\sigma$ 是 $\text{OU}$ 过程的扰动项,表示随机高斯噪声的标准差。$dW_t$ 是布朗运动( $\text{Brownian motion}$ )或者维纳过程( $\text{Wiener process}$ ),是一个随机项,表示随机高斯噪声的微小变化。在实际应用中,我们只需要调整 $\mu$ 和 $\sigma$ 就可以了,$\theta$ 通常是固定的,而 $dW_t$ 是随机项,我们也不需要关注。尽管如此,需要调整的参数还是有点多,这也是为什么 $\text{DDPG}$ 算法的调参比较麻烦的原因之一。 +$\qquad$ 其中 $x_t$ 是 $\text{OU}$ 过程在时间 $t$ 的值,即当前的噪声值,这个 $t$ 也是强化学习中的时步( $\text{time step}$ )。$\mu$ 是回归到的均值,表示噪声在长时间尺度上的平均值。$\theta$ 是 $\text{OU}$ 过程的回归速率,表示噪声向均值回归的速率。$\sigma$ 是 $\text{OU}$ 过程的扰动项,表示随机高斯噪声的标准差。$dW_t$ 是布朗运动( $\text{Brownian motion}$ )或者维纳过程( $\text{Wiener process}$ ),是一个随机项,表示随机高斯噪声的微小变化。 + +$\qquad$ 在实际应用中,我们只需要调整 $\mu$ 和 $\sigma$ 就可以了,$\theta$ 通常是固定的,而 $dW_t$ 是随机项,我们也不需要关注。尽管如此,需要调整的参数还是有点多,这也是为什么 $\text{DDPG}$ 算法的调参比较麻烦的原因之一。 ## DDPG 算法的优缺点 @@ -93,9 +109,13 @@ $\qquad$ 细心的读者会发现,这跟 $\text{Double DQN}$ 的原理是一 ### 延迟更新 -$\qquad$ 延迟更新更像是一种实验技巧,即在训练中 $\text{Actor}$ 的更新频率要低于 $\text{Critic}$ 的更新频率。在学习过程中,$\text{Critic}$ 是不断更新的,可以想象一下,假设在某个时刻 $\text{Actor}$ 好不容易达到一个最高点,这个时候 $\text{Critic}$ 又更新了,那么 $\text{Actor}$ 的最高点就被打破了,这样一来 $\text{Actor}$ 就会不断地追逐 $\text{Critic}$,这样就会造成误差的过分累积,进而导致 $\text{Actor}$ 的训练不稳定,甚至可能会发散。因此,为了避免这种情况,我们就可以在训练中让 $\text{Actor}$ 的更新频率低于 $\text{Critic}$ 的更新频率,这样一来 $\text{Actor}$ 的更新就会比较稳定,不会受到 $\text{Critic}$ 的影响,从而提高算法的稳定性和收敛性。 +$\qquad$ 延迟更新更像是一种实验技巧,即在训练中 $\text{Actor}$ 的更新频率要低于 $\text{Critic}$ 的更新频率。在学习过程中,$\text{Critic}$ 是不断更新的,可以想象一下,假设在某个时刻 $\text{Actor}$ 好不容易达到一个最高点,这个时候 $\text{Critic}$ 又更新了,那么 $\text{Actor}$ 的最高点就被打破了,这样一来 $\text{Actor}$ 就会不断地追逐 $\text{Critic}$,这样就会造成误差的过分累积,进而导致 $\text{Actor}$ 的训练不稳定,甚至可能会发散。 + +$\qquad$ 因此,为了避免这种情况,我们就可以在训练中让 $\text{Actor}$ 的更新频率低于 $\text{Critic}$ 的更新频率,这样一来 $\text{Actor}$ 的更新就会比较稳定,不会受到 $\text{Critic}$ 的影响,从而提高算法的稳定性和收敛性。 + +$\qquad$ 举个不太恰当的例子,$\text{Critic}$ 就好比领导,$\text{Actor}$ 则好比员工,领导不断给员工下达目标,员工不断地去完成目标,如果领导的决策经常失误,那么员工就很容易像无头苍蝇一样不知道该完成哪些目标。 -$\qquad$ 举个不太恰当的例子,$\text{Critic}$ 就好比领导,$\text{Actor}$ 则好比员工,领导不断给员工下达目标,员工不断地去完成目标,如果领导的决策经常失误,那么员工就很容易像无头苍蝇一样不知道该完成哪些目标。因此,一个好的解决方式就是让领导学得比员工更快,领导学得更快了之后下达目标的失误率就会更低,这样员工就能够更好地完成目标了,从而提高整个团队的效率。在实践中,$\text{Actor}$ 的更新频率一般要比 $\text{Critic}$ 的更新频率低一个数量级,例如 $\text{Critic}$ 每更新 $\text{10}$ 次,$\text{Actor}$ 只更新 $\text{1}$ 次。 +$\qquad$ 因此,一个好的解决方式就是让领导学得比员工更快,领导学得更快了之后下达目标的失误率就会更低,这样员工就能够更好地完成目标了,从而提高整个团队的效率。在实践中,$\text{Actor}$ 的更新频率一般要比 $\text{Critic}$ 的更新频率低一个数量级,例如 $\text{Critic}$ 每更新 $\text{10}$ 次,$\text{Actor}$ 只更新 $\text{1}$ 次。 ### 躁声正则 diff --git a/docs/figs/ch10/a2c_CartPole_training.png b/docs/figs/ch10/a2c_CartPole_training.png new file mode 100644 index 0000000..97f3f70 Binary files /dev/null and b/docs/figs/ch10/a2c_CartPole_training.png differ diff --git a/docs/figs/ch10/a3c_architecture.png b/docs/figs/ch10/a3c_architecture.png index 8746b3f..4654e44 100644 Binary files a/docs/figs/ch10/a3c_architecture.png and b/docs/figs/ch10/a3c_architecture.png differ diff --git a/docs/figs/ch10/actor_critic_architecture.png b/docs/figs/ch10/actor_critic_architecture.png index 52b5d0d..d5791ad 100644 Binary files a/docs/figs/ch10/actor_critic_architecture.png and b/docs/figs/ch10/actor_critic_architecture.png differ