强化学习笔记:从Q学习到GRPO

推荐学习huggingface的强化学习课程,全面了解强化学习的发展史。
以下是个人笔记,内容不一定完整,有些是个人理解。

基于值函数(value function)的强化学习

  • 基于值函数(value function)的强化学习:学习的是一个值函数,即Q函数,其能够给出当前state下每个action的Q值。在部署时,可以选择贪婪策略,那么每个state都会选择Q值最大的action,因此策略属于固定策略(state确定时,只有唯一确定的action);或使用随机策略,Q值越大被选择概率越大。
    在这种情况下,重点是学习出一个Q函数,而policy本身是预定义的简单规则(不是神经网络)。

Q与V的定义

V(s): 状态价值函数,表示从这个状态开始,直到最终状态,预期得到的反馈的总和
Q(a,s):动作价值函数,表示从这个状态开始并采取这个action,预期得到的反馈的总和
两者可以互相计算,V就是这个state下所有action在策略下的期望
[图片]

因此,如果选取的是贪婪策略,则 V(st+1)=max⁡aQ(st+1,a)V(s_{t+1})=\max_aQ(s_{t+1},a)V(st+1)=maxaQ(st+1,a),即Q值最大的action的Q值。这在Q学习中将会用到
Q(st,a)=r+γV(st+1)Q(s_t,a)=r+\gamma V(s_{t+1})Q(st,a)=r+γV(st+1),即在 sts_tst采取了a后,获得了反馈r并进入了st+1s_{t+1}st+1

更新V

由于理论上计算V(s)需要从s开始遍历之后所有路径,非常麻烦,因此提出了贝尔曼公式,使用 V(st+1)V(s_{t+1})V(st+1) 来计算 V(st)V(s_t)V(st)
更新V有2种方法,蒙特卡洛法和时间差分法。
蒙特卡洛法:从S_t开始,走到最终状态,计算轨迹上累积的反馈,以此更新V(st)V(s_t)V(st)
时间差分法:从S_t开始,只需要走一步,获取Rt+1R_{t+1}Rt+1St+1S_{t+1}St+1,使用 RtR_tRtV(st+1)V(s_{t+1})V(st+1) 来更新V(st)V(s_t)V(st)。这符合bellman 公式。由于未来的价值与现在的价值不一定等价,我们需要更加考虑现在,因此会给未来的价值一个折扣率,例如γ=0.9\gamma=0.9γ=0.9
[图片]

Q学习

著名的Q学习就是使用 TD learning 来更新Q表,给每个 state-action 对 更新Q值。与环境交互的策略通常是ε-greedy,即ε概率纯随机选取action,1-ε概率选取贪婪action。
使用TD learning+贪婪策略来更新Q值,公式如下
Q(St,At)←Q(St,At)+α[Rt+1+Q(St+1,At+1)−Q(St,At)]Q(S_t,A_t)\leftarrow Q(S_t,A_t)+\alpha[R_{t+1}+Q(S_{t+1},A_{t+1})-Q(S_t,A_t)]Q(St,At)Q(St,At)+α[Rt+1+Q(St+1,At+1)Q(St,At)]
贪婪策略下,A_{t+1}根据最大q值来选取,因此可以写为:
Q(St,At)←Q(St,At)+α[Rt+1+γmaxaQ(St+1,a)−Q(St,At)]Q(S_t,A_t)\leftarrow Q(S_t,A_t)+\alpha[R_{t+1}+\gamma max_aQ(S_{t+1},a)-Q(S_t,A_t)]Q(St,At)Q(St,At)+α[Rt+1+γmaxaQ(St+1,a)Q(St,At)]
因为我们实际运行使用ε-greedy,而更新Q值却使用贪婪策略,这两个策略不一样,因此Q学习属于 off-policy
Sarsa是Q学习的变体,属于on-policy,其在更新Q值时会使用ε-greedy选取action A 来计算 Q(St+1,A)Q(S_{t+1},A)Q(St+1,A),因此属于 on-policy

