深度强化学习Reinforcement Learning|PG|Actor-Critic|A3C|DDPG

devtools/2024/9/19 14:10:38/ 标签: 人工智能, 机器学习, python

目录

一、PG(Policy Gradient)策略梯度算法(on-policy)

1、策略梯度公式推导

2、代码讲解/伪代码

3、改进

3.1Trick Baseline

3.2 Suitable Credit

二、Actor-Critic算法

三、A3C算法

四、DDPG算法


前言

    我们都知道强化学习环境env的不确定性是比较突出的一个特点,那么有很多情况下时需要使用强化学习设计仿真实验的,经常会设置一些随即参数,比如随机正态分布或者一个随机的常量,这种我们就正常使用科学计算库numpy或者深度学习框架比如Tensorflow自带的随机就可以,但这种经常会遇到一个问题,包括我本人在最开始设计深度强化学习相关的仿真实验之后,假设每次都训练1000次,但是我运行多次之后发现结果参数(比如reward、loss、Q-value、V-value等)不一样,有几次巧合会是一致的,经查阅资料得出:

在设置参数时正常使用函数功能,只不过需要在导入库文件之后,设置随机种子seed(如下所示),这样就会使得实验结果可复现,也就是说每次运行相同的代码时,只要在相同实验中的随机种子保持一致,那么每次训练模型得到的随即结果将会是一致的,使得结果相对可靠些。并且即使是在不同的实验中,也建议把随机种子设置为一致的,这样可以确保对比的合理性。

python"># 用到哪个框架使用哪个框架设置随机种子
tf.random.set_seed(1)
np.random.seed(1)

简单介绍一下on-policy和off-policy的区别:对于on-policy算法,边交互边学习,即学习和评估是基于同一个策略进行的,智能体与环境交互的过程中,使用当前的策略生成数据,并利用这些数据来更新策略;而对于off-policy算法,则学习和评估可以基于不同的策略进行,智能体可以利用一个策略下探索环境,而在另一个策略下评估和学习。

一、PG(Policy Gradient)策略梯度算法(on-policy)

    下面讲解一下策略梯度算法,PG是一种基于策略(policy-based)的算法它与传统的基于值的(value-based)算法最不一样的点就在于,PG算法依靠直接训练神经网络NN(网络参数即为策略)输入state输出action,过程中不需要计算Q值。而传统的基于值的算法需要使用Q-Table或Q-Network然后通过评判Critic值函数的方式反过来选择action。

    若把DQN看成是TD+神经网络的算法,那么PG就是一个MC+神经网络的算法。利用reward奖励直接对选择动作的可能性干预,好的action会被增加下一次交互被选择的概率,而坏的action则会被减小下一次交互被选择的概率。

1、策略梯度公式推导

图1

    首先,PG直接使用Actor与环境env交互,每个episode回合最后可以加得一个total reward, 

,由于PG目标就是最大化一整个回合之后的total reward。注意,由于NN中的参数一开始可能是随机的,但环境env肯定具有随机性,即使使用同一个Actor,在遇到同一环境中的相同场景时,也会有做出不同决策(action)的概率,故每次的total reward也大概率会不同。那如果这样的话,对于R_{\theta}来说,它就是一个随机变量,我们的目标不能仅靠最大化某一次的R_{\theta}而是要最大化它的期望值,这样就可以从算法的角度(不包含实际应用问题)来衡量Actor决策的好坏

    接着 ,讨论一下该如何计算? 

假设一个回合的轨迹如下,\tau ={s_{1},a_{1},r_{1},s_{2},a_{2},r_{2},...,s_{T},a_{T},r_{T}},故在这一回合的累计回报可以表示为R(\tau)=\sum_{t=1}^{T}r_{t},然后如果使用Actor与环境交互,每个回合都有被采样的概率,用p(\tau|\theta)表示在网络参数也就是策略下,轨迹\tau的概率,这样的话,与环境交互N次的期望就可以表示为:

     再然后,我们就可以进一步表示出最后的优化目标:

maximize \bar{R_{\theta}}=E[R_{\theta}]=\frac{1}{N}\sum_{n=1}^{N}R(\tau^{n})

