深度强化学习:PPO

深度强化学习算法:PPO

1. Importance Sampling

先说一下什么是采样:对于一个随机变量,我们通常用概率密度函数来描述该变量的概率分布特性。具体来说,给定随机变量的一个取值,可以根据概率密度函数来计算该值对应的概率(密度)。反过来也可以根据概率密度函数提供的概率分布信息来生成随机变量的一个取值,这就是采样

现在假设需要对一个变量 X 的值进行估计,这个变量在真实环境中有一个分布 p ;那么在这个分布下可以获取到它的采样值 x i {x_i} xi ,由于 x i 和 X x_i和X xiX是同分布的,即 E [ x i ] = E [ X ] E[x_i]=E[X] E[xi]=E[X] ,则可以利用大数定理来求解变量X的估计值:
E [ x i ] = 1 2 ∑ i = 1 n x i = E [ X ] (1) E[x_i] = \frac{1}{2}\sum^n_{i=1}x_i = E[X] \tag{1} E[xi]=21i=1nxi=E[X](1)
当获取的采样值数量n增加,估计值越来越趋近于真实值。蒙特卡洛也会这样不是吗。

但是现在有一个新的情况,假如变量 X 的分布为 p ,而获取的采样值的分布为 q ,则此时 E [ x i ] ≠ E [ X ] E[x_i] \neq E[X] E[xi]=E[X] ,那么便无法通过大数定理来估计了。但是现在的情况是我们不能从分布 p 采样数据,只能从分布 q 采样数据,q可以是任何分布。这时候就要用到重要性采样了。
E x ∼ p [ f ( x ) ] = E x ∼ q [ f ( x ) p ( x ) q ( x ) ] (2) E_{x \sim p}[f(x)] = E_{x \sim q}[f(x) \frac{p(x)}{q(x)}] \tag{2} Exp[f(x)]=Exq[f(x)q(x)p(x)](2)
我们从 q 里面采样 x ,再计算 f ( x ) p ( x ) q ( x ) f(x) \frac{p(x)}{q(x)} f(x)q(x)p(x) ,再取期望值。所以就算我们不能从 p 里面采样数据,但只要从 q 里面采样数据,就可以计算从 p 采样 x 代入 f 之后的期望值了。这里需要乘上一个重要性权重 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x) 来修正这两个分布的差异。 q ( x ) q(x) q(x) 可以是任何分布,唯一的限制就是 q ( x ) q(x) q(x) 的概率是 0 的时候, p ( x ) p(x) p(x) 的概率不为 0 ,不然会没有定义。

尽管上面提到分布 q 可以是任何分布,但是仍有限制,就是分布 q 不能和分布 p 的差距太多,两个分布需要差不多的,只有这样两个分布的方差( V a r x ∼ p [ f ( x ) ] 和 V a r x ∼ p [ f ( x ) p ( x ) q ( x ) ] Var_{x \sim p}[f(x)] 和 Var_{x \sim p}[f(x)\frac{p(x)}{q(x)}] Varxp[f(x)]Varxp[f(x)q(x)p(x)])才会没有太多的差距。

分别把 f ( x ) f(x) f(x) f ( x ) p ( x ) q ( x ) f(x)\frac{p(x)}{q(x)} f(x)q(x)p(x) 代入方差的公式 V a r [ X ] = E [ X 2 ] − ( E [ x ] ) 2 Var[X]=E[X^2] - (E[x])^2 Var[X]=E[X2](E[x])2,可得
V a r x ∼ p [ f ( x ) ] = E x ∼ p [ f ( x ) 2 ] − ( E x ∼ p [ f ( x ) ] ) 2 V a r x ∼ q [ f ( x ) p ( x ) q ( x ) ] = E x ∼ q [ ( f ( x ) p ( x ) q ( x ) ) 2 ] − ( E x ∼ q [ f ( x ) p ( x ) q ( x ) ] ) 2 = E x ∼ q [ f ( x ) 2 p ( x ) q ( x ) ] − ( E x ∼ p [ f ( x ) ] ) 2 (3) Var_{x \sim p}[f(x)] = E_{x \sim p}[f(x)^2] - (E_{x \sim p}[f(x)])^2 \tag{3}\\ Var_{x \sim q}[f(x)\frac{p(x)}{q(x)}]=E_{x\sim q}[(f(x)\frac{p(x)}{q(x)})^2] - (E_{x \sim q}[f(x)\frac{p(x)}{q(x)}])^2\\ =E_{x\sim q}[f(x)^2\frac{p(x)}{q(x)}] - (E_{x \sim p}[f(x)])^2 Varxp[f(x)]=Exp[f(x)2](Exp[f(x)])2Varxq[f(x)q(x)p(x)]=Exq[(f(x)q(x)p(x))2](Exq[f(x)q(x)p(x)])2=Exq[f(x)2q(x)p(x)](Exp[f(x)])2(3)
可以看到两者方差的差别在第一项, V a r x ∼ q [ f ( x ) p ( x ) q ( x ) ] Var_{x\sim q}[f(x)\frac{p(x)}{q(x)}] Varxq[f(x)q(x)p(x)] 的第一项多乘了 p ( x ) q ( x ) \frac{p(x)}{q(x)} q(x)p(x) 。如果两个分布的差距较大,那么这个概率估计就会有问题。

