SAC 算法
- soft贝尔曼期望
-
- 策略更新目标
-
- 拉格朗日乘数法
-
- 策略迭代
-
- [soft-策略价值中 α \alpha α的调节](#soft-策略价值中 α \alpha α的调节)
- 最优状态
-
- [证明Soft 贝尔曼算子同样是压缩映射](#证明Soft 贝尔曼算子同样是压缩映射)
- [当 α → 0 , V ∗ ( s ) \alpha\rightarrow0,V^{*}(s) α→0,V∗(s)相当于 max a Q ( s , a ) \max_{a} Q(s,a) maxaQ(s,a)](#当 α → 0 , V ∗ ( s ) \alpha\rightarrow0,V^{*}(s) α→0,V∗(s)相当于 max a Q ( s , a ) \max_{a} Q(s,a) maxaQ(s,a))
soft贝尔曼期望
数学表达式为:
Q ( s t , a t ) = r ( s t , a t ) + γ E s t + 1 V ( s t + 1 ) Q(s_t,a_t)=r(s_t,a_t)+\gamma\mathbb{E}{s{t+1}}V(s_{t+1}) Q(st,at)=r(st,at)+γEst+1V(st+1)
其中,
V ( s t ) = E a ∼ π Q ( s t , a t ) − α log ( a t ∣ s t ) = E a ∼ π Q ( s t , a t ) + α H ( π ( ⋅ ∣ s t ) ) V(s_t)=\mathbb{E}{a\sim \pi}Q(s_t,a_t)-\\alpha \\log (a_t\|s_t)=\mathbb{E}{a\sim \pi}Q(s_t,a_t)+\alpha H(\pi(\cdot|s_t)) V(st)=Ea∼πQ(st,at)−αlog(at∣st)=Ea∼πQ(st,at)+αH(π(⋅∣st))
策略更新目标
由于策略的价值定义为:
J ( π ) = E V π ( s ) J(\pi)=\mathbb{E}V\^{\\pi}(s) J(π)=EVπ(s)
因此最优策略目标为:
π ∗ = arg max π E V π ( s ) \pi^{*}=\argmax_{\pi}\mathbb{E}V\^{\\pi}(s) π∗=πargmaxEVπ(s)
= arg max π E a ∼ π Q ∗ ( s , a ) − α log π ( ⋅ ∣ s ) =\argmax_{\pi}\mathbb{E}_{a\sim \pi}Q\^{\*}(s,a)-\\alpha\\log \\pi(\\cdot\|s) =πargmaxEa∼πQ∗(s,a)−αlogπ(⋅∣s)
= arg max π ∑ a π ( a ∣ s ) Q ∗ ( s , a ) − α log π ( ⋅ ∣ s ) =\argmax_{\pi} \sum_{a}\pi(a|s)Q\^{\*}(s,a)-\\alpha\\log \\pi(\\cdot\|s) =πargmaxa∑π(a∣s)Q∗(s,a)−αlogπ(⋅∣s)
约束为:
∑ a π ( a ∣ s ) = 1 \sum_{a}\pi(a|s)=1 a∑π(a∣s)=1
目标函数第一项: ∑ a π ( a ∣ s ) Q ∗ ( s , a ) \sum_a\pi(a|s)Q^{*}(s,a) ∑aπ(a∣s)Q∗(s,a)这是一个关于 π \pi π的线性函数。线性函数既是凸的也是凹的,不改变凸性。
目标函数第二项: − ∑ a π ( a ∣ s ) log π ( ⋅ ∣ s ) -\sum_a\pi(a|s)\log \pi(\cdot|s) −∑aπ(a∣s)logπ(⋅∣s)这是熵函数,严格凹函数。
因此整个目标函数是严格凹函数。
又因为约束条件 ∑ a π ( a ∣ s ) = 1 \sum_{a}\pi(a|s)=1 ∑aπ(a∣s)=1,定义了一个概率单纯形,这是一个凸集。在概率单纯形上最大化一个严格凹函数,是一个标准的凸优化问题。
在凸优化中,只要目标函数是严格凹的、约束是凸的,那么任何满足KKT条件的局部最优解就是全局最优解。拉格朗日乘数法找到的解满足KKT条件,因此它就是全局最优解。我们不需要"先验证凸性再求解",而是"凸性保证了拉格朗日解就是全局最优"。
拉格朗日乘数法
构建拉格朗日算子如下:
L ( π , λ ) = ∑ a π ( a ∣ s ) Q ∗ ( s , a ) − α log π ( ⋅ ∣ s ) − λ ( 1 − ∑ a π ( a ∣ s ) ) L(\pi,\lambda)=\sum_{a}\pi(a|s)Q\^{\*}(s,a)-\\alpha\\log \\pi(\\cdot\|s)-\lambda(1-\sum_{a}\pi(a|s)) L(π,λ)=a∑π(a∣s)Q∗(s,a)−αlogπ(⋅∣s)−λ(1−a∑π(a∣s))
对指定某一策略求导数,并令其为0可得:
∂ L ( π , λ ) ∂ π ( a ∣ s ) = Q ( s , a ) − α log π ( ⋅ ∣ s ) + π ( a ∣ s ) α π ( a ∣ s ) − λ \frac{\partial L(\pi,\lambda)}{\partial \pi(a|s)}=Q(s,a)-\\alpha\\log \\pi(\\cdot\|s)+\\pi(a\|s)\\frac{\\alpha}{\\pi(a\|s)}-\lambda ∂π(a∣s)∂L(π,λ)=Q(s,a)−αlogπ(⋅∣s)+π(a∣s)π(a∣s)α−λ
∂ L ( π , λ ) ∂ π ( a ∣ s ) = Q ( s , a ) − α log π ( a ⋅ ∣ s ) − ( α + λ ) = 0 \frac{\partial L(\pi,\lambda)}{\partial \pi(a|s)}=Q(s,a)-\\alpha\\log \\pi(a\\cdot\|s)-(\alpha+\lambda)=0 ∂π(a∣s)∂L(π,λ)=Q(s,a)−αlogπ(a⋅∣s)−(α+λ)=0
也即:
log π ( a ⋅ ∣ s ) = Q ( s , a ) α − α + λ α \log \pi(a\cdot|s)=\frac{Q(s,a)}{\alpha}-\frac{\alpha+\lambda}{\alpha} logπ(a⋅∣s)=αQ(s,a)−αα+λ
等式两边取对数得:
π ( a ⋅ ∣ s ) = exp ( Q ( s , a ) α ) ⋅ exp ( α + λ α ) \pi(a\cdot|s)=\exp(\frac{Q(s,a)}{\alpha})\cdot \exp(\frac{\alpha+\lambda}{\alpha}) π(a⋅∣s)=exp(αQ(s,a))⋅exp(αα+λ)
令 C = exp ( α + λ α ) C=\exp(\frac{\alpha+\lambda}{\alpha}) C=exp(αα+λ)
对策略求和可得:
∑ a π ( a ⋅ ∣ s ) = C ∑ a exp ( Q ( s , a ) α ) = 1 \sum_a\pi(a\cdot|s)=C\sum_a\exp(\frac{Q(s,a)}{\alpha})=1 a∑π(a⋅∣s)=Ca∑exp(αQ(s,a))=1
因此可得: C = 1 ∑ a exp ( Q ( s , a ) α ) C=\frac{1}{\sum_a\exp(\frac{Q(s,a)}{\alpha})} C=∑aexp(αQ(s,a))1
令 Z ( s ) = ∑ a exp ( Q ( s , a ) α ) Z(s)=\sum_a\exp(\frac{Q(s,a)}{\alpha}) Z(s)=∑aexp(αQ(s,a))
综上所述:
最优策略为:
π ∗ ( a ∣ s ) = exp ( Q ( s , a ) α ) ∑ a exp ( Q ( s , a ) α ) (3.1) \pi^{*}(a|s)=\frac{\exp(\frac{Q(s,a)}{\alpha})}{\sum_a\exp(\frac{Q(s,a)}{\alpha})}\text{(3.1)} π∗(a∣s)=∑aexp(αQ(s,a))exp(αQ(s,a))(3.1)
策略迭代
由于策略迭代的目的是为了逐步趋近最优策略,首先需要衡量当前策略(概率分布)与最优策略(概率分布)之间的差异。根据之前博客的介绍,我们不难发现可以利用KL散度进行衡量。数学表达如下:
D K L π θ ( ⋅ ∣ s ) ∣ ∣ π ∗ ( ⋅ ∣ s ) = E a ∼ π log π θ ( a ∣ s ) π ∗ ( a ∣ s ) = E a ∼ π log π θ ( a ∣ s ) exp ( Q ( s , a ) α ) / Z ( s ) D_{KL}\\pi_{\\theta}(\\cdot\|s)\|\|\\pi\^{\*}(\\cdot\|s)=\mathbb{E}{a\sim \pi}\\log \\frac{\\pi_{\\theta}(a\|s)}{\\pi\^{\*}(a\|s)}=\mathbb{E}{a\sim \pi}\\log \\frac{\\pi_{\\theta}(a\|s)}{\\exp(\\frac{Q(s,a)}{\\alpha})/Z(s)} DKLπθ(⋅∣s)∣∣π∗(⋅∣s)=Ea∼πlogπ∗(a∣s)πθ(a∣s)=Ea∼πlogexp(αQ(s,a))/Z(s)πθ(a∣s)
展开得:
= E a ∼ π log π θ ( a ∣ s ) − Q ( s , a ) α + log Z ( s ) = E a ∼ π log π θ ( a ∣ s ) − Q ( s , a ) α + log Z ( s ) =\mathbb{E}{a\sim \pi}\\log\\pi_{\\theta}(a\|s)-\\frac{Q(s,a)}{\\alpha}+\\log Z(s)=\mathbb{E}{a\sim \pi}\\log\\pi_{\\theta}(a\|s)-\\frac{Q(s,a)}{\\alpha}+\log Z(s) =Ea∼πlogπθ(a∣s)−αQ(s,a)+logZ(s)=Ea∼πlogπθ(a∣s)−αQ(s,a)+logZ(s)
构建soft-策略价值表达式为:
L π ( θ ) = E α log π θ ( a ∣ s ) − Q ( s , a ) L_{\pi}(\theta)=\mathbb{E}\\alpha \\log\\pi_{\\theta}(a\|s)-Q(s,a) Lπ(θ)=Eαlogπθ(a∣s)−Q(s,a)
- 之所以不考虑 Z ( s ) Z(s) Z(s)主要是因为策略参数得迭代需要利用梯度下降法求增量, Z ( s ) Z(s) Z(s)与策略梯无关。
- 数学上KL需要最小化和pytorch利用梯度下降法求最小值一致,因此不需要改变符号。
- 之所以需要对散度展开式乘以 α \alpha α,主要是为了防止 当 α → 0 时, Q ( s , a ) α → ∞ 当\alpha\rightarrow0时,\frac{Q(s,a)}{\alpha}\rightarrow \infty 当α→0时,αQ(s,a)→∞
soft-策略价值中 α \alpha α的调节
根据soft-策略价值,我们不难看出 α \alpha α的数学意义:
- 当 α \alpha α增大时,该策略的熵增大,更趋向探索。
- 当 α \alpha α降低时,该策略的熵降低,更趋向于依赖价值函数。
根据这一数学意义构建 α \alpha α预估数学建模如下:
L ( α ) = α E H c u r r e n t − H t a r g e t L(\alpha)=\alpha\mathbb{E}H_{current}-H_{target} L(α)=αEHcurrent−Htarget
由于,
∂ L ( α ) ∂ α = H c u r r e n t − H t a r g e t \frac{\partial L(\alpha)}{\partial \alpha}=H_{current}-H_{target} ∂α∂L(α)=Hcurrent−Htarget
- 当 H c u r r e n t − H t a r g e t > 0 H_{current}-H_{target}>0 Hcurrent−Htarget>0,说明探索过度,需要减少 α \alpha α
- 当 H c u r r e n t − H t a r g e t < 0 H_{current}-H_{target}<0 Hcurrent−Htarget<0,说明探索不足,需要增大 α \alpha α
说明适用梯度下降法进行对 α \alpha α适当调节。
最优状态
根据最优策略的定义,我们将最优策略代入价值函数中即可得到最优状态价值(这是符合最优贝尔曼方程的),数学表达为:
V ∗ ( s ) = E a ∼ π Q ( s , a ) − α ( Q ( s , a ) α − log Z ( s ) ) = E a ∼ π α log Z ( s ) = α log Z ( s ) = α log ∑ a exp ( Q ∗ ( s , a ) α ) V^{*}(s)=\mathbb{E}{a\sim \pi}Q(s,a)-\\alpha(\\frac{Q(s,a)}{\\alpha}-\\log Z(s))=\mathbb{E}{a\sim \pi}\\alpha \\log Z(s)=\alpha \log Z(s)=\alpha \log\sum_a \exp(\frac{Q^{*}(s,a)}{\alpha}) V∗(s)=Ea∼πQ(s,a)−α(αQ(s,a)−logZ(s))=Ea∼παlogZ(s)=αlogZ(s)=αloga∑exp(αQ∗(s,a))
证明Soft 贝尔曼算子同样是压缩映射
由前面博客可知,贝尔曼期望能收敛的底层数学依据是巴拿赫不动点定理的保证。
根据巴拿赫不动点定理的保证,任何在完备度量空间上的压缩映射都有:
- 唯一的不动点
- 从任意初始位置出发,都能收敛到唯一不动点。
因此证明soft贝尔曼期望能唯一收敛的核心是证明soft贝尔曼算子是压缩映射(非扩展)
( T ∗ Q ) ( s , a ) = r ( s , a ) + γ E s ′ ∼ P α log ∑ a ′ exp ( Q ( s ′ , a ′ ) α ) (T^{*}Q)(s,a)=r(s,a)+\gamma\mathbb{E}_{s^{'}\sim P}\\alpha\\log\\sum_{a\^{'}}\\exp(\\frac{Q(s\^{'},a\^{'})}{\\alpha}) (T∗Q)(s,a)=r(s,a)+γEs′∼Pαloga′∑exp(αQ(s′,a′))
证明的关键在于:Log-Sum-Exp 函数具有非扩张性,即它不会放大误差。
由于无穷范数的定义就是取绝对值的最大值。
∣ ∣ T ∗ Q 1 − T ∗ Q 2 ∣ ∣ ∞ = γ max s , a ∣ E ( s ′ , a ′ ) ∼ P ( ⋅ ∣ s , a ) α log ∑ a ′ exp ( Q 1 ( s ′ , a ′ ) α ) − α log ∑ a ′ exp ( Q 2 ( s ′ , a ′ ) α ) ∣ ||T^{*}Q_1-T^{*}Q_2||{\infty}=\gamma\max{s,a}|\mathbb{E}_{(s^{'},a^{'})\sim P(\cdot|s,a)}\\alpha\\log\\sum_{a\^{'}}\\exp(\\frac{Q_1(s\^{'},a\^{'})}{\\alpha})-\\alpha\\log\\sum_{a\^{'}}\\exp(\\frac{Q_2(s\^{'},a\^{'})}{\\alpha})| ∣∣T∗Q1−T∗Q2∣∣∞=γs,amax∣E(s′,a′)∼P(⋅∣s,a)αloga′∑exp(αQ1(s′,a′))−αloga′∑exp(αQ2(s′,a′))∣
max 下面的 s , a 代表的是初始状态 − 动作,期望内的括号代表下一个状态 − 动作 \max下面的s,a代表的是初始状态-动作,期望内的括号代表下一个状态-动作 max下面的s,a代表的是初始状态−动作,期望内的括号代表下一个状态−动作
= γ max s , a ∣ E s ′ ∼ P ( ⋅ ∣ s , a ) V 1 ( s ′ ) − V 2 ( s ′ ) ∣ =\gamma\max_{s,a}|\mathbb{E}_{s^{'}\sim P(\cdot|s,a)}V_1(s\^{'})-V_2(s\^{'})| =γs,amax∣Es′∼P(⋅∣s,a)V1(s′)−V2(s′)∣
由期望值小于最大值可得:
≤ γ max s ′ [ V 1 ( s ′ ) − V 2 ( s ′ ) ∣ \le\gamma\max_{s^{'}}[V_1(s^{'})-V_2(s^{'})| ≤γs′max[V1(s′)−V2(s′)∣
= γ max s ′ ∣ α log ∑ a ′ exp ( Q 1 ( s ′ , a ′ ) α ) − α log ∑ a ′ exp ( Q 2 ( s ′ , a ′ ) α ) ∣ =\gamma\max_{s^{'}}|\alpha\log\sum_{a^{'}}\exp(\frac{Q_1(s^{'},a^{'})}{\alpha})-\alpha\log\sum_{a^{'}}\exp(\frac{Q_2(s^{'},a^{'})}{\alpha})| =γs′max∣αloga′∑exp(αQ1(s′,a′))−αloga′∑exp(αQ2(s′,a′))∣
Log-Sum-Exp 函数具有非扩张性,即它不会放大误差。
≤ γ max s ′ , a ′ ∣ Q 1 ( s ′ , a ′ ) − Q 2 ( s ′ , a ′ ) ∣ = γ ∣ ∣ Q 1 − Q 2 ∣ ∣ ∞ \le\gamma\max_{s^{'},a^{'}}|Q_1(s^{'},a^{'})-Q_2(s^{'},a^{'})|=\gamma||Q_1-Q_2||_{\infty} ≤γs′,a′max∣Q1(s′,a′)−Q2(s′,a′)∣=γ∣∣Q1−Q2∣∣∞
证毕,soft贝尔曼期望就是压缩算子,满足巴拿赫不动点定理。
当 α → 0 , V ∗ ( s ) \alpha\rightarrow0,V^{*}(s) α→0,V∗(s)相当于 max a Q ( s , a ) \max_{a} Q(s,a) maxaQ(s,a)
(一)
Q m a x = α log ( exp ( Q / α ) ) ≤ α log ∑ a exp ( Q / α ) Q_{max}=\alpha\log(\exp(Q/\alpha))\le \alpha\log \sum_a\exp(Q/\alpha) Qmax=αlog(exp(Q/α))≤αloga∑exp(Q/α)
(二)
假设共有N个动作,则:
∑ a exp ( Q / α ) ≤ N ⋅ exp ( Q m a x / α ) (3.2) \sum_a\exp(Q/\alpha)\le N\cdot \exp(Q_{max}/\alpha)\text{(3.2)} a∑exp(Q/α)≤N⋅exp(Qmax/α)(3.2)
等价于:
α log ∑ a exp ( Q / α ) ≤ α log N ⋅ exp ( Q m a x / α ) = α log N + Q m a x (3.3) \alpha\log \sum_a\exp(Q/\alpha)\le \alpha\logN\\cdot \\exp(Q_{max}/\\alpha)=\alpha\log N+Q_{max}\text{(3.3)} αloga∑exp(Q/α)≤αlogN⋅exp(Qmax/α)=αlogN+Qmax(3.3)
由(3.2)(3.3)以及夹逼定理可证:
当 α → 0 , V ∗ ( s ) \alpha\rightarrow0,V^{*}(s) α→0,V∗(s)相当于 max a Q ( s , a ) \max_{a} Q(s,a) maxaQ(s,a)
代入soft贝尔曼期望方程: Q ( s t , a t ) = r ( s t , a t ) + γ E s t + 1 V ( s t + 1 ) Q(s_t,a_t)=r(s_t,a_t)+\gamma\mathbb{E}{s{t+1}}V(s_{t+1}) Q(st,at)=r(st,at)+γEst+1V(st+1)可得:
Q ∗ ( s , a ) = r ( s , a ) + γ E s α log ∑ a exp ( Q ∗ ( s , a ) α ) Q^{*}(s,a)=r(s,a)+\gamma\mathbb{E}{s{}}\\alpha \\log\\sum_a \\exp(\\frac{Q\^{\*}(s,a)}{\\alpha}) Q∗(s,a)=r(s,a)+γEsαloga∑exp(αQ∗(s,a))
#代码实现
首先在倒立摆环境下进行实验,然后再尝试将 SAC 应用到与离散动作交互的车杆环境。
1、导入所需要的库
python
import random
import gymnasium as gym
import numpy as np
from tqdm import tqdm
import torch
import torch.nn.functional as F
from torch.distributions import Normal
import matplotlib.pyplot as plt
import rl_utils
2、创造Q函数来critic
python
class QValueNetContinous(torch.nn.Module):
def __init__(self,state_dim,hidden_dim,action_dim):
super(QValueNetContinous,self).__init__()
self.fc1=torch.nn.Linear(state_dim+action_dim,hidden_dim)
self.fc2=torch.nn.Linear(hidden_dim,hidden_dim)
self.fc_out=torch.nn.Linear(hidden_dim,1)
def forward(self,x,a):
cat=torch.cat([x,a],dim=1)
#提取特征
x=F.relu(self.fc1(cat))
x=F.relu(self.fc2(x))
return self.fc_out(x)
3、创造生成动作及其对应的分布log值得actor函数
python
class policyNetContinuous(torch.nn.Module):
def __init__(self,state_dim,hidden_dim,action_dim,action_bound):
super(policyNetContinuous, self).__init__()
self.fc1=torch.nn.Linear(state_dim,hidden_dim)
#mu/std分别是均值和标准差
self.mu=torch.nn.Linear(hidden_dim,action_dim)
self.std=torch.nn.Linear(hidden_dim,action_dim)
self.action_bound=action_bound
def forward(self,x):
#提取特征值
x=F.relu(self.fc1(x))
#根据特征值生成均值和平方差
mu=self.mu(x)
#std需要F.softplus的原因是平方差必须大于等于0,确保标准差始终为正,且输出平滑、梯度流畅。这是连续动作空间策略网络的常见做法。
std=F.softplus(self.std(x))
#创造采样空间
dist=Normal(mu,std)
#在采样空间里采集样本,normal_sample则是依据mu/std的正太分布下获取的动作
normal_action_sample=dist.rsample() # rsample()是重参数化采样让采样过程的梯度不断裂,Q 值梯度 ∇a Q 能通过链式法则传递给 Actor 参数。这是 SAC 的核心技术。
#获取所采动作样本概率分布的log值
log_prob=dist.log_prob(normal_action_sample)
#将对应于正太分布的概率分布的log值映射到torch.tanh(后续也需要在正太分布采样的动作映射到tanh中)
log_prob = log_prob - torch.log(1 - torch.tanh(normal_action_sample).pow(2) + 1e-7)
#由于倒立摆环境下的动作在[-2,2]内部,首先需要将在正太分布下采样的动作映射到[-1,1]的动作空间内
action=torch.tanh(normal_action_sample)
#action*self.action_bound相当于整个模型的幅度等比例增加,不影响概率分布
action=action*self.action_bound
return action,log_prob
.rsample() 重参数采样与普通采样的区别
核心:rsample() 构建了一个可求梯度的函数,而普通采样只是一个不可导的随机操作。这正是 SAC 能够利用 Critic 的 Q 值梯度来直接指导 Actor 更新的根本原因。
在数学上,rsample() 将动作 a 表达为 Actor 参数θ 和独立噪声 ϵ的确定性可微函数。
a = μ θ + σ θ ⋅ ϵ , ϵ ∼ N ( 0 , 1 ) a=\mu_{\theta}+\sigma_{\theta}\cdot \epsilon,\epsilon\sim N(0,1) a=μθ+σθ⋅ϵ,ϵ∼N(0,1)
因此,动作 a 不再是"从分布中随机抽出来的数",而是"以 ϵ 为输入、以 θ 为参数的可微函数"。这与 DDPG 的确定性动作 a = μ θ a=\mu_{\theta} a=μθ在本质上是同构的------两者都构建了一个从参数到动作的可微映射。区别只在于 SAC 的动作函数多了一个独立噪声项 σ θ ( s ) ⋅ ϵ \sigma_{\theta}(s)\cdot \epsilon σθ(s)⋅ϵ这是探索的来源。
反向传播时,梯度会沿着以下路径流畅传递:
∇ θ Q ( s , a ) = ∇ a Q ( s , a ) ⋅ ∇ θ a = ∇ a Q ( s , a ) ⋅ ∇ θ ( μ θ + σ θ ⋅ ϵ ) \nabla_{\theta}Q(s,a)=\nabla_a Q(s,a)\cdot \nabla_{\theta}a=\nabla_a Q(s,a)\cdot \nabla_{\theta}(\mu_{\theta}+\sigma_{\theta}\cdot \epsilon) ∇θQ(s,a)=∇aQ(s,a)⋅∇θa=∇aQ(s,a)⋅∇θ(μθ+σθ⋅ϵ)
Critic 的 Q 值梯度 ∇ a Q ( s , a ) \nabla_a Q(s,a) ∇aQ(s,a) 告诉 Actor"动作应该往哪个方向改",链式法则将这个方向转化为 Actor 参数 μ θ \mu_{\theta} μθ和 σ θ \sigma_{\theta} σθ的更新方向。 整个过程在 actor_loss.backward() 中自动完成。
F.softplus 是 PyTorch 中一个将任意实数平滑地映射为正数的激活函数
数学公式:
s o f t p l u s ( x ) = log ( 1 + e x ) softplus(x)=\log(1+e^{x}) softplus(x)=log(1+ex)
这个公式保证了无论你喂给它多大的负数,它的输出永远是正的;而随着输入变大,它的输出会越来越接近 x 本身(近似线性)。
3、SAC的主框架
python
class SACContinous:
def __init__(self,state_dim,hidden_dim,action_dim,action_bound,actor_lr,critic_lr,alpha_lr,target_entropy,tau,gamma,device):
self.actor=policyNetContinuous(state_dim,hidden_dim,action_dim,action_bound).to(device)
#用两个Q函数主要是避免估值过高,选两个取最小的
self.critic_1=QValueNetContinous(state_dim,hidden_dim,action_dim).to(device)
self.critic_2=QValueNetContinous(state_dim,hidden_dim,action_dim).to(device)
self.target_critic_1=QValueNetContinous(state_dim,hidden_dim,action_dim).to(device)
self.target_critic_2= QValueNetContinous(state_dim,hidden_dim,action_dim).to(device)
#令目标Q网络的初始参数和Q网络一样
self.target_critic_1.load_state_dict(self.critic_1.state_dict())
self.target_critic_2.load_state_dict(self.critic_2.state_dict())
#给需要神经网络更新的网络优化器
self.actor_optimizer=torch.optim.Adam(self.actor.parameters(),lr=actor_lr)
self.critic_1_optimizer=torch.optim.Adam(self.critic_1.parameters(),lr=critic_lr)
self.critic_2_optimizer=torch.optim.Adam(self.critic_2.parameters(),lr=critic_lr)
#alpha操作:1、初始化2、明确求梯度3、指定优化器(使用alpha的log值,可以使训练结果比较稳定)
self.log_alpha=torch.tensor(np.log(0.01),dtype=torch.float32)
self.log_alpha.requires_grad=True
self.log_alpha_optimizer=torch.optim.Adam([self.log_alpha],lr=alpha_lr)
#目标熵的大小
self.target_entropy=target_entropy
self.tau=tau
self.gamma=gamma
self.tau=tau
self.device=device
def take_action(self,state):
state=torch.tensor([state],dtype=torch.float32).to(self.device)
action=self.actor(state)[0]
return [action.item()]
#计算目标Q值
def calc_target(self,rewards,next_states,dones):
next_actions,log_prob=self.actor(next_states)
entropy=-log_prob
q1_value=self.target_critic_1(next_states,next_actions)
q2_value=self.target_critic_2(next_states,next_actions)
next_value=torch.min(q1_value,q2_value)+self.log_alpha.exp()*entropy
td_targets=rewards+self.gamma*next_value*(1-dones)
return td_targets
# 缓更新目标网络
def soft_update(self,net,target_net):
for param_target,param in zip(target_net.parameters(),net.parameters()):
param_target.data.copy_(param_target.data * (1.0 - self.tau) +
param.data * self.tau)
def update(self,transition_dict):
states = torch.tensor(transition_dict['states'],
dtype=torch.float).to(self.device)
actions = torch.tensor(transition_dict['actions'],
dtype=torch.float).view(-1, 1).to(self.device)
rewards = torch.tensor(transition_dict['rewards'],
dtype=torch.float).view(-1, 1).to(self.device)
next_states = torch.tensor(transition_dict['next_states'],
dtype=torch.float).to(self.device)
dones = torch.tensor(transition_dict['dones'],
dtype=torch.float).view(-1, 1).to(self.device)
# 和之前章节一样,对倒立摆环境的奖励进行重塑以便训练
rewards = (rewards + 8.0) / 8.0
#更新两个Q网络
td_target=self.calc_target(rewards,next_states,dones)
critic_1_loss=torch.mean(F.mse_loss(self.critic_1(states,actions),td_target))
critic_2_loss=torch.mean(F.mse_loss(self.critic_2(states,actions),td_target))
self.critic_1_optimizer.zero_grad()
critic_1_loss.backward()
self.critic_1_optimizer.step()
self.critic_2_optimizer.zero_grad()
critic_2_loss.backward()
self.critic_2_optimizer.step()
#更新策略网络
new_actions,log_probs=self.actor(states)
entropy=-log_probs
q1_value=self.critic_1(states,new_actions)
q2_value=self.critic_2(states,new_actions)
actor_loss=torch.mean(-entropy * self.log_alpha.exp()-torch.min(q1_value,q2_value))
self.actor_optimizer.zero_grad()
actor_loss.backward()
self.actor_optimizer.step()
#更新alpha值
alpha_loss=torch.mean((entropy-self.target_entropy).detach()*self.log_alpha.exp())
self.log_alpha_optimizer.zero_grad()
alpha_loss.backward()
self.log_alpha_optimizer.step()
self.soft_update(self.critic_1,self.target_critic_1)
self.soft_update(self.critic_2,self.target_critic_2)
α \alpha α在代码中用 α = e ln ( α ) \alpha=e^{\ln(\alpha)} α=eln(α)表示的原因:
1、保证 α \alpha α永远为正
温度系数 α 的物理意义是"探索的强度",它必须永远是一个正数。如果直接用一个无约束的神经网络参数去存 α,优化器很可能在某个步骤把它更新成一个负数。一个负的温度系数会彻底摧毁 SAC 的数学基础------熵项的符号会反转,导致优化目标完全错误。
因此,我们需要一种无约束的参数化来隐式地保证 α > 0。设 α = exp(log_alpha)。现在,log_alpha 可以是任何实数(负无穷到正无穷),但 α 永远大于 0。无论优化器怎么更新 log_alpha,你都不用担心 α 会越界变成负数。
2、平滑梯度:匹配更新尺度
这是数值稳定的核心。假设你想把 α 从一个很小的值(比如 0.01)开始调节。如果直接优化 α,损失函数对 α 的梯度是线性的。在参数空间的一个固定步长,对应着 α 本身的一个绝对值变化。当 α 很小时,这个绝对值变化可能相对很大,导致它剧烈震荡甚至跳到负值。
如果优化 log_alpha,根据链式法则,损失对 log_alpha 的梯度会被 α 本身的尺度所缩放。这意味着,当 α 很小时,参数空间里的梯度也相应变小,自动产生更小的步长;当 α 很大时,梯度变大,步长也更大。这种自动的尺度调节,让 α 在一个很宽的动态范围(比如 0.001 到 100)内都能平稳更新,不会因为参数太小而卡死,也不会因为参数太大而剧烈震荡。
数学推导如下:
设 L 为损失函数(即 alpha_loss)。我们优化的参数是 log_alpha,而真正的温度系数α=exp(log_alpha)。
根据链式法则:
∂ L ∂ log alpha = ∂ L ∂ α ⋅ ∂ α ∂ log alpha = ∂ L ∂ α ⋅ e log alpha = ∂ L ∂ α ⋅ α \frac{\partial L}{\partial \text{log}\text{ }\text{alpha}}= \frac{\partial L}{\partial \alpha} \cdot \frac{\partial \alpha}{\partial \text{log}\text{ }\text{alpha}}= \frac{\partial L}{\partial \alpha} \cdot e^{\text{log}\text{ }\text{alpha}}= \frac{\partial L}{\partial \alpha} \cdot \alpha ∂log alpha∂L=∂α∂L⋅∂log alpha∂α=∂α∂L⋅elog alpha=∂α∂L⋅α
关键就在这个 ⋅α 上。这意味着,在参数空间里,优化器实际看到的梯度 ∂ L ∂ log alpha \frac{\partial L}{\partial \text{log}\text{ }\text{alpha}} ∂log alpha∂L与 α \alpha α本身成正比。
因此:
- 当 α \alpha α较大时,参数更新步长更大。
- 当 α \alpha α较小时,参数更新步长更小。