希望找到最优的策略\theta^{*}使得\bar{R_{\theta}}最大,故这里可以通过\theta^{*}=argmax_{\theta}\bar{R_{\theta}}表示。

再然后,很明显,\bar{R_{\theta}}作为最后的目标函数,并由于PG是策略梯度算法,则最后一定会用到梯度下降或梯度上升算法,由于是maximize,所以,对\bar{R_{\theta}}求导,做梯度上升更新网络参数即可最大化\bar{R_{\theta}},详细过程如下,

然后,将

 再然后,

 综上,

这样理解起来就容易了,整体梳理一下,如下

    我们需要使用梯度上升法优化寻找函数的最大值,在本情况下,需要最大化期望回报\bar{R_{\theta}}然后分别需要先计算目标函数\bar{R_{\theta}}的梯度以及根据梯度策略更新参数\theta如下公式,\theta\leftarrow \theta+\alpha \bigtriangledown \bar{R_{\theta}}.由于上述求解出的\bigtriangledown \bar{R_{\theta}}可以看出,最后希望求得最大的期望回报,那么由于其是导函数只需要让其大于0使得\bar{R_{\theta}}单调递增即可。当R(\tau)大于0时,只需要让log p的导函数大于0即可,由下述推导1(将推导1中的x替换为\theta)可得p(a_{t}^{n}|s_{t}^{n},\theta)越大越好;反之,当R(\tau)小于0时,则是希望p(a_{t}^{n}|s_{t}^{n},\theta)越小越好,也是得出的最后\bigtriangledown \bar{R_{\theta}}大于0。

推导1:\frac{d log p(x)}{dx}=\frac{1}{p(x)}\cdot \frac{dp(x)}{dx},对于概率p(x)肯定是恒大于0的,因此\frac{1}{p(x)}也恒大于0,

那只有当p(x)随着x的增加而增加时,\frac{dp(x)}{dx}也就大于0,这样的话,\frac{d log p(x)}{dx}也就大于0了,进而推导出log p(x)是随着x的增加而增加的;相反,当p(x)随着x的增加而减小时,\frac{dp(x)}{dx}也就小于0,进而可以推导出\frac{d log p(x)}{dx}也就小于0了,也就是log p(x)是随着x的增加而减小的。

2、代码讲解/伪代码

初始化策略参数 θ
设置学习率 α对于每个训练回合 do初始化状态 s初始化回合奖励 total_reward = 0初始化存储的状态、动作和奖励列表直到回合结束 do根据当前策略 π(a|s; θ) 选择动作 a执行动作 a,观察下一个状态 s' 和奖励 r存储状态 s、动作 a 和奖励 rs = s'total_reward += r计算每个时间步的累计奖励 Gt对于每个时间步 t do计算优势函数 A(s_t, a_t) = Gt - V(s_t; θ)  # 可以用价值函数估计计算策略的梯度 ∇θ log(π(a_t|s_t; θ)) * A(s_t, a_t)更新策略参数 θ = θ + α * ∇θ log(π(a_t|s_t; θ)) * A(s_t, a_t)如果需要更新价值函数 V(s; θ):使用 TD 学习或蒙特卡洛方法更新 V(s; θ)返回策略参数 θ
基于策略的PG算法初始化学习率α,折现率超参数
初始化网络参数θ
输入:状态s
输出:每个动作对应的概率值1:构建神经网络 fc1 = tanh(s * weight + b);all_act = (fc1 * weight + b).self.all_act_prob = softmax(all_act).
2:计算损失 neg_log_prob = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=all_act, labels=self.tf_acts);loss = tf.reduce_mean(neg_log_prob * self.tf_vt)
这里的all_act是一个Tensor,代表着每个动作相对的得分,这里labels=self.tf_acts则表示实际选择到的动作。
3:衰减回合计算的reward:(用作动作值)
# 主要函数如下
def _discount_and_norm_rewards(self):discounted_ep_rs = np.zeros_like(self.ep_rs)  # 创建一个与 ep_rs 同样大小的数组,用于存储折扣回报running_add = 0  # 初始化一个变量,用于累加折扣奖励# 回溯,倒序,self.ep_rs为一个列表for t in reversed(range(0, len(self.ep_rs))):# G值running_add = running_add * self.gamma + self.ep_rs[t]  # 计算当前时间步的折扣回报discounted_ep_rs[t] = running_add  # 将计算得到的 G 值存入 discounted_ep_rs# 归一化一回合的 rewardsdiscounted_ep_rs -= np.mean(discounted_ep_rs)  # 减去均值# 除以标准差,进行标准化discounted_ep_rs /= np.std(discounted_ep_rs) if np.std(discounted_ep_rs) > 0 else 1  # 标准化return discounted_ep_rs  # 返回计算得到的折扣回报