深度Q学习

由于更复杂的游戏中,state的数量无法完全列出,我们只能把当前游戏截图作为state(通常是连续的多帧截图,这样才能反映物品的运动方向),输入给神经网络θ,神经网络给出每个action的Q值。

于是,我们将 Q-Loss=Q-target−Qθ(St,At)=Rt+1+γmaxaQ(St+1,a)−Qθ(St,At)\text{Q-Loss}=\text{Q-target}-Q_\theta(S_t,A_t)=R_{t+1}+\gamma max_aQ(S_{t+1},a)-Q_\theta(S_t,A_t)Q-Loss=Q-targetQθ(St,At)=Rt+1+γmaxaQ(St+1,a)Qθ(St,At)作为loss,利用梯度去更新神经网络

传统Q学习,做一步,学习一次,然后就丢弃这部分数据。为了提高对经验的利用率,deep-Q学习使用了“经验回放”来利用以前的数据:每次得到的 (st,at,rt,st+1)(s_t,a_t,r_t,s_{t+1})st,at,rt,st+1(St,a,r,St+1)会被存入经验池(容量为N)。学习时,从经验池中随机抽取一些数据来更新网络。

然而,由于Q-target和当前Q值都是用同一个神经网络计算出的,所以一旦更新这个网络,那么target也在变化,这导致训练震荡(永远跟不上target的变化)。所以,我们将计算Q-target所用的参数θ固定起来记为θ−\theta^-θ,每经过C步才会更新它为θ\thetaθ

Double DQN: 由于一开始Q值都是0,所以我们不知道下一状态的最优动作是什么,选取最大Q值动作会带来很大噪声,例如,错误的给一个非最优动作一直分配很大Q值,进入恶性循环。因此我们用两个不同网络,将action选取与Q值计算解耦:用 DQN network 来选择 下一状态的动作,即maxaQ(St+1,a)max_aQ(S_{t+1},a)maxaQ(St+1,a)中的a,但是用target network来计算下一状态的动作的Q值。

基于策略的强化学习:学习的是策略 ,不需要学习value function(和policy融合了)

策略梯度法

在这里插入图片描述

策略具有参数 θ,给定state,每个action有不同概率被选取

相比于基于value function的方法,策略梯度法具有以下优点:

  1. 策略具有随机性,而不是贪婪策略,避免被卡在循环里,不需要人为平衡 探索-利用
  2. 连续动作空间下,传统value function下只能给出Q值,我们不知道如何选取最优动作 。而策略梯度法的策略,可以给出整个动作空间的概率分布。
  3. 传统Q学习使用了max,可能会导致Q函数更新后,max的action立刻变为另一个,导致不稳定;而策略梯度法变化更平滑

缺点:

  1. 训练更慢
  2. 经常陷入局部最优
  3. 高方差,即同个环境下,训出来的策略也很不一样

通常,我们让agent在一个episode中不断与环境交互直至结束,如果最后赢了,那么所有动作都会被视为好

强化学习是基于反馈假设的:所有目标都可以被描述为将期望累积反馈最大化。由此,我们优化是目标是使整条动作轨迹τ的总的反馈最大,因此目标函数如下:
[图片]

[图片]

可以解释为,最大化-所有不同轨迹求和-每个轨迹的总反馈*策略θ下这个轨迹发生的概率

轨迹在策略π下发生的概率是:每个t时刻,采取ata_tat的概率 乘以 采取ata_tat后转移到 st+1s_{t+1}st+1 的概率
[图片]

为了优化这个函数,我们需要计算 J 对于 θ 的导数,从而使用梯度法逐步优化 θ:
[图片]

