标题
理论序言基础Q值与V值算法区别
SAC概念Q函数与V函数最大化熵强化学习(Maximum Entropy Reinforcement Learning, MERL)算法流程1个actor,4个Q Critic1个actor,2个V Critic,2个Q Critic
SAC 的核心思想 (双Q网络为例)为什么需要双Q?
SAC 的算法流程**(1) 数据收集****(2) 采样数据****(3) 更新 Critic 网络****(4) 更新 Actor 网络****(5) 更新 Critic 目标网络****(6) 更新温度参数
α
\alpha
α**
**4. 1 个 Actor 和 4 个 Critic 网络的扩展**5. 重参数化技巧**1) 采样过程的不可微性****2) 梯度无法反向传播的原因****3) 重参数化技巧的解决方案****4) 梯度反向传播的过程****5) 代码示例**
6. 总结
代码详解Actor网络Critic网络SAC结构理论中的训练策略 π(
ϕ
\phi
ϕ) 时的损失函数:Q函数训练时的损失函数:温度系数的更新
参考连接:SAC(Soft Actor-Critic)阅读笔记 - Feliks的文章 - 知乎
基于最大嫡强化学习的理论基础保证了算法的收敛性·双Q网络设计有效降低了值函数估计的过度偏差自适应温度参数实现了探索-利用的动态平衡采用重参数化技巧确保了策略梯度的连续性·软更新机制提升了训练稳定性算法在连续动作空间中表现优异。样本效率高,适合实际应用场景·训练过程稳定,调参难度相对较小
首先回答一个问题:为什么叫Soft AC算法?
SAC的目标函数是最大化熵正则化的累积奖励:
SAC在优化目标中引入熵正则化项,使得策略在追求高奖励的同时,还鼓励高熵(随机性),保持一定的随机性(即“软”策略),而不是完全确定性的“硬”策略。
在策略优化中,SAC通过最大化以下目标更新策略:
SAC以策略的entropy作为策略优化目标的一部分,鼓励策略的多样性,鼓励探索,同时提升策略对于环境变化的鲁棒性
理论
序言基础
Q值与V值
在强化学习中,Critic网络可以采用Q值(动作值函数)或V值(状态值函数),具体选择取决于你使用的算法以及问题的特性。
Q值(动作值函数): Critic网络输出每个状态动作对的Q值,表示在给定状态下采取某个动作的预期累积奖励。这种方法通常用于Q-learning和Deep Q Network(DQN)等算法中,其中主要关注最优动作的选择。
V值(状态值函数): Critic网络输出每个状态的V值,表示在给定状态下的预期累积奖励。这种方法通常用于值迭代方法,如异策略(Off-policy)的蒙特卡洛控制和异策略时序差分学习。 V(s) 表示智能体在状态 s 下,从该状态开始直到未来所能获得的累积奖励的期望值。换句话说,它是智能体处于状态 s 时,遵循某种策略所带来的长期回报的估计。
选择Q值还是V值通常取决于你解决的问题。如果你关心在每个状态下选择最优动作,那么使用Q值更为合适。如果你更关心每个状态的价值,而不仅仅是最优动作的话,那么使用V值可能更合适。
Q
(
s
,
a
)
Q(s, a)
Q(s,a) 表示智能体在状态 s 下执行动作 a 后,紧接着直到未来的累积奖励的期望值。与 V 值相比,Q 值不仅考虑了状态,还考虑了特定的动作选择。
在一些算法中,如深度确定性策略梯度(Deep Deterministic Policy Gradient,DDPG),使用的是一个Critic网络同时输出Q值和Actor网络的参数。这种情况下,Critic网络的输出可以同时用于评估状态动作对的Q值和评估状态的V值。
算法区别
D4PG(引入分布式的critic,并使用多个actor(learner)共同与环境交互)
TD3(参考了double Q-learning的思想来优化critic,延缓actor的更新,计算critic的优化目标时在action上加一个小扰动)
PPO:依赖于importance sampling实现的off-policy算法在面对太大的策略差异时将无能为力(正在训练的policy与实际与环境交互时的policy差异过大),所以学者们认为PPO其实是一种on-policy的算法,这类算法在训练时需要保证生成训练数据的policy与当前训练的policy一致,对于过往policy生成的数据难以再利用,所以在sample efficiency这条衡量强化学习(Reinforcement Learning, RL)算法的重要标准上难以取得优秀的表现。
SAC
概念
SAC是基于最大熵(maximum entropy)这一思想发展的RL算法,其采用与PPO类似的随机分布式策略函数(Stochastic Policy),并且是一个off-policy,actor-critic算法 将熵引入RL算法的好处为,可以让策略(policy)尽可能随机,agent可以更充分地探索状态空间,避免策略早早地落入局部最优点(local optimum),并且可以探索到多个可行方案来完成指定任务,提高抗干扰能力。
Q函数与V函数
最大化熵强化学习(Maximum Entropy Reinforcement Learning, MERL)
MERL采用了独特的策略模型。为了适应更复杂的任务,MERL中的策略不再是以往的高斯分布形式,而是用基于能量的模型(energy-based model)来表示策略:
算法流程
算法同样包括策略评估(Policy Evaluation),与策略优化(Policy Improvement),在这两个步骤交替运行下,值函数与策略都可以不断逼近最优。
1个actor,4个Q Critic
SAC的论文有两篇,一篇是《Soft Actor-Critic Algorithms and Applications》,2018年12月挂arXiv,其中SAC算法流程如下所示,它包括1个actor网络,4个Q Critic网络:(代码使用的是这个:Github链接)
1个actor,2个V Critic,2个Q Critic
一篇是《Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor》,2018年1月挂arXiv,其中SAC算法流程如下所示,它包括1个actor网络,2个V Critic网络(1个V Critic网络,1个Target V Critic网络),2个Q Critic网络: 参考知乎
SAC 的核心思想 (双Q网络为例)
SAC 的核心思想是在优化策略时,不仅最大化累积奖励,还最大化策略的熵。熵表示策略的随机性,熵越大,策略探索性越强。SAC 的目标函数为:
J
(
π
)
=
E
τ
∼
π
[
∑
t
=
0
T
(
r
t
+
α
H
(
π
(
⋅
∣
s
t
)
)
)
]
J(\pi) = \mathbb{E}_{\tau \sim \pi} \left[ \sum_{t=0}^T \left( r_t + \alpha \mathcal{H}(\pi(\cdot|s_t)) \right) \right]
J(π)=Eτ∼π[t=0∑T(rt+αH(π(⋅∣st)))] 其中:
r
t
r_t
rt是时刻
t
t
t的奖励。
H
(
π
(
⋅
∣
s
t
)
)
\mathcal{H}(\pi(\cdot|s_t))
H(π(⋅∣st))是策略
π
\pi
π在状态
s
t
s_t
st下的熵。
α
\alpha
α是温度参数,用于平衡奖励和熵的重要性。
为什么需要双Q?
主要是为了解决 高估偏差(Overestimation Bias) 问题,并提高算法的稳定性和鲁棒性。
减少高估偏差:
使用最小值可以避免单个Q网络的高估偏差,从而提供更准确的Q值估计。 提高稳定性:
两个Q网络可以互相监督,避免训练过程中出现极端值,从而提高算法的稳定性。 增强鲁棒性:
两个Q网络的独立性使得算法对噪声和误差更加鲁棒。
SAC 的算法流程
以下是 SAC 的具体步骤:
(1) 数据收集
使用当前策略
π
(
a
∣
s
)
\pi(a|s)
π(a∣s)与环境交互,收集经验数据
(
s
t
,
a
t
,
r
t
,
s
t
+
1
)
(s_t, a_t, r_t, s_{t+1})
(st,at,rt,st+1),并存储到经验回放池中。
(2) 采样数据
从经验回放池中随机采样一批数据
(
s
,
a
,
r
,
s
′
)
(s, a, r, s')
(s,a,r,s′)。
(3) 更新 Critic 网络
计算目标值
y
y
y:
y
=
r
+
γ
(
min
i
=
1
,
2
Q
θ
i
′
(
s
′
,
a
~
′
)
−
α
log
π
(
a
~
′
∣
s
′
)
)
y = r + \gamma \left( \min_{i=1,2} Q_{\theta_i'}(s', \tilde{a}') - \alpha \log \pi(\tilde{a}'|s') \right)
y=r+γ(i=1,2minQθi′(s′,a~′)−αlogπ(a~′∣s′)) 其中:
a
~
′
∼
π
(
⋅
∣
s
′
)
\tilde{a}' \sim \pi(\cdot|s')
a~′∼π(⋅∣s′)是从当前策略中采样的动作。
min
i
=
1
,
2
Q
θ
i
′
(
s
′
,
a
~
′
)
\min_{i=1,2} Q_{\theta_i'}(s', \tilde{a}')
mini=1,2Qθi′(s′,a~′)是两个 Critic 目标网络的最小值,用于减少高估偏差。 更新 Critic 网络的参数
θ
1
\theta_1
θ1和
θ
2
\theta_2
θ2:
L
(
θ
i
)
=
E
(
s
,
a
,
r
,
s
′
)
[
(
Q
θ
i
(
s
,
a
)
−
y
)
2
]
,
i
=
1
,
2
L(\theta_i) = \mathbb{E}_{(s,a,r,s')} \left[ \left( Q_{\theta_i}(s, a) - y \right)^2 \right], \quad i=1,2
L(θi)=E(s,a,r,s′)[(Qθi(s,a)−y)2],i=1,2
(4) 更新 Actor 网络
通过最大化目标函数
J
(
π
)
J(\pi)
J(π)更新 Actor 网络的参数
ϕ
\phi
ϕ:
L
(
ϕ
)
=
E
s
∼
D
[
α
log
π
(
a
∣
s
)
−
min
i
=
1
,
2
Q
θ
i
(
s
,
a
)
]
L(\phi) = \mathbb{E}_{s \sim \mathcal{D}} \left[ \alpha \log \pi(a|s) - \min_{i=1,2} Q_{\theta_i}(s, a) \right]
L(ϕ)=Es∼D[αlogπ(a∣s)−i=1,2minQθi(s,a)] 其中:
a
∼
π
(
⋅
∣
s
)
a \sim \pi(\cdot|s)
a∼π(⋅∣s)是从当前策略中采样的动作。
α
\alpha
α是温度参数,可以通过梯度下降自动调整。
(5) 更新 Critic 目标网络
使用指数移动平均(EMA)更新 Critic 目标网络的参数:
θ
i
′
←
τ
θ
i
+
(
1
−
τ
)
θ
i
′
,
i
=
1
,
2
\theta_i' \leftarrow \tau \theta_i + (1-\tau) \theta_i', \quad i=1,2
θi′←τθi+(1−τ)θi′,i=1,2 其中
τ
\tau
τ是目标网络的更新率(通常取 0.005)。
(6) 更新温度参数
α
\alpha
α
通过优化以下目标函数自动调整
α
\alpha
α:
L
(
α
)
=
E
s
∼
D
[
−
α
(
log
π
(
a
∣
s
)
+
H
ˉ
)
]
L(\alpha) = \mathbb{E}_{s \sim \mathcal{D}} \left[ -\alpha \left( \log \pi(a|s) + \bar{\mathcal{H}} \right) \right]
L(α)=Es∼D[−α(logπ(a∣s)+Hˉ)] 其中
H
ˉ
\bar{\mathcal{H}}
Hˉ是目标熵(通常取动作维度的负数)。
4. 1 个 Actor 和 4 个 Critic 网络的扩展
在 1 个 Actor 和 4 个 Critic 网络 的架构中:
使用 4 个 Critic 网络(
Q
θ
1
,
Q
θ
2
,
Q
θ
3
,
Q
θ
4
Q_{\theta_1}, Q_{\theta_2}, Q_{\theta_3}, Q_{\theta_4}
Qθ1,Qθ2,Qθ3,Qθ4)和 4 个 Critic 目标网络(
Q
θ
1
′
,
Q
θ
2
′
,
Q
θ
3
′
,
Q
θ
4
′
Q_{\theta_1'}, Q_{\theta_2'}, Q_{\theta_3'}, Q_{\theta_4'}
Qθ1′,Qθ2′,Qθ3′,Qθ4′)。在更新 Critic 网络时,取 4 个 Critic 目标网络的最小值 计算目标值:
y
=
r
+
γ
(
min
i
=
1
,
2
,
3
,
4
Q
θ
i
′
(
s
′
,
a
~
′
)
−
α
log
π
(
a
~
′
∣
s
′
)
)
y = r + \gamma \left( \min_{i=1,2,3,4} Q_{\theta_i'}(s', \tilde{a}') - \alpha \log \pi(\tilde{a}'|s') \right)
y=r+γ(i=1,2,3,4minQθi′(s′,a~′)−αlogπ(a~′∣s′))其他步骤与标准 SAC 相同。
5. 重参数化技巧
在深度强化学习中,策略网络通常输出一个随机分布(如高斯分布),然后从该分布中采样动作。然而,直接采样动作会导致梯度无法反向传播,这是因为采样过程是一个不可微的操作。
1) 采样过程的不可微性
假设策略网络输出一个高斯分布的参数(均值
μ
θ
(
s
)
\mu_\theta(s)
μθ(s) 和标准差
σ
θ
(
s
)
\sigma_\theta(s)
σθ(s)),然后从该分布中采样动作
a
a
a:
a
∼
N
(
μ
θ
(
s
)
,
σ
θ
(
s
)
2
)
a \sim \mathcal{N}(\mu_\theta(s), \sigma_\theta(s)^2)
a∼N(μθ(s),σθ(s)2) 采样过程可以表示为:
a
=
μ
θ
(
s
)
+
σ
θ
(
s
)
⋅
ϵ
a = \mu_\theta(s) + \sigma_\theta(s) \cdot \epsilon
a=μθ(s)+σθ(s)⋅ϵ 其中
ϵ
∼
N
(
0
,
1
)
\epsilon \sim \mathcal{N}(0, 1)
ϵ∼N(0,1) 是从标准高斯分布中采样的噪声。
问题在于,采样操作
ϵ
\epsilon
ϵ 是一个随机过程,它不依赖于网络的参数
θ
\theta
θ。因此,梯度无法通过采样过程反向传播到策略网络的参数
θ
\theta
θ。
2) 梯度无法反向传播的原因
在反向传播中,梯度是通过链式法则计算的。具体来说,损失函数
L
L
L 对参数
θ
\theta
θ 的梯度为:
∂
L
∂
θ
=
∂
L
∂
a
⋅
∂
a
∂
θ
\frac{\partial L}{\partial \theta} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial \theta}
∂θ∂L=∂a∂L⋅∂θ∂a 然而,采样动作
a
a
a 是一个随机变量,它的值依赖于随机噪声
ϵ
\epsilon
ϵ,而不是直接依赖于
θ
\theta
θ。因此,
∂
a
∂
θ
\frac{\partial a}{\partial \theta}
∂θ∂a 无法计算,梯度无法通过采样过程反向传播。
3) 重参数化技巧的解决方案
重参数化技巧通过将采样过程从随机性中分离出来,解决了梯度无法反向传播的问题。具体来说,它将采样动作
a
a
a 表示为确定性函数和随机噪声的组合:
a
=
μ
θ
(
s
)
+
σ
θ
(
s
)
⋅
ϵ
a = \mu_\theta(s) + \sigma_\theta(s) \cdot \epsilon
a=μθ(s)+σθ(s)⋅ϵ 其中:
μ
θ
(
s
)
\mu_\theta(s)
μθ(s) 和
σ
θ
(
s
)
\sigma_\theta(s)
σθ(s) 是策略网络输出的均值和标准差,它们是确定性函数,依赖于参数
θ
\theta
θ;
ϵ
\epsilon
ϵ 是从标准高斯分布中采样的噪声,它是一个固定的随机变量,不依赖于
θ
\theta
θ。
通过这种方式,采样动作
a
a
a 的随机性完全由
ϵ
\epsilon
ϵ 决定,而
μ
θ
(
s
)
\mu_\theta(s)
μθ(s) 和
σ
θ
(
s
)
\sigma_\theta(s)
σθ(s) 是确定性函数,梯度可以通过它们反向传播。
注意,即便两者的采用动作a表达式一样,但:
不使用重参数化技巧:采样动作α被视为一个整体,随机性嵌入在采样过程中,导致梯度无法传播。使用重参数化技巧:采样动作α被显式地分解为确定性部分和随机部分,随机性被分离出来,梯度可以通过确定性部分传播。
4) 梯度反向传播的过程
在重参数化技巧中,梯度反向传播的过程如下:
计算损失函数
L
L
L 对动作
a
a
a 的梯度
∂
L
∂
a
\frac{\partial L}{\partial a}
∂a∂L。计算动作
a
a
a 对策略网络参数
θ
\theta
θ 的梯度
∂
a
∂
θ
\frac{\partial a}{\partial \theta}
∂θ∂a:
∂
a
∂
μ
θ
(
s
)
=
1
,
∂
a
∂
σ
θ
(
s
)
=
ϵ
\frac{\partial a}{\partial \mu_\theta(s)} = 1, \quad \frac{\partial a}{\partial \sigma_\theta(s)} = \epsilon
∂μθ(s)∂a=1,∂σθ(s)∂a=ϵ通过链式法则,计算损失函数
L
L
L 对参数
θ
\theta
θ 的梯度:
∂
L
∂
θ
=
∂
L
∂
a
⋅
∂
a
∂
θ
\frac{\partial L}{\partial \theta} = \frac{\partial L}{\partial a} \cdot \frac{\partial a}{\partial \theta}
∂θ∂L=∂a∂L⋅∂θ∂a
由于
μ
θ
(
s
)
\mu_\theta(s)
μθ(s) 和
σ
θ
(
s
)
\sigma_\theta(s)
σθ(s) 是确定性函数,梯度可以通过它们反向传播,从而更新策略网络的参数
θ
\theta
θ。
5) 代码示例
以下是重参数化技巧的代码示例(基于PyTorch):
import torch
# 策略网络输出均值和标准差
mu = policy_network_mean(state)
log_std = policy_network_std(state)
std = log_std.exp()
# 从标准高斯分布中采样噪声
epsilon = torch.randn_like(mu)
# 重参数化技巧采样动作
action = mu + std * epsilon
# 计算损失函数
loss = compute_loss(action)
# 反向传播
loss.backward()
或者是使用:
a = dist.rsample() # reparameterization trick: mean+std*N(0,1)
总结 从随机分布中采样动作时,梯度无法反向传播的原因是采样过程是一个不可微的随机操作。重参数化技巧通过将采样过程从随机性中分离出来,使得梯度可以通过确定性函数反向传播,从而解决了这个问题。这是深度强化学习中一种重要的技术,被广泛应用于SAC等算法中。
6. 总结
SAC 的算法流程如下:
收集数据并存储到经验回放池。采样数据并更新 Critic 网络。更新 Actor 网络以最大化目标函数。更新 Critic 目标网络。更新温度参数
α
\alpha
α。
在 1 个 Actor 和 4 个 Critic 网络 的架构中,Critic 网络的数量增加,进一步减少了高估偏差,提高了算法的稳定性和鲁棒性。
代码详解
Actor网络
class Actor(nn.Module):
def __init__(self, state_dim, action_dim, hidden_width, max_action):
super(Actor, self).__init__()
self.max_action = max_action
self.l1 = nn.Linear(state_dim, hidden_width)
self.l2 = nn.Linear(hidden_width, hidden_width)
self.mean_layer = nn.Linear(hidden_width, action_dim)
self.log_std_layer = nn.Linear(hidden_width, action_dim)
def forward(self, x, deterministic=False, with_logprob=True):
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
mean = self.mean_layer(x)
log_std = self.log_std_layer(x) # We output the log_std to ensure that std=exp(log_std)>0
log_std = torch.clamp(log_std, -20, 2)
std = torch.exp(log_std)
dist = Normal(mean, std) # Generate a Gaussian distribution
if deterministic: # When evaluating,we use the deterministic policy
a = mean
else:
a = dist.rsample() # reparameterization trick: mean+std*N(0,1)
if with_logprob: # The method refers to Open AI Spinning up, which is more stable.
log_pi = dist.log_prob(a).sum(dim=1, keepdim=True)
log_pi -= (2 * (np.log(2) - a - F.softplus(-2 * a))).sum(dim=1, keepdim=True)
else:
log_pi = None
a = self.max_action * torch.tanh(a) # Use tanh to compress the unbounded Gaussian distribution into a bounded action interval.
return a, log_pi
Critic网络
class Critic(nn.Module): # According to (s26蒙特卡洛树搜索 (MCTS),a), directly calculate Q(s26蒙特卡洛树搜索 (MCTS),a)
def __init__(self, state_dim, action_dim, hidden_width):
super(Critic, self).__init__()
# Q1
self.l1 = nn.Linear(state_dim + action_dim, hidden_width)
self.l2 = nn.Linear(hidden_width, hidden_width)
self.l3 = nn.Linear(hidden_width, 1)
# Q2
self.l4 = nn.Linear(state_dim + action_dim, hidden_width)
self.l5 = nn.Linear(hidden_width, hidden_width)
self.l6 = nn.Linear(hidden_width, 1)
def forward(self, s, a):
s_a = torch.cat([s, a], 1)
q1 = F.relu(self.l1(s_a))
q1 = F.relu(self.l2(q1))
q1 = self.l3(q1)
q2 = F.relu(self.l4(s_a))
q2 = F.relu(self.l5(q2))
q2 = self.l6(q2)
return q1, q2
SAC结构
class SAC(object):
def __init__(self, state_dim, action_dim, max_action):
self.max_action = max_action
self.hidden_width = 256 # The number of neurons in hidden layers of the neural network
self.batch_size = 256 # batch size
self.GAMMA = 0.99 # discount factor
self.TAU = 0.005 # Softly update the target network
self.lr = 3e-4 # learning rate
self.adaptive_alpha = True # Whether to automatically learn the temperature alpha
if self.adaptive_alpha:
# Target Entropy = −dim(A) (e.g. , -6 for HalfCheetah-v2) as given in the paper
self.target_entropy = -action_dim
# We learn log_alpha instead of alpha to ensure that alpha=exp(log_alpha)>0
self.log_alpha = torch.zeros(1, requires_grad=True)
self.alpha = self.log_alpha.exp()
self.alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=self.lr)
else:
self.alpha = 0.2
self.actor = Actor(state_dim, action_dim, self.hidden_width, max_action)
self.critic = Critic(state_dim, action_dim, self.hidden_width)
self.critic_target = copy.deepcopy(self.critic)
self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=self.lr)
self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=self.lr)
理论中的训练策略 π(
ϕ
\phi
ϕ) 时的损失函数:
对应代码的:
# Compute actor loss
a, log_pi = self.actor(batch_s)
Q1, Q2 = self.critic(batch_s, a)
Q = torch.min(Q1, Q2)
actor_loss = (self.alpha * log_pi - Q).mean() ##这里就是关键了撒
Q函数训练时的损失函数:
对应代码:
with torch.no_grad():
batch_a_, log_pi_ = self.actor(batch_s_) # a' from the current policy
# Compute target Q
target_Q1, target_Q2 = self.critic_target(batch_s_, batch_a_)
target_Q = batch_r + self.GAMMA * (1 - batch_dw) * (torch.min(target_Q1, target_Q2) - self.alpha * log_pi_)
# Compute current Q
current_Q1, current_Q2 = self.critic(batch_s, batch_a)
# Compute critic loss
critic_loss = F.mse_loss(current_Q1, target_Q) + F.mse_loss(current_Q2, target_Q)
温度系数的更新
H
0
\mathcal{H_0}
H0 是预先定义好的最小策略熵的阈值。
# Update alpha
if self.adaptive_alpha:
# We learn log_alpha instead of alpha to ensure that alpha=exp(log_alpha)>0
alpha_loss = -(self.log_alpha.exp() * (log_pi + self.target_entropy).detach()).mean()
self.alpha_optimizer.zero_grad()
alpha_loss.backward()
self.alpha_optimizer.step()
self.alpha = self.log_alpha.exp()