2.深度强化学习------SAC(Soft Actor-Critic)算法资料+原理整理

SAC是深度强化学习中对于连续动作控制的又一经典。近期简单的看了一下SAC算法的思想,目前尚未深入研究,先把当前的资料整理一波,便于进一步学习。后续再作进一步更新

文章地址:
Soft Actor-Critic: Off-Policy Maximum Entropy Deep Reinforcement Learning with a Stochastic Actor

一. SAC资料

可重点看前三篇

SAC原理讲解BLOG①:这篇blog非常详细的讲解了SAC的实现过程,包括理论推导过程,看懂这篇blog基本就差不多明白SAC了。
SAC原理讲解BLOG②:这篇blog对于SAC的算法思路做了一个很好的梳理,不涉及理论推导。
SAC原理讲解BLOG③:这篇blog与第二篇类似,同时还附带了SAC的pytorch实现。
SAC原理讲解BLOG④
SAC原理讲解BLOG⑤
Spinningup资料

1.1. SAC中的trick

信息熵的概念:暂时把它可理解为一个概率

1.2. 基于PyTorch的SAC的实现

Demo①
Demo②


2020-09-11更新

二、SAC原理介绍
2.1 背景

SAC是off-policy的,且使用的是stochastic policy,SAC的一个主要特征是entropy regularization 熵正则化. policy可以被训练去最大程度上权衡期望回报和熵。是策略随机性的一个衡量。 增加熵也就说明策略的随机性增强,所以会增加更多的探索,从而可以加快后续的学习速度。可以防止policy过早的收敛到局部最优值。上面已经提到,SAC的一个重要特征就是熵正则化,那么首先我们需要了解熵正则化在强化学习中的相关内容。

2.2 entropy-regularized RL

entropy可以理解为是一个值,一个什么样的值呢?用来衡量一个随机变量的随机性有多强。举个例子,如果对一个硬币出现正反面的变量进行加权,如果总是出现正面,那么这个变量的熵就很小;反之,如果出现正反面的加权值都接近0.5,那么就说明这个变量的熵很大。
假设 x ∼ P x\sim P xP,P是一个分布,那么x的熵H的计算方式为:
H ( P ) = E x ∼ P [ − log ⁡ P ( x ) ] H(P)=\underset{x\sim P}E\lbrack-\log P(x)\rbrack H(P)=xPE[logP(x)]
在entropy-regulaized的RL中,agent每个step都会获得一个与当前time step对应的policy的熵成比例的一个奖励。这样一来,强化学习中的奖励就变为了:
π ∗ = arg ⁡ max ⁡ π E τ ∼ π [ ∑ t = 0 ∞ γ t ( R ( s t , a t , s t + 1 ) + α H ( π ( ⋅ ∣ s t ) ) ) ] \pi^{*}=\arg \max _{\pi} \underset{\tau \sim \pi}{\mathrm{E}}\left[\sum_{t=0}^{\infty} \gamma^{t}\left(R\left(s_{t}, a_{t}, s_{t+1}\right)+\alpha H\left(\pi\left(\cdot \mid s_{t}\right)\right)\right)\right] π=argπmaxτπE[t=0γt(R(st,at,st+1)+αH(π(st)))]
其中 α \alpha α就是熵正则化系数。
其值函数对应为:
V π ( s ) = E τ ∼ π [ ∑ t = 0 ∞ γ t ( R ( s t , a t , s t + 1 ) + α H ( π ( ⋅ ∣ s t ) ) ) ∣ s 0 = s ] V^{\pi}(s)=\underset{\tau \sim \pi}{\mathrm{E}}\left[\sum_{t=0}^{\infty} \gamma^{t}\left(R\left(s_{t}, a_{t}, s_{t+1}\right)+\alpha H\left(\pi\left(\cdot \mid s_{t}\right)\right)\right) \mid s_{0}=s\right] Vπ(s)=τπE[t=0γt(R(st,at,st+1)+αH(π(st)))s0=s]
Q函数对应为:
Q π ( s , a ) = E τ ∼ π [ ∑ t = 0 ∞ γ t R ( s t , a t , s t + 1 ) + α ∑ t = 1 ∞ γ t H ( π ( ⋅ ∣ s t ) ) ∣ s 0 = s , a 0 = a ] Q^{\pi}(s, a)=\underset{\tau \sim \pi}{\mathrm{E}}\left[\sum_{t=0}^{\infty} \gamma^{t} R\left(s_{t}, a_{t}, s_{t+1}\right)+\alpha \sum_{t=1}^{\infty} \gamma^{t} H\left(\pi\left(\cdot \mid s_{t}\right)\right) \mid s_{0}=s, a_{0}=a\right] Qπ(s,a)=τπE[t=0γtR(st,at,st+1)+αt=1γtH(π(st))s0=s,a0=a]
V π V^{\pi} Vπ and Q π Q^{\pi} Qπ 的关系:
V π ( s ) = E a ∼ π [ Q π ( s , a ) ] + α H ( π ( ⋅ ∣ s ) ) V^{\pi}(s)=\underset{a \sim \pi}{\mathrm{E}}\left[Q^{\pi}(s, a)\right]+\alpha H(\pi(\cdot \mid s)) Vπ(s)=aπE[Qπ(s,a)]+αH(π(s))
Q π Q^{\pi} Qπ 的贝尔曼方程为:
Q π ( s , a ) = E s ′ ∼ P a ′ ∼ π [ R ( s , a , s ′ ) + γ ( Q π ( s ′ , a ′ ) + α H ( π ( ⋅ ∣ s ′ ) ) ) ] = E s ′ ∼ P [ R ( s , a , s ′ ) + γ V π ( s ′ ) ] \begin{aligned} Q^{\pi}(s, a) &=\underset{s^{\prime} \sim P \atop a^{\prime} \sim \pi}{\mathrm{E}}\left[R\left(s, a, s^{\prime}\right)+\gamma\left(Q^{\pi}\left(s^{\prime}, a^{\prime}\right)+\alpha H\left(\pi\left(\cdot \mid s^{\prime}\right)\right)\right)\right] \\ &=\underset{s^{\prime} \sim P}{\mathrm{E}}\left[R\left(s, a, s^{\prime}\right)+\gamma V^{\pi}\left(s^{\prime}\right)\right] \end{aligned} Qπ(s,a)=aπsPE[R(s,a,s)+γ(Qπ(s,a)+αH(π(s)))]=sPE[R(s,a,s)+γVπ(s)]
SAC会同时学习策略(一个策略网络)+两个Q函数(两个Q网络)。目前对于SAC来讲有两种实现方式,一种是使用一个固定的熵正则化系数 α \alpha α,另一种是在训练过程中自动求解熵正则化系数。

