深度强化学习Task2:策略梯度算法

本篇博客是本人参加Datawhale组队学习第二次任务的笔记
【教程地址】

文章目录

  • 基于价值算法和基于策略算法的比较
  • 策略梯度算法
    • 策略梯度算法的直观理解
    • 策略梯度算法
    • REINFORCE算法
    • 基于平稳分布的策略梯度算法
    • REINFORCE算法实现
      • 策略函数设计
      • 模型设计
      • 更新函数设计
  • 练习
  • 总结

基于价值算法和基于策略算法的比较

  • 基于价值的算法是通过学习价值函数来指导策略的,而基于策略的算法则是对策略进行优化,并且通过计算轨迹的价值期望来指导策略的更新

  • 基于价值算法的缺点:

    • 无法表示连续动作。由于 DQN \text{DQN} DQN 等算法是通过学习状态和动作的价值函数来间接指导策略的,因此它们只能处理离散动作空间的问题,无法表示连续动作空间的问题。而在一些问题中,比如机器人的运动控制问题,连续动作空间是非常常见的,比如要控制机器人的运动速度、角度等等,这些都是连续的量。

    • 高方差。基于价值的方法通常都是通过采样的方式来估计价值函数,这样会导致估计的方差很高,从而影响算法的收敛性。尽管一些 DQN \text{DQN} DQN 改进算法,通过改善经验回放、目标网络等方式,可以在一定程度上减小方差,但是这些方法并不能完全解决这个问题。

    • 探索与利用的平衡问题 DQN \text{DQN} DQN 等算法在实现时通常选择贪心的确定性策略,而很多问题的最优策略是随机策略,即需要以不同的概率选择不同的动作。虽然可以通过 ϵ -greedy \epsilon\text{-greedy} ϵ-greedy 策略等方式来实现一定程度的随机策略,但是实际上这种方式并不是很理想,因为它并不能很好地平衡探索与利用的关系。

    探索(Exploration):这一方面是关于尝试新的可能性。在算法层面,这意味着偶尔选择不是当前看起来最优的行为,以获得更多信息。
    利用(Exploitation):这是基于目前已知信息去做最优决策的过程,即“赚取”阶段。
    这两者之间需要权衡,因为过多的探索可能导致错过即时的奖励,而过多的利用则可能陷入局部最优而错过更好的选择。

  • 相比于 DQN \text{DQN} DQN 之类的基于价值的算法,策略梯度算法的优缺点:

    • 优点:

      • 适配连续动作空间。将策略描述成一个带有参数 θ \theta θ的连续函数,该函数将某个状态作为输入,输出的不再是某个确定性( deterministic \text{deterministic} deterministic )的离散动作,而是对应的动作概率分布,通常用 π θ ( a ∣ s ) \pi_{\theta}(a|s) πθ(as) 表示,称作随机性( stochastic \text{stochastic} stochastic )策略。
      • 适配随机策略。由于策略梯度算法是基于策略函数的,因此可以适配随机策略,而基于价值的算法则需要一个确定的策略。此外其计算出来的策略梯度是无偏的,而基于价值的算法则是有偏的。
    • 缺点:

      • 采样效率低。由于使用的是蒙特卡洛估计,与基于价值算法的时序差分估计相比其采样速度必然是要慢很多的,这个问题在前面相关章节中也提到过。
      • 高方差。虽然跟基于价值的算法一样都会导致高方差,但是策略梯度算法通常是在估计梯度时蒙特卡洛采样引起的高方差,这样的方差甚至比基于价值的算法还要高。
      • 收敛性差。容易陷入局部最优,策略梯度方法并不保证全局最优解,因为它们可能会陷入局部最优点。策略空间可能非常复杂,存在多个局部最优点,因此算法可能会在局部最优点附近停滞。
      • 难以处理高维离散动作空间:对于离散动作空间,采样的效率可能会受到限制,因为对每个动作的采样都需要计算一次策略。当动作空间非常大时,这可能会导致计算成本的急剧增加。

策略梯度算法

策略梯度算法的直观理解