计算导数推导过程如下,主要使用了 log 求导技巧,即 ∇xlogf(x)=∇xf(x)f(x)\nabla_xlogf(x)=\frac{\nabla_xf(x)}{f(x)}xlogf(x)=f(x)xf(x)
于是我们可以化简导数:
∇θJ(θ)=∑τ∇θP(τ;θ)R(τ)=∑τP(τ;θ)P(τ;θ)∇θP(τ;θ)R(τ)=∑τP(τ;θ)∇θlogP(τ;θ)R(τ)\nabla_\theta J(\theta)=\sum_\tau\nabla_\theta P(\tau;\theta)R(\tau)=\sum_\tau \frac{P(\tau;\theta)}{P(\tau;\theta)}\nabla_\theta P(\tau;\theta)R(\tau) \\ =\sum_\tau P(\tau;\theta) \nabla_\theta log P(\tau;\theta)R(\tau)θJ(θ)=τθP(τ;θ)R(τ)=τP(τ;θ)P(τ;θ)θP(τ;θ)R(τ)=τP(τ;θ)θlogP(τ;θ)R(τ)
因为 ∇θlogP(τ(i);θ)=∇θlogμ(s0)+∇θ∑t=0HlogP(st+1(i)∣st(i)at(i))+∇θ∑t=0Hlogπθ(at(i)∣st(i))=∇θ∑t=0Hlogπθ(at(i)∣st(i))\nabla_\theta logP(\tau^{(i)};\theta)=\nabla_\theta log\mu(s_0)+\nabla_\theta\sum_{t=0}^HlogP(s_{t+1}^{(i)}|s_t^{(i)}a_t^{(i)})+\nabla_\theta\sum_{t=0}^Hlog\pi_\theta(a_t^{(i)}|s_t^{(i)})=\nabla_\theta\sum_{t=0}^Hlog\pi_\theta(a_t^{(i)}|s_t^{(i)}) θlogP(τ(i);θ)=θlo(s0)+θt=0HlogP(st+1(i)st(i)at(i))+θt=0Hlogπθ(at(i)st(i))=θt=0Hlogπθ(at(i)st(i))
从而导数化简为
∇θJ(θ)=∑τP(τ;θ)∑t=0H∇θlogπθ(at(i)∣st(i))R(τ)\nabla_\theta J(\theta) =\sum_\tau P(\tau;\theta) \sum_{t=0}^H\nabla_\theta log\pi_\theta(a_t^{(i)}|s_t^{(i)})R(\tau)θJ(θ)=τP(τ;θ)t=0Hθlogπθ(at(i)st(i))R(τ)

现实中,我们不可能遍历所有情况,所以我们通过m次采样获得m条轨迹,从而近似此导数为:
∇θJ(θ)≈1n∑i=1n(R(τ(i))∑t=0T∇θlog⁡πθ(at(i)∣st(i)))\nabla_\theta J(\theta) \approx\frac{1}{n}\sum_{i=1}^{n}(R(\tau^{(i)}) \sum_{t=0}^T\nabla_{\theta}\log\pi_{\theta}(a_{t^{(i)}}\mid s_{t^{(i)}}))θJ(θ)n1i=1n(R(τ(i))t=0Tθlogπθ(at(i)st(i)))
R(τi)R(\tau^i)R(τi)表示从0到T时刻这条轨迹累积的反馈,n表示样本个数(轨迹条数)
利用此公式,即可优化θ

Actor-critic

策略梯度法的方差大,因为我们需要使用蒙特卡洛法进行采样,即使初始状态一样,后续产生的采样轨迹也很不同,从而带来方差。如果想要方差小,那就要非常多的样本数量,但这样又导致训练缓慢。但好处是,这种方法直接使用真实反馈(而不是估计的反馈)来计算总反馈,因此是无偏见的。
Actor-critic结合了value-based和policy-based方法,使用了两个模块:

actor (θ):控制了agent的行动,使用policy-based

critic (ω):评价动作的好坏,使用value-based

Critic 相当于一个从环境中学习到的 value function,可以给出动作的Q值。这样更稳定,每一步都有Q值,而策略梯度法很多时候只有最后的状态才有反馈。