如上图所示,如果我们要计算 f ( x ) f(x) f(x) 的期望,从分布 p ( x ) p(x) p(x) 采样数据,那么 E x ∼ p [ f ( x ) ] E_{x \sim p}[f(x)] Exp[f(x)] 是负的,这是由于左边区域 p ( x ) p(x) p(x) 的概率很高,所以采样更倾向于在这个区域,而 f ( x ) f(x) f(x) 在这个区域是负的,所以理论上这一项算出来会是负的。

如果从分布 q ( x ) q(x) q(x) 中采样,由于 q ( x ) q(x) q(x) 在右边区域的概率比较高,所以更倾向于在右侧这个区域采样,那么 E x ∼ q [ f ( x ) p ( x ) q ( x ) ] E_{x \sim q}[f(x)\frac{p(x)}{q(x)}] Exq[f(x)q(x)p(x)] 就可能是正的。虽然左侧概率很低,但是也有可能被采样到。如果 f ( x ) f(x) f(x) 好不容易采样到了左边的的点,在左边区域 p ( x ) p(x) p(x) 很大, q ( x ) q(x) q(x) 很小,,也就是 f ( x ) f(x) f(x) 采样到一个负的点,这个负值来之不易,这个负值就应该乘上一个非常大的权重,这样就可以平衡其他采样到的正的值,那么最终得到的 f ( x ) f(x) f(x) 的期望值还是负的。

那么之前的同策略(式(4))就换成了异策略(式(5))。
∇ R θ ˉ = E τ ∼ p θ ( τ ) [ R ( τ ) ∇ log ⁡ p θ ( τ ) ] (4) \nabla \bar{R_\theta} = E_{\tau \sim p_\theta(\tau)}[R(\tau)\nabla \log p_\theta(\tau)] \tag{4} Rθˉ=Eτpθ(τ)[R(τ)logpθ(τ)](4)

∇ R θ ˉ = E τ ∼ p θ ′ ( τ ) [ p θ ( τ ) p θ ′ R ( τ ) ∇ log ⁡ p θ ( τ ) ] (5) \nabla \bar{R_\theta} = E_{\tau\sim p_{\theta^{'}(\tau)}}[\frac{p_\theta (\tau)}{p_{\theta^{'}}}R(\tau) \nabla \log p_\theta (\tau)] \tag{5} Rθˉ=Eτpθ(τ)[pθpθ(τ)R(τ)logpθ(τ)](5)

