【强化学习】18 —— SAC( Soft Actor-Critic)

news2024/12/27 12:53:06

文章目录

  • 前言
  • 最大熵强化学习
    • 不同动作空间下的最大熵强化学习
    • 基于能量的模型
    • 软价值函数
    • 最大熵策略
  • Soft Q-learning
    • Soft Q-Iteration
    • Soft Q-Learning
    • 近似采样与SVGD
    • 伪代码
  • Soft Actor-Critic
    • 伪代码
    • 代码实践
      • 连续动作空间
      • 离散动作空间
  • 参考与推荐

前言

之前的章节提到过在线策略算法的采样效率比较低,我们通常更倾向于使用离线策略算法。然而,虽然 DDPG 是离线策略算法,但是它的训练非常不稳定,收敛性较差,对超参数比较敏感,也难以适应不同的复杂环境。2018 年,一个更加稳定的离线策略算法 Soft Actor-Critic(SAC)被提出。SAC 的前身是 Soft Q-learning,它们都属于最大熵强化学习的范畴。Soft Q-learning 不存在一个显式的策略函数,而是使用一个函数 Q Q Q的波尔兹曼分布,在连续空间下求解非常麻烦。于是 SAC 提出使用一个 Actor 表示策略函数,从而解决这个问题。目前,在无模型的强化学习算法中,SAC 是一个非常高效的算法,它学习一个随机性策略,在不少标准环境中取得了领先的成绩。

最大熵强化学习

熵是策略中随机性的一种度量,设 x x x为随机变量,概率密度函数为 P P P,熵 H H H的计算式为: H ( p ) = E x ∼ p [ − log ⁡ p ( x ) ] H(p)=\mathbb{E}_{x\sim p}[-\log p(x)] H(p)=Exp[logp(x)]在强化学习中,我们可以使用 H ( π ( ⋅ ∣ s ) ) H(\pi(\cdot|s)) H(π(s))来表示策略 π \pi π在状态 s s s下的随机程度。最大熵强化学习(maximum entropy RL)的思想就是除了要最大化累积奖励,还要使得策略更加随机,除此之外还要增加算法的鲁棒性以及提升策略的预训练效果。

传统的RL难以适应于复杂变化的环境。如图所示,通过学习,智能体会倾向于选择上面的路径(因为更短),但若在上方的通道设置一个障碍,智能体则无法通过,且需重新学习下方的路径以达到终点。
在这里插入图片描述

如图3a所示,传统的RL策略类似于一个单峰的高斯分布,而最大熵强化学习则是类似于3b中所示的多峰分布。前者会忽视掉其他较低概率的策略,而后者则是增加了对其他的策略的探索,使之能够适应更复杂的环境。
在这里插入图片描述

不同动作空间下的最大熵强化学习

离散动作空间
在最大熵强化学习中,带有最大熵的目标函数通过增加一个熵项,使得最优策略的熵在每次迭代下达到最大。
π M a x E n t ∗ = arg ⁡ max ⁡ π ∑ t E ( s t , a t ) ∼ ρ π [ r ( s t , a t ) + α H ( π ( ⋅ ∣ s t ) ) ] \begin{aligned}\pi_\mathrm{MaxEnt}^*=&\arg\max_\pi\sum_t\mathbb{E}_{(\mathbf{s}_t,\mathbf{a}_t)\sim\rho_\pi}\left[r(\mathbf{s}_t,\mathbf{a}_t)+\alpha\mathcal{H}(\pi(\cdot|\mathbf{s}_t))\right]\end{aligned} πMaxEnt=argπmaxtE(st,at)ρπ[r(st,at)+αH(π(st))] H ( π ( ⋅ ∣ s t ) ) = − ∑ a t π ( a t ∣ s t ) l o g ( π ( a t ∣ s t ) ) {\mathcal H}\big(\pi(\cdot|s_{t})\big)=-\sum_{a_{t}}\pi(a_{t}|s_{t})\mathrm{log}\big(\pi(a_{t}|s_{t})\big) H(π(st))=atπ(atst)log(π(atst))其中, α \alpha α是一个正则化的系数,用来控制熵的重要程度。熵正则化增加了强化学习算法的探索程度,当 α = 0 \alpha=0 α=0时,最大熵强化学习与传统的 RL算法相同;当 α > 0 \alpha>0 α>0时, α \alpha α越大,探索性就越强,有助于加速后续的策略学习,并减少策略陷入较差的局部最优的可能性,鲁棒性越好。

⚠️You Should Know
请注意,优化目标与玻尔兹曼探索(Sallans & Hinton, 2004)和PGQ (O’Donoghue et al., 2016)的行为有质的不同,后者基于贪婪策略在当前时间步长最大化熵,但没有明确会优化未来可能到达高熵状态的策略。这个不同之处很重要,因为最大熵优化目标会最大化策略 π \pi π的整个轨迹的熵。

连续动作空间
a = μ ( ⋅ ∣ s t ) + ϵ a=\mu(\cdot|s_t)+\epsilon a=μ(st)+ϵ
对于连续动作空间,通常会加上噪声项 ϵ \epsilon ϵ,以增强算法的探索能力。 ϵ \epsilon ϵ通常是一个高斯分布。但是这种方法会有难以学到多样化的策略 μ \mu μ以及因为 ϵ \epsilon ϵ服从预定义的分布,而难以泛化到多模态的复杂情况等问题。

基于能量的模型

优化最大熵目标为我们提供了一个训练随机策略的框架,但是其中的策略还未进行表示。以往的工作中有采用多项分布或者高斯分布进行表示,然而,如果我们想使用一类通用的分布来表示复杂的、多模态的行为,我们可以选择使用这种形式的基于能量的通用策略: π ( a t ∣ s t ) ∝ exp ⁡ ( − E ( s t , a t ) ) , \pi(\mathbf{a}_t|\mathbf{s}_t)\propto\exp\left(-\mathcal{E}(\mathbf{s}_t,\mathbf{a}_t)\right), π(atst)exp(E(st,at)),

其中, E \mathcal{E} E代表能量函数,可用深度神经网络进行拟合。接下来简要介绍一下基于能量的模型。

基于能量的模型(Energy-Based Model, EBM)的目标是最大化打分函数 ϕ ( x ) \phi(x) ϕ(x)的期望,同时增加探索性:
max ⁡ p E x ∼ p [ ϕ ( x ) ] + α H ( p ) \max_p \mathbb{E}_{x\sim p}[\phi(x)]+\alpha\mathcal{H}(p) pmaxExp[ϕ(x)]+αH(p)