在这里插入图片描述

  • 基于价值的算法相当于是在学一个地图导航工具,它会告诉并指导驾驶员从当前位置到目的地的最佳路径。但是这样会出现一个问题,就是当地图导航工具在学习过程中产生偏差时会容易一步错步步错,也就是估计价值的方差会很高,从而影响算法的收敛性。

  • 而基于策略的算法则是直接训练驾驶员自身,并且同时也在学地图导航,只是这个时候地图导航只会告诉驾驶员当前驾驶的方向是不是对的,而不会直接让驾驶员去做什么。换句话说,这个过程,驾驶员和地图导航工具的训练是相互独立的,地图导航工具并不会干涉驾驶员的决策,只是会给出建议。这样的好处就是驾驶员可以结合经验自己去探索,当导航工具出现偏差的时候也可以及时纠正,反过来当驾驶员决策错误的时候,导航工具也可以及时矫正错误。

策略梯度算法

以下内容仅仅考虑算法流程的完整性,证明过程请参考原书第九章:策略梯度。
我们对生成每条轨迹的概率和相应的奖励的乘积进行求和,可以得到价值期望,我们的目标就是最大化策略的价值期望

在这里插入图片描述
由于我们的目标是最大化目标函数,因此我们使用梯度上升的方法,之后进行一系列计算得到了梯度:
在这里插入图片描述

REINFORCE算法

由于采样的轨迹是无限多的,难以进行计算,因此采用蒙特卡洛的方法近似求解,采样一部分且数量足够多的轨迹,然后利用这些轨迹的平均值来近似求解目标函数的梯度。这种方法就是蒙特卡洛策略梯度算法,也称作
REINFORCE \text{REINFORCE} REINFORCE算法

在这里插入图片描述

其中 N N N 理论上越大越好,但实际上我们可能只采样几个回合的轨迹就能近似求解梯度了。此外,注意这里我们把奖励函数换成了带有折扣因子的回报 G t n = ∑ k = t T n γ k − t r k n G_{t}^{n}=\sum_{k=t}^{T_{n}} \gamma^{k-t} r_{k}^{n} Gtn=k=tTnγktrkn,其中 T n T_n Tn 表示第 n n n 条轨迹的长度, γ \gamma γ 表示折扣因子, r k n r_{k}^{n} rkn 表示第 n n n 条轨迹在第 k k k 步的奖励。尽管回报计算起来麻烦,但可以结合前面章节讲到的贝尔曼公式将当前时刻和下一时刻的回报联系起来,从而在一定程度上简化计算。

G t = ∑ k = t + 1 T γ k − t − 1 r k = r t + 1 + γ G t + 1 \begin{aligned} G_{t} &=\sum_{k=t+1}^{T} \gamma^{k-t-1} r_{k} \\ &=r_{t+1}+\gamma G_{t+1} \end{aligned} Gt=k=t+1Tγkt1rk=rt+1+γGt+1

REINFORCE \text{REINFORCE} REINFORCE算法的缺点:

  • 最后推导出来的公式实际上不可求,因为理论上有无限条轨迹,所以只能用近似的方法。
  • 我们假定了目标是使得每回合的累积价值最大,每回合的累积奖励或回报会受到很多因素的影响,比如回合的长度、奖励的稀疏性等等,泛化性不够。

基于平稳分布的策略梯度算法

基于平稳分布的策略梯度算法克服了 REINFORCE \text{REINFORCE} REINFORCE算法的缺点,在正式介绍它之前先介绍一下平稳的马尔可夫过程。
对于任意马尔可夫链,如果满足以下两个条件:

  • 非周期性:由于马尔可夫链需要收敛,那么就一定不能是周期性的,实际上我们处理的问题基本上都是非周期性的,这点不需要做过多的考虑。
  • 状态连通性:即存在概率转移矩阵 P P P,能够使得任意状态 s 0 s_0 s0经过有限次转移到达状态 s s s,反之亦然。

\qquad 这样我们就可以得出结论,即该马氏链一定存在一个平稳分布,我们用 d π ( s ) d^{\pi}(s) dπ(s)表示。