摘自spinningup
SAC中Q函数的学习与TD3中的Q学习相似,但也有些不同。
相同之处:
①Q函数的学习都是通过最小化MSBE来实现(也就是两个Q网络的loss都是最小化MSBE)
②使用了clipped double-Q trick,两个Q函数共享一个目标,计算时使用两个Q函数中给出的最小Q值得那个:
y ( r , s ′ , d ) = r + γ ( 1 − d ) min ⁡ i = 1 , 2 Q ϕ i ,  targ  ( s ′ , a ′ ( s ′ ) ) , y\left(r, s^{\prime}, d\right)=r+\gamma(1-d) \min _{i=1,2} Q_{\phi_{i, \text { targ }}}\left(s^{\prime}, a^{\prime}\left(s^{\prime}\right)\right), y(r,s,d)=r+γ(1d)i=1,2minQϕi, targ (s,a(s)),
L ( ϕ 1 , D ) = E ( s , a , r , s ′ , d ) ∼ D ( Q ϕ 1 ( s , a ) − y ( r , s ′ , d ) ) 2 L ( ϕ 2 , D ) = E ( s , a , r , s ′ , d ) ∼ D ( Q ϕ 2 ( s , a ) − y ( r , s ′ , d ) ) 2 \begin{array}{l} L\left(\phi_{1}, \mathcal{D}\right)=E_{\left(s, a, r, s^{\prime}, d\right) \sim \mathcal{D}}\left(Q_{\phi_{1}}(s, a)-y\left(r, s^{\prime}, d\right)\right)^{2} \\ L\left(\phi_{2}, \mathcal{D}\right)=E_{\left(s, a, r, s^{\prime}, d\right) \sim \mathcal{D}}\left(Q_{\phi_{2}}(s, a)-y\left(r, s^{\prime}, d\right)\right)^{2} \end{array} L(ϕ1,D)=E(s,a,r,s,d)D(Qϕ1(s,a)y(r,s,d))2L(ϕ2,D)=E(s,a,r,s,d)D(Qϕ2(s,a)y(r,s,d))2
对目标使用较小的Q值,逐步回归该值,可以避免Q函数的过高估计。
不同之处:
①SAC中Q函数的学习加入了熵的概念,在最大化回报的同时也要最大化熵
②与TD3不同,SAC计算目标所使用的下一个状态来自于当前策略而不是目标策略。
③与TD3不同,SAC中并没有明确的目标策略平滑处理,TD3训练得到的是确定性策略,因此需要通过对下一状态添加随机噪声来实现平滑。SAC训练得到的是一个随机策略,其随机性所造成的噪声已经足以产生类似的效果了。