工作流程是:

  1. 从s_t开始,actor选择动作a_t,与环境交互,获得 st+1,rt+1s_{t+1}, r_{t+1}st+1,rt+1
  2. Critic 评价动作,获得 Q值 qw(st,at)q_w(s_t,a_t)qw(st,at)
  3. actor使用Q值更新自身参数 Δθ=α∇θ(logπθ(s,a))q^w(s,a)\Delta\theta=\alpha\nabla_\theta(log\pi_\theta(s,a))\hat{q}_w(s,a)Δθ=αθ(logπθ(s,a))q^w(s,a) ,然后再使用更新的参数选择动作a_{t+1}
  4. Critic 使用 TD error 来更新自身参数(类似于DQN)

为了进一步稳定训练,我们引入了优势函数 A(s,a)=Q(s,a)−V(s)A(s,a)=Q(s,a)-V(s)A(s,a)=Q(s,a)V(s) 其代表了这个动作的相对优势,即采取这个动作后,相比于平均反馈,我们能额外获得多少反馈。用优势函数去代替actor更新公式里的Q值,可以使反馈有正有负,使训练更稳定。
但是这样子需要有2个critic分别估计Q和V,很麻烦,因此我们只保留估计V的critic网络(也可以视为是优势函数的估计网络),用V去估计Q:Q(st,a)=r+γV(st+1)Q(s_t,a)=r+\gamma V(s_{t+1})Q(st,a)=r+γV(st+1),从而actor更新公式变为:
Δθ=α∇θ(logπθ(s,a))A(s,a)=α∇θ(logπθ(s,a))(r+γV(st+1)−V(st)\Delta\theta=\alpha\nabla_\theta(log\pi_\theta(s,a))A(s,a)=\alpha\nabla_\theta(log\pi_\theta(s,a))(r+\gamma V(s_{t+1})-V(s_t)Δθ=αθ(logπθ(s,a))A(s,a)=αθ(logπθ(s,a))(r+γV(st+1)V(st)

PPO

传统策略梯度法容易导致参数更新太大或太小,因此TRPO等方法引入了KL散度惩罚项,防止更新太大。
PPO-penalty 和TRPO类似,也使用了KL散度惩罚项,但其惩罚力度是动态的,如果上一步更新过大,则加大惩罚系数,反之减小。
现在常用的PPO是PPO-clip,其在目标函数里面直接对更新的幅度进行限制。

描述更新策略相对于原策略的预期优势称为替代优势(surrogate advantage)
A 称为优势函数,衡量了一个动作的相对优势,A大于0,代表这个动作相对于其它动作有优势。
J(πθ+Δθ)−J(πθ)≈Es∼ρπθπθ+Δθ(a∣s)πθ(a∣s)Aπθ(s,a)=Lπθ(πθ+Δθ)J(\pi_{\theta+\Delta\theta})-J(\pi_\theta)\approx\mathbb{E}_{s\sim\rho_{\pi\theta}}\frac{\pi_{\theta+\Delta\theta}(a\mid s)}{\pi_\theta(a\mid s)}A^{\pi_\theta}(s,a)=\mathcal{L}_{\pi_\theta}(\pi_{\theta+\Delta\theta})J(πθ+Δθ)J(πθ)Esρπθπθ(as)πθ+Δθ(as)Aπθ(s,a)=Lπθ(πθ+Δθ)
为了直接对更新幅度进行限制,PPO将原本的目标函数里的 $log \pi_\theta(a_t|s_t) $替换为相对选取这个action的概率,所以PPO 的目标函数如下:
LCLIP(θ)=Et[min⁡(rt(θ)At,clip⁡(rt(θ),1−ϵ,1+ϵ)At)]L^{CLIP}(\theta)=\mathbb{E}_t\left[\min\left(r_t(\theta)A_t,\operatorname{clip}(r_t(\theta),1-\epsilon,1+\epsilon)A_t\right)\right]LCLIP(θ)=Et[min(rt(θ)At,clip(rt(θ),1ϵ,1+ϵ)At)]
其中:
$ r_t(\theta)是概率比率,是概率比率,是概率比率,r_t(\theta)=\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_\mathrm{old}}(a_t|s_t)}$它表示新策略和旧策略在同一状态下选择动作的概率比值。注意 θold\theta_{old}θold是永远固定的,因此是定值,对θ导数为0。
剪辑操作clip⁡(rt(θ),1−ϵ,1+ϵ):将rt(θ)限制在区间[1−ϵ,1+ϵ]内,防止策略变化过大。\operatorname{clip}(r_t(\theta),1-\epsilon,1+\epsilon)_\text{:将}r_t(\theta)_\text{限制在区间}[1-\epsilon,1+\epsilon]_\text{内,防止策略变化过大。}clip(rt(θ),1ϵ,1+ϵ):rt(θ)限制在区间[1ϵ,1+ϵ]内,防止策略变化过大。

ϵ\epsilonϵ通常选0.2

在优势函数分别为正和负时,clip的功能不一样:

  1. 优势函数为正时,此时如果我们的策略选取a的概率本来就已经很高(r超过了 1+ϵ1+\epsilon1+ϵ),我们不想太贪婪地继续增加其概率,会将其限制在不超过 1+ϵ1+\epsilon1+ϵ,此时目标函数对θ\thetaθ的导数为0,即不再继续更新网络增加a的概率
  2. 优势函数为负时,此时如果我们的策略选取a的概率本来就已经很低(r低于 1−ϵ1-\epsilon1ϵ),我们不想太贪婪地继续减小其概率,会将其限制在不低于 1−ϵ1-\epsilon1ϵ,此时目标函数对θ\thetaθ的导数也为0
  3. 其余情况,相当于未被clip,正常求导,和actor-critic一样
    如果只有clip项,会导致在需要快速更新参数的情况下,即a概率低但A为正的情况下(或a概率高在A为负)导数也为0,导致收敛太慢或无法收敛。

PPO实际训练llm时的操作

  1. 实际在训练llm时,reward model 会输入整个序列,然后给每个token输出一个分数,作为 rtr_trt。value model 同样也是输入整个序列,给每个token输出一个分数,作为 V(st)V(s_t)V(st),再使用 At=rt+1+γV(st+1)−V(st)A_t=r_{t+1}+\gamma V(s_{t+1})-V(s_t)At=rt+1+γV(st+1)V(st)计算出优势函数
  2. 实际训练时,将目标函数加一个负号,就变成损失函数。
  3. πθ(at∣st)\pi_\theta(a_t|s_t)πθ(atst)即每个token的概率时,得到 logits 后,会使用 log_softmax(比softmax更快)得到 log_prob,然后计算rt(θ)r_t(\theta)rt(θ)时,再加一个exp,即rt(θ)=πθ(at∣st)πθold(at∣st)=exp(logπθ(at∣st)−logπθold(at∣st))r_t(\theta)=\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_\mathrm{old}}(a_t|s_t)}=exp(log \pi_\theta(a_t|s_t) - log \pi_{\theta_\mathrm{old}}(a_t|s_t))rt(θ)=πθold(atst)πθ(atst)=exp(logπθ(atst)logπθold(atst)),大幅加快计算