d π ( s ) = lim ⁡ t → ∞ P ( s t = s ∣ s 0 , π θ ) d^\pi(s)=\lim _{t \rightarrow \infty} P\left(s_t=s \mid s_0, \pi_\theta\right) dπ(s)=tlimP(st=ss0,πθ)
一言以蔽之就是无论初始状态是什么,经过多次概率转移之后都会存在一个稳定的状态分布。
在这里插入图片描述

REINFORCE算法实现

最基础的策略梯度算法就是REINFORCE算法,又称作Monte-Carlo Policy Gradient算法。我们策略优化的目标如下:

J θ = Ψ π ∇ θ log ⁡ π θ ( a t ∣ s t ) J_{\theta}= \Psi_{\pi} \nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right) Jθ=Ψπθlogπθ(atst)

其中 Ψ π \Psi_{\pi} Ψπ在REINFORCE算法中表示衰减的回报(具体公式见伪代码),也可以用优势来估计,也就是我们熟知的A3C算法,这个在后面包括GAE算法中都会讲到。

策略函数设计

既然策略梯度是直接对策略函数进行梯度计算,有两种设计方式:
softmax函数:用于离散动作空间
高斯分布 N ( ϕ ( s ) π θ , σ 2 ) \mathbb{N}\left(\phi(\mathbb{s})^{\mathbb{\pi}} \theta, \sigma^2\right) N(ϕ(s)πθ,σ2):多用于连续动作空间