再来看一下熵正则化的贡献,重写基于熵正则化的RL的Q函数贝尔曼方程:
Q π ( s , a ) = E s ′ ∼ P a ′ ∼ π [ R ( s , a , s ′ ) + γ ( Q π ( s ′ , a ′ ) + α H ( π ( ⋅ ∣ s ′ ) ) ) ] = E s ′ ∼ P a ′ ∼ π [ R ( s , a , s ′ ) + γ ( Q π ( s ′ , a ′ ) − α log ⁡ π ( a ′ ∣ s ′ ) ) ] \begin{aligned} Q^{\pi}(s, a) &=\underset{s^{\prime} \sim P \atop a^{\prime} \sim \pi}{\mathrm{E}}\left[R\left(s, a, s^{\prime}\right)+\gamma\left(Q^{\pi}\left(s^{\prime}, a^{\prime}\right)+\alpha H\left(\pi\left(\cdot \mid s^{\prime}\right)\right)\right)\right] \\ &=\underset{s^{\prime} \sim P \atop a^{\prime} \sim \pi}{\mathrm{E}}\left[R\left(s, a, s^{\prime}\right)+\gamma\left(Q^{\pi}\left(s^{\prime}, a^{\prime}\right)-\alpha \log \pi\left(a^{\prime} \mid s^{\prime}\right)\right)\right] \end{aligned} Qπ(s,a)=aπsPE[R(s,a,s)+γ(Qπ(s,a)+αH(π(s)))]=aπsPE[R(s,a,s)+γ(Qπ(s,a)αlogπ(as))]
求期望是对下一个状态(来自于replay buffer)和下一个动作(来自于当前策略而不是replay buffer)求期望,(这里的动作需要强调指出,不是从replay buffer采样得到的,而是基于当前策略计算得到的新的action),因此可以使用样本进行近似估算Q值:
Q π ( s , a ) ≈ r + γ ( Q π ( s ′ , a ~ ′ ) − α log ⁡ π ( a ~ ′ ∣ s ′ ) ) , a ~ ′ ∼ π ( ⋅ ∣ s ′ ) Q^{\pi}(s, a) \approx r+\gamma\left(Q^{\pi}\left(s^{\prime}, \tilde{a}^{\prime}\right)-\alpha \log \pi\left(\tilde{a}^{\prime} \mid s^{\prime}\right)\right), \quad \tilde{a}^{\prime} \sim \pi\left(\cdot \mid s^{\prime}\right) Qπ(s,a)r+γ(Qπ(s,a~)αlogπ(a~s)),a~π(s)
SAC中Q网络的损失函数(这里用到了clipped double-Q trick):
L ( ϕ i , D ) = E ⁡ ( s , a , r , s ′ , d ) ∼ D [ ( Q ϕ i ( s , a ) − y ( r , s ′ , d ) ) 2 ] L\left(\phi_{i}, \mathcal{D}\right)=\operatorname{E}_{\left(s, a, r, s^{\prime}, d\right) \sim \mathcal{D}}\left[\left(Q_{\phi_{i}}(s, a)-y\left(r, s^{\prime}, d\right)\right)^{2}\right] L(ϕi,D)=E(s,a,r,s,d)D[(Qϕi(s,a)y(r,s,d))2]
目标是:
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\left(r, s^{\prime}, d\right)=r+\gamma(1-d)\left(\min _{j=1,2} Q_{\phi_{\mathrm{targ}, j}}\left(s^{\prime}, \tilde{a}^{\prime}\right)-\alpha \log \pi_{\theta}\left(\tilde{a}^{\prime} \mid s^{\prime}\right)\right), \quad \tilde{a}^{\prime} \sim \pi_{\theta}\left(\cdot \mid s^{\prime}\right) y(r,s,d)=r+γ(1d)(j=1,2minQϕtarg,j(s,a~)αlogπθ(a~s)),a~πθ(s)
学习策略:最大化 V π ( s ) , V^{\pi}(s), Vπ(s), 在每个state熵最大化期望回报与期望entropy的和。
V π ( s ) = E a ∼ π [ Q π ( s , a ) ] + α H ( π ( ⋅ ∣ s ) ) = E a ∼ π [ Q π ( s , a ) − α log ⁡ π ( a ∣ s ) ] \begin{aligned} V^{\pi}(s) &=\underset{a \sim \pi}{\mathrm{E}}\left[Q^{\pi}(s, a)\right]+\alpha H(\pi(\cdot \mid s)) \\ &=\underset{a \sim \pi}{\mathrm{E}}\left[Q^{\pi}(s, a)-\alpha \log \pi(a \mid s)\right] \end{aligned} Vπ(s)=aπE[Qπ(s,a)]+αH(π(s))=aπE[Qπ(s,a)αlogπ(as)]
优化策略的方法使用:reparameterization trick,
a ~ θ ( s , ξ ) = tanh ⁡ ( μ θ ( s ) + σ θ ( s ) ⊙ ξ ) , ξ ∼ N ( 0 , I ) \tilde{a}_{\theta}(s, \xi)=\tanh \left(\mu_{\theta}(s)+\sigma_{\theta}(s) \odot \xi\right), \quad \xi \sim \mathcal{N}(0, I) a~θ(s,ξ)=tanh(μθ(s)+σθ(s)ξ),ξN(0,I)
通过计算状态、策略参数以及独立噪声的确定性函数,从中抽取样本。
reparameterization trick使得我们可以将对动作的期望(这里有一个痛点:动作分布取决于策略参数)重写为对噪声的期望(消除痛点:此时动作分布不依赖于策略参数)
E a ∼ π θ [ Q π θ ( s , a ) − α log ⁡ π θ ( a ∣ s ) ] = E ξ ∼ N [ Q π θ ( s , a ~ θ ( s , ξ ) ) − α log ⁡ π θ ( a ~ θ ( s , ξ ) ∣ s ) ] \underset{a \sim \pi_{\theta}}{\mathrm{E}}\left[Q^{\pi_{\theta}}(s, a)-\alpha \log \pi_{\theta}(a \mid s)\right]=\underset{\xi \sim \mathcal{N}}{\mathrm{E}}\left[Q^{\pi_{\theta}}\left(s, \tilde{a}_{\theta}(s, \xi)\right)-\alpha \log \pi_{\theta}\left(\tilde{a}_{\theta}(s, \xi) \mid s\right)\right] aπθE[Qπθ(s,a)αlogπθ(as)]=ξNE[Qπθ(s,a~θ(s,ξ))αlogπθ(a~θ(s,ξ)s)]
policy的loss:
max ⁡ θ E ξ ∼ N [ min ⁡ j = 1 , 2 Q ϕ j ( s , a ~ θ ( s , ξ ) ) − α log ⁡ π θ ( a ~ θ ( s , ξ ) ∣ s ) ] \max _{\theta} \underset{\xi \sim \mathcal{N}}{\mathrm{E}}\left[\min _{j=1,2} Q_{\phi_{j}}\left(s, \tilde{a}_{\theta}(s, \xi)\right)-\alpha \log \pi_{\theta}\left(\tilde{a}_{\theta}(s, \xi) \mid s\right)\right] θmaxξNE[j=1,2minQϕj(s,a~θ(s,ξ))αlogπθ(a~θ(s,ξ)s)]
伪代码如下(在连续任务下):
在这里插入图片描述