3、改进

3.1Trick Baseline基准线
图2

如图2所示,加入基线就是在原来的目标累计回报求导公式中的某一个轨迹\tau^{n}基础上减b,为什么要减去b呢?下面来解释一下原因,以及加入基线b的好处, 

     真实环境中的奖励reward并不是分布在0周围,很多游戏的奖励全是正数,由第二部分代码讲解中就可以知道,policy-based的算法直接在神经网络输出对应动作的概率值,对应的R(\tau^{n})(每个轨迹的total reward)若大于0,则在该回合的轨迹中被采样到的动作action所对应的概率就会被增加(也就是说鼓励该动作的发生),相反,就会被减小。一般在训练agent时,会跑很多个回合,若未加入基线b的话,有这样一种情况,当所有的R(\tau^{n})(1=<n<=N)都大于0时,那么被采样到的轨迹中的所包含的动作对应的概率也会变大只不过变大的程度不一样,有大有小,那这样的话,轨迹中未被采样到的动作对应的概率就一定会变小,很明显,这不是我们所希望的看到的,理想情况下未被采样的动作我们希望的是让其对应的概率并不发生改变。这种方式会严重导致策略过于集中在某些动作上,忽略了未被采样到的动作。 

    加入基线b之后,b其实就相当于平均值,整体对奖励信号进行平均归一化操作。希望R(\tau^{n})能分布在0周围,它代表了R(\tau^{n})的平均水平,会导致有些轨迹对用的total reward是大于0的,代表此轨迹中动作的表现要高于平均水平;有些轨迹对应的totla reward是小于0的,代表此轨迹中的动作的表现差于平均水平,让其有正有负。这样的话,当total reward大于0时,会把轨迹中包含的动作的概率增大,当total reward小于0时,就会把轨迹中所包含的动作对应的概率值减小,两种情况的未被当前轨迹采样的动作所对应的概率值都要保持不变(理想状态下),但实际上,基线的引入可以帮助在多个回合中未被采样的动作的概率不会因为某些轨迹的总回报而发生剧烈的变化(多个小变化)。通过这种方式,基线帮助我们在更新策略时保持相对的平衡,避免了过度集中在某些动作上。

图3
3.2 因果性

      在之前的所有算法中,对于时间戳为t的动作a,它对\tau_{1:t-1}并没有影响,而只是对后续的\tau_{t:T}产生影响,因此只考虑从时间戳为t开始的累计回报R(\tau_{t:T}),公式如下所示。轨迹只要是在同一个轨迹中的所有的state->action都会有相同的reward权重,显然有些情况是不那么公平的,因为明显是在某些回合中有些action非常好,但在有些回合中的action并不好,综上,也就是说整个回合的整体回报好并不能代表每个动作都是好的,相反,整个回合的整体回报不好,并不能代表每个动作action都不好,因此,我们希望给不同的action上乘以不同的权重weight,如图4所示,

图4

左右两侧才有控制变量法,保证三个状态都分别相同,然后让对应的动作和对应的reward不同,可以看出,让一个轨迹的整体回报产生差异的主要在(s_{a},a_{1})(s_{a},a_{2}),相同的状态,但是采取的动作不一样,产生的即使奖励reward不同,进而导致不同的两条轨迹之间的整体回报不同。

图5

 再然后,其实Q函数Q(s_{t},a_{t})就是代表着从状态st开始执行动作at后的策略的回报估计值可以将R(\tau^{n})替换为Q(s_{t},a_{t}),则有

\bigtriangledown R_{\theta}=\frac{1}{N}\sum_{n=1}^{N}\sum_{t=1}^{T_{n}}(Q(s_{t},a_{t})-b)\bigtriangledown logp_{\theta}(a_{t}^{n}|s_{t}^{n}).