GRPO

为了让训练更快、更稳,grpo直接不使用神经网络来估计优势函数 A,而是直接使用反馈来计算。

对于一个prompt,模型先多次采样,生成多个completion,组成一个group,然后,每个completion的优势函数由 它的反馈 相对于 这个group里的平均反馈,计算得到,即 Ai,t=ri−mean(r)std(r)A_{i,t}=\frac{r_i-mean(\textbf{r})}{std(\textbf{r})}Ai,t=std(r)rimean(r)
其目标函数可以写为

一些后续改进:

  1. 除以 std(r)std(\textbf{r})std(r)可能带来bias,例如,问题太简单,导致方差小,A就会过大。因此现在通常不除以方差
  2. KL散度惩罚项对训练影响不大,因此现在的目标函数里通常不需要KL散度惩罚
  3. DAPO:认为grpo的目标函数里对每个样本除以其completion的长度,这导致对于很长的completion的惩罚过小(对每个token的loss都更小),因此不除以completion的长度
  4. DR-GRPO:认为DAPO依然受到response的长度的影响,因此直接除以常数
    实际训练时的操作:
    通常,我们不会对每个token计算一个单独的reward,而是对整个completion计算reward,例如,如果做对了数学题,则reward=1。因此,对于completion中的每个token,其优势函数都是一样的,即Ai,t=AiA_{i,t}=A_iAi,t=Ai

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

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