于是可得
E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ∇ log ⁡ p θ ( a t n ∣ s t n ) ] (6) E_{(s_t,a_t)\sim\pi_{\theta^{'}}}[\frac{p_\theta(a_t|s_t)}{p_{\theta^{'}}(a_t|s_t)}A^{\theta^{'}}(s_t,a_t) \nabla \log p_\theta(a_t^n|s_t^n)] \tag{6} E(st,at)πθ[pθ(atst)pθ(atst)Aθ(st,at)logpθ(atnstn)](6)
那么基于重要性采样的目标函数为:
J θ ′ ( θ ) = E ( s t , a t ) ∼ π θ ′ [ p θ ( a t , s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ] (7) J^{\theta^{'}}(\theta) = E_{(s_t,a_t)\sim \pi_{\theta^{'}}}[\frac{p_\theta(a_t,s_t)}{p_{\theta^{'}}(a_t|s_t)}A^{\theta^{'}}(s_t,a_t)] \tag{7} Jθ(θ)=E(st,at)πθ[pθ(atst)pθ(at,st)Aθ(st,at)](7)
上式中 J θ ′ ( θ ) J^{\theta^{'}}(\theta) Jθ(θ) 中的 θ \theta θ 是我们要去优化的参数, θ ′ \theta^{'} θ 是我们用来做示范,也就是真正与环境交互的策略。 A A A 是优势函数。

2. Proximal Policy Optimization

在重要性采样中有一个关键性问题: p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(atst) p θ ′ ( a t ∣ s t ) p_{\theta^{'}}(a_t|s_t) pθ(atst)不能相差太多,否则重要性采样的结果就会不好。至于如何做到这一点,就要用到 PPO 了。

要使得 p θ ′ ( a t ∣ s t ) p_{\theta^{'}}(a_t|s_t) pθ(atst) 不能相差太多,就应该加上一个约束,这个约束可以是 θ \theta θ θ ′ \theta^{'} θ 输出动作的 KL 散度 (KL divergence),这一项用来衡量两个策略的相似程度。两个策略越相似越好,如果不相似,那么最后的结果就会不太好。
J P P O θ ′ ( θ ) = J θ ′ ( θ ) − β K L ( θ , θ ′ ) (8) J_{PPO}^{\theta^{'}}(\theta)=J^{\theta^{'}}(\theta) - \beta KL(\theta, \theta^{'}) \tag{8} JPPOθ(θ)=Jθ(θ)βKL(θ,θ)(8)
其中的 J θ ′ ( θ ) J^{\theta^{'}}(\theta) Jθ(θ) 就是式(7) 。

需要注意的是,虽然 PPO 的优化目标涉及到了重要性采样,但是只用到了上一轮策略 θ ′ \theta^{'} θ 的数据。PPO 目标函数中加入了 KL 散度的约束,行为策略 θ ′ \theta^{'} θ 和目标策略 θ \theta θ 非常相近,所以 PPO 的行为策略和目标策略可认为是同一个策略,因此 PPO 是同策略算法。

这时候可能会有疑问:重要性采样用的是两个策略,但是 PPO 又是同策略,那为什么还要用重要性策略呢?这时可以再想想重要性采样的作用是什么,其要解决的问题又是什么。在重要性采样中我们用 θ ′ \theta^{'} θ 采样,解决 θ \theta θ 的问题,不需要再像之前那样采集一次数据学习一次,而是可以从另一个分布中采样数据进行学习,并且这些数据可以多次利用,也就是 θ \theta θ 可以进行多次更新,一直到 θ \theta θ 训练到一定的程度之后再让 θ ′ \theta^{'} θ 重新采样,这样训练就会更有效率。

PPO 源自信任区域策略优化(Trust Region Policy Optimization, TRPO), TRPO 可表示为:
J T R P O θ ′ ( θ ) = E ( s t , a t ) ∼ π θ ′ [ p θ ( a t ∣ s t ) p θ ′ ( a t ∣ s t ) A θ ′ ( s t , a t ) ] , K L ( θ , θ ′ ) < δ (9) J_{TRPO}^{\theta^{'}}(\theta) = E_{(s_t,a_t) \sim \pi_{\theta^{'}}}[\frac{p_\theta(a_t|s_t)}{p_{\theta^{'}}(a_t|s_t)}A^{\theta^{'}}(s_t,a_t)], KL(\theta,\theta^{'}) < \delta \tag{9} JTRPOθ(θ)=E(st,at)πθ[pθ(atst)pθ(atst)Aθ(st,at)],KL(θ,θ)<δ(9)
TRPO 的核心思想是,当策略网络是深度模型时,直接沿着策略梯度更新参数可能会导致策略更新幅度过大,从而显著降低策略的性能,影响训练效果。为了避免这种情况, TRPO 在更新参数时引入了信任区域的概念,通过限制新旧策略之间的KL 散度,使策略更新控制在一个安全范围内,从而保证更新后的策略不会偏离得太远。这种做法有效提高了训练的稳定性。但是 TRPO 需要求解一个带约束的优化问题(通常通过共轭梯度法实现),实现较为复杂且对超参数敏感。 PPO 对此进行了改进,通过引入**裁剪(Clipping)**策略或者额外的 KL 散度惩罚项,简化了约束优化过程,同时保留了对策略更新幅度的限制。

通过式(8) 和式(9) 两个目标函数可以看到 TRPO 与 PPO 不一样的地方是约束所在的位置不一样,PPO 直接将约束放到要优化的式子里面,这样就可以用梯度上升的方法去最大化式(8)。 而 TRPO 是把 KL 散度当作约束,其希望 θ \theta θ θ ′ \theta^{'} θ 的 KL 散度小于 δ \delta δ

上面提到 PPO 通过引入裁剪或 KL 散度惩罚简化了约束优化过程,接下来就看一下 PPO 算法的这两个变种:近端策略优化惩罚(PPO-penalty) 和 近端策略优化裁剪(PPO-clip)

3. PPO-Penalty

近端策略优化惩罚算法使用拉格朗日乘数法将 KL 散度的约束转换为目标函数中的惩罚项,变成了一个无约束的优化问题。在目标函数中加入 KL 散度惩罚项后,算法会在迭代过程中动态调整惩罚系数(KL 散度前的系数,比如式(8)中的 β \beta β) ,以控制策略更新幅度。如果 KL 散度过大,增大惩罚系数,从而限制更新幅度;如果 KL 散度过小,则减小惩罚系数,允许更积极地更新。这样即保留了 PPO 的更新稳定性,又显著简化了优化过程。

具体实现是,先初始化一个策略的参数 θ 0 \theta^{0} θ0 。在每一个迭代里面,我们用前一个训练的迭代得到的演员的参数 θ k \theta^{k} θk 与环境交互,采样到大量状态-动作对。根据 θ k \theta^{k} θk 交互的结果,我们估测 A θ k ( s t , a t ) A^{\theta^k}(s_t,a_t) Aθk(st,at) 。这里使用 PPO 的优化公式,但与原来的策略梯度不一样,原来的策略梯度只能更新一次参数,更新完以后,我们就要重新采样数据。但是现在不同,我们用 θ k \theta^k θk 与环境交互,采样到这组数据以后,我们可以让 θ \theta θ 更新很多次,并最大化目标函数。

此外还有一个自适应 KL 散度(adaptive KL divergence) 。这里的自适应要解决的问题是 β \beta β 应该设置为多少。这里可以动态调整,先设一个可以接受的 KL 散度的最大值。如果优化完式(8) 之后, KL 散度的值太大,这就代表后面惩罚的项 β K L ( θ , θ k ) \beta KL(\theta, \theta^{k}) βKL(θ,θk) 没有发挥作用,此时需要将 β \beta β 增大,另外,再设一个 KL 散度的最小值。如果做完目标函数优化之后,KL 散度的值比最小值还要小,那么就说明后面这一项的效果太强了,担心优化时只优化后一项使得 θ \theta θ θ k \theta^k θk 一样,所以需要减小 β \beta β 。由于 β \beta β 是可以动态调整的,所以称之为自适应KL惩罚(adaptive KL penalty)

  • 如果 K L ( θ , θ k ) > K L m a x KL(\theta,\theta^k) > KL_{max} KL(θ,θk)>KLmax,增大 β \beta β
  • 如果 K L ( θ , θ k ) < K L m i n KL(\theta,\theta^k) < KL_{min} KL(θ,θk)<KLmin,减小 β \beta β

近端策略惩罚优化可表示为
J P P O θ k ( θ ) = J θ k ( θ ) − β K L ( θ , θ k ) J θ k ( θ ) ≈ ∑ ( s t , a t ) p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) A θ k ( s t , a t ) (10) J_{PPO}^{\theta^{k}}(\theta)=J^{\theta^{k}}(\theta) - \beta KL(\theta, \theta^{k}) \\ J^{\theta^k}(\theta) \approx \sum_{(s_t,a_t)}\frac{p_\theta(a_t|s_t)}{p_{\theta^k}(a_t|s_t)}A^{\theta^k}(s_t,a_t) \tag{10} JPPOθk(θ)=Jθk(θ)βKL(θ,θk)Jθk(θ)(st,at)pθk(atst)pθ(atst)Aθk(st,at)(10)

4. PPO-Clip

近端策略优化裁剪的目标函数中没有 KL 散度,其要最大化的目标函数为
J P P O − c l i p θ k ( θ ) ≈ ∑ ( s t , a t ) m i n ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) A θ k ( s t , a t ) , c l i p ( p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) , 1 − ϵ , 1 + ϵ ) A θ k ( s t , a t ) ) (11) J^{\theta^{k}}_{PPO-clip}(\theta) \approx \sum_{(s_t,a_t)}min \Big(\frac{p_\theta(a_t|s_t)}{p_{\theta^k}(a_t|s_t)}A^{\theta^k}(s_t,a_t), clip(\frac{p_\theta(a_t|s_t)}{p_{\theta^k}(a_t|s_t)},1-\epsilon,1+\epsilon)A^{\theta^k}(s_t,a_t)\Big) \tag{11} JPPOclipθk(θ)(st,at)min(pθk(atst)pθ(atst)Aθk(st,at),clip(pθk(atst)pθ(atst),1ϵ,1+ϵ)Aθk(st,at))(11)

  • $\epsilon $ 是一个超参数

PPO-Clip 直接在目标函数中进行限制,确保新旧策略差距不会过大,其核心思想是通过裁剪(clipping)的方式限制策略更新的幅度,从而在提高性能的同时,避免策略更新过渡导致的不稳定性。

p θ ( a t ∣ s t ) p θ k ( a t ∣ s t ) \frac{p_\theta(a_t|s_t)}{p_{\theta^k}(a_t|s_t)} pθk(atst)pθ(atst) 这一项是重要性采样,可以将它看作优势函数 A θ k ( s t , a t ) A^{\theta^k}(s_t,a_t) Aθk(st,at) 的比例因子 r θ r_\theta rθ ,它反映了前后两个策略在某个动作上的相对变化。裁剪操作会限制 r θ r_\theta rθ 的范围,防止某个动作的概率发生过大的相对变化。这种局部限制有助于间接控制新旧策略在整体分布上的差异幅度,从而避免策略更新过度导致训练不稳定,裁剪(clip)操作的具体实现为:
clip ⁡ ( r θ , 1 − ϵ , 1 + ϵ ) = { 1 − ϵ i f   r θ < 1 − ϵ , r θ i f   1 − ϵ ≤ r θ ≤ 1 + ϵ , 1 + ϵ i f   r θ > 1 + ϵ . (12) \operatorname{clip}(r_\theta,1-\epsilon,1+\epsilon)= \begin{cases} 1-\epsilon & \mathrm{if} \ r_\theta<1-\epsilon, \\ r_\theta & \mathrm{if} \ 1-\epsilon\leq r_\theta\leq1+\epsilon, \\ 1+\epsilon & \mathrm{if} \ r_\theta>1+\epsilon. & \end{cases} \tag{12} clip(rθ,1ϵ,1+ϵ)= 1ϵrθ1+ϵif rθ<1ϵ,if 1ϵrθ1+ϵ,if rθ>1+ϵ.(12)
从式(11)中可以看出 PPO-clip 中的目标函数的关键是 m i n min min 操作:
m i n ( r θ A θ k ( s t , a t ) , c l i p ( r θ , 1 − ϵ , 1 + ϵ ) A θ k ( s t , a t ) ) (13) min\Big(r_\theta A^{\theta^k}(s_t,a_t), clip(r_\theta,1-\epsilon,1+\epsilon)A^{\theta^k}(s_t,a_t)\Big) \tag{13} min(rθAθk(st,at),clip(rθ,1ϵ,1+ϵ)Aθk(st,at))(13)
r θ A θ k ( s t , a t ) r_\theta A^{\theta^k}(s_t,a_t) rθAθk(st,at) 是未加限制的目标,直接用重要性采样因子 r θ r_\theta rθ 来放大或缩小优势函数的作用;
c l i p ( r θ , 1 − ϵ , 1 + ϵ ) A θ k ( s t , a t ) clip(r_\theta,1-\epsilon,1+\epsilon)A^{\theta^k}(s_t,a_t) clip(rθ,1ϵ,1+ϵ)Aθk(st,at) 是经过裁剪的版本,来限制 r θ r_\theta rθ 的变化范围,防止策略更新幅度过大。

如上图(a)所示,如果 A>0 ,取最小的结果,即红色的线;如上图(b)所示,如果 A<0 ,取最小的结果也就是取 r θ r_\theta rθ 最大的结果,即红色的线。

优势函数 A 表示在某个状态下某个动作相对基线的好坏,如果 A>0 ,也就是该状态-动作对是好的,那么就可以增大这个状态-动作对的概率,也就是让 p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(atst) 越大越好,一直大到 1 + ϵ 1+\epsilon 1+ϵ 最大;如果 A<0 ,也就是该状态-动作对是不好的,那么就应该减小 p θ ( a t ∣ s t ) p_\theta(a_t|s_t) pθ(atst) ,一直减到 1 − ϵ 1-\epsilon 1ϵ 最小。

相比于 PPO-penalty ,PPO-clip 不需要动态调整 KL 散度的惩罚系数 β \beta β ,而是直接通过裁剪实现对策略更新幅度的控制,所以其计算效率更高,仅需要简单的比较运算,而 PPO-penalty 需要动态监控 KL 散度大小并调整惩罚系数。

在具体实现时优势函数可以使用 广义优势估计(GAE)。

参考:
1.李宏毅老师的课程的文字整理《强化学习教程》
2.张伟楠老师的书《动手学强化学习》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

亚博microros小车-原生ubuntu支持系列:11手指控制与手势识别

识别框架还是沿用之前的了MediaPipe Hand。 背景知识不摘重复&#xff0c;参见之前的&#xff1a;亚博microros小车-原生ubuntu支持系列&#xff1a;10-画笔-CSDN博客 手指控制 src/yahboom_esp32_mediapipe/yahboom_esp32_mediapipe/目录下新建文件10_HandCtrl.py&#xff…

OpenCV:在图像中添加高斯噪声、胡椒噪声

目录 在图像中添加高斯噪声 高斯噪声的特性 添加高斯噪声的实现 给图像添加胡椒噪声 实现胡椒噪声的步骤 相关阅读 OpenCV&#xff1a;图像处理中的低通滤波-CSDN博客 OpenCV&#xff1a;高通滤波之索贝尔、沙尔和拉普拉斯-CSDN博客 OpenCV&#xff1a;图像滤波、卷积与…

【模拟集成电路】锁相环(phase-locked loops,PLL)设计_环形振荡器相关(简)

0. 前言 未来将会不定时更新PLL相关的文章&#xff0c;主要目的是作为个人的学习笔记&#xff0c;关于锁相环的基础&#xff0c;可以参考《模拟CMOS集成电路设计_Behzad Razavi》后面几章的内容&#xff0c;下面的文章主要参考书籍是的英文书籍《DESIGN OF CMOS PHASE‑LOCKED …

Qt简单迷宫游戏

目录 你将学到你将准备你将改变你将设计你将编程开始界面游玩界面胜利界面其它bug修复 你可扩展下一篇博客要说的东西 你将学到 Qt中QKeySequence对象的基本创建Qt中QShortcut对象的基本应用Qt中QSoundEffect对象的基本应用 你将准备 在开始制作Qt简单迷宫游戏之前&#xff…

SSM电子商城系统

&#x1f345;点赞收藏关注 → 添加文档最下方联系方式咨询本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345; 项目视频 电…

springboot3 集成 knife4j(接口文档)

提示&#xff1a;文章是集成 knife4j&#xff0c;而非 swagger2 或者 swagger3&#xff0c;效果如图 文章目录 前言一、添加依赖二、如何集成1.配置文件2.注解部分1.Tag2.Operation3.Parameter4.Schema 3.使用 总结 前言 提示&#xff1a;&#xff1a;大家在开发阶段&#xff…

亚博microros小车-原生ubuntu支持系列:7-脸部检测

背景知识 官网介绍&#xff1a; Face Mesh - mediapipe mpFaceMesh.FaceMesh() 类的参数有&#xff1a;self.staticMode, self.maxFaces, self.minDetectionCon, self.minTrackCon staticMode:是否将每帧图像作为静态图像处理。如果为 True&#xff0c;每帧都会进行人脸检测…

写作利器:如何用 PicGo + GitHub 图床提高创作效率

你好呀&#xff0c;欢迎来到 Dong雨 的技术小栈 &#x1f331; 在这里&#xff0c;我们一同探索代码的奥秘&#xff0c;感受技术的魅力 ✨。 &#x1f449; 我的小世界&#xff1a;Dong雨 &#x1f4cc; 分享我的学习旅程 &#x1f6e0;️ 提供贴心的实用工具 &#x1f4a1; 记…

thingsboard 动态报警

前言 考虑将报警上下限写入设备属性&#xff0c;设备遥测数据与设备属性实时做报警逻辑。这样做的好处在于&#xff0c;可以动态修改设备属性&#xff0c;进而修改设备报警触发上下限。 1、修改设备属性 基于mq &#xff0c;向设备写入属性。 topic v1/devices/me/attribut…

三、双链表

链表的种类有很多&#xff0c;单链表是不带头不循环单向链表&#xff0c;但双链表是带头循环双向链表&#xff0c;并且双链表还有一个哨兵位&#xff0c;哨兵位不是头节点 typedef int LTDataType;typedef struct ListNode{struct ListNode* next; //指针保存下⼀个结点的地址s…

(算法竞赛)使用广度优先搜索(BFS)解决迷宫最短路径问题

在这个充满奇思妙想的世界里&#xff0c;每一次探索都像是打开了一扇通往新世界的大门。今天&#xff0c;我们将踏上一段特别的旅程&#xff0c;去揭开那些隐藏在代码、算法、数学谜题或生活智慧背后的秘密。&#x1f389;&#x1f60a; 所以&#xff0c;系好安全带&#xff0…

支持大功率输出高速频闪的图像处理用光源控制器

机器视觉系统中的光源控制器在确保图像质量、提高系统稳定性、降低能耗以及方便系统扩展和升级等方面发挥着重要作用。它可提供稳定光源&#xff0c;调节参数&#xff0c;另外具有操作便捷性。 下面我们来看Gardasoft的光源控制器&#xff0c;Gardasoft拥有作为图像处理用LED光…

鸿蒙模块概念和应用启动相关类(HAP、HAR、HSP、AbilityStage、UIAbility、WindowStage、window)

目录 鸿蒙模块概念 HAP entry feature har shared 使用场景 HAP、HAR、HSP介绍 HAP、HAR、HSP开发 应用的启动 AbilityStage UIAbility WindowStage Window 拉起应用到显示到前台流程 鸿蒙模块概念 HAP hap包是手机安装的最小单元&#xff0c;1个app包含一个或…

为什么IDEA提示不推荐@Autowired❓️如果使用@Resource呢❓️

前言 在使用 Spring 框架时&#xff0c;依赖注入&#xff08;DI&#xff09;是一个非常重要的概念。通过注解&#xff0c;我们可以方便地将类的实例注入到其他类中&#xff0c;提升开发效率。Autowired又是被大家最为熟知的方式&#xff0c;但很多开发者在使用 IntelliJ IDEA …

【Uniapp-Vue3】uni-icons的安装和使用

一、uni-icon的安装 进入到如下页面中&#xff0c;点击“点击下载&安装”。 uni-icons 图标 | uni-app官网 点击“下载插件并导入HBuilder”&#xff0c;如果没有登录就登陆一下 网页中会打开Hbuilder&#xff0c;进入Hbuilder以后&#xff0c;选择需要使用该插件的项目进…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(三)

Understanding Diffusion Models: A Unified Perspective&#xff08;三&#xff09; 文章概括 文章概括 引用&#xff1a; article{luo2022understanding,title{Understanding diffusion models: A unified perspective},author{Luo, Calvin},journal{arXiv preprint arXiv:…

群晖docker获取私有化镜像http: server gave HTTP response to HTTPS client].

群晖docker获取私有化镜像提示http: server gave HTTP response to HTTPS clien 问题描述 层级时间用户事件Information2023/07/08 12:47:45cxlogeAdd image from xx.xx.31.240:1923/go-gitea/gitea:1.19.3Error2023/07/08 12:47:48cxlogeFailed to pull image [Get "http…

Charles 4.6.7 浏览器网络调试指南:HTTPS抓包(三)

概述 在现代互联网应用中&#xff0c;网络请求和响应是服务交互的核心。对于开发者和测试人员来说&#xff0c;能够准确捕获并分析这些请求&#xff0c;是保证系统稳定性和性能的关键。Charles作为一个强大的网络调试工具&#xff0c;不仅可以捕获普通的HTTP请求&#xff0c;还…

从spec到iso的koji使用

了解一下Linux发行版流程&#xff1a;:从spec到iso的koji使用 for Fedora 41。 Fedora 41有24235个包&#xff0c;我们选择 minimal 的几十个源码包&#xff0c;百多个rpm包构建。 配3台服务器 40C64G 44C64G 80C128G&#xff0c;有点大材小用&#xff0c;一台就够了 &#xf…

系统思考—复杂问题的根源分析

在企业中&#xff0c;许多问题看似简单&#xff0c;背后却潜藏着复杂的因果关系。传统的思维方式往往只能看到表面&#xff0c;而无法深入挖掘问题的真正根源。我们常常通过“表面解决”来应对眼前的症状&#xff0c;但这往往只是治标不治本。 比如&#xff0c;销量下降时&…