Q(s_{t},a_{t})-b称为优势值函数,它代表了当前动作序列的回报相比于平均回报的优势程度。为什么可以这样看呢?主要就是因为由于Q(st,at)具体指的是在状态st条件下,选取动作at对应的状态动作值,这个动作at是唯一的,而b呢,可以使用V(st)来估计,V即是指状态值,代表的是在状态st条件下,所有的动作对应的累计回报的期望值,相当于一个平均值,因此使用Q-V可以评判出该动作相比于平均回报的优势程度,若结果大于0,则说明选择该动作是积极的positive,但若结果小于0,则说明该动作是消极的negative,结果是一个标量(scalar)。

如图5所示,将每一个轨迹的总回报换为从时间步t开始的累计加权奖励。PG用的就是MC的G值,也就是PG是用MC的G值来更新网络,也就是说,PG先会让智能体从某条轨迹一直走到最后,然后通过回溯计算G值。

讨论:添加基线b之后会不会改变\bigtriangledown \bar{R_{\theta}}

由于\bigtriangledown \bar{R_{\theta}}分别对不同轨迹的回报求和取平均(期望),所以这一时刻我们只需要判断E[\bigtriangledown logp_{\theta}(a_{t}^{n}|s_{t}^{n})\cdot b]=E_{\tau\sim p_{\theta}(\tau)}[\bigtriangledown logp_{\theta}(\tau)\cdot b]是否等于0即可,下面,将其展开得:

E_{\tau\sim p_{\theta}(\tau)}[\bigtriangledown log\pi_{\theta}(\tau)\cdot b]=\int \pi_{\theta}(\tau)\bigtriangledown _{\theta}log\pi_{\theta}(\tau)\cdot bd_{\tau}

 \pi_{\theta}(\tau)\bigtriangledown _{\theta}log\pi_{\theta}(\tau)=\bigtriangledown _{\theta}\pi_{\theta}(\tau)(代入).

可得:

 E_{\tau\sim p_{\theta}(\tau)}[\bigtriangledown log\pi_{\theta}(\tau)\cdot b]=\int\bigtriangledown_{\theta}\pi_{\theta}(\tau)bd_{\tau}=b\bigtriangledown_{\theta}\int\pi_{\theta}(\tau)d_{\tau}.(\int\pi_{\theta}(\tau)d_{\tau}=1)\Rightarrow b\bigtriangledown_{\theta}1=0.

这样就是加入基线b之后,并不会改变 \bigtriangledown \bar{R_{\theta}},但是却会是减小方差(减小离散程度)。

二、Actor-Critic算法(即可以离散动作空间,又可以连续动作空间)

    Actor-Critic其实就是用两个网络,两个网络均输入状态S,但一个网络输出策略\pi,即负责选择动作,这个网络称为Actor表演者;另一个网络负责评分,计算每个动作对应的分数,这个网络被称为Critic评判者。如图6所示,主要的过程就是Actor网络运用策略\pi选择动作action,然后Critic网络对其打分,评估相应的值函数,Actor网络通过Critic评判的值函数进行相应的学习,若Critic评判的值函数较好,则Actor会对轨迹中的动作的输出概率相应的增大;相反,若Critic评判的值函数较差,则Actor会把相应轨迹中的动作的输出概率减小。综上,就是说Actor就是用于优化策略的,使得动作越来越好,而Critic适用于评估值函数的,使得对其的估计越来越准确。

图6

 算法