其中, α \alpha α越大,越鼓励探索。最终求解出的分布符合波兹曼分布(Boltzmann distribution) , p ∗ ( x ) ∝ e − ϕ ( x ) / α ,p^{*}(x)\propto e^{-\phi(x)/\alpha} p(x)eϕ(x)/α(其中 α \alpha α又可以被称为温度系数)。将目标函数除以 α \alpha α,因为 H ( p ) = E x ∼ p [ − log ⁡ p ( x ) ] H(p)=\mathbb{E}_{x\sim p}[-\log p(x)] H(p)=Exp[logp(x)],将两项合并,最终最大化目标函数等价于最小化负的 p p p p ϕ p_\phi pϕ的KL散度。
E x ∼ p [ ϕ ( x ) ] α + H ( p ) = E x ∼ p [ ϕ ( x ) α − log ⁡ p ( x ) ] = − K L ( p ∣ ∣ p ϕ ) \frac{\mathbb{E}_{x\sim p}[\phi(x)]}{\alpha}+\mathcal{H}(p)=\mathbb{E}_{x\sim p}\left[\frac{\phi(x)}{\alpha}-\log p(x)\right]=-KL\big(p||p_{\phi}\big) αExp[ϕ(x)]+H(p)=Exp[αϕ(x)logp(x)]=KL(p∣∣pϕ)

波兹曼分布(Boltzmann distribution) p ϕ ( x ) = e ϕ ( x ) / α z = e − ε ( x ) z p_{\phi}(x)=\frac{e^{\phi(x)/\alpha}}{z}=\frac{e^{-\varepsilon(x)}}{z} pϕ(x)=zeϕ(x)/α=zeε(x)的能量函数为 ε ( x ) = − ϕ ( x ) α \varepsilon(x)=-\frac{\phi(x)}{\alpha} ε(x)=αϕ(x),能量越高越不确定。

在最大熵强化学习中,策略优化目标为(即,令 E ( s t , a t ) = − 1 α Q s o f t ( s t , a t ) \mathcal{E}(s_t, a_t) =-\frac{1}{\alpha}Q_{soft}(s_t, a_t) E(st,at)=α1Qsoft(st,at)):
max ⁡ a E x ∼ π ( ⋅ ∣ s ) [ Q ( s , a ) ] + α H ( π ( ⋅ ∣ s ) ) \max_a \mathbb{E}_{x\sim {\pi(\cdot|s)}}[Q(s,a)]+\alpha\mathcal{H}(\pi(\cdot|s)) amaxExπ(s)[Q(s,a)]+αH(π(s))因此,最大熵强化学习不会像传统RL那样直接选取最大的Q函数值,而是将探索性考虑进来,从而避免陷入局部最小值。在实践应用中,最大熵的优点包括:1)它鼓励更广泛的探索,同时放弃多余的样本;2)该策略可以得到多个近似最优动作;3)该方法可以有效地提高训练速度,是优化传统RL目标函数的一种新方法。

软价值函数

考虑到熵的因素,我们可以对价值函数做一些变更:
V π ( s ) = E τ ∼ π ∑ t = 0 ∞ γ t ( R ( s t , a t , s t + 1 ) + α H ( π ( ⋅ ∣ s t ) ) ) ∣ s 0 = s V^{\pi}(s) = E_{\tau \sim \pi}{ \left. \sum_{t=0}^{\infty} \gamma^t \bigg( R(s_t, a_t, s_{t+1}) + \alpha H\left(\pi(\cdot|s_t)\right) \bigg) \right| s_0 = s} Vπ(s)=Eτπt=0γt(R(st,at,st+1)+αH(π(st))) s0=s 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) = E_{\tau \sim \pi}{ \left. \sum_{t=0}^{\infty} \gamma^t R(s_t, a_t, s_{t+1}) + \alpha \sum_{\textcolor{red}{t=1}}^{\infty} \gamma^t H\left(\pi(\cdot|s_t)\right)\right| s_0 = s, a_0 = a} Qπ(s,a)=Eτπt=0γtR(st,at,st+1)+αt=1γtH(π(st)) s0=s,a0=a