相关文章

MySQL索引底层原理与性能优化实践

#技术栈深潜计划一、前言 在日常开发中,MySQL数据库以其高效、易用、可扩展等特性成为最主流的关系型数据库之一。而索引作为数据库查询优化的核心工具,被誉为“数据库的加速器”。但你真的了解MySQL索引的底层原理吗?为什么InnoDB默认采用B树…

Ext系列文件系统

1.硬件常见的硬件有磁盘、服务器、机柜、机房机械磁盘但是计算机中唯一的一个机械设备磁盘外设的特点就是外设慢容量大,价格便宜1.1.磁盘的物理结构磁盘的物理图:磁盘的存储图扇区:是磁盘存储数据的基本单位,512字节,块设备磁盘的…

前缀函数——KMP的本质

前缀函数我个人觉得 oiwiki 上的学习顺序是很合理的,学 KMP 之前先了解前缀函数是非常便于理解的。前后缀定义 前缀 prefixprefixprefix 指的是从字符串 SSS 的首位到某个位置 iii 的一个子串,这样的子串写作 prefix(S,i)prefix(S,i)prefix(S,i)。 后缀 …

解决chrome下载crx文件被自动删除,加载未打包的扩展程序时提示“无法安装扩展程序,因为它使用了不受支持的清单版本解决方案”

解决chrome下载crx文件被自动删除 【chrome设置-隐私与安全-安全浏览】,选择 不保护 【chrome设置-下载内容】,勾选 下载前询问每个文件的保存位置 下载crx文件时,选择保存文件夹,将 .crx后缀 改为 .zip后缀,再确定。 …

嵌入式学习day23-shell命令