softmax函数可以表示为:
π θ ( s , a ) = e ϕ ( s , a ) T θ ∑ b e ϕ ( s , b ) T T \pi_\theta(s, a)=\frac{e^{\phi(s, a)^{T_\theta}}}{\sum_b e^{\phi(s, b)^{T^T}}} πθ(s,a)=beϕ(s,b)TTeϕ(s,a)Tθ
对应的梯度为:
∇ θ log ⁡ π θ ( s , a ) = ϕ ( s , a ) − E π θ [ ϕ ( s , ) \nabla_\theta \log \pi_\theta(s, a)=\phi(s, a)-\mathbb{E}_{\pi_\theta}[\phi(s,) θlogπθ(s,a)=ϕ(s,a)Eπθ[ϕ(s,)
高斯分布对应的梯度为:
∇ θ log ⁡ π θ ( s , a ) = ( a − ϕ ( s ) T θ ) ϕ ( s ) σ 2 \nabla_\theta \log \pi_\theta(s, a)=\frac{\left(a-\phi(s)^T \theta\right) \phi(s)}{\sigma^2} θlogπθ(s,a)=σ2(aϕ(s)Tθ)ϕ(s)
但是对于一些特殊的情况,例如在本次演示中动作维度=2且为离散空间,这个时候可以用伯努利分布来实现,这种方式其实是不推荐的,这里给大家做演示也是为了展现一些特殊情况,启发大家一些思考,例如Bernoulli,Binomial,Gaussian分布之间的关系。简单说来,Binomial分布, n = 1 n = 1 n=1时就是Bernoulli分布, n → ∞ n \rightarrow \infty n时就是Gaussian分布。

模型设计

前面讲到,尽管本次演示是离散空间,但是由于动作维度等于2,此时就可以用特殊的高斯分布来表示策略函数,即伯努利分布。伯努利的分布实际上是用一个概率作为输入,然后从中采样动作,伯努利采样出来的动作只可能是0或1,就像投掷出硬币的正反面。在这种情况下,我们的策略模型就需要在MLP的基础上,将状态作为输入,将动作作为倒数第二层输出,并在最后一层增加激活函数来输出对应动作的概率。不清楚激活函数作用的同学可以再看一遍深度学习相关的知识,简单来说其作用就是增加神经网络的非线性。既然需要输出对应动作的概率,那么输出的值需要处于0-1之间,此时sigmoid函数刚好满足我们的需求,实现代码参考如下。

import torch
import torch.nn as nn
import torch.nn.functional as F
class PGNet(nn.Module):
    def __init__(self, input_dim,output_dim,hidden_dim=128):
        """ 初始化q网络,为全连接网络
            input_dim: 输入的特征数即环境的状态维度
            output_dim: 输出的动作维度
        """
        super(PGNet, self).__init__()
        self.fc1 = nn.Linear(input_dim, hidden_dim) # 输入层
        self.fc2 = nn.Linear(hidden_dim,hidden_dim) # 隐藏层
        self.fc3 = nn.Linear(hidden_dim, output_dim) # 输出层
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = torch.sigmoid(self.fc3(x))
        return x

更新函数设计

前面提到我们的优化目标也就是策略梯度算法的损失函数如下:
J θ = Ψ π ∇ θ log ⁡ π θ ( a t ∣ s t ) J_{\theta}= \Psi_{\pi} \nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right) Jθ=Ψπθlogπθ(atst)

我们需要拆开成两个部分 Ψ π \Psi_{\pi} Ψπ ∇ θ log ⁡ π θ ( a t ∣ s t ) \nabla_\theta \log \pi_\theta\left(a_t \mid s_t\right) θlogπθ(atst)分开计算,首先看值函数部分 Ψ π \Psi_{\pi} Ψπ,在REINFORCE算法中值函数是从当前时刻开始的衰减回报,如下:
G ← ∑ k = t + 1 T γ k − 1 r k G \leftarrow \sum_{k=t+1}^{T} \gamma^{k-1} r_{k} Gk=t+1Tγk1rk

这个实际用代码来实现的时候可能有点绕,我们可以倒过来看,在同一回合下,我们的终止时刻是 T T T,那么对应的回报 G T = γ T − 1 r T G_T=\gamma^{T-1}r_T GT=γT1rT,而对应的 G T − 1 = γ T − 2 r T − 1 + γ T − 1 r T G_{T-1}=\gamma^{T-2}r_{T-1}+\gamma^{T-1}r_T GT1=γT2rT1+γT1rT,在这里代码中我们使用了一个动态规划的技巧,如下:

running_add = running_add * self.gamma + reward_pool[i] # running_add初始值为0

这个公式也是倒过来循环的,第一次的值等于:
r u n n i n g _ a d d = r T running\_add = r_T running_add=rT
第二次的值则等于:
r u n n i n g _ a d d = r T ∗ γ + r T − 1 running\_add = r_T*\gamma+r_{T-1} running_add=rTγ+rT1
第三次的值等于:
r u n n i n g _ a d d = ( r T ∗ γ + r T − 1 ) ∗ γ + r T − 2 = r T ∗ γ 2 + r T − 1 ∗ γ + r T − 2 running\_add = (r_T*\gamma+r_{T-1})*\gamma+r_{T-2} = r_T*\gamma^2+r_{T-1}*\gamma+r_{T-2} running_add=(rTγ+rT1)γ+rT2=rTγ2+rT1γ+rT2

import torch
from torch.distributions import Bernoulli
from torch.autograd import Variable
import numpy as np

class PolicyGradient:
    
    def __init__(self, model,memory,cfg):
        self.gamma = cfg['gamma']
        self.device = torch.device(cfg['device']) 
        self.memory = memory
        self.policy_net = model.to(self.device)
        self.optimizer = torch.optim.RMSprop(self.policy_net.parameters(), lr=cfg['lr'])

    def sample_action(self,state):

        state = torch.from_numpy(state).float()
        state = Variable(state)
        probs = self.policy_net(state)
        m = Bernoulli(probs) # 伯努利分布
        action = m.sample()
        
        action = action.data.numpy().astype(int)[0] # 转为标量
        return action
    def predict_action(self,state):

        state = torch.from_numpy(state).float()
        state = Variable(state)
        probs = self.policy_net(state)
        m = Bernoulli(probs) # 伯努利分布
        action = m.sample()
        action = action.data.numpy().astype(int)[0] # 转为标量
        return action
        
    def update(self):
        state_pool,action_pool,reward_pool= self.memory.sample()
        state_pool,action_pool,reward_pool = list(state_pool),list(action_pool),list(reward_pool)
        # Discount reward
        running_add = 0
        for i in reversed(range(len(reward_pool))):
            if reward_pool[i] == 0:
                running_add = 0
            else:
                running_add = running_add * self.gamma + reward_pool[i]
                reward_pool[i] = running_add

        # Normalize reward
        reward_mean = np.mean(reward_pool)
        reward_std = np.std(reward_pool)
        for i in range(len(reward_pool)):
            reward_pool[i] = (reward_pool[i] - reward_mean) / reward_std

        # Gradient Desent
        self.optimizer.zero_grad()

        for i in range(len(reward_pool)):
            state = state_pool[i]
            action = Variable(torch.FloatTensor([action_pool[i]]))
            reward = reward_pool[i]
            state = Variable(torch.from_numpy(state).float())
            probs = self.policy_net(state)
            m = Bernoulli(probs)
            loss = -m.log_prob(action) * reward  # Negtive score function x reward
            # print(loss)
            loss.backward()
        self.optimizer.step()
        self.memory.clear()

练习

  1. 基于价值和基于策略的算法各有什么优缺点?
  • 基于价值的算法:
    • 优点有:简单易用:通常只需要学习一个值函数,往往收敛性也会更好。保守更新:更新策略通常是隐式的,通过更新价值函数来间接地改变策略,这使得学习可能更加稳定。
    • 缺点有:受限于离散动作可能存在多个等价最优策略:当存在多个等效的最优策略时,基于价值的方法可能会在它们之间不停地切换。
  • 基于策略的算法:
    • 后者的优点有:直接优化策略:由于这些算法直接操作在策略上,所以它们可能更容易找到更好的策略;适用于连续动作空间;更高效的探索:通过调整策略的随机性,基于策略的方法可能会有更高效的探索策略。
    • 缺点有:高方差:策略更新可能会带来高方差,这可能导致需要更多的样本来学习。可能会收敛到局部最优:基于策略的方法可能会收敛到策略的局部最优,而不是全局最优,且收敛较缓慢。在实践中,还存在结合了基于价值和基于策略方法的算法,即
      Actor-Critic \text{Actor-Critic} Actor-Critic 算法,试图结合两者的优点来克服各自的缺点。选择哪种方法通常取决于具体的应用和其特点。
  1. 马尔可夫平稳分布需要满足什么条件?

状态连通性:从任何一个状态可以在有限的步数内到达另一个状态;
非周期性:由于马尔可夫链需要收敛,那么就一定不能是周期性的。

  1. REINFORCE \text{REINFORCE} REINFORCE 算法会比 Q-learning \text{Q-learning} Q-learning 算法训练速度更快吗?为什么?

两者的速度不能一概而论,尽管前者往往会比后者慢。主要考虑几个因素:

  • 样本效率:因为 REINFORCE \text{REINFORCE} REINFORCE 算法是一个无偏的估计,但其方差可能很高,这意味着为了得到一个稳定和准确的策略更新,它可能需要与环境交互更多的样本,如果与环境交互的成本很高, REINFORCE \text{REINFORCE} REINFORCE
    算法将会显得更加劣势。
  • 稳定性与收敛 Q-learning \text{Q-learning} Q-learning 和其他基于值的方法,特别是当与深度神经网络结合时,可能会遇到训练不稳定的问题。这可能会影响其训练速度。
  1. 确定性策略与随机性策略的区别?
  • 对于同一个状态,确定性策略会给出一个明确的、固定的动作,随机性策略则会为每一个可能的动作(legal action)提供一个概率分布。
  • 前者在训练中往往需要额外的探索策略,后者则只需要调整动作概率。
  • 但前者相对更容易优化,因为不需要考虑所有可能的动作,但也容易受到噪声的影响。后者则相对更加鲁棒,适用面更广,因为很多的实际问题中,我们往往无法得到一个确定的最优策略,而只能得到一个概率分布,尤其是在博弈场景中。

总结

本文从对比基于价值的算法和基于梯度的算法,分析了它们各自的优缺点,之后用一个直观的例子引入了策略梯度算法,REINFORCE算法用来解决轨迹无限多而无法计算的问题,但是这个算法由于假定了目标是使得每回合的累积价值最大,而每回合的累积奖励或回报会受到很多因素的影响,比如回合的长度、奖励的稀疏性等等,从而泛化性不够。为了解决这个问题,提出了基于平稳分布的策略梯度算法。最后介绍了一下策略函数的实现。

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

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

相关文章

注解实现校验接口传参是否超出取值范围

文章目录 1、定义注解2、使用注解3、其余校验实现思路2.04、其余校验实现思路3.0 写接口,Dto里很多字段要检验传参范围,自定义个注解来校验。 1、定义注解 注解定义代码: import javax.validation.Constraint; import javax.validation.Con…

2023年12月 电子学会 青少年软件编程等级考试Scratch三级真题

202312 青少年软件编程等级考试Scratch三级真题 一、单项题 第 1 题 运行左图程序,想得到右图中的效果,红色框应填写的数值是?( ) A:12 B:11 C:10 D:9 第 2 题 下列…

网站转小程序系统,任意网址打包成小程序

源码介绍 将任意网站打包成小程序,只需简单修改域名,即可轻松实现!这一创新技术让您的网站内容在小程序平台上焕发新生。通过智能转换工具,您可以将任意网站迅速转化为小程序,无需繁琐的编码和开发工作。只需简单修改…

小程序学习-19

Vant Weapp - 轻量、可靠的小程序 UI 组件库 ​​​​​ Vant Weapp - 轻量、可靠的小程序 UI 组件库 安装出现问题:rollbackFailedOptional: verb npm-session 53699a8e64f465b9 解决办法:http://t.csdnimg.cn/rGUbe Vant Weapp - 轻量、可靠的小程序…

微服务不死 — 共享变量在策略引擎项目的落地详解

01 背景 1、共享变量的提出 前段时间,来自亚马逊 Prime Video 团队的一个案例研究在开发者社区中掀起了轩然大波。大体是这样一件事,作为一个流媒体平台,Prime Video每天都会向客户提供成千上万的直播流。为了确保客户无缝接收内容&#xff0…

多人在线聊天交友工具,匿名聊天室网站源码,附带搭建教程

源码介绍 匿名聊天室(nodejs vue) 多人在线聊天交友工具,无需注册即可畅所欲言!你也可以放心讲述自己的故事,说出自己的秘密,因为谁也不知道对方是谁。 运行说明 安装依赖项:npm install 启动…

SpringBoot整合Dubbo和Zookeeper分布式服务框架使用的入门项目实例

文章目录 SpringBoot整合Dubbo和Zookeeper分布式服务框架使用的入门项目实例Dubbo定义其核心部分包含: 工作原理为什么要用dubbo各个节点角色说明:调用关系说明: dubbo为什么需要和zookeeper结合使用,zookeeper在dubbo体系中起到什么作用&…

Chatgpt+Comfyui绘图源码说明及本地部署文档

其他文档地址: ChatgptComfyui绘图源码运营文档 ChatgptComfyui绘图源码线上部署文档 一、源码说明 1、源码目录说明 app_home:app官网源码chatgpt-java:管理后台服务端源码、用户端的服务端源码chatgpt-pc:电脑网页前端源码cha…

论文阅读笔记AI篇 —— Transformer模型理论+实战 (四)

论文阅读笔记AI篇 —— Transformer模型理论实战 (四) 一、理论1.1 理论研读1.2 什么是AI Agent? 二、实战2.1 先导知识2.1.1 tensor的创建与使用2.1.2 PyTorch的模块2.1.2.1 torch.nn.Module类的继承与使用2.1.2.2 torch.nn.Linear类 2.2 Transformer代…

YOLOv5改进 | 主干篇 | 华为GhostnetV1一种移动端的专用特征提取网络

一、本文介绍 本文给大家带来的改进机制是华为移动端模型Ghostnetv1,华为GhostnetV1一种移动端的专用特征提取网络,旨在在计算资源有限的嵌入式设备上实现高性能的图像分类。GhostNet的关键思想在于通过引入Ghost模块,以较低的计算成本增加了特征图的数量,从而提高了模型的…

一、用户管理中心——前端初始化

一、Ant Design Pro初始化 1.创建空文件夹 2.打开Ant Design Pro官网 3.打开终端进行初始化 在终端输入npm i ant-design/pro-cli -g 在终端输入pro create myapp 选择umi3 选择simple 项目创建成功后,在文件夹中出现myapp 4.安装依赖 使用vscode打开项目 …

Java学习笔记(八)——Lambda表达式

文章目录 Lambda表达式Lambda表达式的省略写法Lambda练习练习1练习2 算法题算法题1 斐波那契数列算法题2 猴子吃桃子算法题3 爬楼梯 Lambda表达式 Lambda表达式是JDK8开始的一种新语法形式。 基本作用:简化函数式接口的匿名内部类的写法。 注意: Lam…

lambda

文章目录 lambda 概述lambda的演变过程lambda 表达式的基本格式案例:调用接口里面的方法几种方式 lambda省略写法案例一:抽象方法一个参数抽象方法两个参数 啦么大 使用的注意事项啦么大 与 匿名内部类 lambda 概述 函数式编程思想 面向对象思想在乎的是…

Java 面向对象02 封装 (黑马)

人画圆:画圆这个方法应该定义在园这个类里面。 人关门:是人给了门一个作用力,然后门自己关上了门,所以关门的方法是在门的类里面 封装对象的好处: 调用Java自带的方法举例实现: 在测试类中,对其…

PDshell16逆向PostgreSQL 工程显示字段comment备注

现状:当刚逆向成功的表结构是没有原来表结构中的,comment备注如下 然后pd逆向工程的sql已经返回了这个备注的含义 解决方案: 1、设置显示注释列 tools——Display Preferences…如下 勾选-按照下面得方式勾选这三个 复制这里的VBS脚本&a…

触摸屏监控双速电动机-确定地址分配

I/O地址分配 当选择了PLC之后,首先需要确定的是系统中各I/O点的绝对地址。在某些PLC 中1/O绝对地址的分配方式共有固定地址型、自动分配型、用户定义型3种。实际所使用的方式取决于所采用的PLC的CPU型号、编程软件、软件版本、编程人员的选择等因素。 本任务输入信…

51单片机原理及应用张毅刚版课后习题以及答案

AT89S51单片机内部集成了哪些外围功能部件 ①8位微处理器CPU ②数据存储器 128B RAM ③程序存储器 ④4个8位可编程并行I/O口 ⑤1个全双工的异步串行口 ⑥2个可编程的16位定时器/计数器 ⑦1个看门狗定时器WDT ⑧中断系统具有五个中断源 五个中断向量 ⑨特殊功能寄存器SFR 26个…

低代码技术杂谈

一、探讨低代码的定义 “Low-Code”是什么?身为技术人员听到这种技术名词,咱们第一反应就是翻看维基百科 或者其他相关技术论文,咱们想看维基百科的英文介绍: A low-code development platform (LCDP) provides a development env…

web蓝桥杯真题--11、蓝桥知识网

介绍 蓝桥为了帮助大家学习,开发了一个知识汇总网站,现在想设计一个简单美观的首页。本题请根据要求来完成一个首页布局。 准备 开始答题前,需要先打开本题的项目代码文件夹,目录结构如下: ├── css │ └──…

【JavaEE Spring】SpringBoot 配置文件

SpringBoot 配置文件 1. 配置文件的作用1.1 配置文件的说明1.2 SpringBoot 配置文件 2. 配置文件的格式特殊说明 3. properties 配置文件说明3.1 properties 基本语法3.2 读取配置文件3.3 properties 缺点分析 4. yml 配置文件说明4.1 yml 的基本语法4.2 yml 使⽤进阶4.2.1 yml…
最新文章