相应的bellman方程为:
V π ( s ) = E a ∼ π Q π ( s , a ) + α H ( π ( ⋅ ∣ s ) ) V^{\pi}(s) = E_{a \sim \pi}{Q^{\pi}(s,a)} + \alpha H\left(\pi(\cdot|s)\right) Vπ(s)=EaπQπ(s,a)+αH(π(s)) 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) =& E_{s' \sim P ,a' \sim \pi}{[R(s,a,s') + \gamma\left(Q^{\pi}(s',a') + \alpha H\left(\pi(\cdot|s')\right) \right)}] \\=& E_{s' \sim P}{[R(s,a,s') + \gamma V^{\pi}(s')]}.\end{aligned} Qπ(s,a)==EsP,aπ[R(s,a,s)+γ(Qπ(s,a)+αH(π(s)))]EsP[R(s,a,s)+γVπ(s)].

最大熵策略

最大熵强化学习的策略目标是 max ⁡ a E x ∼ π ( ⋅ ∣ s ) [ Q s o f t ( s , a ) ] + α H ( π ( ⋅ ∣ s ) ) \max_a \mathbb{E}_{x\sim {\pi(\cdot|s)}}[Q_{soft}(s,a)]+\alpha\mathcal{H}(\pi(\cdot|s)) maxaExπ(s)[Qsoft(s,a)]+αH(π(s)),基于能量模型的形式 ,则可表示为
π M a x l n t ( a ∣ s ) ∝ exp ⁡ ( Q s o f t ( s , a ) / α ) \pi_{\mathrm{Maxlnt}}(a|s)\propto\exp\bigl(Q_{soft}(s,a)/\alpha\bigr) πMaxlnt(as)exp(Qsoft(s,a)/α)
状态值函数的具体形式为:
V s o f t ( s t ) = E a ∼ π [ Q s o f t ( s t , a t ) ] + α H ( π ( ⋅ ∣ s t ) ) V s o f t ( s t ) ← α log ⁡ ∫ exp ⁡ ( 1 α Q s o f t ( s t , a ′ ) ) d a ′ \begin{aligned}V_{soft}(s_t)&=\mathbb{E}_{a\sim\pi}\big[Q_{soft}(s_t,a_t)\big]+\alpha\mathcal{H}\big(\pi(\cdot|s_t)\big)\\V_{soft}(s_t)&\leftarrow\alpha\log\int\exp\left(\frac{1}{\alpha}Q_{soft}(s_t,a')\right)da'\end{aligned} Vsoft(st)Vsoft(st)=Eaπ[Qsoft(st,at)]+αH(π(st))αlogexp(α1Qsoft(st,a))da

基于此的最优策略为:

π M a x l n t ( a ∣ s ) = exp ⁡ ( 1 α ( Q s o f t ∗ ( s t , a t ) − V s o f t ∗ ( s t ) ) ) \pi_{\mathrm{Maxlnt}}(a|s)=\exp\left(\frac{1}{\alpha}\Big(Q_{soft}^{*}(s_{t},a_{t})-V_{soft}^{*}(s_{t})\Big)\right) πMaxlnt(as)=exp(α1(Qsoft(st,at)Vsoft(st)))

⚠️You Should Know
证明过程可参考 Reinforcement Learning with Deep Energy-Based Policies, 2017, arXiv

Soft Q-learning

Soft Q-Iteration

类似于Q-learning,通过对 Q s o f t Q_{soft}^{} Qsoft以及 V s o f t V_{soft}^{} Vsoft的不断迭代,可以使得结果不断向收敛至 Q s o f t ∗ Q_{soft}^{*} Qsoft V s o f t ∗ V_{soft}^{*} Vsoft以及 π → π M a x E n t ∗ \pi\rightarrow\pi_\mathrm{MaxEnt}^* ππMaxEnt Q s o f t ( s t , a t ) ← r t + γ E s t + 1 ∼ p s [ V s o f t ( s t + 1 ) ] , ∀ s t , a t ( 8 ) V s o f t ( s t ) ← α log ⁡ ∫ A exp ⁡ ( 1 α Q s o f t ( s t , a ′ ) ) d a ′ , ∀ s t ( 9 ) \begin{aligned}Q_{\mathrm{soft}}(\mathbf{s}_t,\mathbf{a}_t)\leftarrow r_t+\gamma\mathbb{E}_{\mathbf{s}_{t+1}\sim p_\mathbf{s}}\left[V_{\mathrm{soft}}(\mathbf{s}_{t+1})\right],\forall\mathbf{s}_t,\mathbf{a}_t\quad(8)\\V_{\mathrm{soft}}(\mathbf{s}_t)\leftarrow\alpha\log\int_{\mathcal{A}}\exp\left(\frac{1}{\alpha}Q_{\mathrm{soft}}(\mathbf{s}_t,\mathbf{a}^{\prime})\right)d\mathbf{a}^{\prime},\forall\mathbf{s}_t(9)\end{aligned} Qsoft(st,at)rt+γEst+1ps[Vsoft(st+1)],st,at(8)Vsoft(st)αlogAexp(α1Qsoft(st,a))da,st(9) c o n v e r g e s   t o   Q s o f t ∗   a n d   V s o f t ∗ , r e s p e c t i v e l y . converges ~to ~Q_\mathrm{soft}^* ~and ~V_\mathrm{soft}^*, respectively. converges to Qsoft and Vsoft,respectively.

⚠️You Should Know
收敛性证明过程可参考 Reinforcement Learning with Deep Energy-Based Policies, 2017, arXiv

但是上述软贝尔曼更新(soft Bellman backup)的方式不能精确地在连续或大的状态和动作空间中执行,其次基于能量模型进行采样是比较困难的。

Soft Q-Learning

由于(9)中存在的积分以及存在无限的状态和动作集合,上述过程难以实现,因此,作者使用了随机优化的方法进行描述,同时上述过程变为了一个随机梯度下降更新过程。Soft Q-function可以被建模成关于参数 θ \theta θ的函数 Q s o f t θ ( s t , a t ) Q_{soft}^{\theta}(s_t,a_t) Qsoftθ(st,at)。为了描述上述的随机过程,Soft Q-Learning使用了重要性采样对价值函数进行描述: V soft θ ( s t ) = α log ⁡ E π [ exp ⁡ ( 1 α Q soft θ ( s t , a ′ ) ) π ( a ′ ) ] V_{\text{soft}}^{\theta}(\mathbf{s}_{t})=\alpha\log\mathbb{E}_{\pi}\left[\frac{\exp\left(\frac{1}{\alpha}Q_{\text{soft}}^{\theta}(\mathbf{s}_{t},\mathbf{a'})\right)}{\pi(\mathbf{a'})}\right] Vsoftθ(st)=αlogEπ[π(a)exp(α1Qsoftθ(st,a))]

π \pi π可以表示为任意的策略分布。之后利用均方误差计算目标函数, Q ^ s o f t θ ˉ ( s t , a t ) = r t + γ E s t + 1 ∼ p s [ V s o f t θ ˉ ( s t + 1 ) ] \hat{Q}_{\mathrm{soft}}^{\bar{\theta}}(\mathbf{s}_t,\mathbf{a}_t)=r_t+\gamma\mathbb{E}_{\mathbf{s}_{t+1}\thicksim p_\mathbf{s}}[V_{\mathrm{soft}}^{\bar{\theta}}(\mathbf{s}_{t+1})] Q^softθˉ(st,at)=rt+γEst+1ps[Vsoftθˉ(st+1)]为目标的Q-值:

J Q ( θ ) = E s t ∼ q s t , a t ∼ q a t [ 1 2 ( Q ^ s o f t θ ˉ ( s t , a t ) − Q s o f t θ ( s t , a t ) ) 2 ] J_Q(\theta)=\mathbb{E}_{\mathbf{s}_t\sim q_{\mathbf{s}_t},\mathbf{a}_t\sim q_{\mathbf{a}_t}}\left[\frac{1}{2}\left(\hat{Q}_{\mathrm{soft}}^{\bar{\theta}}(\mathbf{s}_t,\mathbf{a}_t)-Q_{\mathrm{soft}}^{\theta}(\mathbf{s}_t,\mathbf{a}_t)\right)^2\right] JQ(θ)=Estqst,atqat[21(Q^softθˉ(st,at)Qsoftθ(st,at))2]

⚠️You Should Know
因为 q s t , q a t q_{\mathbf{s}_t},q_{\mathbf{a}_t} qst,qat采样的分布可以是任意的。通常来说可以基于当前policy的一些rollouts进行采样 π ( a t ∣ s t ) ∝ exp ⁡ ( 1 α Q s o f t θ ( s t , a t ) ) \pi(\mathbf{a}_t|\mathbf{s}_t)\propto\exp\left(\frac{1}{\alpha}Q_{\mathrm{soft}}^{\theta}(\mathbf{s}_{t},\mathbf{a}_{t})\right) π(atst)exp(α1Qsoftθ(st,at))
对于 π \pi π的分布,通常来说可以采用均匀分布,但更好的选择是使用当前策略,以达到无偏估计的效果。

近似采样与SVGD

然而,在连续空间中,我们仍然需要一种可行的方法来从策略 π ( a t ∣ s t ) ∝ exp ⁡ ( 1 α Q s o f t θ ( s t , a t ) ) \pi(\mathbf{a}_t|\mathbf{s}_t)\propto\exp\left(\frac{1}{\alpha}Q_{\mathrm{soft}}^{\theta}(\mathbf{s}_{t},\mathbf{a}_{t})\right) π(atst)exp(α1Qsoftθ(st,at))中采样,不仅用于采取策略 π π π的行为,还可以在需要时生成动作样本以估计软值函数。由于策略的形式非常广泛,因此直接从它中采样是不可行的。因此需要一种近似采样的方法。

以往的方案大致可以分为两类:基于马尔可夫链蒙特卡罗(MCMC)的采样;从目标分布中训练输出近似样本的随机抽样网络。但是MCMC的方法不太适用于在线推理过程,因此作者将使用Stein variational gradient descent (SVGD) 和amortized SVGD的采样网络。Amortized SVGD有一些比较有趣的性质:

  1. 它为我们提供了一个随机体素网络,可以用于极快地生成样本。
  2. 它可以证明会收敛到 EBM 的后验分布的准确估计
  3. 该算法类似于actor-critic算法

通过学习一个状态条件随机神经网络 a t = f ϕ ( ξ ; s t ) a_t = f^\phi(ξ; s_t) at=fϕ(ξ;st),由 ϕ \phi ϕ 参数化,它将从正常高斯或其他任意分布中提取的噪声样本 ξ ξ ξ 映射到对应于 Q s o f t θ Q^θ_{soft} Qsoftθ 的目标 EBM 的无偏动作样本。将学习到的策略标为 π ϕ ( s t , a t ) \pi^\phi(s_t,a_t) πϕ(st,at),通过KL 散度(KL divergence)进行近似: J π ( ϕ ; s t ) = K L ( π ϕ ( ⋅ ∣ s t ) ∣ ∣ exp ⁡ ( 1 α ( Q s o f t θ ( s t , ⋅ ) − V s o f t θ ) ) ) a t = f ϕ ( ξ ; s t ) , ξ ∼ N \begin{aligned}J_{\pi}(\phi;s_{t})&=KL\left(\pi^{\phi}(\cdot|s_{t})||\exp\left(\frac{1}{\alpha}\big(Q_{soft}^{\theta}(s_{t},\cdot)-V_{soft}^{\theta}\big)\right)\right)\\a_{t}&=f^{\phi}(\xi;s_{t}),\xi\sim\mathcal{N}\end{aligned} Jπ(ϕ;st)at=KL(πϕ(st)∣∣exp(α1(Qsoftθ(st,)Vsoftθ)))=fϕ(ξ;st),ξN

SVGD的方法提供了贪婪的搜索策略: Δ f ϕ ( ⋅ ; s t ) = E a t ∼ π ϕ ∣ κ ( a t , f ϕ ( ⋅ ; s t ) ) ∇ a ′ Q s o f t θ ( s t , a ′ ) ∣ a ′ = a t + α ∇ a ′ κ ( a ′ , f ϕ ( ⋅ ; s t ) ) ∣ a ′ = a t ] , \begin{gathered} \Delta f^{\phi}(\cdot;\mathbf{s}_{t}) =\mathbb{E}_{\mathbf{a}_{t}\sim\pi^{\phi}}\left|\kappa(\mathbf{a}_{t},f^{\phi}(\cdot;\mathbf{s}_{t}))\nabla_{\mathbf{a}^{\prime}}Q_{\mathrm{soft}}^{\theta}(\mathbf{s}_{t},\mathbf{a}^{\prime})\right|_{\mathbf{a}^{\prime}=\mathbf{a}_{t}} \\ \left.+\alpha\left.\nabla_{\mathbf{a'}}\kappa(\mathbf{a'},f^\phi(\cdot;\mathbf{s}_t))\right|_{\mathbf{a'}=\mathbf{a}_t}\right], \end{gathered} Δfϕ(;st)=Eatπϕ κ(at,fϕ(;st))aQsoftθ(st,a) a=at+αaκ(a,fϕ(;st)) a=at],

之后使用链式法则,并将SVGD反向传播到策略网络中,并使用任意基于梯度的优化方法来学习最优采样网络参数。 ∂ J π ( ϕ ; s t ) ∂ ϕ ∝ E ξ [ Δ f ϕ ( ξ ; s t ) ∂ f ϕ ( ξ ; s t ) ∂ ϕ ] , \frac{\partial J_\pi(\phi;\mathbf{s}_t)}{\partial\phi}\propto\mathbb{E}_\xi\left[\Delta f^\phi(\xi;\mathbf{s}_t)\frac{\partial f^\phi(\xi;\mathbf{s}_t)}{\partial\phi}\right], ϕJπ(ϕ;st)Eξ[Δfϕ(ξ;st)ϕfϕ(ξ;st)],

⚠️You Should Know
Δ f ϕ \Delta f^{\phi} Δfϕ不是严格的策略梯度,而是核函数 κ \kappa κ再现核希尔伯特空间(reproducing kernel Hilbert space)的最优方向

伪代码

在这里插入图片描述

Soft Actor-Critic

Haarnoja T, Zhou A, Abbeel P, et al. Soft actor-critic: Off-policy maximum entropy deep reinforcement learning with a stochastic actor[C]//International conference on machine learning. PMLR, 2018: 1861-1870.

用SVGD做近似推断可能存在需要对随机数的分布进行假设的问题同时会使得复杂度和不稳定性提升。除此之外,相比策略梯度或者actor-critic等算法,近似推断可能不那么直接与有效。SAC对策略和价值函数都进行了函数逼近,通过交替使用随机梯度下降优化两个网络。以下是相关算法细节:

价值函数学习目标:

J V ( ψ ) = E s t ∼ D [ 1 2 ( V ψ ( s t ) − E a t ∼ π ϕ [ Q θ ( s t , a t ) − log ⁡ π ϕ ( a t ∣ s t ) ] ) 2 ] J Q ( θ ) = E ( s t , a t ) ∼ D [ 1 2 ( r ( s t , a t ) + γ E s t + 1 ∼ p [ V ψ ‾ ( s t + 1 ) ] − Q θ ( s t , a t ) ) 2 ] \begin{gathered} J_{V}(\psi)=\mathbb{E}_{s_{t}\sim\mathcal{D}}\left[\frac{1}{2}\Big(V_{\psi}(s_{t})-\mathbb{E}_{a_{t}\sim\pi_{\phi}}\big[Q_{\theta}(s_{t},a_{t})-\log\pi_{\phi}(a_{t}|s_{t})\big]\Big)^{2}\right] \\ J_{Q}(\theta)=\mathbb{E}_{(s_{t},a_{t})\sim\mathbb{D}}\left[{\frac{1}{2}}\left(r(s_{t},a_{t})+\gamma\mathbb{E}_{s_{t+1}\sim p}[V_{\overline{\psi}}(s_{t+1})]-Q_{\theta}(s_{t},a_{t})\right)^{2}\right] \end{gathered} JV(ψ)=EstD[21(Vψ(st)Eatπϕ[Qθ(st,at)logπϕ(atst)])2]JQ(θ)=E(st,at)D[21(r(st,at)+γEst+1p[Vψ(st+1)]Qθ(st,at))2]

策略函数学习目标: J π ( ϕ ) = E s t ∼ D , ϵ t ∼ N [ log ⁡ π ϕ ( f ϕ ( ϵ t ; s t ) ∣ s t ) − Q θ ( s t , f ϕ ( ϵ t ; s t ) ) ] \begin{aligned}J_\pi(\phi)=\mathbb{E}_{\mathbf{s}_t\sim\mathcal{D},\epsilon_t\sim\mathcal{N}}[\log\pi_\phi(f_\phi(\epsilon_t;\mathbf{s}_t)|\mathbf{s}_t)-Q_\theta(\mathbf{s}_t,f_\phi(\epsilon_t;\mathbf{s}_t))]\end{aligned} Jπ(ϕ)=EstD,ϵtN[logπϕ(fϕ(ϵt;st)st)Qθ(st,fϕ(ϵt;st))]

利用两个动作价值函数网络,每次使用Q网络时,使用较小的那一个: L Q ( ω ) = E ( s t , a t , r t , s t + 1 ) ∼ R [ 1 2 ( Q ω ( s t , a t ) − ( r t + γ V ω − ( s t + 1 ) ) ) 2 ] = E ( s t , a t , r t , s t + 1 ) ∼ R , a t + 1 ∼ π θ ( ⋅ ∣ s t + 1 ) [ 1 2 ( Q ω ( s t , a t ) − ( r t + γ ( min ⁡ j = 1 , 2 Q ω j − ( s t + 1 , a t + 1 ) − α log ⁡ π ( a t + 1 ∣ s t + 1 ) ) ) ) 2 ] \begin{aligned} L_{Q}(\omega)& =\mathbb{E}_{(s_t,a_t,r_t,s_{t+1})\sim R}\left[\frac12\left(Q_\omega(s_t,a_t)-(r_t+\gamma V_{\omega^-}(s_{t+1}))\right)^2\right] \\ &=\mathbb{E}_{(s_t,a_t,r_t,s_{t+1})\sim R,a_{t+1}\sim\pi_\theta(\cdot|s_{t+1})}\left[\frac{1}{2}\left(Q_\omega(s_t,a_t)-(r_t+\gamma(\min_{j=1,2}Q_{\omega_j^-}(s_{t+1},a_{t+1})-\alpha\log\pi(a_{t+1}|s_{t+1})))\right)^2\right] \end{aligned} LQ(ω)=E(st,at,rt,st+1)R[21(Qω(st,at)(rt+γVω(st+1)))2]=E(st,at,rt,st+1)R,at+1πθ(st+1)[21(Qω(st,at)(rt+γ(j=1,2minQωj(st+1,at+1)αlogπ(at+1st+1))))2]

重参数化技巧自动调整熵正则项参考《动手学强化学习》SAC 算法

伪代码

在这里插入图片描述

在这里插入图片描述

代码实践

连续动作空间

import gymnasium as gym
import numpy as np
from tqdm import tqdm
import torch
import torch.nn.functional as F
import util

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)
        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(self.std(x))
        dist = torch.distributions.Normal(mu, std)
        normal_sample = dist.rsample()  # rsample()是重参数化采样
        log_prob = dist.log_prob(normal_sample)
        action = torch.tanh(normal_sample)
        # 计算tanh_normal分布的对数概率密度
        log_prob = log_prob - torch.log(1 - torch.tanh(action).pow(2) + 1e-7)
        action = action * self.action_bound
        return action, log_prob

class QValueNetContinuous(torch.nn.Module):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(QValueNetContinuous, 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)

class SACContinuous:
    ''' 处理连续动作的SAC算法 '''
    def __init__(self, state_dim, hidden_dim, action_dim, action_bound, actor_lr, critic_lr, gamma,
                 alpha_lr, target_entropy, tau, buffer_size, minimal_size, batch_size,
                 device, numOfEpisodes, env):
        self.actor = PolicyNetContinuous(state_dim, hidden_dim, action_dim, action_bound).to(device)
        self.critic_1 = QValueNetContinuous(state_dim, hidden_dim, action_dim).to(device)
        self.critic_2 = QValueNetContinuous(state_dim, hidden_dim, action_dim).to(device)
        self.target_critic_1 = QValueNetContinuous(state_dim, hidden_dim, action_dim).to(device)
        self.target_critic_2 = QValueNetContinuous(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的log值,可以使训练结果比较稳定
        self.log_alpha = torch.tensor(np.log(0.01), dtype=torch.float)
        self.log_alpha.requires_grad = True  # 可以对alpha求梯度
        self.log_alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=alpha_lr)
        self.target_entropy = target_entropy
        self.gamma = gamma
        self.tau = tau
        self.device = device
        self.env = env
        self.numOfEpisodes = numOfEpisodes
        self.buffer_size = buffer_size
        self.minimal_size = minimal_size
        self.batch_size = batch_size

    def take_action(self, state):
        state = torch.FloatTensor(np.array([state])).to(self.device)
        action = self.actor(state)[0]
        return [action.item()]

    def calc_target(self, rewards, next_states, terminateds, truncateds):
        next_action, log_prob = self.actor(next_states)
        entropy = -log_prob
        q1_value = self.target_critic_1(next_states, next_action)
        q2_value = self.target_critic_2(next_states, next_action)
        next_value = torch.min(q1_value, q2_value) + self.log_alpha.exp() * entropy
        td_target = rewards + self.gamma * next_value * (1 - terminateds + truncateds)
        return td_target

    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(np.array(transition_dict['states']), dtype=torch.float).to(self.device)
        actions = torch.tensor(np.array(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(np.array(transition_dict['next_states']), dtype=torch.float).to(self.device)
        terminateds = torch.tensor(transition_dict['terminateds'], dtype=torch.float).view(-1, 1).to(self.device)
        truncateds = torch.tensor(transition_dict['truncateds'], dtype=torch.float).view(-1, 1).to(self.device)
        rewards = (rewards + 8.0) / 8.0
        # 更新两个Q网络
        td_target = self.calc_target(rewards, next_states, terminateds, truncateds)
        critic_loss1 = torch.mean(F.mse_loss(td_target.detach(), self.critic_1(states, actions)))
        critic_loss2 = torch.mean(F.mse_loss(td_target.detach(), self.critic_2(states, actions)))
        self.critic_1_optimizer.zero_grad()
        critic_loss1.backward()
        self.critic_1_optimizer.step()
        self.critic_2_optimizer.zero_grad()
        critic_loss2.backward()
        self.critic_2_optimizer.step()
        # 更新策略网络
        new_actions, log_prob = self.actor(states)
        entropy = -log_prob
        q1_value = self.critic_1(states, new_actions)
        q2_value = self.critic_2(states, new_actions)
        next_value = torch.min(q1_value, q2_value)
        actor_loss = torch.mean(-self.log_alpha.exp() * entropy - next_value)
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()
        # 更新alpha值
        alpha_loss = torch.mean(self.log_alpha.exp() * (entropy - self.target_entropy).detach())
        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)

    def SACtrain(self):
        replay_buffer = util.ReplayBuffer(self.buffer_size)
        returnList = []
        for i in range(10):
            with tqdm(total=int(self.numOfEpisodes / 10), desc='Iteration %d' % i) as pbar:
                for episode in range(int(self.numOfEpisodes / 10)):
                    # initialize state
                    state, info = self.env.reset()
                    terminated = False
                    truncated = False
                    episodeReward = 0
                    # Loop for each step of episode:
                    while (not terminated) or (not truncated):
                        action = self.take_action(state)
                        next_state, reward, terminated, truncated, info = self.env.step(action)
                        replay_buffer.add(state, action, reward, next_state, terminated, truncated)
                        state = next_state
                        episodeReward += reward
                        # 当buffer数据的数量超过一定值后,才进行Q网络训练
                        if replay_buffer.size() > self.minimal_size:
                            b_s, b_a, b_r, b_ns, b_te, b_tr = replay_buffer.sample(self.batch_size)
                            transition_dict = {
                                'states': b_s,
                                'actions': b_a,
                                'next_states': b_ns,
                                'rewards': b_r,
                                'terminateds': b_te,
                                'truncateds': b_tr
                            }
                            self.update(transition_dict)
                        if terminated or truncated:
                            break
                    returnList.append(episodeReward)
                    if (episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报
                        pbar.set_postfix({
                            'episode':
                                '%d' % (self.numOfEpisodes / 10 * i + episode + 1),
                            'return':
                                '%.3f' % np.mean(returnList[-10:])
                        })
                    pbar.update(1)
        return returnList

def test01():
    device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
    env = gym.make("Pendulum-v1")
    agent = SACContinuous(state_dim=env.observation_space.shape[0],
                          hidden_dim=256,
                          action_dim=env.action_space.shape[0],
                          action_bound=env.action_space.high[0],
                          actor_lr=3e-4,
                          critic_lr=3e-3,
                          gamma=0.99,
                          alpha_lr=3e-4,
                          target_entropy=-env.action_space.shape[0],
                          tau=0.005,
                          buffer_size=100000,
                          minimal_size=1000,
                          batch_size=64,
                          device=device,
                          numOfEpisodes=200,
                          env=env)
    returnLists1 = agent.SACtrain()
    ReturnList = []
    ReturnList.append(util.smooth([returnLists1], sm=20))
    labelList = ['SAC']
    util.PlotReward(200, ReturnList, labelList, 'Pendulum-v1')
    np.save("D:\LearningRL\Hands-on-RL\SAC_Pendulum\ReturnData\SAC_v1_1.npy", returnLists1)
    env.close()

结果:

在这里插入图片描述
可以看到SAC在连续动作空间的任务中表现优秀。

离散动作空间

⚠️You Should Know
对于离散的动作空间,策略网络和价值网络的网络结构将发生如下改变:

  • 策略网络的输出修改为在离散动作空间上的 softmax 分布;
    该策略网络输出一个离散的动作分布,所以在价值网络的学习过程中,不需要再对下一个动作 a t + 1 a_{t+1} at+1进行采样,而是直接通过概率计算来得到下一个状态的价值。同理,在 α \alpha α的损失函数计算中,也不需要再对动作进行采样。
  • 价值网络直接接收状态和离散动作空间的分布作为输入。
class PolicyNet(torch.nn.Module):
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(PolicyNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return F.softmax(self.fc2(x), dim=1)

class QValueNet(torch.nn.Module):
    ''' 只有一层隐藏层的Q网络 '''
    def __init__(self, state_dim, hidden_dim, action_dim):
        super(QValueNet, self).__init__()
        self.fc1 = torch.nn.Linear(state_dim, hidden_dim)
        self.fc2 = torch.nn.Linear(hidden_dim, action_dim)

    def forward(self, x):
        x = F.relu(self.fc1(x))
        return self.fc2(x)

class SACContinuous:
    ''' 处理连续动作的SAC算法 '''
    def __init__(self, state_dim, hidden_dim, action_dim, actor_lr, critic_lr, gamma,
                 alpha_lr, target_entropy, tau, buffer_size, minimal_size, batch_size,
                 device, numOfEpisodes, env):
        self.actor = PolicyNet(state_dim, hidden_dim, action_dim).to(device)
        self.critic_1 = QValueNet(state_dim, hidden_dim, action_dim).to(device)
        self.critic_2 = QValueNet(state_dim, hidden_dim, action_dim).to(device)
        self.target_critic_1 = QValueNet(state_dim, hidden_dim, action_dim).to(device)
        self.target_critic_2 = QValueNet(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的log值,可以使训练结果比较稳定
        self.log_alpha = torch.tensor(np.log(0.01), dtype=torch.float)
        self.log_alpha.requires_grad = True  # 可以对alpha求梯度
        self.log_alpha_optimizer = torch.optim.Adam([self.log_alpha], lr=alpha_lr)
        self.target_entropy = target_entropy
        self.gamma = gamma
        self.tau = tau
        self.device = device
        self.env = env
        self.numOfEpisodes = numOfEpisodes
        self.buffer_size = buffer_size
        self.minimal_size = minimal_size
        self.batch_size = batch_size

    # 根据动作概率分布随机采样
    def take_action(self, state):
        state = torch.tensor(np.array([state]), dtype=torch.float).to(self.device)
        action_probs = self.actor(state)
        action_dist = torch.distributions.Categorical(action_probs)
        action = action_dist.sample()
        return action.item()

    def calc_target(self, rewards, next_states, terminateds, truncateds):
        next_probs = self.actor(next_states)
        next_log_probs = torch.log(next_probs + 1e-8)
        entropy = -torch.sum(next_probs * next_log_probs, dim=1, keepdim=True)
        q1_value = self.target_critic_1(next_states)
        q2_value = self.target_critic_2(next_states)
        next_value = torch.sum(next_probs * torch.min(q1_value, q2_value),
                               dim=1,
                               keepdim=True)  # 直接根据概率计算期望
        next_value = next_value + self.log_alpha.exp() * entropy
        td_target = rewards + self.gamma * next_value * (1 - terminateds + truncateds)
        return td_target

    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(np.array(transition_dict['states']), dtype=torch.float).to(self.device)
        actions = torch.tensor(transition_dict['actions']).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(np.array(transition_dict['next_states']), dtype=torch.float).to(self.device)
        terminateds = torch.tensor(transition_dict['terminateds'], dtype=torch.float).view(-1, 1).to(self.device)
        truncateds = torch.tensor(transition_dict['truncateds'], dtype=torch.float).view(-1, 1).to(self.device)
        # 更新两个Q网络
        td_target = self.calc_target(rewards, next_states, terminateds, truncateds)
        critic_value1 = self.critic_1(states).gather(1, actions)
        critic_loss1 = torch.mean(F.mse_loss(critic_value1, td_target.detach()))
        critic_value2 = self.critic_2(states).gather(1, actions)
        critic_loss2 = torch.mean(F.mse_loss(critic_value2, td_target.detach()))
        self.critic_1_optimizer.zero_grad()
        critic_loss1.backward()
        self.critic_1_optimizer.step()
        self.critic_2_optimizer.zero_grad()
        critic_loss2.backward()
        self.critic_2_optimizer.step()
        # 更新策略网络
        probs = self.actor(states)
        log_probs = torch.log(probs + 1e-8)
        entropy = -torch.sum(log_probs * probs, dim=1, keepdim=True)
        q1_value = self.critic_1(states)
        q2_value = self.critic_2(states)
        next_value = torch.sum(probs * torch.min(q1_value, q2_value),
                               dim=1,
                               keepdim=True)  # 直接根据概率计算期望
        actor_loss = torch.mean(-self.log_alpha.exp() * entropy - next_value)
        self.actor_optimizer.zero_grad()
        actor_loss.backward()
        self.actor_optimizer.step()
        # 更新alpha值
        alpha_loss = torch.mean(self.log_alpha.exp() * (entropy - self.target_entropy).detach())
        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)

    def SACtrain(self):
        replay_buffer = util.ReplayBuffer(self.buffer_size)
        returnList = []
        for i in range(10):
            with tqdm(total=int(self.numOfEpisodes / 10), desc='Iteration %d' % i) as pbar:
                for episode in range(int(self.numOfEpisodes / 10)):
                    # initialize state
                    state, info = self.env.reset()
                    terminated = False
                    truncated = False
                    episodeReward = 0
                    # Loop for each step of episode:
                    while (not terminated) or (not truncated):
                        action = self.take_action(state)
                        next_state, reward, terminated, truncated, info = self.env.step(action)
                        replay_buffer.add(state, action, reward, next_state, terminated, truncated)
                        state = next_state
                        episodeReward += reward
                        # 当buffer数据的数量超过一定值后,才进行Q网络训练
                        if replay_buffer.size() > self.minimal_size:
                            b_s, b_a, b_r, b_ns, b_te, b_tr = replay_buffer.sample(self.batch_size)
                            transition_dict = {
                                'states': b_s,
                                'actions': b_a,
                                'next_states': b_ns,
                                'rewards': b_r,
                                'terminateds': b_te,
                                'truncateds': b_tr
                            }
                            self.update(transition_dict)
                        if terminated or truncated:
                            break
                    returnList.append(episodeReward)
                    if (episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报
                        pbar.set_postfix({
                            'episode':
                                '%d' % (self.numOfEpisodes / 10 * i + episode + 1),
                            'return':
                                '%.3f' % np.mean(returnList[-10:])
                        })
                    pbar.update(1)
        return returnList

结果:
在这里插入图片描述

参考与推荐

[1] Soft actor-critic: Off-policy maximum entropy deep reinforcement learning with a stochastic actor, 2018, arXiv
[2] Soft Actor-Critic Algorithms and Applications, 2019, arXiv
[3] Reinforcement Learning with Deep Energy-Based Policies, 2017, arXiv
[4] 伯禹AI
[5] 动手学强化学习
[6] https://zhuanlan.zhihu.com/p/444441890
[7] https://zhuanlan.zhihu.com/p/70360272/
[8] https://bair.berkeley.edu/blog/2017/10/06/soft-q-learning/
[9] https://towardsdatascience.com/entropy-in-soft-actor-critic-part-2-59821bdd5671
[10] https://medium.com/towards-data-science/entropy-in-soft-actor-critic-part-1-92c2cd3a3515
[11] Haarnoja T, Zhou A, Abbeel P, et al. Soft actor-critic: Off-policy maximum entropy deep reinforcement learning with a stochastic actor[C]//International conference on machine learning. PMLR, 2018: 1861-1870.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1199517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

macOS Big Sur(macos11版本)

macOS Big Sur是苹果推出的最新操作系统,具有以下特点: 全新的设计风格:Big Sur采用了全新的设计语言,包括更加圆润的窗口和控件、更加鲜明的色彩和更加简洁的界面。这种设计风格使得操作系统更加美观和易用。强大的性能表现&…

国际阿里云:云服务器灾备方案!!!

保障企业业务稳定、IT系统功能正常、数据安全十分重要,可以同时保障数据备份与系统、应用容灾的灾备解决方案应势而生,且发展迅速。ECS可使用快照、镜像进行备份。 灾备设计 快照备份 阿里云ECS可使用快照进行系统盘、数据盘的备份。目前,阿…

Ubuntu取消sudo的输入密码

Ubuntu最近要安装软件,每次sudo都要输入一次密码,感觉很麻烦,于是想能不能设置为不输入密码,在网上找了一下解决办法。 主要参考这篇文章: Ubuntu取消sudo时输入密码 上面这篇文章使用的是vim,但是按照博…

Kotlin基础——接口和类

接口 使用 : 表示继承关系&#xff0c;只能继承一个类&#xff0c;但可以实现多个接口override修饰符表示重写可以有默认方法&#xff0c;若父类的默认方法冲突&#xff0c;则需要子类重写&#xff0c;使用super<XXX>.xxx()调用某一父类方法 interface Focusable {fun …

免费录屏软件哪个好用?免费录屏软件排行榜

对于您的团队&#xff0c;屏幕录像机可以用于多种原因——从为您的网站创建教程到记录反复出现的技术问题&#xff0c;再到向您的营销团队发送快速说明而不是电子邮件。 此外&#xff0c;我们不能忘记产品演示和培训视频&#xff0c;它们可供您团队中的许多部门使用&#xff0…

matlab模糊控制文件m代码实现和基础理论

1、内容简介 略 15-可以交流、咨询、答疑 通过m代码来实现生成模糊文件fis文件 2、内容说明 模糊文件m代码实现和基础理论 matlab模糊控制文件m代码实现和基础理论 模糊文件、m代码和模糊基础理论 3、仿真分析 略 4、参考论文 略 链接&#xff1a;https://pan.baidu.co…

web前端开发第3次Dreamweave课堂练习/html练习代码《网页设计语言基础练习案例》

目标图片&#xff1a; 文字素材&#xff1a; 网页设计语言基础练习案例 ——几个从语义上和文字相关的标签 * h标签&#xff08;h1~h6&#xff09;&#xff1a;用来定义网页的标题&#xff0c;成对出现。 * p标签&#xff1a;用来设置网页的段落&#xff0c;成对出现。 * b…

NZ系列工具NZ08:图表添加标签工具

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

【车载开发系列】AutoSar中的CANTP

【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP一. CANTP相关术语二. CANTP相关概念1&#xff09;单帧&#xff1a;SF(Single Frame)2&#xff09;首帧&#xff1a;FF(First Frame)3&#xff09;连续帧CF(Consecutive F…

腾讯云3年轻量2核4G5M服务器756元,抓紧数量不多

腾讯云轻量应用服务器特价是有新用户限制的&#xff0c;所以阿腾云建议大家选择3年期轻量应用服务器&#xff0c;一劳永逸&#xff0c;免去续费困扰。腾讯云轻量应用服务器3年可以选择2核2G4M和2核4G5M带宽&#xff0c;3年轻量2核2G4M服务器540元&#xff0c;2核4G5M轻量应用服…

十五、信号量

1、概述 (1)前面介绍的队列(queue)可以用于传输数据&#xff1a;在任务之间、任务和中断之间。 (2)有些时候我们只需要传递状态&#xff0c;并不需要传递具体的信息&#xff0c;比如&#xff1a; 我的事做完了&#xff0c;通知一下你。卖包子了、卖包子了&#xff0c;做好了…

python 中用opencv开发虚拟键盘------可以只选择一个单词不会出现一下选择多个

一. 介绍 OpenCV是最流行的计算机视觉任务库&#xff0c;它是用于机器学习、图像处理等的跨平台开源库&#xff0c;用于开发实时计算机视觉应用程序。 CVzone 是一个计算机视觉包&#xff0c;它使用 OpenCV 和 Media Pipe 库作为其核心&#xff0c;使我们易于运行&#xff0c…

基于STM32控制直流电机加减速正反转仿真设计

**单片机设计介绍&#xff0c;基于STM32控制直流电机加减速正反转仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 本设计由STM32F103、L298N电机驱动电路、按键电路组成。通过按键可以控制电机&#xff0c;正转、反转、加…

【FISCO BCOS】十九、区块链浏览器部署

目录 一、环境依赖 检查环境 1.检查java 二、拉取安装脚本 获取部署安装包 ​编辑 解压安装包 进入目录 三、修改配置 四、部署服务 五、状态检查 检查前后端进程 1.检查后端server进程 2.检查前端的nginx进程 检查进程端口 六、使用区块链浏览器 1.配置群组…

关于DataLoader是否shuffle在VOC2007语义分割数据集上引发的问题

问题描述&#xff1a; 在训练过程中&#xff0c;训练集和验证集实时得到的F1分数相差很大&#xff0c;如下图&#xff1a; 这个问题之前从未遇到过&#xff0c;后来经过不断的排查&#xff0c;发现是因为验证集的数据加载器中shuffle设置的为False&#xff0c;而训练集设置的为…

[C/C++]数据结构 深入挖掘环形链表问题

前言 在上一篇文章中讲述了如何判断链表是否带环,在观看本片文章时建议先了解一下这篇文章的内容[C/C]数据结构 链表OJ题:环形链表。本篇文章我们将讲述关于环形链表的几种不同的情况如下,同时我们要解决另一个环形链表问题----找到入环点 slow一次走一步fast一次走两步一定会…

进亦忧,退亦忧,Github Copilot 集成进入 Visual Studio 带来的思考

开篇想到《岳阳楼记》的结尾&#xff1a; 不以物喜&#xff0c;不以己悲&#xff1b;居庙堂之高则忧其民&#xff1b;处江湖之远则忧其君。是进亦忧&#xff0c;退亦忧。然则何时而乐耶&#xff1f;其必曰&#xff1a;“先天下之忧而忧&#xff0c;后天下之乐而乐”乎。未来30…

第26章_事务概述与隔离级别

文章目录 事务事务的特征事务的控制语句事务的生命周期事务的执行过程 ACID特性原子性一致性隔离性持久性 隔离级别不同隔离级别并发异常脏读不可重复读幻读区别 总结 事务 &#xff08;1&#xff09;事务的前提&#xff1a;并发连接访问。MySQL的事务就是将多条SQL语句作为整…

SpringDataJpa(二)

三、Spring Data JPA概述 Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架&#xff0c;可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能&#xff0c;且易于扩展&#xff01;学习并使用 Spring D…

汽车操纵稳定性matlab仿真

1、内容简介 略 14-可以交流、咨询、答疑 2、内容说明 汽车操纵稳定性matlab仿真&#xff0c;包含完整的论文 操纵动力学、两自由度 摘要&#xff1a;当今&#xff0c;仿真技术日益广泛地应用于汽车工程领域&#xff0c;操纵稳定性研究越来越多地使用成熟的计算机仿真理论…