linux软件编程学习大纲:1.IO操作文件2.多任务编程3.网络编程4.数据库编程5.硬件设备管理学习目标:1.学习接口调用(第一层)2.软件操作流程和思想(第二层)3.软件设计思想和流程架构(第三层&#x…

GPT-5 系列深度详解:第1章-引言(目录)

1 引言2 模型数据与训练3 观察到的安全挑战与评估 3.1 从强制拒绝到安全完成 3.2 禁⽌内容 3.3 拍⻢屁 3.4 越狱 3.5 指令层级 3.6 幻觉 3.7 欺骗 3.7.1 欺骗思维链监控 3.8 图像输入 3.9 健康 3.10 多语言性能 3.1.1公平性与偏见: BBQ评估4 红队测试与外部评估…

NineData 新增支持 AWS ElastiCache 复制链路

2025 年,绝大多数企业已完成业务上云,以获取更高的弹性、可扩展性和成本效益。AWS ElastiCache 作为 AWS 提供的全托管式内存数据库服务,已成为许多企业在云上构建高并发、低延迟应用的理想选择。NineData 数据复制现已全面支持从自建 Redis …

人工智能-python-特征选择-皮尔逊相关系数

以下是关于特征选择中常用方法的表格总结,并且详细阐述了皮尔逊相关系数的原理、计算方法、步骤以及示例。 常用特征选择方法总结方法原理优点缺点使用场景过滤法(Filter Method)基于特征的统计信息(如相关性、方差等)…

LabVIEW多循环架构

​LabVIEW的多循环架构是一种常见的架构,本文Temperature Monitoring.vi 采用 LabVIEW 典型的多循环并行架构,通过功能模块化设计实现温度监测全流程,各循环独立运行又协同工作,构成完整的监测系统。1. 事件处理循环(E…

深入理解Maven BOM

一、什么是Maven BOM? 1.1 BOM的基本概念 Maven BOM(Bill of Materials,材料清单)是一种特殊的POM文件,它主要用于集中管理多个相关依赖的版本。BOM本身不包含任何实际代码,而是作为一个 版本管理的"参…

Mysql分页:高效处理海量数据的核心技术

Mysql分页:高效处理海量数据的核心技术01 引言 在Web应用、移动应用或数据分析场景中,数据库常常需要处理百万甚至千万级的数据记录。一次性加载所有数据不仅效率低下,还会消耗大量网络带宽和内存资源。数据库分页技术正是解决这一挑战的关键…

通过 Docker 运行 Prometheus 入门

Promethues 组件 prometheus serverexporteralertmanager 环境准备 Docker 拉取镜像备用 # https://hub.docker.com/r/prom/prometheus docker pull m.daocloud.io/docker.io/prom/prometheus:main# https://hub.docker.com/r/prom/node-exporter docker pull m.daocloud.io/do…

Java 8特性(一)

目录 一、Lambda表达式 1、语法格式: (1)接口名 对象名(参数类型1参数名1,....参数类型n 参数名n)->{方法体;} (2)参数类型h 参数名n:接口中抽象方法的参数项 (3)->:表示连接操作 &a…

【代码随想录|232.用栈实现队列、225.用队列实现栈、20.有效的括号、1047.删除字符串中的所有相邻重复项】

232.用栈实现队列 timutimtit232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; class MyQueue { public:stack<int> Sin;stack<int> Sout;MyQueue() {}void push(int x) {Sin.push(x);}int pop() {if (Sout.empty()) { // 出栈为空就把入栈的数导出来w…

码上爬第三题【协程+浏览器调试检测】

前言&#xff1a;图灵第三题就是对用户浏览器调试检测&#xff0c;检测鼠标右击打开控制台&#xff0c;检测键盘按键ctrlshifti&#xff0c;从浏览器设置打开开发者工具也不行&#xff0c;应该是有浏览器宽高检测的&#xff0c;所以我们保证浏览器页面宽高不变即可。你如果想右…

windows、linux应急响应入侵排查

windows入侵排查 1.1检查账号 1.查看服务器是否有弱口令&#xff0c;远程管理端口是否对公网开放 2.查看服务器是否存在可疑账号、新增账号 检查方法&#xff1a;打开 cmd 窗口&#xff0c;输入 lusrmgr.msc 命令&#xff0c;查看是否有新增/可疑的账号&#xff0c;如有管…

11. 为什么要用static关键字

11. 为什么要用static关键字 static&#xff1a;通常来说&#xff1a;在new一个对象的时候&#xff0c;数据存储空间才会被分配&#xff0c;方法才能被外界使用。但是有时只想单独分配一个存储空间&#xff0c;不考虑需要创建对象或不创建对象&#xff0c;在没有对象的情况下也…

[Oracle] MAX()和MIN()函数

MAX() 和 MIN() 是 Oracle 常用的聚合函数&#xff0c;用于从一组值中找出最大值和最小值1.MAX()函数MAX()函数返回指定列或表达式中的最大值语法格式MAX(expression)参数说明expression&#xff1a;可以是列名、计算列或表达式示例-- 返回employees表中salary列的最大值 SELEC…

网络资源模板--基于Android Studio 实现的麻雀笔记App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 添加页面 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载也可以)&…

96-基于Flask的酷狗音乐数据可视化分析系统

基于Flask的酷狗音乐数据可视化分析系统 &#x1f4cb; 目录 项目概述技术栈系统架构功能特性数据库设计核心代码实现数据可视化部署指南项目总结 &#x1f3af; 项目概述 本项目是一个基于Flask框架开发的酷狗音乐数据可视化分析系统&#xff0c;旨在为用户提供音乐数据的…