伪代码 
分别初始化Actor网络和Critic网络的学习率
下面主要分为两个网络讲解Actor-Critic架构
一、Actor表演者网络中,Actor网络主要负责产生策略pi
1、在网络中输入状态s,经过可能几层神经网络的正向传播,最后一层加上蒸馏函数softmax,直接将各个动作对应的动作值转化为各个动作所占的百分比。
2、然后Actor网络采用从Critic网络传来的TD-error以及对动作的对应的概率求log计算期望V值(公式为logP * (r + gamma * vt+1 - vt)). 
3、 由于目标是要最大化最后的v的期望值,故应采用梯度上升算法,具体操作为 self.train_op = tf.train.AdamOptimizer(lr).minimize(-self.exp_v).可以这样做的原因就是minimize是采用梯度下降算法最小化的过程,故若想要取最大值,将其参数加一个负号即可更改为梯度上升法.二、Critic网络中,Critic网络主要适用于估计V值以及计算TD-error的。 -> 
1、在网络中输入状态s,神经网络正向传播计算得到V值 
2、然后根据下一时刻状态s_将其传入到神经网络中得到计算结果v_,然后目标为r + gamma * v_,估计值为v,故可以通过时序差分估计出TD-eror = r + gamma * v_ - v. -> 
3、然后,将损失函数设定为均方差损失函数简称为MSE,这块包括反向传播(链式求导),采用优化器比如Adam通过梯度下降法最小化损失函数,然后将TD-error传到Actor网络中即可。最后这一步的过程都体现在self.train_op = tf.train.AdamOptimizer(lr).minimize(self.loss)中,也包括vt的更新公式由于有V的更新公式vt = vt + 学习率 * (r + gamma * vt+1 - vt).

改进:Advantage Actor-Critic,简称为A2C算法

    Avantage Actor-Critic算法,它是目前使用Actor Critic思想的主流算法之一,其实Actor Critic系列算法不一定要使用优势函数A(s,a),还可以有其他的变种。

Avantage Actor-Critic算法在训练时,Actor会根据当前的状态st和策略采样获得动作at,并与环境交互得到下一时刻的状态st+1和奖励rt,通过TD方法即可估计每一步的状态st对应的V值,从而更新Critic网络使得值网络的估计更加接近真实环境的期望回报,通过At = rt + gammaV(st+1) - V(st)来估计当前动作的优势值,并采用E[\bigtriangledown logp_{\theta}(a_{t}^{n}|s_{t}^{n})\cdot A(a_{t}^{n},s_{t}^{n})]计算Actor网络的梯度信息,如此循环网络采用梯度上升法,最大化总汇报的期望值,Critic网络会判断的越来越准确,而Actor网络也会调整自己的策略,使得下一次做得更好。

一般地,使用Advantage Actor-Critic算法就是Actor-Critic结构的扩展了,够用。

三、A3C算法

    Asynchronous Advantage Actor-Critic,简称为A3C。这是基于Advantage Actor Critic网络之后的一个异步版本,将Actor-Critic网络部署在多个线程中同时进行训练,并通过全局网络来同步参数,这种异步训练的模式大大提升了训练效率,训练速度更快,并且算法性能也更好。

    在A3C算法中,算法会创建一个全局网络Global Network和M个Worker线程,Global Network包含Actor和Critic网络,每个线程均会创建一个交互环境和Actor和Critic网络。Global Network并不直接参与到与环境的互动当中,而是每个Worker与环境有直接的交互,并且把所学习到的梯度信息上传到Global Network。初始化阶段Global Network随机初始化参数\theta\phi,Worker中的Actor-Critic网络从Global Network中同步拉取最新参数来初始化网络。在训练时,Worker中的Actor-Critic网络首先先从Global Network拉取最新参数,然后在最新策略\pi下与私有环境交互采样动作,并根据Advantage Actor-Critic算法计算参数\theta\phi的梯度信息。在完成梯度计算之后,各个Worker将梯度信息提交到Global Network中,并利用Global Network的优化器完成Global Network的网络参数更新。在算法测试阶段只使用Global Network与环境交互即可。

算法

执行流程

伪代码

四、DDPG算法(Online-Policy,在连续动作空间上表现较好)

    Deep Deterministic Policy Gradient,简称DDPG,深度确定性算法。DDPG也是解决连续控制型问题的一个算法,不过和PPO不一样,PPO输出的是一个策略,也就是一个概率分布,而DDPG输出的则直接是一个动作action,从DQN来延伸理解DDPG,相比于DQN算法而言,主要沿用了以下三种技术:

1、经验回放(Experience replay)