SACSoft Actor-Critic算法是一种基于深度强化学习算法,它可以用于连续动作空间的强化学习问题。SAC算法是由Tuomas Haarnoja等人于2018年提出的,其主要思想是在强化学习的过程中引入熵的概念,使得智能体的策略更加多样化和探索性。 SAC算法的基本原理是通过学习一个策略网络,使得智能体可以在环境中获得最大的奖励。SAC算法的策略网络由两个部分组成:一个是Actor网络,用于生成动作;另一个是Critic网络,用于估计当前状态的价值。 SAC算法的损失函数包括三个部分:策略损失、Q值损失和熵损失。策略损失用于优化Actor网络,Q值损失用于优化Critic网络,熵损失用于增加策略的探索性。 SAC算法的伪代码如下: 1. 初始化Actor网络和Critic网络的参数; 2. 初始化目标网络的参数; 3. 初始化策略优化器和Critic优化器的参数; 4. 重复执行以下步骤: a. 从环境中采样一批数据; b. 计算动作的熵; c. 计算Q值和策略损失; d. 计算熵损失; e. 更新Actor网络和Critic网络的参数; f. 更新目标网络的参数; 5. 直到达到停止条件。 SAC算法的代码实现可以使用Python和TensorFlow等工具完成。以下是SAC算法的Python代码示例: ``` import tensorflow as tf import numpy as np class SAC: def __init__(self, obs_dim, act_dim, hidden_size, alpha, gamma, tau): self.obs_dim = obs_dim self.act_dim = act_dim self.hidden_size = hidden_size self.alpha = alpha self.gamma = gamma self.tau = tau # 创建Actor网络 self.actor = self._create_actor_network() self.target_actor = self._create_actor_network() self.target_actor.set_weights(self.actor.get_weights()) # 创建Critic网络 self.critic1 = self._create_critic_network() self.critic2 = self._create_critic_network() self.target_critic1 = self._create_critic_network() self.target_critic2 = self._create_critic_network() self.target_critic1.set_weights(self.critic1.get_weights()) self.target_critic2.set_weights(self.critic2.get_weights()) # 创建优化器 self.actor_optimizer = tf.keras.optimizers.Adam(self.alpha) self.critic_optimizer1 = tf.keras.optimizers.Adam(self.alpha) self.critic_optimizer2 = tf.keras.optimizers.Adam(self.alpha) # 创建Actor网络 def _create_actor_network(self): inputs = tf.keras.layers.Input(shape=(self.obs_dim,)) x = tf.keras.layers.Dense(self.hidden_size, activation='relu')(inputs) x = tf.keras.layers.Dense(self.hidden_size, activation='relu')(x) outputs = tf.keras.layers.Dense(self.act_dim, activation='tanh')(x) model = tf.keras.Model(inputs=inputs, outputs=outputs) return model # 创建Critic网络 def _create_critic_network(self): inputs = tf.keras.layers.Input(shape=(self.obs_dim + self.act_dim,)) x = tf.keras.layers.Dense(self.hidden_size, activation='relu')(inputs) x = tf.keras.layers.Dense(self.hidden_size, activation='relu')(x) outputs = tf.keras.layers.Dense(1)(x) model = tf.keras.Model(inputs=inputs, outputs=outputs) return model # 选择动作 def select_action(self, obs): action = self.actor(obs)[0] return action.numpy() # 更新网络参数 def update(self, obs, action, reward, next_obs, done): with tf.GradientTape(persistent=True) as tape: # 计算动作的熵 action_prob = self.actor(obs) log_prob = tf.math.log(action_prob + 1e-10) entropy = -tf.reduce_sum(action_prob * log_prob, axis=-1) # 计算Q值损失 target_action_prob = self.target_actor(next_obs) target_q1 = self.target_critic1(tf.concat([next_obs, target_action_prob], axis=-1)) target_q2 = self.target_critic2(tf.concat([next_obs, target_action_prob], axis=-1)) target_q = tf.minimum(target_q1, target_q2) target_q = reward + self.gamma * (1 - done) * target_q q1 = self.critic1(tf.concat([obs, action], axis=-1)) q2 = self.critic2(tf.concat([obs, action], axis=-1)) critic_loss1 = tf.reduce_mean((target_q - q1) ** 2) critic_loss2 = tf.reduce_mean((target_q - q2) ** 2) # 计算策略损失 action_prob = self.actor(obs) q1 = self.critic1(tf.concat([obs, action_prob], axis=-1)) q2 = self.critic2(tf.concat([obs, action_prob], axis=-1)) q = tf.minimum(q1, q2) policy_loss = tf.reduce_mean(entropy * self.alpha - q) # 计算熵损失 entropy_loss = tf.reduce_mean(-entropy) # 更新Actor网络 actor_grads = tape.gradient(policy_loss, self.actor.trainable_variables) self.actor_optimizer.apply_gradients(zip(actor_grads, self.actor.trainable_variables)) # 更新Critic网络 critic_grads1 = tape.gradient(critic_loss1, self.critic1.trainable_variables) self.critic_optimizer1.apply_gradients(zip(critic_grads1, self.critic1.trainable_variables)) critic_grads2 = tape.gradient(critic_loss2, self.critic2.trainable_variables) self.critic_optimizer2.apply_gradients(zip(critic_grads2, self.critic2.trainable_variables)) # 更新目标网络 self._update_target_network(self.target_actor, self.actor, self.tau) self._update_target_network(self.target_critic1, self.critic1, self.tau) self._update_target_network(self.target_critic2, self.critic2, self.tau) return critic_loss1.numpy(), critic_loss2.numpy(), policy_loss.numpy(), entropy_loss.numpy() # 更新目标网络参数 def _update_target_network(self, target_network, network, tau): target_weights = target_network.get_weights() network_weights = network.get_weights() for i in range(len(target_weights)): target_weights[i] = tau * network_weights[i] + (1 - tau) * target_weights[i] target_network.set_weights(target_weights) ``` 以上就是SAC算法原理及Python代码实现。需要注意的是,SAC算法的实现需要根据具体的问题进行调整和修改。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值