参考视频:周博磊强化学习课程
价值函数优化学习主线:Q-learning→DQN→DDPG→TD3→SAC
Q-Learning,DQN和DDPG请可以参考我之前的文章:强化学习实践教学
TD3可以参考我之前的博客:强化学习之TD3(pytorch实现)
SAC可以参考博客:https://blog.csdn.net/qq_38587510/article/details/104970837
参考论文:
- Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor,2018年8月发表。
- Soft Actor-Critic Algorithms and Applications,2019年1月发表。
SAC全称Soft Actor-Critic,它整合了entropy regularization的思想。论文有以上两篇,第一篇采用模型包括一个actor网络,两个状态价值V网络,两个动作价值Q网络,第二篇的模型包括一个actor网络,四个动作价值Q网络。
model-free深度强化学习算法面临两个主要挑战:高采样复杂度和脆弱的收敛性,因此严重依赖调参,这两个挑战限制了强化学习向现实应用的推广。SAC引入了最大熵(Maximum Entropy)强化学习,要求actor在同时最大化期望和策略分布的熵,也就是说,在保证任务成果的同时希望策略尽可能的随机。
信息熵
这里说明一下信息熵的概念:当一件事情发生的概率越小,这件事情的信息量越大,对于一个分布而言,信息量的计算方式是:
H
(
U
)
=
E
[
−
log
p
i
]
=
−
∑
i
=
1
n
p
i
l
o
g
p
i
H(U) = E[-\log p_i] = - \sum_{i=1}^n p_i log p_i
H(U)=E[−logpi]=−i=1∑npilogpi
因此actor输出总概率相加为1的情况下,动作概率的分布越散,越不集中于一个action,这个熵的值越大。对于连续动作领域来说,就是随机噪声的采样值越偏离平均值,越出现在高斯分布边缘,这个值越大。
Maximum Entropy
在标准强化学习中需要最大化累积期望reward:
∑
t
E
(
s
t
,
a
t
)
~
p
π
[
γ
t
r
(
s
t
,
a
t
)
]
\sum_{t} E_{(s_t,a_t)~ p_{\pi}}[\gamma^t r(s_t,a_t)]
t∑E(st,at)~pπ[γtr(st,at)]
在最大熵强化学习中需要优化的目标是:
a
r
g
m
a
x
π
∑
t
E
τ
~
π
[
γ
t
(
R
(
s
t
,
a
r
,
s
t
+
1
)
+
α
H
(
π
(
.
∣
s
t
)
)
)
]
argmax_{\pi}\sum_{t} E_{\tau ~ \pi}[\gamma^t (R(s_t,a_r,s_{t+1})+ \alpha H(\pi(.|s_t)))]
argmaxπt∑Eτ~π[γt(R(st,ar,st+1)+αH(π(.∣st)))]
最大熵强化学习在标准的最大reward强化目标上增加了一个最大熵项,提高了探索能力和鲁棒性。既降低了采样复杂度,又提高了收敛稳定性。可以学到更多near-optimal的行为,也就是在一些状态下,可能存在多个动作都是最优的,那么使选择它们的概率相同,可以提高学习的速度。
此时有:
V
π
(
s
)
=
E
τ
~
π
[
∑
t
γ
t
(
R
(
s
t
,
a
r
,
s
t
+
1
)
+
α
H
(
π
(
.
∣
s
t
)
)
)
∣
s
0
=
s
]
V^{\pi}(s) = E_{\tau ~ \pi}[\sum_t \gamma^t (R(s_t,a_r,s_{t+1})+ \alpha H(\pi(.|s_t))) | s_0 = s]
Vπ(s)=Eτ~π[t∑γt(R(st,ar,st+1)+αH(π(.∣st)))∣s0=s]
Q π ( s , a ) = E s ′ ~ P , a ′ ~ π [ R ( s , a , s ′ ) + γ ( Q π ( s ′ , a ′ ) + α H ( π ( . ∣ s t ) ) ) ] = E s ′ ~ P , a ′ ~ π [ R ( s , a , s ′ ) + γ ( Q π ( s ′ , a ′ ) − α log π ( a ′ ∣ s ′ ) ) ] Q^{\pi}(s,a) = E_{s' ~ P,a'~ \pi }[R(s,a,s')+ \gamma (Q^{\pi}(s',a') + \alpha H(\pi(.|s_t))) ] \\ = E_{s' ~ P,a'~ \pi }[R(s,a,s')+ \gamma (Q^{\pi}(s',a') - \alpha \log \pi(a'|s')) ] Qπ(s,a)=Es′~P,a′~π[R(s,a,s′)+γ(Qπ(s′,a′)+αH(π(.∣st)))]=Es′~P,a′~π[R(s,a,s′)+γ(Qπ(s′,a′)−αlogπ(a′∣s′))]
因此,采样更新公式应该是:
Q
π
(
s
,
a
)
≈
r
+
γ
(
Q
π
(
s
′
,
a
′
^
)
−
α
log
π
(
a
′
^
∣
s
′
)
)
,
a
′
^
~
π
(
.
∣
s
′
)
Q^{\pi}(s,a) \approx r + \gamma (Q^{\pi}(s',\hat{ a'}) - \alpha \log \pi(\hat {a'}|s')) ,\hat{a'} ~ \pi(.|s')
Qπ(s,a)≈r+γ(Qπ(s′,a′^)−αlogπ(a′^∣s′)),a′^~π(.∣s′)
损失函数为:
L
(
ϕ
i
,
D
)
=
E
[
(
Q
ϕ
(
s
,
a
)
−
y
(
r
,
s
′
,
d
)
)
2
]
L(\phi_i,D) = E[(Q_{\phi}(s,a) - y(r,s',d))^2]
L(ϕi,D)=E[(Qϕ(s,a)−y(r,s′,d))2]
TD3类似,SAC也采用了两个Q网络的更新形式,然后使用较小的Q值进行更新。
y
(
r
,
s
′
,
d
)
=
r
+
γ
(
1
−
d
)
(
min
j
=
1
,
2
Q
ϕ
t
a
r
g
,
j
(
s
′
,
a
′
^
)
−
α
log
π
θ
(
a
′
^
∣
s
′
)
)
,
a
′
^
~
π
(
.
∣
s
′
)
y(r,s',d) = r + \gamma (1-d)(\min_{j=1,2}Q_{\phi_{targ,j}}(s',\hat{a'}) - \alpha \log \pi_{\theta}(\hat{a'}|s')) ,\hat{a'} ~ \pi(.|s')
y(r,s′,d)=r+γ(1−d)(j=1,2minQϕtarg,j(s′,a′^)−αlogπθ(a′^∣s′)),a′^~π(.∣s′)
对于状态价值V有:
V
π
(
s
)
=
E
a
~
π
[
Q
π
(
s
,
a
)
]
+
α
H
(
π
(
.
∣
s
)
)
=
E
a
~
π
[
Q
π
(
s
,
a
)
]
−
α
log
π
(
a
∣
s
)
)
V^{\pi}(s) = E_{a ~ \pi}[Q^{\pi}(s,a)] + \alpha H(\pi(.|s)) \\ = E_{a ~ \pi}[Q^{\pi}(s,a)] - \alpha \log \pi(a|s))
Vπ(s)=Ea~π[Qπ(s,a)]+αH(π(.∣s))=Ea~π[Qπ(s,a)]−αlogπ(a∣s))
Reparameterization Trick
同时也在动作输出中加入了噪声。这就是Reparameterization Trick。让a从策略网络中进行采样转变成和其没有什么关系的采样。
a
θ
^
(
s
,
ϵ
)
=
tanh
(
μ
θ
(
s
)
+
σ
θ
(
s
)
⊙
ϵ
)
,
ϵ
~
N
(
0
,
1
)
\hat{a_{\theta}}(s,\epsilon) = \tanh(μ_{\theta}(s) + \sigma_{\theta}(s) \odot \epsilon),\epsilon~N(0,1)
aθ^(s,ϵ)=tanh(μθ(s)+σθ(s)⊙ϵ),ϵ~N(0,1)
其中μ和σ都是由神经网络学习得到的,因此在奖励最大化的同时鼓励探索的话,σ会尽可能增大以达到探索的目的,这样一个μ和σ都会变化的动作分布大大增加了动作策略的灵活性。
actor部分代码:
class GaussianPolicy(nn.Module):
def __init__(self, num_inputs, num_actions, hidden_dim, action_space=None):
super(GaussianPolicy, self).__init__()
self.linear1 = nn.Linear(num_inputs, hidden_dim)
self.linear2 = nn.Linear(hidden_dim, hidden_dim)
self.mean_linear = nn.Linear(hidden_dim, num_actions)
self.log_std_linear = nn.Linear(hidden_dim, num_actions)
# 使用对应函数初始化所有的全连接层参数
self.apply(weights_init_)
# action rescaling
if action_space is None:
self.action_scale = torch.tensor(1.)
self.action_bias = torch.tensor(0.)
else:
self.action_scale = torch.FloatTensor(
(action_space.high - action_space.low) / 2.)
self.action_bias = torch.FloatTensor(
(action_space.high + action_space.low) / 2.)
def forward(self, state):
x = F.relu(self.linear1(state))
x = F.relu(self.linear2(x))
mean = self.mean_linear(x)
log_std = self.log_std_linear(x)
log_std = torch.clamp(log_std, min=LOG_SIG_MIN, max=LOG_SIG_MAX)
return mean, log_std
def sample(self, state):
mean, log_std = self.forward(state)
std = log_std.exp()
# 创建高斯分布
normal = Normal(mean, std)
x_t = normal.rsample() # for reparameterization trick (mean + std * N(0,1))
y_t = torch.tanh(x_t)
action = y_t * self.action_scale + self.action_bias
log_prob = normal.log_prob(x_t)
# Enforcing Action Bound
# ---重点问题---
log_prob -= torch.log(self.action_scale * (1 - y_t.pow(2)) + epsilon)
log_prob = log_prob.sum(1, keepdim=True)
mean = torch.tanh(mean) * self.action_scale + self.action_bias
return action, log_prob, mean
def to(self, device):
self.action_scale = self.action_scale.to(device)
self.action_bias = self.action_bias.to(device)
return super(GaussianPolicy, self).to(device)