agent将每一轮与环境交互得到的经验数据(s,a,r,s',done)存入回放缓存(replay buffer)中,更新网络参数时按照批量的方式采样,能加快训练速度,提高训练的稳定性,但是比较耗费内存(memory)资源和计算(computation)资源.

对于经典的经验回放,就是分为存储和回放两个部分。

存储的角度看,经验回访可以分为集中式存储和分布式存储

集中式存储:智能体在一个环境中运行,把经验统一存储在经验池中。

分布式存储:多个智能体同时在多个环境中运行,并将经验统一存储在经验池中。由于多个智能体同时生成经验,所以能够使用更多资源的同时更快的收集经验。

采样的角度来看,回放也就是从存储中批量取数据的方式可以分为均匀回放和优先回放

均匀回放:等概率的从经验池中采样经验(数据)。

优先回放:为每条经验设定一个优先级,在采样经验时更倾向于选择优先级别更高的经验,证明其越需要被学习。

2、目标网络(fixed target Q-Network)

除了AC架构中的Actor和Critic网络,再使用一套Target Actor和Target Critic网络,这样在更新目标网络时,为避免网络参数更新过快带来的不稳定,采用软更新的方式.

3、噪声探索(Noisy explore)

确定性策略输出的动作为确定性动作,缺乏对于环境的探索,在训练阶段,给Actor网络输出的动作加入噪声,从而让智能体具有一定的探索能力.

伪代码

参考论文:https://arxiv.org/abs/1602.01783


http://www.ppmy.cn/devtools/113028.html

相关文章

【Android】处理线程中未捕获的异常

需求 项目出现异常问题&#xff0c;获取崩溃信息 实现 定义一个类&#xff0c;实现【Thread.UncaughtExceptionHandler】方法 public class CrashHandler implements Thread.UncaughtExceptionHandler {private static CrashHandler sInstance new CrashHandler();private…

探索CentOS:入门必备基础知识

探索CentOS:入门必备基础知识 CentOS(Community ENTerprise Operating System)是一个基于Red Hat Enterprise Linux(RHEL)的开源操作系统,广泛应用于服务器环境。对于初学者来说,掌握CentOS的基础知识是非常重要的。本文将详细介绍CentOS的安装、基本命令、软件管理、用…

OpenCV结构分析与形状描述符(20)计算一个包围给定点集的最小外接圆函数minEnclosingCircle()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 找到一个包围二维点集的最小面积的圆。 该函数使用迭代算法来寻找一个二维点集的最小外接圆。这意味着函数将会通过反复逼近的过程来计算出能够…

嵌入式Linux学习笔记(5)-进程间常见通讯方式(c语言实现)

一、概述 进程间通信&#xff08;IPC&#xff0c;InterProcess Communication&#xff09;是指在多个进程之间进行数据传输和共享的机制。在操作系统中&#xff0c;进程是运行中的程序的实例&#xff0c;每个进程都有自己的内存空间和资源。 进程间通信可以用于在不同的进程之间…

网站按钮检测系统源码分享

网站按钮检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

树莓派5上手

1 安装系统 Raspberry Pi OS 是基于 Debian 的免费操作系统&#xff0c;针对 Raspberry Pi 硬件进行了优化。Raspberry Pi OS 支持超过 35,000 个 Debian 软件包。树莓派 5 可以安装各种系统&#xff0c;但是如果对于系统没有特殊的要求&#xff0c;还是安装 Raspberry Pi OS …

基于Keil软件实现读写备份寄存器(江协科技HAL库)

读写备份寄存器实验是基于江协科技STM32的HAL库工程模板创建的(可以在作品“基于江科大STM32创建的HAL库工程模板”中的结尾处获取工程模板的百度网盘链接) 复制“OLED显示”的工程文件——“4-1 OLED显示屏”,并命名为“12-1 读写备份寄存器 ”。打开工程,把下面的程序复…

科技之光,照亮未来之路“2024南京国际人工智能展会”

全球科技产业的版图正以前所未有的速度重构&#xff0c;而位于中国东部沿海经济带的江浙沪地区&#xff0c;作为科技创新与产业升级的高地&#xff0c;始终站在这一浪潮的最前沿。2024年&#xff0c;这一区域的科技盛宴——“2024南京人工智能展会”即将在南京国际博览中心盛大…

Linux基础---08软件的安装

安装方式优缺点编译安装自由定制&#xff0c;但较为繁琐rmp安装安装简单&#xff0c;但需要自己解决依赖&#xff0c;不支持定制yum安装自动解决rmp依赖&#xff0c;但不支持定制&#xff08;用的更多&#xff09; 下面就具体介绍三大安装方式&#xff1a; 一.编译安装 用Li…

Spring Boot-WebSocket相关问题

Spring Boot 中的 WebSocket 相关问题及解决方案 WebSocket 是一种双向的实时通信协议&#xff0c;它允许客户端和服务器之间建立持久连接&#xff0c;并在此连接上双向传输数据。与传统的 HTTP 请求-响应模型不同&#xff0c;WebSocket 能够显著减少网络开销和延迟&#xff0…

信息安全数学基础(14)欧拉函数

前言 在信息安全数学基础中&#xff0c;欧拉函数&#xff08;Eulers Totient Function&#xff09;是一个非常重要的概念&#xff0c;它与模运算、剩余类、简化剩余系以及密码学中的许多应用紧密相关。欧拉函数用符号 φ(n) 表示&#xff0c;其中 n 是一个正整数。 一、定义 欧…

MySQL索引优化与B+树【后端 14】

MySQL索引优化与B树 在MySQL数据库中&#xff0c;索引是优化查询性能的关键技术之一。B树作为一种广泛使用的索引结构&#xff0c;在MySQL的InnoDB存储引擎中扮演着核心角色。本文将详细介绍B树的结构特点及其在MySQL索引优化中的应用。 B树的结构特点 B树是B-树的一个变体&a…

AIGC图片相关知识和实战经验(Flux.1,ComfyUI等等)

最近看了网上的一些新闻&#xff0c;flux.1火出圈了&#xff0c;因此自己也尝试跑了一下&#xff0c;作图的质量还是蛮高的&#xff0c;在这里做个知识总结回顾。 flux.1是什么&#xff1f; 根据介绍&#xff0c;flux.1是由stable diffusion 一作&#xff0c;Stability AI的核…

计算机二级office操作技巧——Excel篇

文章目录 函数公式总结写在前面五大基本函数sum求和函数average求平均函数max求最大值函数min求最小值函数count求个数函数 rank排名函数if逻辑判断函数条件求个数函数countif单条件求个数函数countifs多条件求个数函数 条件求和函数sumifs多条件求和函数sumproduct乘积求和函数…

stm32单片机个人学习笔记3(GPIO输出)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

【VUE】实现当前页面刷新,刷新当前页面的两个方法(如何在一个页面写一个方法提供给全局其他地方调用)(如何重复调用同一个路由实现页面的重新加载)

实现前端某个页面刷新&#xff0c;非F5整个系统刷新&#xff0c;只刷新内容部分&#xff0c;按具体需求可以采用一下两个方式实现 方法一&#xff1a;路由快速切换实现页面重新加载 特点&#xff1a;只刷新页面而不是整个系统&#xff0c;样式会重新加载 本质&#xff1a;如何…

外包干了半年,快要废了。。。

先说一下自己的情况&#xff0c;普通本科&#xff0c;在外包干了半年多的功能测试&#xff0c;这几年因为大环境不好&#xff0c;我整个人心惊胆战的&#xff0c;怕自己卷铺盖走人了&#xff0c;我感觉自己不能够在这样蹉跎下去了&#xff0c;长时间呆在一个舒适的环境真的会让…

句子成分——每日一划(八)

目录 一、原句 二、第一部分 三、第二部分 一、原句 In class society everyone lives as a member of a particular class, and every kind of thinking, without exception, is stamped with the brand of a class. 来源&#xff1a;二、阶级和阶级斗争 二、第一部分 In…

代码随想录 第九章 动态规划part03 01背包问题 一维 416. 分割等和子集

01背包问题 一维 #include <bits/stdc.h> using namespace std; int main(){int n, bagWeight;cin >> n >> bagWeight;std::vector<int> value(n, 0);std::vector<int> weight(n, 0);for (int i 0; i < n; i) cin >> weight[i];for (…

Dubbo 与 Zookeeper 在项目中的应用:原理与实现详解

引言 在微服务架构日益普及的今天&#xff0c;如何实现服务的高效调用和管理成为了关键问题。Dubbo 作为阿里巴巴开源的高性能 RPC 框架&#xff0c;在分布式服务治理方面具有显著的优势。Zookeeper 作为一款分布式协调服务&#xff0c;能够高效地管理和协调服务节点信息。因此…